使用 nest elastic 根据术语中的单词数进行搜索
Search based on number of words in term using nest elastic
我有一个电子商务网站。用户可以搜索产品。每个产品都有列表 "name"、"brand"、"category"。
如果用户搜索 "Tommy t-shirt men",则应仅返回男士 tommy T 恤作为结果,不应返回包含单个词 tommy 或 t 恤的结果。如果用户搜索 "Tommy",则应返回所有包含单词 tommy 的结果。如果用户搜索 "Tommy men",则应仅返回 tommy mens 产品,而不应返回单个匹配词 tommy 或单个匹配词 men。
我的代码是这样的:
public ISearchResponse<Models.Product> Search(string term, int minMatch)
{
var response = client.Search<Models.Product>(search => search
.Query(q => q.Bool(b => b.Should(
s => s.Match(m => m.Query(term).Field(f => f.ProductName).Boost(5).Fuzziness(Fuzziness.EditDistance(0))),
s => s.Match(m => m.Query(term).Field(f => f.Brand).Boost(15).Fuzziness(Fuzziness.EditDistance(0))),
s => s.Match(m => m.Query(term).Field(f => f.Category).Boost(10).Fuzziness(Fuzziness.EditDistance(0)))
).MinimumShouldMatch(minMatch))));
return response;
}
public ISearchResponse<Models.Product> Read(string term)
{
var fixedInput = Regex.Split(term, @"[^\p{L}]*\p{Z}[^\p{L}]*");
int minMatch;
if (fixedInput.Count() > 1) minMatch = 2;
else minMatch = 1;
var results = Search(term, minMatch);
if (!results.Documents.Any() && minMatch.Equals(2))
{
results = Search(term, 1);
}
return results;
}
如果我正在搜索 "Tommy men",第一个结果是 "Tommey men",但其他结果是 "Diesel men"、"Boss men",如果搜索字词不止一个。
你是说下面句子中的 "tommy" != "Tommy" 吗?
no results with single word
tommy or t-shirt should be returned. If user searching for "Tommy"
then all results with word tommy should be returned.
对于其余示例,您可以使用 multi-match query with cross_fields 类型。
示例应用程序:
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public string Brand { get; set; }
public string Category { get; set; }
public override string ToString() => $"Id: {Id} Name: {Name} Brand: {Brand} Category: {Category}";
}
static async Task Main(string[] args)
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
connectionSettings.DefaultIndex("documents");
var client = new ElasticClient(connectionSettings);
var deleteIndexResponse = await client.Indices.DeleteAsync("documents");
var createIndexResponse = await client.Indices.CreateAsync("documents", d => d
.Map(m => m.AutoMap<Document>()));
var indexDocument = await client
.IndexDocumentAsync(new Document {Id = 1, Brand = "Tommy", Category = "men"});
var indexDocument2 = await client
.IndexDocumentAsync(new Document {Id = 2, Brand = "Diesel", Category = "men"});
var indexDocument3 = await client
.IndexDocumentAsync(new Document {Id = 3, Brand = "Boss", Category = "men"});
var refreshAsync = client.Indices.RefreshAsync();
var query = "Tommy";
var searchResponse = await Search(client, query);
PrintResults(query, searchResponse);
query = "Tommy men";
searchResponse = await Search(client, query);
PrintResults(query, searchResponse);
query = "men";
searchResponse = await Search(client, query);
PrintResults(query, searchResponse);
}
private static async Task<ISearchResponse<Document>> Search(ElasticClient client, string query)
{
var searchResponse = await client.SearchAsync<Document>(s => s.Query(q => q
.MultiMatch(mm => mm
.Fields(f => f.Fields(ff => ff.Brand, ff => ff.Category, ff => ff.Name))
.Query(query)
.Type(TextQueryType.CrossFields)
.MinimumShouldMatch("100%"))));
return searchResponse;
}
private static void PrintResults(string query, ISearchResponse<Document> searchResponse)
{
Console.WriteLine($"query: {query}");
Console.WriteLine(searchResponse.Total);
Console.WriteLine($"results: ");
searchResponse.Documents.ToList().ForEach(Console.WriteLine);
Console.WriteLine();
}
}
打印:
query: Tommy
found: 1
Id: 1 Name: Brand: Tommy Category: men
query: Tommy men
found: 1
Id: 1 Name: Brand: Tommy Category: men
query: men
found: 3
Id: 1 Name: Brand: Tommy Category: men
Id: 2 Name: Brand: Diesel Category: men
Id: 3 Name: Brand: Boss Category: men
希望对您有所帮助。
我有一个电子商务网站。用户可以搜索产品。每个产品都有列表 "name"、"brand"、"category"。 如果用户搜索 "Tommy t-shirt men",则应仅返回男士 tommy T 恤作为结果,不应返回包含单个词 tommy 或 t 恤的结果。如果用户搜索 "Tommy",则应返回所有包含单词 tommy 的结果。如果用户搜索 "Tommy men",则应仅返回 tommy mens 产品,而不应返回单个匹配词 tommy 或单个匹配词 men。
我的代码是这样的:
public ISearchResponse<Models.Product> Search(string term, int minMatch)
{
var response = client.Search<Models.Product>(search => search
.Query(q => q.Bool(b => b.Should(
s => s.Match(m => m.Query(term).Field(f => f.ProductName).Boost(5).Fuzziness(Fuzziness.EditDistance(0))),
s => s.Match(m => m.Query(term).Field(f => f.Brand).Boost(15).Fuzziness(Fuzziness.EditDistance(0))),
s => s.Match(m => m.Query(term).Field(f => f.Category).Boost(10).Fuzziness(Fuzziness.EditDistance(0)))
).MinimumShouldMatch(minMatch))));
return response;
}
public ISearchResponse<Models.Product> Read(string term)
{
var fixedInput = Regex.Split(term, @"[^\p{L}]*\p{Z}[^\p{L}]*");
int minMatch;
if (fixedInput.Count() > 1) minMatch = 2;
else minMatch = 1;
var results = Search(term, minMatch);
if (!results.Documents.Any() && minMatch.Equals(2))
{
results = Search(term, 1);
}
return results;
}
如果我正在搜索 "Tommy men",第一个结果是 "Tommey men",但其他结果是 "Diesel men"、"Boss men",如果搜索字词不止一个。
你是说下面句子中的 "tommy" != "Tommy" 吗?
no results with single word tommy or t-shirt should be returned. If user searching for "Tommy" then all results with word tommy should be returned.
对于其余示例,您可以使用 multi-match query with cross_fields 类型。
示例应用程序:
public class Document
{
public int Id { get; set; }
public string Name { get; set; }
public string Brand { get; set; }
public string Category { get; set; }
public override string ToString() => $"Id: {Id} Name: {Name} Brand: {Brand} Category: {Category}";
}
static async Task Main(string[] args)
{
var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
var connectionSettings = new ConnectionSettings(pool);
connectionSettings.DefaultIndex("documents");
var client = new ElasticClient(connectionSettings);
var deleteIndexResponse = await client.Indices.DeleteAsync("documents");
var createIndexResponse = await client.Indices.CreateAsync("documents", d => d
.Map(m => m.AutoMap<Document>()));
var indexDocument = await client
.IndexDocumentAsync(new Document {Id = 1, Brand = "Tommy", Category = "men"});
var indexDocument2 = await client
.IndexDocumentAsync(new Document {Id = 2, Brand = "Diesel", Category = "men"});
var indexDocument3 = await client
.IndexDocumentAsync(new Document {Id = 3, Brand = "Boss", Category = "men"});
var refreshAsync = client.Indices.RefreshAsync();
var query = "Tommy";
var searchResponse = await Search(client, query);
PrintResults(query, searchResponse);
query = "Tommy men";
searchResponse = await Search(client, query);
PrintResults(query, searchResponse);
query = "men";
searchResponse = await Search(client, query);
PrintResults(query, searchResponse);
}
private static async Task<ISearchResponse<Document>> Search(ElasticClient client, string query)
{
var searchResponse = await client.SearchAsync<Document>(s => s.Query(q => q
.MultiMatch(mm => mm
.Fields(f => f.Fields(ff => ff.Brand, ff => ff.Category, ff => ff.Name))
.Query(query)
.Type(TextQueryType.CrossFields)
.MinimumShouldMatch("100%"))));
return searchResponse;
}
private static void PrintResults(string query, ISearchResponse<Document> searchResponse)
{
Console.WriteLine($"query: {query}");
Console.WriteLine(searchResponse.Total);
Console.WriteLine($"results: ");
searchResponse.Documents.ToList().ForEach(Console.WriteLine);
Console.WriteLine();
}
}
打印:
query: Tommy
found: 1
Id: 1 Name: Brand: Tommy Category: men
query: Tommy men
found: 1
Id: 1 Name: Brand: Tommy Category: men
query: men
found: 3
Id: 1 Name: Brand: Tommy Category: men
Id: 2 Name: Brand: Diesel Category: men
Id: 3 Name: Brand: Boss Category: men
希望对您有所帮助。