Mikey Hogarth
Mikey Hogarth

Reputation: 4712

Cache only parts of an object

I'm trying to achieve a super-fast search, and decided to rely heavily on caching to achieve this. The order of events is as follows;

1) Cache what can be cached (from entire database, around 3000 items)

2) When a search is performed, pull the entire result set out of the cache

3) Filter that result set based on the search criteria. Give each search result a "relevance" score.

4) Send the filtered results down to the database via xml to get the bits that can't be cached (e.g. prices)

5) Display the final results

This is all working and going at lightning speed, but in order to achieve (3) I've given each result a "relevance" score. This is just a member integer on each search result object. I iterate through the entire result set and update this score accordingly, then order-by it at the end.

The problem I am having is that the "relevance" member is retaining this value from search to search. I assume this is because what I am updating is a reference to the search results in the cache, rather than a new object, so updating it also updates the cached version. What I'm looking for is a tidy solution to get around this. What I've come up with so far is either;

a) Clone the cache when i get it.

b) Create a seperate dictionary to store relevances in and match them up at the end

Am I missing a really obvious and clean solution or should i go down one of these routes? I'm using C# and .net.

Hopefully it should be obvious from the description what I'm getting at, here's some code anyway; this first one is the iteration through the cached results in order to do the filtering;

    private List<QuickSearchResult> performFiltering(string keywords, string regions, List<QuickSearchResult> cachedSearchResults)
    {
        List<QuickSearchResult> filteredItems = new List<QuickSearchResult>();

        string upperedKeywords = keywords.ToUpper();
        string[] keywordsArray = upperedKeywords.Split(' ');
        string[] regionsArray = regions.Split(',');

        foreach (var item in cachedSearchResults)
        {

            //Check for keywords
            if (keywordsArray != null)
            {
                if (!item.ContainsKeyword(upperedKeywords, keywordsArray))
                    continue;
            }
            //Check for regions
            if (regionsArray != null)
            {
                if (!item.IsInRegion(regionsArray))
                    continue;
            }

            filteredItems.Add(item);
        }


        return filteredItems.OrderBy(t=> t.Relevance).Take(_maxSearchResults).ToList<QuickSearchResult>();

    }

and here is an example of the "IsInRegion" method of the QuickSearchResult object;

        public bool IsInRegion(string[] regions)
        {
            int relevanceScore = 0;
            foreach (var region in regions)
            {
                int parsedRegion = 0;
                if (int.TryParse(region, out parsedRegion))
                {
                    foreach (var thisItemsRegion in this.Regions)
                    {
                        if (thisItemsRegion.ID == parsedRegion)
                            relevanceScore += 10;
                    }
                }
            }

            Relevance += relevanceScore;

            return relevanceScore > 0;
        }

And basically if i search for "london" i get a score of "10" the first time, "20" the second time...

Upvotes: 1

Views: 159

Answers (1)

Nick Ryan
Nick Ryan

Reputation: 2670

If you use the NetDataContractSerializer to serialize your objects in the cache, you could use a [DataMember] attribute to control what gets serialized and what doesn't. For instance, you could store your temporarary calculated relevance value in a field that is not serialized.

Upvotes: 1

Related Questions