如何使用Nest ElasticSearch搜索多个索引?

时间:2017-12-15 17:16:01

标签: elasticsearch nest

我有两个带有以下映射的索引(我将快捷地映射它们):

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)我的第二个问题是:我想拥有搜索功能,所以如果用户通过键入文本框进行搜索,我希望获得帐户和产品的最佳匹配(这里我将搜索产品&#39; s标题和描述以及帐户的名称和描述然后获得最佳匹配):

所以这里有如何使用 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)
                                              )
                                            )
                                          );

1 个答案:

答案 0 :(得分:7)

  

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查询,并使用术语查询将查询约束到account索引。使用should子句组合两个bool查询,因为其中一个bool查询或另一个需要匹配。

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

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