Запрос Elasticsearch с вложенными наборами

Я новичок в Elasticsearch, поэтому, пожалуйста, будьте со мной и дайте мне знать, если мне нужно предоставить какую-либо дополнительную информацию. Я унаследовал проект, и мне нужно реализовать новую функцию поиска. Структура документа/сопоставления уже существует, но ее можно изменить, если она не может облегчить то, что я пытаюсь достичь. Я использую Elasticsearch версии 5.6.16.

Компания может предложить ряд услуг. Каждое предложение услуг сгруппировано в набор. Каждый набор состоит из 3-х категорий;

  • Товар(ы) (ID 1)
  • Процесс(ы) (ID 3)
  • Материал(ы) (ИД 4)

Структура документа выглядит следующим образом;

[{
  "id": 4485,
  "name": "Company A",
  // ...
  "services": {
    "595": {
      "1": [
        95, 97, 91
      ],
      "3": [
        475, 476, 471
      ],
      "4": [
        644, 645, 683
      ]
    },
    "596": {
      "1": [
        91, 89, 76
      ],
      "3": [
        476, 476, 301
      ],
      "4": [
        644, 647, 555
      ]
    },
    "597": {
      "1": [
        92, 93, 89
      ],
      "3": [
        473, 472, 576
      ],
      "4": [
        641, 645, 454
      ]
    },
  }
}]

В приведенном выше примере; 595, 596 и 597 — это идентификаторы, относящиеся к набору. 1, 3 и 4 относятся к категориям (упомянутым выше).

Отображение выглядит так;

[{
  "id": {
    "type": "long"
  },
  "name": {
    "type": "text",
    "fields": {
      "keyword": {
        "type": "keyword",
        "ignore_above": 256
      }
    }
  },
  "services": {
    "properties": {
      // ...
      "595": {
        "properties": {
          "1": {"type": "long"},
          "3": {"type": "long"},
          "4": {"type": "long"}
        }
      },
      "596": {
        "properties": {
          "1": {"type": "long"},
          "3": {"type": "long"},
          "4": {"type": "long"}
        }
      },
      // ...
    }
  },
}]

При поиске компании, которая предоставляет продукт (ID 1) — поиск 91 и 95, который вернет компанию A, поскольку эти идентификаторы находятся в одном наборе. Но если бы я искал 95 и 76, это не вернуло бы компанию А - хотя компания производит оба этих продукта, они не входят в один и тот же набор. Эти же правила будут применяться при поиске процессов и материалов или их комбинации.

Я ищу подтверждение того, что текущая структура документа/сопоставления облегчит этот тип поиска.

  • Если да, то с учетом 3 массивов идентификаторов (продукты, процессы и материалы), каков JSON для поиска всех компаний, которые предоставляют эти услуги в одном и том же наборе?
  • Если нет, то как следует изменить документ/отображение, чтобы разрешить этот поиск?

Спасибо за помощь.


person bigstylee    schedule 29.03.2020    source источник


Ответы (1)


Плохая идея иметь ID для того, что выглядит как значение как само field, так как это может привести к созданию такого количества инвертированных индексов (помните, что в Elasticsearch инвертированный индекс создается для каждого поля), и я чувствую, что это не так. разумно иметь что-то подобное.

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

Обратите внимание, что для простоты я сосредоточусь только на поле services, которое вы упомянули в своем отображении.

Отображение:

PUT my_services_index
{
  "mappings": {
    "properties": {
      "services":{
        "type": "nested",                   <----- Note this
        "properties": {
          "service_key":{
            "type": "keyword"               <----- Note that I have mentioned keyword here. Feel free to use text and keyword if you plan to implement partial + exact search.
          },
          "product_key": {
            "type": "keyword"
          },
          "product_values": {
            "type": "keyword"
          },
          "process_key":{
            "type": "keyword"
          },
          "process_values":{
            "type": "keyword"
          },
          "material_key":{
            "type": "keyword"
          },
          "material_values":{
            "type": "keyword"
          }
        }
      }
    }
  }
}

Обратите внимание, что я использовал тип данных nested. . Я предлагаю вам перейти по этой ссылке, чтобы понять, зачем нам это нужно, вместо использования простого object.

Образец документа:

