当前位置: 首页 > 知识库问答 >
问题:

Elasticsearch:查询嵌套对象

訾稳
2023-03-14

尊敬的弹性搜索专家,
我在查询嵌套对象时遇到问题。允许使用以下简化映射:

{
  "mappings" : {
    "_doc" : {
      "properties" : {
        "companies" : {
          "type": "nested",
          "properties" : {
            "company_id": { "type": "long" },
            "name": { "type": "text" }
          }
        },
        "title": { "type": "text" }
      }
    }
  }
}

并将一些文档放在索引中:

PUT my_index/_doc/1
{
  "title" : "CPU release",
  "companies" : [
    { "company_id" : 1, "name" :  "AMD" },
    { "company_id" : 2, "name" :  "Intel" }
  ]
}

PUT my_index/_doc/2
{
  "title" : "GPU release 2018-01-10",
  "companies" : [
    { "company_id" : 1, "name" :  "AMD" },
    { "company_id" : 3, "name" :  "Nvidia" }
  ]
}

PUT my_index/_doc/3
{
  "title" : "GPU release 2018-03-01",
  "companies" : [
    { "company_id" : 3, "name" :  "Nvidia" }
  ]
}

PUT my_index/_doc/4
{
  "title" : "Chipset release",
  "companies" : [
    { "company_id" : 2, "name" :  "Intel" }
  ]
}

现在我想执行这样的查询:

{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "GPU" } },
        { "nested": {
            "path": "companies",
            "query": {
              "bool": {
                "must": [
                  { "match": { "companies.name": "AMD" } }
                ]
              }
            },
            "inner_hits" : {}
          }
        }
      ]
    }
  }
}

因此,我想得到匹配的公司与数量匹配的文件。因此,上面的查询应该会告诉我:

[
  { "company_id" : 1, "name" : "AMD", "matched_documents:": 1 }
]

以下查询:

{
  "query": {
    "bool": {
      "must": [
        { "match": { "title": "GPU" } }
        { "nested": {
            "path": "companies",
            "query": { "match_all": {} },
            "inner_hits" : {}
          }
        }
      ]
    }
  }
}

应该给我所有公司分配到一个文件,其标题包含“GPU”与匹配的文件数量:

[
  { "company_id" : 1, "name" : "AMD", "matched_documents:": 1 },
  { "company_id" : 3, "name" : "Nvidia", "matched_documents:": 2 }
]

如果表现良好,是否有可能实现这一结果?我显然对匹配的文档不感兴趣,只对匹配文档和嵌套对象的数量感兴趣。

谢谢你的帮助。

共有1个答案

沈开畅
2023-03-14

就弹性搜索而言,你需要做的是:

  1. 根据所需条件过滤“父”文档(例如在title中包含GPU,或者在公司列表中提及Nvidia);
  2. 根据一定的标准,一个桶(例如company_id)对"嵌套"文档进行分组;
  3. 计算每个桶有多少个“嵌套”文档。

数组中的每个嵌套对象都作为单独的隐藏文档进行索引,这会使生活变得复杂一些。让我们看看如何对它们进行聚合。

您可以通过嵌套、术语和top_hits聚合的组合来实现此目的:

POST my_index/doc/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "title": "GPU"
          }
        },
        {
          "nested": {
            "path": "companies",
            "query": {
              "match_all": {}
            }
          }
        }
      ]
    }
  },
  "aggs": {
    "Extract nested": {
      "nested": {
        "path": "companies"
      },
      "aggs": {
        "By company id": {
          "terms": {
            "field": "companies.company_id"
          },
          "aggs": {
            "Examples of such company_id": {
              "top_hits": {
                "size": 1
              }
            }
          }
        }
      }
    }
  }
}

这将给出以下输出:

