I need to make a search based on a set of keywords, that return all the Ads related with those keywords. Then the result is a list of Categories with the Ads Count for each
I am suggesting you to add regex and omit that special characters and then use Linq for that
So Mercedez-Benz can become Mercedez and benz
I have posted my answer to: https://github.com/n074v41l4bl34u/StackOverflow19796132 Feel free to review it.
Here is the most important snippet.
with:
internal class SearchDomain
{
public List<Keyword> Keywords { get; set; }
public List<Category> Categories { get; set; }
public List<KeywordAdCategory> KeywordAdCategories { get; set; }
}
then:
private static char[] keywordPartsSplitter = new char[] { ' ', '-' };
internal static Dictionary<Category, Dictionary<int, List<KeywordAdCategory>>> FromStringInput(string searchPhrase, SearchDomain searchDomain)
{
var identifiedKeywords = searchPhrase
.Split(keywordPartsSplitter);
var knownKeywordParts = identifiedKeywords
.Where
(ik =>
searchDomain
.Keywords
.SelectMany(x => x.GetKeywordParts())
.Any(kp => kp.Equals(ik, StringComparison.InvariantCultureIgnoreCase))
);
var keywordkSearches = knownKeywordParts
.Select((kkp, n) => new KeywordSearch()
{
Id = n,
Name = kkp,
Keyword = searchDomain
.Keywords
.Single
(k =>
k.GetKeywordParts()
.Any(kp => kp.Equals(kkp, StringComparison.InvariantCultureIgnoreCase))
)
});
var relevantKeywords = keywordkSearches
.Select(ks => ks.Keyword)
.Distinct();
var keywordAdCategoriesByCategory = searchDomain.Categories
.GroupJoin
(
searchDomain.KeywordAdCategories,
c => c.Id,
kac => kac.Category_Id,
(c, kac) => new { Category = c, AdKeywordsForCategory = kac }
);
var relevantKeywordAdCategories = keywordAdCategoriesByCategory
.Where
(kacbk =>
relevantKeywords
.All
(rk =>
kacbk
.AdKeywordsForCategory
.Any(kac => kac.Keyword_Id == rk.Id)
)
);
var foundAdsInCategories = relevantKeywordAdCategories
.ToDictionary
(rkac =>
rkac.Category,
rkac => rkac.AdKeywordsForCategory
.GroupBy(g => g.Ad_Id)
.ToDictionary(x => x.Key, x => x.ToList())
);
return foundAdsInCategories;
}
It does exactly what you want however I find something fishy about keywords being divisible to sub-keywords. Than again, maybe it is just the naming.
I haven't compile-checked this or anything, so it may require some tweaking, but you're looking for something along these lines.
var matchingKac = keywordIds.Distinct().ToList()
.Aggregate(
keywordAdCategoryQuery.AsQueryable(),
(q, id) => q.Where(kac => kac.Keyword_Id == id));
You're effectively saying, "Start with keywordAdCategoryQuery
, and for each keyword add a .Where()
condition saying that it must have that keyword in it. You could do the same thing with a for
loop if you find Aggregate
difficult to read.
this may not be the direct answer, but in such "multiple parameter search" situations i just forget about anything and do the simple thing, for ex: Search By Car Manufacturer, CategoryId, MillageMax, Price :
var searchResults = from c in carDb.Cars
where (c.Manufacturer.Contains(Manufacturer) || Manufacturer == null) &&
(c.CategoryId == CategoryId || CategoryId == null) &&
(c.Millage <= MillageMax || MillageMax== null) &&
(c.Price <= Price || Price == null)
select c
now if any of the parameters is null
it cancels the containing line by making the whole expression in brackets True
and so it does not take a part in search any more
I recommend to NOT define keywords to objects that way, because you might search and find too many objects or you'll find possibly nothing. You will always spoil your time when searching. Classify your objects in a way that the users focus is to FIND and not to search.
If you try to make your own search engine you will probably fail.Why don't you try Lucene. Here's a link http://lucenenet.apache.org/. Cheers