使用 NEST C# 在弹性搜索中使用多个索引进行全文搜索

Posted

技术标签:

【中文标题】使用 NEST C# 在弹性搜索中使用多个索引进行全文搜索【英文标题】:Full text Search with Multiple index in Elastic Search using NEST C# 【发布时间】:2019-10-22 00:04:42 【问题描述】:

我正在尝试使用 NEST 客户端搜索多个索引 Elasticsearch,我只需点击以下链接 [堆放帖]How to search inside multiple indices using Nest ElasticSearch? 唯一的区别是我的索引已经存在但没有返回

示例代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Elasticsearch.Net;
using Nest;

namespace ElasticSearchDemo



    public class ExceptionData
    
        public bool HasException  get; set; 
        public string ExceptionMessage  get; set; 
    
    public class ElasticSearchResponse : ExceptionData
    
        public ISearchResponse<dynamic> elasticSearchResponse  get; set; 
    

    public class ComponentTypES
    
        public string ComponentID  get; set; 
        public string Componentname  get; set; 
        public string Summary  get; set; 
    

    public class ProjectTypES
    
        public string ProjectID  get; set; 
        public string Projectname  get; set; 
        public string Summary  get; set; 
        public string Description  get; set; 
    
    class Program
    
        static void Main(string[] args)
        
            // calling the function
            var response = GetAllSearchResults("test", 0, 10);



        

        public static ElasticClient GetElasticSearchCommonSearch()
        
            ElasticClient elasticClient = null;
            try
            
                const string strElasticSearchURL = "http://localhost:9200/";
                const string componentIndex = "componenttypeindex";
                const string projectIndex = "projecttypeindex";

                if (!string.IsNullOrEmpty(strElasticSearchURL))
                
                    ConnectionSettings connectionSettings = new ConnectionSettings(new Uri(strElasticSearchURL))
                        .DefaultIndex(componentIndex)
                        .DefaultMappingFor<ComponentTypES>(i => i.IndexName(componentIndex).TypeName("Componenttype"))
                        .DefaultMappingFor<ProjectTypES>(j => j.IndexName(projectIndex).TypeName("Projecttype"))

                        .DisableDirectStreaming()
                        .PrettyJson()
                                .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");
                                    
                                
                        );

                    elasticClient = new ElasticClient(connectionSettings);
                

            
            catch (Exception ex)
            
                throw new Exception(ex.Message + "  ConnectionObject for : Common Search");
            

            return elasticClient;
        

        public static ElasticSearchResponse GetAllSearchResults(string query = "test", int
                                              page = 1, int pagesize = 10)
        
            ElasticSearchResponse combinedResponse = new   ElasticSearchResponse();

            try
            
                ElasticClient elasticClient =  GetElasticSearchCommonSearch();

                var clusterHealth = elasticClient.ClusterHealth();
                if (clusterHealth.IsValid && string.Compare(clusterHealth.Status.ToString(), "red", true) != 0 && clusterHealth.ServerError == null)
                
                    string Componentindex = "componenttypeindex";
                    string Projectindex =  "projecttypeindex";

                    var indices = Indices.Index(typeof(ComponentTypES)).And(typeof(ProjectTypES));

                    //elasticClient.Refresh(indices);

                    //TODO : Development time coding 

                    if (null != (indices))
                    
                        var indexExists = elasticClient.IndexExists(Indices.Index(Componentindex));
                        var projectExists = elasticClient.IndexExists(Indices.Index(Projectindex));

                        if (indexExists.Exists && indexExists.IsValid && projectExists.Exists && projectExists.IsValid)
                        


                            //full text example 1

                            combinedResponse.elasticSearchResponse = elasticClient.Search<object>(s => s
                             .Index(indices)
                             .Type(Types.Type(typeof(ComponentTypES), typeof(ProjectTypES)))
                             .Query(q => (q
                             .MultiMatch(m => m
                              .Fields(f => f
                                      .Field(Infer.Field<ComponentTypES>(ff => ff.Componentname))
                                      .Field(Infer.Field<ComponentTypES>(ff => ff.Summary, 1.1))
                                        )
                              .Operator(Operator.Or)
                              .Query(query)
                                         ) && +q
                             .Term("_index", Componentindex)) || (q
                             .MultiMatch(m => m
                             .Fields(f => f
                                         .Field(Infer.Field<ProjectTypES>(ff => ff.Projectname))
                                         .Field(Infer.Field<ProjectTypES>(ff => ff.Summary, 0.3))
                              )
                              .Operator(Operator.Or)
                              .Query(query)
                               ) && +q
                                 .Term("_index", Projectindex))
                              ).From(page - 1)
                              .Size(pagesize)

                               );


                            //free text example 2
                            combinedResponse.elasticSearchResponse = elasticClient.Search<object>(s => s
                                                             .Index(indices)
                                                             .Type(Types.Type(typeof(ComponentTypES), typeof(ProjectTypES)))
                                                             .Query(q => (q
                                                                 .MatchPhrase(m => m
                                                                         .Field(Infer.Field<ComponentTypES>(ff => ff.Componentname))
                                                                         .Query(query)
                                                                 ) && +q
                                                                 .Term("_index", Componentindex)) || (q
                                                                 .MatchPhrase(m => m
                                                                     .Field(Infer.Field<ProjectTypES>(ff => ff.Projectname))
                                                                     .Query(query)
                                                                     )
                                                                 ) && +q
                                                                 .Term("_index", Projectindex)
                                                             ).From(page - 1)
                                                             .Size(pagesize)
                                                        );



                        
                        else
                        
                            combinedResponse.HasException = true;
                            combinedResponse.ExceptionMessage = "Index Not Found";
                        
                    
                    else
                    
                        combinedResponse.HasException = true;
                        combinedResponse.ExceptionMessage = "Index Not Found In Config File";
                    
                
                else
                
                    combinedResponse.HasException = true;
                    combinedResponse.ExceptionMessage = "Error on connecting with ElasticSearch";
                
            
            catch (Exception ex)
            
                combinedResponse.HasException = true;
                combinedResponse.ExceptionMessage = ex.Message;
                return combinedResponse;
            

            return combinedResponse;
        


    

