Elasticsearch 5.4嵌套搜索仅返回匹配的嵌套数据

时间:2017-09-05 14:36:56

标签: elasticsearch elasticsearch-5

运行Elasticsearch的5.4版。

使用此映射:

PUT pizzas
{
  "mappings": {
    "pizza": {
      "properties": {
        "name": {
          "type": "keyword"
        },
        "types": {
          "type": "nested",
          "properties": {
            "topping": {
              "type": "keyword"
            },
            "base": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

这个数据:

PUT pizzas/pizza/1
{
  "name": "meat",
  "types": [
    {
      "topping": "bacon",
      "base": "normal"
    },
    {
      "topping": "bacon",
      "base": "sour dough"
    },
    {
      "topping": "pepperoni",
      "base": "sour dough"
    }
  ]
}

如果我运行此查询:

GET pizzas/_search
{
  "query": {
    "nested": {
      "path": "types",
      "query": {
        "bool": {
          "filter": {
            "term": {
              "types.topping": "bacon"
            }
          }
        }
      }
    }
  }
}

我明白了:

{
  "took": 1,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0,
    "hits": [
      {
        "_index": "pizzas",
        "_type": "pizza",
        "_id": "1",
        "_score": 0,
        "_source": {
          "name": "meat",
          "types": [
            {
              "topping": "bacon",
              "base": "normal"
            },
            {
              "topping": "bacon",
              "base": "sour dough"
            },
            {
              "topping": "pepperoni",
              "base": "sour dough"
            }
          ]
        }
      }
    ]
  }
}

但我真正想要的是我的热门歌曲:

"hits": [
  {
    "_index": "pizzas",
    "_type": "pizza",
    "_id": "1",
    "_score": 0,
    "_source": {
      "name": "meat",
      "types": [
        {
          "topping": "bacon",
          "base": "normal"
        }
      ]
    }
  },
  {
    "_index": "pizzas",
    "_type": "pizza",
    "_id": "1",
    "_score": 0,
    "_source": {
      "name": "meat",
      "types": [
        {
          "topping": "bacon",
          "base": "sour dough"
        }
      ]
    }
  }
]

我想这样做,所以如果用户搜索“培根”,他们会得到一个披萨选项列表,他们可以使用哪些包括该顶部。

这甚至得到了Elasticsearch的支持吗?我可以通过编程方式分离出我的结果,但我希望它是内置的。

感谢您的时间。

2 个答案:

答案 0 :(得分:0)

解决此问题的一种可能方法可能是使用_parent_child关系并从其类型中分割出比萨饼:

PUT pizzas
{
  "mappings": {
    "pizza": {
      "properties": {
        "name": {
          "type": "keyword"
        },
        "rating": {
          "type": "integer"
        }
      }
    },
    "type": {
      "_parent": {
        "type": "pizza" 
      },
      "properties": {
        "types": {
          "properties": {
            "topping": {
              "type": "keyword"
            },
            "base": {
              "type": "keyword"
            }
          }
        }
      }
    }
  }
}

PUT pizzas/pizza/1
{
  "name": "meat",
  "rating": 5
}

PUT pizzas/type/1?parent=1
{
  "topping": "bacon",
  "base": "normal"
}

PUT pizzas/type/2?parent=1
{
  "topping": "bacon",
  "base": "sour dough"
}

PUT pizzas/type/3?parent=1
{
  "topping": "pepperoni",
  "base": "sour dough"
}

然后,您可以只搜索孩子,还可以查看与其相关的父母。

查询:

GET pizzas/type/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "topping": "bacon"
        }
      }
    }
  }
}

结果:

{
  "took": 0,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 2,
    "max_score": 0,
    "hits": [
      {
        "_index": "pizzas",
        "_type": "type",
        "_id": "1",
        "_score": 0,
        "_routing": "1",
        "_parent": "1",
        "_source": {
          "topping": "bacon",
          "base": "normal"
        }
      },
      {
        "_index": "pizzas",
        "_type": "type",
        "_id": "2",
        "_score": 0,
        "_routing": "1",
        "_parent": "1",
        "_source": {
          "topping": "bacon",
          "base": "sour dough"
        }
      }
    ]
  }
}

在您的代码中,您可以将数据结合起来以创建所需的原始数据结构。

注意事项

更改结构有两个问题:

一:如果您需要按子女(source)对父母进行排序,则可以设置普通排序。

二:如果您还需要过滤其他字段,则最终需要运行查询,例如:

GET pizzas/pizza/_search
{
  "query": {
    "bool": {
      "filter": {
        "term": {
          "rating": 5
        }
      },
      "must": {
        "has_child": {
          "type": "type",
          "query": {
            "bool": {
              "filter": {
                "term": {
                  "topping": "bacon"
                }
              }
            }
          }
        }
      }
    }
  }
}

然后针对那些需要重新连接到父级的特定子级的另一个查询。

答案 1 :(得分:0)

您可以使用"inner_hits"在嵌套搜索中获取特定匹配的匹配:

查询:

GET pizzas/_search
{
  "query": {
    "nested": {
      "path": "types",
      "query": {
        "bool": {
          "filter": {
            "term": {
              "types.topping": "bacon"
            }
          }
        }
      },
      "inner_hits": {
          "size": 10
      }
    }
  }
}

请注意,“inner_hits”将返回3个结果,除非明确告知返回不同的金额。您可以看到选项here

似乎没有设置size的选项,您只需将其设置为高于您将拥有的最大内部数量。

结果:

{
  "took": 3,
  "timed_out": false,
  "_shards": {
    "total": 5,
    "successful": 5,
    "failed": 0
  },
  "hits": {
    "total": 1,
    "max_score": 0,
    "hits": [
      {
        "_index": "pizzas",
        "_type": "pizza",
        "_id": "1",
        "_score": 0,
        "_source": {
          "name": "meat",
          "types": [
            {
              "topping": "bacon",
              "base": "normal"
            },
            {
              "topping": "bacon",
              "base": "sour dough"
            },
            {
              "topping": "pepperoni",
              "base": "sour dough"
            }
          ]
        },
        "inner_hits": {
          "types": {
            "hits": {
              "total": 2,
              "max_score": 0,
              "hits": [
                {
                  "_nested": {
                    "field": "types",
                    "offset": 1
                  },
                  "_score": 0,
                  "_source": {
                    "topping": "bacon",
                    "base": "sour dough"
                  }
                },
                {
                  "_nested": {
                    "field": "types",
                    "offset": 0
                  },
                  "_score": 0,
                  "_source": {
                    "topping": "bacon",
                    "base": "normal"
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

使用您的代码,您可以将hits和inner_hits连接在一起,以便返回的唯一类型是相关的。