【问题标题】:How to search inside multiple indices using Nest ElasticSearch?如何使用 Nest ElasticSearch 在多个索引中搜索?
【发布时间】:2018-05-29 22:30:36
【问题描述】:

我有两个具有以下映射的索引(我将缩短它们的映射):

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) 产品类型映射:

 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 nest


    【解决方案1】:

    1) 首先,最好有一个索引,在我的情况下是帐户,并且将产品作为嵌套对象,但是每次我想更新/添加新产品时,我都必须重新索引(更新)整个账户文件?

    通常建议每个索引使用一种类型,in Elasticsearch 6.0+, you can only have one type per index。如果产品在帐户中表示为嵌套对象,那么向帐户中添加新产品将需要更新整个文档(在您的应用程序代码中,或在 Elasticsearch 中)。

    2)我的第二个问题是:我想要搜索功能,所以如果用户通过在文本框中输入搜索,我希望获得帐户和产品的最佳匹配(这里我将搜索产品的标题和描述加上帐户名称和描述,然后获得最佳匹配):

    您可以跨多个索引进行搜索,查看the documentation of covariant search results;它显示了从一个索引返回多种不同类型的示例(此示例将更新为 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"
                      }
                    }
                  }
                ]
              }
            }
          ]
        }
      }
    }
    

    搜索在productaccount 索引中执行,在producttypeaccounttype 类型中执行。对 titledescription 字段执行 multi_match 查询,并结合使用 bool 查询的术语查询,将查询限制为 product 索引。术语查询位于过滤子句中,因为不应为术语查询计算相关性分数。这个 bool 查询与另一个 bool 查询相结合,该查询在 namedescription 字段上执行 multi_match 查询,并与 term 查询结合以将查询约束到 account 索引。这两个 bool 查询使用 should 子句组合,因为其中一个 bool 查询或另一个需要匹配。

    object 用作Search&lt;T&gt;() 方法调用的通用参数类型,因为ProductTypeAccountType 不共享一个公共基类(除了object!),生成的文档集合可以是该基类打字。然而,我们可以从结果中看到,NEST 实际上已经将 producttype 类型的文档反序列化为 ProductType 的实例,并将 accounttype 类型的文档反序列化为 AccountType 的实例。

    查询使用operator overloading 更简洁地组合查询。

    【讨论】:

    • 兄弟,你是 Nest elasticsearch 的大师。每次我问一个问题,我都会从你那里得到准确的答案。非常感谢你太详细的回答。
    • 对不起我的英语不好。似乎“像地狱一样”有不好的意思
    • 我不认为这很糟糕,但不认为它翻译正确:)
    猜你喜欢
    • 2018-01-12
    • 2021-08-12
    • 1970-01-01
    • 2021-05-18
    • 2021-07-29
    • 1970-01-01
    • 1970-01-01
    • 2013-08-13
    • 1970-01-01
    相关资源
    最近更新 更多