弹性表架构:

PUT componenttypeindex

  "mappings": 
    "Componenttype":
      "properties":
        "ComponentID":"type":"text",
        "Componentname":"type":"text",
        "Summary":"type":"text"
           
        
    


PUT projecttypeindex

  "mappings": 
    "Projecttype":
      "properties":
        "ProjectID":"type":"text",
        "Projectname":"type":"text",
        "Summary":"type":"text",
         "Description":"type":"text"
                
            
         

它应该返回查询匹配的项目,但没有返回 抱歉我尝试了丑陋的代码格式,但新编辑器不会改变任何东西

更新: 我已经按照@RussCam 的建议更新了查询中的索引值,但仍然没有预期的结果,而且当扩展响应对象并直接在浏览器中运行 URI 参数时,它的所有结果都很奇怪,不知道为什么没有显示响应计数

通过对 POST 的成功低级别调用构建的有效 NEST 响应:/componenttypeindex%2Cprojecttypeindex/Componenttype%2CProjecttype/_search?typed_keys=true

此 API 调用的审计跟踪:

[1] 健康响应:节点:http://localhost:9200/ 接受:00:00:00.0620000

请求:

URI = "http://localhost:9200/componenttypeindex%2Cprojecttypeindex/Componenttype%2CProjecttype/_search?typed_keys=true"

我的 POCO 课程:

public class ComponentTypES

    public string ComponentID  get; set; 
    public string Componentname  get; set; 
    public string Summary  get; set; 

  

public class ProjectTypES


    public string ProjectID  get; set; 
    public string Projectname  get; set; 
    public string Summary  get; set; 
    public string Description  get; set;  

样本数据:

