弹性搜索 - 产品选项的聚合过滤器

Elastic search - aggregation filter for product options

我有一个产品目录,其中每个产品的索引如下(从 http://localhost:9200/products/_doc/1 查询)作为示例:

{
  "_index": "products_20201202145032789",
  "_type": "_doc",
  "_id": "1",
  "_version": 1,
  "_seq_no": 0,
  "_primary_term": 1,
  "found": true,
  "_source": {
    "title": "Roncato Eglo",
    "description": "Amazing LED light made of wood and description continues.",
    "price": 3990,
    "manufacturer": "Eglo",
    "category": [
      "Lights",
      "Indoor lights"
    ],
    "options": [
      {
        "title": "Mount type",
        "value": "E27"
      },
      {
        "title": "Number of bulps",
        "value": "4"
      },
      {
        "title": "Batteries included",
        "value": "true"
      },
      {
        "title": "Ligt temperature",
        "value": "warm"
      },
      {
        "title": "Material",
        "value": "wood"
      },
      {
        "title": "Voltage",
        "value": "230"
      }
    ]
  }
}

每个选项包含不同的值,因此有很多安装类型值、光照温度值、Material 值等。

如何创建聚合(过滤器),让客户在各种 安装类型 选项中进行选择:

[ ] E27
[X] E14
[X] GU10
...

或者让他们从显示为复选框的不同 Material 选项中进行选择:

[X] Wood
[ ] Metal
[ ] Glass
...

创建存储桶后,我可以在前端处理它。为这些选项创建不同的存储桶是我正在努力的。

我已成功创建、显示和使用 CategoryManufacturer 和其他基本类别的聚合。这些产品选项存储在数据库中的 has_many_through 关系中。我正在使用 Rails + searchkick gem,但这些允许我为弹性搜索创建原始查询。

这种聚合的先决条件是 options 字段为 nested

示例索引映射:

PUT test
{
  "mappings": {
    "properties": {
      "title": {
        "type": "keyword"
      },
      "options": {
        "type": "nested",
        "properties": {
          "title": {
            "type": "keyword"
          },
          "value": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

示例文档:

PUT test/_doc/1
{
  "title": "Roncato Eglo",
  "options": [
    {
      "title": "Mount type",
      "value": "E27"
    },
    {
      "title": "Material",
      "value": "wood"
    }
  ]
}

PUT test/_doc/2
{
  "title": "Eglo",
  "options": [
    {
      "title": "Mount type",
      "value": "E27"
    },
    {
      "title": "Material",
      "value": "metal"
    }
  ]
}

假设: 对于给定的文档,option 下的 title 仅出现一次。例如option 下只能存在一个嵌套文档,其中 titleMaterial

聚合查询:

GET test/_search
{
  "size": 0, 
  "aggs": {
    "OPTION": {
      "nested": {
        "path": "options"
      },
      "aggs": {
        "TITLE": {
          "terms": {
            "field": "options.title",
            "size": 10
          },
          "aggs": {
            "VALUES": {
              "terms": {
                "field": "options.value",
                "size": 10
              }
            }
          }
        }
      }
    }
  }
}

响应:

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 2,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "aggregations" : {
    "OPTION" : {
      "doc_count" : 4,
      "TITLE" : {
        "doc_count_error_upper_bound" : 0,
        "sum_other_doc_count" : 0,
        "buckets" : [
          {
            "key" : "Material",
            "doc_count" : 2,
            "VALUES" : {
              "doc_count_error_upper_bound" : 0,
              "sum_other_doc_count" : 0,
              "buckets" : [
                {
                  "key" : "metal",
                  "doc_count" : 1
                },
                {
                  "key" : "wood",
                  "doc_count" : 1
                }
              ]
            }
          },
          {
            "key" : "Mount type",
            "doc_count" : 2,
            "VALUES" : {
              "doc_count_error_upper_bound" : 0,
              "sum_other_doc_count" : 0,
              "buckets" : [
                {
                  "key" : "E27",
                  "doc_count" : 2
                }
              ]
            }
          }
        ]
      }
    }
  }
}