griegs
griegs

Reputation: 22770

Search Keywords using LINQ

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

Answers (4)

RedPanda
RedPanda

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

Tim Jarvis
Tim Jarvis

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

StriplingWarrior
StriplingWarrior

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

Andras Zoltan
Andras Zoltan

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

Related Questions