PUT componenttypeindex/Componenttype/5342e739-1635-4021-baf2-55e25b95b8ec

    "ComponentID":"5342e739-1635-4021-baf2-55e25b95b8ec",
    "Componentname":"TestComponent1",
    "Summary":"this is summary of test component1"


PUT componenttypeindex/Componenttype/90781386-8065-11e9-bc42-526af7764f64
    
    "ComponentID":"90781386-8065-11e9-bc42-526af7764f64",
    "Componentname":"TestComponent2",
    "Summary":"this is summary of test component3"  

PUT componenttypeindex/Componenttype/19871386-8065-11e9-bc42-526af7764f64

    "ComponentID":"19871386-8065-11e9-bc42-526af7764f64",
    "Componentname":"some xyz component test",
    "Summary":"this is summary test of test xyz"



PUT projecttypeindex/Projecttype/5342e739-2019-4021-baf2-55e25b95b8ec

        "ProjectID":"5342e739-2019-4021-baf2-55e25b95b8ec",
        "Projectname":"Test Project1",
        "Summary":"summary of Test Project1",
        "Description":"Description of TestProject1"


PUT projecttypeindex/Projecttype/5342f739-2019-4021-baf2-55e25b95b8ba

        "ProjectID":"5342f739-2019-4021-baf2-55e25b95b8ba",
        "Projectname":"Test Project2",
        "Summary":"summary of Test Project2",
        "Description":"Description of TestProject1"


PUT projecttypeindex/Projecttype/6342f739-2020-4021-baf2-55e25b95b8ac

        "ProjectID":"6342f739-2020-4021-baf2-55e25b95b8ac",
        "Projectname":"some PQRS project",
        "Summary":"summary of PQRS Project",
        "Description":"Description of PQORS Project1"

【问题讨论】:

您使用的是什么版本的 Elasticsearch?您使用的是哪个版本的 NEST? @RussCam ElasticSearch.NET 6.4.0 版,NEST 6.4.0 版,如果您需要任何其他信息,请告诉我 一个完整的例子真的很有帮助,因为问题中只有部分信息。在您使用.Term("_index", "Componenttype") 的地方,看起来您可以使用componentIndex,同样,在您使用.Term("_index", "Projecttype") 的地方,您似乎可以使用projectIndex @RussCam 感谢您的 cmets 将尝试根据您的建议进行更改,我还将添加诸如 POCO 对象之类的附加信息 @RussCam 我已经添加了请求的附加信息,如果您需要任何其他详细信息,请告诉我,我已经运行了返回结果但点击次数始终为零的响应对象 URI 参数,请建议 【参考方案1】:

您的示例中有很多多余的信息,这使得使用起来很棘手,这增加了希望提供帮助的人所需的努力障碍。我是否可以建议您将示例缩减为最小、简洁但完整的示例,以展示您将来面临的问题?这真的有助于更快地解决问题的症结!

我认为根本问题是索引映射中字段内的属性大小写和 NEST 默认发送的属性大小写不同,因此 should 子句中的嵌套 must 子句由于字段大小写的不同,NEST 生成的bool 查询永远不会被任何文档匹配。

NEST by default camel cases property names,但是您的示例中的索引映射和文档中的字段都是帕斯卡大小写的,因此 NEST 生成的字段名称将与映射中的字段名称不匹配。您可以使用ConnectionSettings 上的DefaultFieldNameInferrer(Func&lt;string, string&gt;) 方法轻松更改NEST 的现场套管行为。仅返回传递的字符串值的委托将保留 POCO 上的字段名称。

这是一个完整但简洁的工作示例