{
  ...
  "hits": { ... },
  "aggregations": {
    "Extract nested": {
      "doc_count": 4, <== How many "nested" documents there were?
      "By company id": {
        "doc_count_error_upper_bound": 0,
        "sum_other_doc_count": 0,
        "buckets": [
          {
            "key": 3,  <== this bucket's key: "company_id": 3
            "doc_count": 2, <== how many "nested" documents there were with such company_id?
            "Examples of such company_id": {
              "hits": {
                "total": 2,
                "max_score": 1.5897496,
                "hits": [  <== an example, "top hit" for such company_id
                  {
                    "_nested": {
                      "field": "companies",
                      "offset": 1
                    },
                    "_score": 1.5897496,
                    "_source": {
                      "company_id": 3,
                      "name": "Nvidia"
                    }
                  }
                ]
              }
            }
          },
          {
            "key": 1,
            "doc_count": 1,
            "Examples of such company_id": {
              "hits": {
                "total": 1,
                "max_score": 1.5897496,
                "hits": [
                  {
                    "_nested": {
                      "field": "companies",
                      "offset": 0
                    },
                    "_score": 1.5897496,
                    "_source": {
                      "company_id": 1,
                      "name": "AMD"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

请注意,对于< code>Nvidia,我们有< code >“doc _ count”:2 。

但是,如果我们想计算得到NvidiaIntel

这可以通过< code>reverse_nested聚合来实现。

我们需要稍微修改一下我们的查询:

POST my_index/doc/_search
{
  "query": { ... },
  "aggs": {
    "Extract nested": {
      "nested": {
        "path": "companies"
      },
      "aggs": {
        "By company id": {
          "terms": {
            "field": "companies.company_id"
          },
          "aggs": {
            "Examples of such company_id": {
              "top_hits": {
                "size": 1
              }
            },
            "original doc count": { <== we ask ES to count how many there are parent docs
              "reverse_nested": {}
            }
          }
        }
      }
    }
  }
}

结果将如下所示:

{
  ...
  "hits": { ... },
  "aggregations": {
    "Extract nested": {
      "doc_count": 3,
      "By company id": {
        "doc_count_error_upper_bound": 0,
        "sum_other_doc_count": 0,
        "buckets": [
          {
            "key": 3,
            "doc_count": 2,
            "original doc count": {
              "doc_count": 2  <== how many "parent" documents have such company_id
            },
            "Examples of such company_id": {
              "hits": {
                "total": 2,
                "max_score": 1.5897496,
                "hits": [
                  {
                    "_nested": {
                      "field": "companies",
                      "offset": 1
                    },
                    "_score": 1.5897496,
                    "_source": {
                      "company_id": 3,
                      "name": "Nvidia"
                    }
                  }
                ]
              }
            }
          },
          {
            "key": 1,
            "doc_count": 1,
            "original doc count": {
              "doc_count": 1
            },
            "Examples of such company_id": {
              "hits": {
                "total": 1,
                "max_score": 1.5897496,
                "hits": [
                  {
                    "_nested": {
                      "field": "companies",
                      "offset": 0
                    },
                    "_score": 1.5897496,
                    "_source": {
                      "company_id": 1,
                      "name": "AMD"
                    }
                  }
                ]
              }
            }
          }
        ]
      }
    }
  }
}

为了使区别更加明显,让我们稍微更改一下数据,并在文档列表中添加另一个< code>Nvidia项目:

PUT my_index/doc/2
{
  "title" : "GPU release 2018-01-10",
  "companies" : [
    { "company_id" : 1, "name" :  "AMD" },
    { "company_id" : 3, "name" :  "Nvidia" },
    { "company_id" : 3, "name" :  "Nvidia" }
  ]
}

最后一个查询(带有< code>reverse_nested的查询)将给出以下内容:

  "By company id": {
    "doc_count_error_upper_bound": 0,
    "sum_other_doc_count": 0,
    "buckets": [
      {
        "key": 3,
        "doc_count": 3,    <== 3 "nested" documents with Nvidia
        "original doc count": {
          "doc_count": 2   <== but only 2 "parent" documents
        },
        "Examples of such company_id": {
          "hits": {
            "total": 3,
            "max_score": 1.5897496,
            "hits": [
              {
                "_nested": {
                  "field": "companies",
                  "offset": 2
                },
                "_score": 1.5897496,
                "_source": {
                  "company_id": 3,
                  "name": "Nvidia"
                }
              }
            ]
          }
        }
      },

如您所见,这是一个难以理解的细微差异,但它完全改变了语义。

虽然在大多数情况下,嵌套查询和聚合的性能应该足够了,但它当然会带来一定的成本。因此,在调整搜索速度时,建议避免使用嵌套或父子类型。

在Elasticsearch中,最佳性能通常是通过非规范化实现的,尽管没有单一的配方,您应该根据需要选择数据模型。

希望这能为您澄清这个嵌套的事情!

 类似资料:
  • 问题内容: 这是我在elasticsearch中存储在索引上的数据类型。我必须找到包含主要成分牛肉(且重量小于1000)和成分-(辣椒粉且重量小于250),(橄榄油和重量小于300)以及所有其他成分类似的食谱。 索引的映射是 我的查询是 但这给了Null。有人可以帮我吗?我认为我没有正确使用嵌套查询 问题答案: 试试这个:

  • 我试图创建一个嵌套查询,它将过滤掉一些带有特定术语的文档。在本例中,我试图过滤掉在user.first中有匹配术语的文档。数据示例: 我的查询没有得到所需的结果,因为它返回给我所有未筛选的记录。我尝试使用: 我希望这里得到与过滤器不匹配的文档。在这种情况下,它应该只返回第二个文档。做这件事的正确方法是什么?

  • 问题内容: 如何转换这种查询。 使用QueryBuilders进行Java客户端查询 问题答案: 以下Java代码将生成您的查询 参数为: 参数为:

  • 问题内容: 我正在尝试使用query_string编写查询以检索嵌套对象的数据查询。 我想做的查询的一个例子是: 其中“ a”是嵌套对象,“ id”是“ a”的字段。 我知道我可以使用嵌套查询成功地执行此任务,编写如下查询: 但是,我想避免这种情况。我不想自己弄清楚用户正在搜索嵌套字段并修改查询。我尝试使用“ fields”参数,但它似乎不适用于嵌套对象。 是否可以使用“ query_string

  • 我有一个带有嵌套映射的索引。我想预先形成一个查询,该查询将返回以下内容:给我搜索词中每个单词出现在一个或多个嵌套文档中的所有文档。 以下是索引: 以下是我尝试的最新查询: 例如,如果我搜索单词“食物和水”,我希望每个单词至少出现在嵌套文档中。即使只有一个单词存在,当前搜索也会返回文档 谢谢你的帮助! 更新:正如克里斯托所建议的,解决方案有效。现在我有以下问题。 这是我的索引: 我要执行的查询是,如

  • 问题内容: 我在获取与Elasticsearch一起使用的嵌套查询时遇到问题(如果我删除了查询字符串之一,则可以使用)。我要解决的问题是我有一个包含关闭列表的文档(关闭)。我想在条件满足另一个值的闭包列表中搜索一个值。那只是从argan = 1的闭包中获得价值 我正在得到这个错误响应; 我的映射如下所示。 有人知道我在做什么错吗? 问题答案: 您的查询不是有效的查询。您需要使用适当的复合查询将其他