Полнотекстовый поиск с несколькими индексами в Elastic Search с использованием NEST C # - PullRequest
1 голос
/ 06 июня 2019

Я пытаюсь выполнить поиск по нескольким индексам Elasticsearch с NEST Client, я просто перехожу по ссылке ниже
[stackover post] Как искать по нескольким индексам с помощью 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 непосредственно в Браузере, он имеет все результаты, что-то странное, не уверен, почему это не показано в счетчике ответов

Действительный ответ NEST, созданный из успешного вызова низкого уровня на POST: / componenttypeindex% 2Cprojecttypeindex /Тип компонента% 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"
}

1 Ответ

1 голос
/ 13 июня 2019

В вашем примере много лишней информации, с которой сложно работать, что поднимает барьер усилий, требуемый тем, кто хочет помочь. Могу ли я предложить вам сократить пример до самого маленького, краткого, но полного примера, который демонстрирует проблему, с которой вы столкнетесь в будущем; это действительно поможет быстрее понять суть проблемы!

Я думаю, что фундаментальная проблема заключается в том, что регистр свойств в полях в отображениях индекса и регистр свойств, которые NEST будет отправлять по умолчанию, различаются, поэтому вложенные предложения must в предложении should bool запрос, сгенерированный NEST, никогда не будет сопоставлен никаким документам из-за разницы в регистре полей.

NEST по умолчанию имена свойств случаев верблюдов , но все поля в отображениях индекса и документах в вашем примере - все в паскале, поэтому имена полей, сгенерированные NEST, не будут совпадать с именами в отображении. Вы можете легко изменить поведение полей NEST, используя метод DefaultFieldNameInferrer(Func<string, string>) для ConnectionSettings. Делегат, который просто возвращает переданное строковое значение, оставит имена полей такими, какие они есть в 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"
        }
      }
    ]
  }
}
...