private static void Main()

    const string componentIndex = "componenttypeindex";
    const string projectIndex = "projecttypeindex";

    var pool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));

    var settings = new ConnectionSettings(pool)
        .DefaultIndex(componentIndex)
        .DefaultMappingFor<ComponentTypES>(i => i.IndexName(componentIndex).TypeName("Componenttype").IdProperty(f => f.ComponentID))
        .DefaultMappingFor<ProjectTypES>(j => j.IndexName(projectIndex).TypeName("Projecttype").IdProperty(f => f.ProjectID))
        .DefaultFieldNameInferrer(f => f)
        .DefaultTypeName("_doc")
        .DisableDirectStreaming()
        .PrettyJson()
        .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);

    foreach (var index in new[]  componentIndex, projectIndex ) 
    
        if (client.IndexExists(index).Exists)
            client.DeleteIndex(index);

        client.CreateIndex(index, c => c
            .Mappings(m => 
                if (index == projectIndex)
                    return m.Map<ProjectTypES>(mm => mm.AutoMap());
                else
                    return m.Map<ComponentTypES>(mm => mm.AutoMap());
            )
        );
    

    client.Bulk(b => b
        .IndexMany(new [] 
            new ComponentTypES 
            
                ComponentID = "5342e739-1635-4021-baf2-55e25b95b8ec",
                Componentname = "TestComponent1",
                Summary = "this is summary of test component1"
            ,
            new ComponentTypES
            
                ComponentID = "90781386-8065-11e9-bc42-526af7764f64",
                Componentname = "TestComponent2",
                Summary = "this is summary of test component3"
            ,
            new ComponentTypES
            
                ComponentID = "19871386-8065-11e9-bc42-526af7764f64",
                Componentname = "some xyz component test",
                Summary = "this is summary test of test xyz"
            ,
        )
        .IndexMany(new [] 
            new ProjectTypES
            
                ProjectID = "5342e739-2019-4021-baf2-55e25b95b8ec",
                Projectname = "Test Project1",
                Summary = "summary of Test Project1",
                Description = "Description of TestProject1"
            ,
            new ProjectTypES
            
                ProjectID = "5342f739-2019-4021-baf2-55e25b95b8ba",
                Projectname = "Test Project2",
                Summary = "summary of Test Project2",
                Description = "Description of TestProject1"
            ,
            new ProjectTypES
            
                ProjectID = "6342f739-2020-4021-baf2-55e25b95b8ac",
                Projectname = "some PQRS project",
                Summary = "summary of PQRS Project",
                Description = "Description of PQORS Project1"
            ,
        )
        .Refresh(Refresh.WaitFor)
    );

    var query = "test";

    var response = client.Search<object>(s => s
        .Index(Indices.Index(typeof(ComponentTypES)).And(typeof(ProjectTypES)))
        .Type(Types.Type(typeof(ComponentTypES), typeof(ProjectTypES)))
        .Query(q => 
            (q
                .MultiMatch(m => m
                    .Fields(f => f
                        .Field(Infer.Field<ComponentTypES>(ff => ff.Componentname))
                        .Field(Infer.Field<ComponentTypES>(ff => ff.Summary, 1.1))
                    )
                    .Operator(Operator.Or)
                    .Query(query)
                ) && +q
                .Term("_index", componentIndex)
            ) || 
            (q
                .MultiMatch(m => m
                    .Fields(f => f
                        .Field(Infer.Field<ProjectTypES>(ff => ff.Projectname))
                        .Field(Infer.Field<ProjectTypES>(ff => ff.Summary, 0.3))
                    )
                    .Operator(Operator.Or)
                    .Query(query)
                ) && +q
                .Term("_index", projectIndex)
            )
        )
    );


public class ComponentTypES

    public string ComponentID  get; set; 
    public string Componentname  get; set; 
    public string Summary  get; set; 



public class ProjectTypES


    public string ProjectID  get; set; 
    public string Projectname  get; set; 
    public string Summary  get; set; 
    public string Description  get; set; 

搜索结果的 JSON 查询是

