J'essaie d'implémenter une recherche de mots clés très basique dans une application utilisant linq-to-sql. Mes termes de recherche sont dans un tableau de chaînes, chaque élément du tableau étant un mot, et je voudrais trouver les lignes qui contiennent les termes de recherche. Cela ne me dérange pas s'ils contiennent plus que les termes de recherche (très probablement, ils le seront), mais tous les termes de recherche doivent être présents.
Idéalement, j'aimerais quelque chose de similaire à l'extrait ci-dessous, mais je sais que cela ne fonctionnera pas. Aussi, j'ai regardé cette question ici , mais l'auteur de cette question semble content de faire les choses dans l'autre sens (query.Contains(part.partName)
), ce qui ne fonctionne pas pour moi.
public IQueryable<Part> SearchForParts(string[] query)
{
return from part in db.Parts
where part.partName.Contains(query)
select part;
}
Comment puis-je réécrire cette requête pour qu'elle fasse ce dont j'ai besoin?
Regarder les autres tentatives m'attriste :(
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;
}
Hypothèses:
partName ressemble à: "ABC 123 XYZ"
la requête est {"ABC", "123", "XY"}
Une solution plus simple et plus correcte (puis leppie):
public IQueryable<Part> SearchForParts(string[] query)
{
var q = db.Parts.AsQueryable();
foreach (string qs in query)
{
q = q.Where(x => x.partName.Contains(qs));
}
return q;
}
Cela fonctionnera tant que partName
est une chaîne (ou un équivalent SQL d'une chaîne).
La chose importante à noter est partName.Contains(qs)
est différente de query.Contains(partName)
.
Avec partName.Contains(qs)
, partName
est recherché pour toute occurrence de qs
. Le SQL résultant serait équivalent (où <qs> est la valeur de qs
):
select * from Parts where partName like '%<qs>%';
A noter également StartsWith
et EndsWith
qui sont similaires à Contains
mais regardez pour la chaîne à l'emplacement spécifique.
query.Contains(partName)
est identique à une commande SQL in
. Le SQL résultant serait équivalent à (où <query0> est la valeur de query[0]
, <query1> est la valeur de query[1]
Et <queryN> est la dernière valeur dans le tableau de requête) :
select * from Parts where partName in ( <query0>, <query1>, ..., <queryN> );
Mise à jour:
Il est également important de noter que la réponse de leppie n'échappe pas aux caractères génériques avant de les ajouter à l'instruction comme . Ce n'est pas un problème avec la solution Contains
puisque Linq échappera à la requête avant de l'envoyer. Une version échappée de la solution SqlMethods.Like
Serait:
public IQueryable<Part> SearchForParts(string[] query)
{
var q = db.Parts.AsQueryable();
foreach (var qs in query)
{
string escaped_bs = qs.Replace("/", "//"),
escaped_us = escaped_bs.Replace("_", "/_"),
escaped_p = escaped_us.Replace("%", "/%"),
escaped_br = escaped_p.Replace("[", "/["),
likestr = string.Format("%{0}%", escaped_br);
q = q.Where(x => SqlMethods.Like(x.partName, likestr, '/'));
}
return q;
}
Vous n'avez pas à vous en préoccuper car Linq vous échappera.
Tu pourrais essayer:
public IQueryable<Part> SearchForParts(string[] query)
{
return from part in db.Parts
where query.All(term => part.partName.Contains(term))
select part;
}
Cependant, je ne sais pas si LINQ to SQL pourra le transformer en T-SQL. Une autre option serait:
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;
}
Ce n'est pas aussi joli, mais ça devrait marcher. Vous obtiendrez une requête avec beaucoup de AND
s dans la clause where.
Vous pouvez l'écrire comme ceci
var result = db.Parts.Where(p => query.All(q => p.partName.Contains(q)));
L'utilisation du package nuget NinjaNye.SearchExtension vous permet d'effectuer facilement cette recherche:
string[] terms = new[]{"search", "term", "collection"};
var result = db.Parts.Search(terms, p => p.PartName);
Vous pouvez également rechercher plusieurs propriétés de chaîne
var result = db.Parts.Search(terms, p => p.PartName, p.PartDescription);
Ou effectuez un RankedSearch
qui renvoie IQueryable<IRanked<T>>
qui inclut simplement une propriété qui montre combien de fois les termes de recherche sont apparus:
//Perform search and rank results by the most hits
var result = db.Parts.RankedSearch(terms, p => p.PartName, p.PartDescription)
.OrderByDescending(r = r.Hits);
J'espère que cela aidera les futurs visiteurs
Je pense que cela est quelque peu simple et fonctionne pour moi:
string[] product = products.Split(',');
using (var context = new ProjectTrackerEntities())
{ var result = from part in context.DBAudits where product.Contains(part.TableName) select part; }