views:

645

answers:

4

I'm trying to implement a very basic keyword search in an application using linq-to-sql. My search terms are in an array of strings, each array item being one word, and I would like to find the rows that contain the search terms. I don't mind if they contain more than just the search terms (most likely, they will), but they all the search terms do have to be present.

Ideally, I would like something similar to the snippet below, but I know that this won't work. Also, I have looked at this question here, but the author of that question seems content to do things the other way round ( query.Contains(part.partName) ), which doesn't work for me.

public IQueryable<Part> SearchForParts(string[] query)
{
    return from part in db.Parts
           where part.partName.Contains(query)
           select part;
}

How can I rewrite this query so that it will do what I need?

+1  A: 

You could try:

public IQueryable<Part> SearchForParts(string[] query)
{
    return from part in db.Parts
           where query.All(term => part.partName.Contains(term))
           select part;
}

However, I'm not sure if LINQ to SQL will be able to transform it into T-SQL. Another option would be:

public IQueryable<Part> SearchForParts(string[] query)
{
    var result = from part in db.Parts
                 select part;

    foreach(var term in query)
    {
        result = from part in result
                 where part.partName.Contains(term)
                 select part;
    }

    return result;
}

It's not as pretty, but it should work. You'll get a query with a lot of ANDs in the where clause.

Rory
Doing this results in the error _"Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator."_
a_m0d
@casperOne: The expression passed to `All` uses the `Contains` method, not the equals operator, meaning the part name would be tested to see if it **contains** the query term instead of being exactly equal to it. As for using `Any`, the question states that the results must match all of the search terms, not just one of them (which is what any would do).
Rory
@a_m0d: I did say I wasn't sure if it would transform. Try the second suggestion, you should have better results.
Rory
To be honest, I don't really care too much whether they are all there or not, but I feel that the search would be much better if they are all there, and probably easier to implement as well.
a_m0d
Yeah, the second suggestion does work, but I don't really like the idea of loading it all into memory like that and then searching through it. Ideally, I would let the database search for me, but maybe that is just premature optimisation on my part.
a_m0d
@a_m0d: Well if that's the case, you should put it in your question. The way it's currently worded makes it seem like its a requirement. That aside, try running the profiler and checking the query that gets sent to the DB: it shouldn't be loading everything into memory. One of the benefits of deferred execution is that the query is only executed when the results are enumerated (which should only happen in your calling method).
Rory
A: 

You can write it as this

var result = db.Parts.Where(p => query.All(q => p.partName.Contains(q)));
Fadrian Sudaman
Doing this results in the error _"Local sequence cannot be used in LINQ to SQL implementation of query operators except the Contains() operator."_ - same as below.
a_m0d
A: 

please try this:

public IQueryable<Part> SearchForParts(string[] query)
{
    return from part in db.Parts
           where Search(part.Name,query)
           select part;
}

public bool Search(string partName,string[] query)
{
    for (int i = 0; i < query.Length; i++)
    {
        if(partName.Contains(query[i]))
           return true;
    }

    return false;
}
masoud ramezani
Wont work. You obviously dont use Linq2SQL regularly...
leppie
Your Search method isn't available to SQL - unless you fetched EVERY part by adding, say, .ToList() after db.Parts it's not going to work
Hightechrider
+2  A: 

Looking at the other attempts saddens me :(

public IQueryable<Part> SearchForParts(string[] query)
{
  var q = db.Parts.AsQueryable(); 

  foreach (var qs in query)
  { 
    var likestr = string.Format("%{0}%", qs);
    q = q.Where(x => SqlMethods.Like(x.partName, likestr));
  }

  return q;
}

Assumptions:

  • partName looks like: "ABC 123 XYZ"

  • query is { "ABC", "123", "XY" }

leppie
This also works for me - thanks for your help.
a_m0d