如何使用Nest ElasticSearch在多个索引中进行搜索? [英] How to search inside multiple indices using Nest ElasticSearch?

查看:99
本文介绍了如何使用Nest ElasticSearch在多个索引中进行搜索?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have two indices with the following mapping(I will shortcut their mappings):

1)AccountType映射:

1) AccountType mapping:

 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")
                                                    )
                                                )
                                    )
                            )
                          );

现在我有几件事需要弄清楚它们:

Now I have several things I need to get them clear:

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

1) First is it a good idea to have one index which in my case is account and has products as nested objects, but here for each time I want to update/add new product I have to re-index(update) the whole account document?

2)我的第二个问题是:我想具有搜索功能,因此,如果用户通过在文本框中键入内容进行搜索,则我希望同时获得帐户"和产品"的最佳匹配(此处将搜索产品的标题和说明以及帐户的名称和描述,然后获得最佳匹配):

2) My second questions is: I want to have search functionality, so if the user search by typing in a textbox I would like to get best matches for both Accounts and Products(here I will search against product's title and description plus account's name and description then getting best matches) :

因此,此处介绍如何使用 Nest ElasticSeach 搜索多个索引,或者如果不可能的话,最好是从每个索引中获取最佳匹配,然后根据得分从两个结果中获取最佳匹配?

So here how to search against multiple indices using Nest ElasticSeach, or if it's not possible is it a good idea to get best matches from every index, then getting best matches from both results depending on score?

PS: 这是一个搜索产品内部索引的示例:

PS: Here is an example for searching inside product index:

        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)
                                              )
                                            )
                                          );

推荐答案

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

1) First is it a good idea to have one index which in my case is account and has products as nested objects, but here for each time I want to update/add new product I have to re-index(update) the whole account document?

通常建议每个索引使用一种类型,并且

It's generally recommended to have one type per index, and in Elasticsearch 6.0+, you can only have one type per index. If products are represented as nested objects on an account then adding a new product to an account will require updating the whole document (either in your application code, or within Elasticsearch).

2)我的第二个问题是:我想具有搜索功能,因此,如果用户通过在文本框中键入内容进行搜索,我希望同时获得帐户"和产品"的最佳匹配(这里我将搜索产品的标题和说明以及帐户的名称和说明,然后获得最佳匹配):

2) My second questions is: I want to have search functionality, so if the user search by typing in a textbox I would like to get best matches for both Accounts and Products(here I will search against product's title and description plus account's name and description then getting best matches) :

您可以搜索多个索引,并查看协变搜索结果的文档;它显示了一个示例从一个索引返回多个不同类型的示例(此示例将更新为6.0!),但是可以跨多个索引执行此操作.这是一个示例:

You can search across multiple indices, check out the documentation of covariant search results; it shows an example of returning multiple different types from one index (this example will be updated for 6.0!), but it's possible to perform this across multiple indices. Here's an example:

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如下所示:

There's a lot going on here so let me explain. The search request JSON looks like:

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索引.术语查询位于过滤器子句中,因为不应为该术语查询计算相关性得分.此布尔查询与另一个布尔查询组合,该布尔查询在namedescription字段上执行multi_match查询,并与术语查询组合以将查询约束到account索引.这两个布尔查询使用should子句组合在一起,因为其中一个布尔查询或另一个布尔布尔查询都需要匹配.

The search is executed across both the product and account indices, across producttype and accounttype types. A multi_match query is performed on the title and description fields, and this is combined with a term query using a bool query, to constrain the query to the product index. The term query is in a filter clause because no relevancy score should be calculated for the term query. This bool query is combined with another bool query that performs a multi_match query on name and description fields, cmbined with a term query to constrain the query to the account index. The two bool queries are combined using should clauses because either one of the bool queries or the other needs to match.

object用作Search<T>()方法调用的通用参数类型,因为ProductTypeAccountType不共享可用于生成文档集合的公共基类(除了object!).输入.但是,从结果中我们可以看到,NEST实际上已将类型为producttype的文档反序列化为ProductType的实例,并将类型为accounttype的文档反序列化为AccountType的实例.

object is used as the generic parameter type for the Search<T>() method call because ProductType and AccountType do not share a common base class (besides object!) to which the resulting document collection can be typed. We can see however from the results that NEST has actually deserialized documents with type producttype to instances of ProductType and documents with type accounttype to instances of AccountType.

查询使用运算符重载以更简洁地组合查询.

The query uses operator overloading to more succinctly combine queries.

这篇关于如何使用Nest ElasticSearch在多个索引中进行搜索?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