我有两个具有以下映射的索引(我将简化它们的映射):
1)AccountType映射:
elasticClient.CreateIndex("account", i => i .Settings(s => s .NumberOfShards(2) .NumberOfReplicas(0) ) .Mappings(m => m .Map<AccountType>(map => map .AutoMap() .Properties(p => p .Text(c => c .Name(n => n.Name) .Analyzer("standard") ) .Text(c => c .Name(n => n.Description) .Analyzer("standard") ) ) ) ) );
2)ProductType映射:
elasticClient.CreateIndex("proudct", i => i .Settings(s => s .NumberOfShards(2) .NumberOfReplicas(0) ) .Mappings(m => m .Map<ProductType>(map => map .AutoMap() .Properties(p => p .Text(c => c .Name(n => n.Title) .Analyzer("standard") ) .Text(c => c .Name(n => n.Description) .Analyzer("standard") ) ) ) ) );
现在,我有几件事需要弄清楚:
1)首先,有一个索引是一个好主意,在我的情况下是帐户,并且产品是嵌套对象,但是在这里每次我要更新/添加新产品时,我都必须重新索引(更新)整个帐户文件?
2)我的第二个问题是:我想具有搜索功能,因此,如果用户通过在文本框中键入内容进行搜索,则我希望同时获得“帐户”和“产品”的最佳匹配(此处,我将针对产品的名称和说明以及帐户名称和说明,然后获得最佳匹配):
因此,在这里如何使用 Nest ElasticSeach 搜索多个索引,或者如果不可能从每个索引中获得最佳匹配,然后根据得分从两个结果中获得最佳匹配是一个好主意吗?
PS:这是一个搜索产品内部索引的示例:
var result = elasticClient.Search<ProductType>(s => s .Size(10) .Query(q => q .MultiMatch(m => m .Fields(f => f.Field(p => p.Title, 1.5).Field(p => p.Description, 0.8)) .Operator(Operator.Or) .Query(query) ) ) );
通常建议每个索引具有一种类型,在Elasticsearch 6.0+中,每个索引只能具有一种类型。如果将产品表示为帐户上的嵌套对象,则向帐户中添加新产品将需要更新整个文档( 在您的应用程序代码中或在Elasticsearch中 )。
您可以搜索多个索引,查看协变搜索结果的文档;它显示了从一个索引返回多种不同类型的示例(此示例将更新为6.0!),但是可以跨多个索引执行此操作。这是一个例子:
private static void Main() { var settings = new ConnectionSettings(new Uri("http://localhost:9200")) .InferMappingFor<AccountType>(i => i .IndexName("account") ) .InferMappingFor<ProductType>(i => i .IndexName("product") ) // useful for development, to make the request/response bytes // available on the response .DisableDirectStreaming() // indented JSON in requests/responses .PrettyJson() // log out all requests/responses .OnRequestCompleted(callDetails => { if (callDetails.RequestBodyInBytes != null) { Console.WriteLine( $"{callDetails.HttpMethod} {callDetails.Uri} \n" + $"{Encoding.UTF8.GetString(callDetails.RequestBodyInBytes)}"); } else { Console.WriteLine($"{callDetails.HttpMethod} {callDetails.Uri}"); } Console.WriteLine(); if (callDetails.ResponseBodyInBytes != null) { Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" + $"{Encoding.UTF8.GetString(callDetails.ResponseBodyInBytes)}\n" + $"{new string('-', 30)}\n"); } else { Console.WriteLine($"Status: {callDetails.HttpStatusCode}\n" + $"{new string('-', 30)}\n"); } }); var client = new ElasticClient(settings); if (client.IndexExists("account").Exists) client.DeleteIndex("account"); client.CreateIndex("account", i => i .Settings(s => s .NumberOfShards(2) .NumberOfReplicas(0) ) .Mappings(m => m .Map<AccountType>(map => map .AutoMap() .Properties(p => p .Text(c => c .Name(n => n.Name) .Analyzer("standard") ) .Text(c => c .Name(n => n.Description) .Analyzer("standard") ) ) ) ) ); if (client.IndexExists("product").Exists) client.DeleteIndex("product"); client.CreateIndex("product", i => i .Settings(s => s .NumberOfShards(2) .NumberOfReplicas(0) ) .Mappings(m => m .Map<ProductType>(map => map .AutoMap() .Properties(p => p .Text(c => c .Name(n => n.Title) .Analyzer("standard") ) .Text(c => c .Name(n => n.Description) .Analyzer("standard") ) ) ) ) ); client.IndexMany(new[] { new AccountType { Name = "Name 1", Description = "Description 1" }, new AccountType { Name = "Name 2", Description = "Description 2" }, new AccountType { Name = "Name 3", Description = "Description 3" }, new AccountType { Name = "Name 4", Description = "Description 4" }, }); client.IndexMany(new[] { new ProductType { Title = "Title 1", Description = "Description 1" }, new ProductType { Title = "Title 2", Description = "Description 2" }, new ProductType { Title = "Title 3", Description = "Description 3" }, new ProductType { Title = "Title 4", Description = "Description 4" }, }); var indices = Indices.Index(typeof(ProductType)).And(typeof(AccountType)); client.Refresh(indices); var searchResponse = client.Search<object>(s => s .Index(indices) .Type(Types.Type(typeof(ProductType), typeof(AccountType))) .Query(q => (q .MultiMatch(m => m .Fields(f => f .Field(Infer.Field<ProductType>(ff => ff.Title, 1.5)) .Field(Infer.Field<ProductType>(ff => ff.Description, 0.8)) ) .Operator(Operator.Or) .Query("Title 1") ) && +q .Term("_index", "product")) || (q .MultiMatch(m => m .Fields(f => f .Field(Infer.Field<AccountType>(ff => ff.Name, 3)) .Field(Infer.Field<AccountType>(ff => ff.Description, 0.3)) ) .Operator(Operator.Or) .Query("Name 4") ) && +q .Term("_index", "account")) ) ); foreach (var document in searchResponse.Documents) Console.WriteLine($"document is a {document.GetType().Name}"); } public class ProductType { public string Title { get; set; } public string Description { get; set; } } public class AccountType { public string Name { get; set; } public string Description { get; set; } }
结果是
document is a AccountType document is a ProductType document is a AccountType document is a ProductType document is a AccountType document is a AccountType document is a ProductType document is a ProductType
这里有很多事情,所以让我解释一下。搜索请求JSON如下所示:
POST http://localhost:9200/product%2Caccount/producttype%2Caccounttype/_search?pretty=true { "query": { "bool": { "should": [ { "bool": { "must": [ { "multi_match": { "query": "Title 1", "operator": "or", "fields": [ "title^1.5", "description^0.8" ] } } ], "filter": [ { "term": { "_index": { "value": "product" } } } ] } }, { "bool": { "must": [ { "multi_match": { "query": "Name 4", "operator": "or", "fields": [ "name^3", "description^0.3" ] } } ], "filter": [ { "term": { "_index": { "value": "account" } } } ] } } ] } } }
该搜索是在product和account索引之间,在producttype和accounttype类型之间执行的。在title和description字段上执行multi_match查询,并将其与使用布尔查询的术语查询结合,以将查询约束到product索引。术语查询位于过滤器子句中,因为不应为该术语查询计算相关性得分。此布尔查询与另一个布尔查询结合在一起,该布尔查询对name和description字段执行multi_match查询,并与术语查询组合以将查询约束到account索引。这两个布尔查询使用should子句合并,因为其中一个布尔查询或另一个布尔布尔查询需要匹配。
product
account
producttype
accounttype
title
description
name
object作为通用的参数类型的Search<T>()方法调用,因为ProductType和AccountType不共享一个公共基类(除了object!),其产生的文档集合可以分型。但是,从结果中我们可以看到,NEST实际上已反序列化了类型producttype为的实例的ProductType文档和类型accounttype为的实例的文档AccountType。
object
Search<T>()
ProductType
AccountType
该查询使用运算符重载来更简洁地组合查询。