Reputation: 22770
If I have a text field that contains say a title and i have a list of keywords, how can i search the title checking for (n) numbers of keywords in the title?
So if my title is "Baking a chicken, bacon and leek pie" and the user searches for "chicken bacon turnip" i'd like to return the above recipe.
essentially i'd like to say that if the title contains say 2 or more of the search terms then it's deemed as valid and should be returned. but if it only contains 1 then disregard it.
ideally i'd like them weighted so that the more terms that are present the higher in the list it is but that may be version 2. :)
edit
I should mention at this point that i'd like this to be native .net and c#.
Upvotes: 3
Views: 2234
Reputation: 135
AODBDataContext db = new AODBDataContext();
var fItems = from item in db.Items
where SqlMethods.Like(item.Name, l)
where cats.Contains(item.ItemType)
where item.QL >= minQL
where item.QL <= maxQL
select item;
Upvotes: 0
Reputation: 18825
if it were me I would simply do something like this....
Create a helper class that does 2 things, splits the title and returns a score based on the keyword match....
public static class Helper
{
public static int GetScore(string Title, params string[] keywords)
{
// your routine that calcs a score based on the matchs against the Title.
}
}
then you can use a linq statement like....
var matches = from t in GetYourTitles
let score = Helper.GetScore(t, keywordlist)
where score >= 2
orderby score
select t;
Upvotes: 0
Reputation: 156748
Doing a text index like Andras suggests is probably your best bet. But to answer the question: you can write a method to custom-build an expression tree to represent a selector that adds 1 to a property for each search keyword that matches. See below:
var entries = new[] { new Entry{ ID = 1, Title = "Baking a chicken, bacon and leek pie"} }.AsQueryable();
var search = "chicken bacon turnip";
var q = entries.Select(GetSelector(search));
var matches = q.Where(e => e.MatchCount > 1);
public Expression<Func<Entry, EntryMatchCount>> GetSelector(string search)
{
var searchWords = search.Split(new[] {' '});
// Rather than creating the selector explicitly as below, you'll want to
// write code to generate this expression tree.
Expression<Func<Entry, EntryMatchCount>> selector = e =>
new EntryMatchCount
{
ID = e.ID,
MatchCount = (e.Title.Contains(searchWords[0]) ? 1 : 0) +
(e.Title.Contains(searchWords[1]) ? 1 : 0) +
(e.Title.Contains(searchWords[2]) ? 1 : 0)
};
return selector;
}
Upvotes: 1
Reputation: 42363
Okay, I know you said 'do it in Linq'. ASsuming you're talking about taking .Net native string and doing it using Linq to Objects, then I guess the most obvious solution is going to be to break up the text by a regex working on word boundaries; and then to iterate through each result matching against the input phrases.
However...
Judging by your idea for the 'v2' I think you should probably be looking at something more powerful and geared around text searching - so how about using a Lucene.Net text index?
It offers very powerful and very fast full-text search - and has the ability to process boolean rules; aliases, stemming, all that kind of stuff.
It really does rock.
UPDATE - Since you mentioned Linq to Sql in your comments
You can also use SQL Full-Text indexes on your table however, there is one catch: there is no native Linq To Sql translation to the CONTAINSTABLE et al clauses.
So instead you can use dynamic query generation via a string, and then feed that into the DataContext.ExecuteQuery<TResult>
member. If the select returns columns required to construct the entity type you want, it'll work like a charm.
Or, of course, you can just wrap a stored procedure that does it instead ;)
Upvotes: 4