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

Full text Search with Multiple index in Elastic Search using NEST C#

我正在尝试使用 NEST 客户端搜索 Elasticsearch 的多个索引,我只是按照下面的 link
[堆叠post] 唯一的区别是我的索引已经存在但没有 returns

示例代码:

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


    }
}

弹性 table 架构:

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"}
                }
            }
         }
}

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

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

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

此 API 调用的审计跟踪:

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"
}

你的例子中有很多多余的信息,这使得处理起来很棘手,这增加了希望提供帮助的人所需要付出的努力的障碍。我能否建议您将示例简化为最小、简洁但完整的示例,以证明您将来面临的问题;它确实有助于更快地找到问题的症结所在!

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

NEST by default camel cases property names,但是您的示例中的索引映射和文档中的字段都是 pascal 大小写的,因此 NEST 生成的字段名称将与映射中的字段名称不匹配。您可以使用 ConnectionSettings 上的 DefaultFieldNameInferrer(Func<string, string>) 方法轻松更改 NEST 的字段套管行为。仅 returns 传递字符串值的委托将保留 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"
                }
              }
            ]
          }
        }
      ]
    }
  }
}

其中 returns 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"
        }
      }
    ]
  }
}