POST http://localhost:9200/componenttypeindex%2Cprojecttypeindex/Componenttype%2CProjecttype/_search?pretty=true&typed_keys=true 

  "query": 
    "bool": 
      "should": [
        
          "bool": 
            "filter": [
              
                "term": 
                  "_index": 
                    "value": "componenttypeindex"
                  
                
              
            ],
            "must": [
              
                "multi_match": 
                  "fields": [
                    "Componentname",
                    "Summary^1.1"
                  ],
                  "operator": "or",
                  "query": "test"
                
              
            ]
          
        ,
        
          "bool": 
            "filter": [
              
                "term": 
                  "_index": 
                    "value": "projecttypeindex"
                  
                
              
            ],
            "must": [
              
                "multi_match": 
                  "fields": [
                    "Projectname",
                    "Summary^0.3"
                  ],
                  "operator": "or",
                  "query": "test"
                
              
            ]
          
        
      ]
    
  

返回 5 个结果


  "took" : 53,
  "timed_out" : false,
  "_shards" : 
    "total" : 10,
    "successful" : 10,
    "skipped" : 0,
    "failed" : 0
  ,
  "hits" : 
    "total" : 5,
    "max_score" : 0.7549128,
    "hits" : [
      
        "_index" : "projecttypeindex",
        "_type" : "Projecttype",
        "_id" : "5342e739-2019-4021-baf2-55e25b95b8ec",
        "_score" : 0.7549128,
        "_source" : 
          "ProjectID" : "5342e739-2019-4021-baf2-55e25b95b8ec",
          "Projectname" : "Test Project1",
          "Summary" : "summary of Test Project1",
          "Description" : "Description of TestProject1"
        
      ,
      
        "_index" : "componenttypeindex",
        "_type" : "Componenttype",
        "_id" : "19871386-8065-11e9-bc42-526af7764f64",
        "_score" : 0.5565415,
        "_source" : 
          "ComponentID" : "19871386-8065-11e9-bc42-526af7764f64",
          "Componentname" : "some xyz component test",
          "Summary" : "this is summary test of test xyz"
        
      ,
      
        "_index" : "componenttypeindex",
        "_type" : "Componenttype",
        "_id" : "5342e739-1635-4021-baf2-55e25b95b8ec",
        "_score" : 0.3164503,
        "_source" : 
          "ComponentID" : "5342e739-1635-4021-baf2-55e25b95b8ec",
          "Componentname" : "TestComponent1",
          "Summary" : "this is summary of test component1"
        
      ,
      
        "_index" : "projecttypeindex",
        "_type" : "Projecttype",
        "_id" : "5342f739-2019-4021-baf2-55e25b95b8ba",
        "_score" : 0.2876821,
        "_source" : 
          "ProjectID" : "5342f739-2019-4021-baf2-55e25b95b8ba",
          "Projectname" : "Test Project2",
          "Summary" : "summary of Test Project2",
          "Description" : "Description of TestProject1"
        
      ,
      
        "_index" : "componenttypeindex",
        "_type" : "Componenttype",
        "_id" : "90781386-8065-11e9-bc42-526af7764f64",
        "_score" : 0.20706992,
        "_source" : 
          "ComponentID" : "90781386-8065-11e9-bc42-526af7764f64",
          "Componentname" : "TestComponent2",
          "Summary" : "this is summary of test component3"
        
      
    ]
  

【讨论】:

非常感谢你拯救了这一天,我在开始工作前一周才刚接触弹性搜索,过去五天我一直在寻找这个解决方案,最后你让它工作了,我确保在以后的问题中添加工作示例,需要一点帮助如何为其他字段添加 where 子句条件,例如 description ="somestring" 和 description != "somestring"

以上是关于使用 NEST C# 在弹性搜索中使用多个索引进行全文搜索的主要内容,如果未能解决你的问题,请参考以下文章

为弹性搜索指定和使用带有 C# NEST 客户端的 NGramTokenizer

Nest Client c# 7.0 用于弹性搜索删除别名

将对象序列化为 JSON,然后使用 NEST 在弹性搜索中发送查询

NEST 中的模拟 Elasticsearch 客户端存在异步搜索方法问题

弹性搜索,多个索引与不同数据集的一个索引和类型?

NEST Api SearchAfter 在 NEST 中返回 null 但在 Kibana 中有效