POST my_services_index/_doc/1
{
  "services":[
  {
    "service_key": "595",
    "process_key": "1",
    "process_values": ["95", "97", "91"],
    "product_key": "3",
    "product_values": ["475", "476", "471"],
    "material_key": "4",
    "material_values": ["644", "645", "643"]
  },
  {
    "service_key": "596",
    "process_key": "1",
    "process_values": ["91", "89", "75"],
    "product_key": "3",
    "product_values": ["476", "476", "301"],
    "material_key": "4",
    "material_values": ["644", "647", "555"]
  }
    ]
}

Обратите внимание, как теперь вы можете управлять своими данными, если в конечном итоге они состоят из нескольких комбинаций или product_key, process_key and material_key.

То, как вы интерпретируете приведенный выше документ, заключается в том, что у вас есть два вложенных документа внутри документа my_services_index.

Пример запроса:

POST my_services_index/_search
{
  "_source": "services.service_key", 
  "query": {
    "bool": {
      "must": [
        {
          "nested": {                                      <---- Note this
            "path": "services",
            "query": {
              "bool": {
                "must": [
                  {
                    "term": {
                      "services.service_key": "595"
                    }
                  },
                  {
                    "term": {
                      "services.process_key": "1"
                    }
                  },
                  {
                    "term": {
                      "services.process_values": "95"
                    }
                  }
                ]
              }
            },
            "inner_hits": {}                              <---- Note this
          }
        }
      ]
    }
  }
}

Обратите внимание, что я использовал Вложенный запрос.

Ответ:

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.828546,
    "hits" : [                              <---- Note this. Which would return the original document. 
      {
        "_index" : "my_services_index",
        "_type" : "_doc",
        "_id" : "1",
        "_score" : 1.828546,
        "_source" : {
          "services" : [
            {
              "service_key" : "595",
              "process_key" : "1",
              "process_values" : [
                "95",
                "97",
                "91"
              ],
              "product_key" : "3",
              "product_values" : [
                "475",
                "476",
                "471"
              ],
              "material_key" : "4",
              "material_values" : [
                "644",
                "645",
                "643"
              ]
            },
            {
              "service_key" : "596",
              "process_key" : "1",
              "process_values" : [
                "91",
                "89",
                "75"
              ],
              "product_key" : "3",
              "product_values" : [
                "476",
                "476",
                "301"
              ],
              "material_key" : "4",
              "material_values" : [
                "644",
                "647",
                "555"
              ]
            }
          ]
        },
        "inner_hits" : {                    <--- Note this, which would tell you which inner document has been a hit. 
          "services" : {
            "hits" : {
              "total" : {
                "value" : 1,
                "relation" : "eq"
              },
              "max_score" : 1.828546,
              "hits" : [
                {
                  "_index" : "my_services_index",
                  "_type" : "_doc",
                  "_id" : "1",
                  "_nested" : {
                    "field" : "services",
                    "offset" : 0
                  },
                  "_score" : 1.828546,
                  "_source" : {
                    "service_key" : "595",
                    "process_key" : "1",
                    "process_values" : [
                      "95",
                      "97",
                      "91"
                    ],
                    "product_key" : "3",
                    "product_values" : [
                      "475",
                      "476",
                      "471"
                    ],
                    "material_key" : "4",
                    "material_values" : [
                      "644",
                      "645",
                      "643"
                    ]
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

Обратите внимание, что я использовал тип данных keyword. Пожалуйста, не стесняйтесь использовать тип данных в соответствии с вашими бизнес-требованиями для всех полей.

Идея, которую я предложил, состоит в том, чтобы помочь вам понять модель документа.

Надеюсь это поможет!

person Opster ES Ninja - Kamal    schedule 29.03.2020
comment
Большое спасибо, что нашли время, чтобы помочь. Я подозревал, что структура была частью проблемы. Будучи новичком в Elasticsearch и не зная всего жаргона, мне было трудно найти то, что я искал. - person bigstylee; 30.03.2020
comment
Убедитесь, что вы отметили возможные варианты использования, которые у вас есть, и, соответственно, ваш запрос, на основе которого вы можете изменить приведенную выше модель. Я рада, что смогла быть полезной!! - person Opster ES Ninja - Kamal; 30.03.2020