Elasticsearch - 通用构面结构 - 计算与过滤器结合的聚合

Reo*_*kot 14 aggregate facet elasticsearch

在我们的一个新的项目,我们在这篇文章的启发http://project-a.github.io/on-site-search-design-patterns-for-e-commerce/#generic-faceted-search做我们的"facet"结构.虽然我已经在文章描述的范围内使用它,但是在选择方面时我遇到了让它工作的问题.我希望有人能给出一些可以尝试的提示,所以我不必再将所有聚合重做为单独的聚合计算.

问题基本上是我们使用单个聚合一次计算所有"方面",但是当我添加过滤器(fx.检查品牌名称)时,它会在返回聚合时"删除"所有其他品牌.我基本上想要的是它在计算其他方面时应该使用该品牌作为过滤器,但在计算品牌聚合时则不然.这是必要的,因此用户可以选择多个品牌.

看看https://www.contorion.de/search/Metabo_Fein/ou1-ou2?q=Winkelschleifer&c=bovy(这是上面文章中描述的网站),我选择了"Metabo"和"Fein"制造商( Hersteller),并展示了Hersteller菜单,它显示了所有制造商,而不仅仅是所选择的制造商.所以我知道它有可能以某种方式,我希望有一个关于如何编写聚合/过滤器的提示,所以我得到了"正确的电子商务方面行为".

在ES中的产品我有以下结构:(与原始文章相同,但在命名时使用"C#"ified)

"attributeStrings": [
    {
        "facetName": "Property",
        "facetValue": "Organic"
    },
    {
        "facetName": "Property",
        "facetValue": "Without parfume"
    },
    {
        "facetName": "Brand",
        "facetValue": "Adidas"
    }
]
Run Code Online (Sandbox Code Playgroud)

所以上面的产品有2个属性/方面组 - 属性有2个值(有机,没有香水)和品牌有1个值(阿迪达斯).没有任何过滤器,我从以下查询计算聚合:

  "aggs": {
    "agg_attr_strings_filter": {
      "filter": {},
      "aggs": {
        "agg_attr_strings": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "attr_name": {
              "terms": {
                "field": "attributeStrings.facetName"
              },
              "aggs": {
                "attr_value": {
                  "terms": {
                    "field": "attributeStrings.facetValue",
                    "size": 1000,
                    "order": [
                      {
                        "_term": "asc"
                      }
                    ]
   } } } } } } } }
Run Code Online (Sandbox Code Playgroud)

现在,如果我选择属性"有机"和品牌"阿迪达斯"我构建相同的聚合,但使用过滤器来应用这两个约束(这是有点出错......):

  "aggs": {
    "agg_attr_strings_filter": {
      "filter": {
        "bool": {
          "filter": [
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Property"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Organic"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            },
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Brand"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Adidas"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            }
          ]
        }
      },
      "aggs": {
        "agg_attr_strings": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "attr_name": {
              "terms": {
                "field": "attributeStrings.facetName",
              },
              "aggs": {
                "attr_value": {
                  "terms": {
                    "field": "attributeStrings.facetValue",
                    "size": 1000,
                    "order": [
                      {
                        "_term": "asc"
                      }
                    ]
   } } } } } } } }
Run Code Online (Sandbox Code Playgroud)

我可以看到这个模型的唯一方法是计算每个选定方面的聚合,并以某种方式合并结果.但它看起来非常复杂,有点像文章中所描述的那样使模型失败,所以我希望有一个更清晰的解决方案,有人可以提供一些尝试.

hak*_*kaa 19

我可以看到这个模型的唯一方法是计算每个选定方面的聚合,并以某种方式合并结果.

这是完全正确的.如果您选择了一个方面(例如品牌)而不是使用全球品牌过滤器,如果您还想获取其他品牌进行多选.您可以执行的操作是应用所选构面上的所有其他滤镜以及未选定构面上的所有 滤镜.作为结果,您将为选定的过滤器n+1分别进行聚合n- 第一个用于所有方面,其余用于选定方面.

在您的情况下,查询可能如下所示:

{
  "aggs": {
    "agg_attr_strings_filter": {
      "filter": {
        "bool": {
          "filter": [
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Property"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Organic"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            },
            {
              "nested": {
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Brand"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Adidas"
                          ]
                        }
                      }
                    ]
                  }
                },
                "path": "attributeStrings"
              }
            }
          ]
        }
      },
      "aggs": {
        "agg_attr_strings": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "attr_name": {
              "terms": {
                "field": "attributeStrings.facetName"
              },
              "aggs": {
                "attr_value": {
                  "terms": {
                    "field": "attributeStrings.facetValue",
                    "size": 1000,
                    "order": [
                      {
                        "_term": "asc"
                      }
                    ]
                  }
                }
              }
            }
          }
        }
      }
    },
    "special_agg_property": {
      "filter": {
        "nested": {
          "query": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "attributeStrings.facetName": {
                      "value": "Brand"
                    }
                  }
                },
                {
                  "terms": {
                    "attributeStrings.facetValue": [
                      "Adidas"
                    ]
                  }
                }
              ]
            }
          },
          "path": "attributeStrings"
        }
      },
      "aggs": {
        "special_agg_property": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "agg_filtered_special": {
              "filter": {
                "query": {
                  "match": {
                    "attributeStrings.facetName": "Property"
                  }
                }
              },
              "aggs": {
                "facet_value": {
                  "terms": {
                    "size": 1000,
                    "field": "attributeStrings.facetValue"
                  }
                }
              }
            }
          }
        }
      }
    },
    "special_agg_brand": {
      "filter": {
        "nested": {
          "query": {
            "bool": {
              "filter": [
                {
                  "term": {
                    "attributeStrings.facetName": {
                      "value": "Property"
                    }
                  }
                },
                {
                  "terms": {
                    "attributeStrings.facetValue": [
                      "Organic"
                    ]
                  }
                }
              ]
            }
          },
          "path": "attributeStrings"
        }
      },
      "aggs": {
        "special_agg_brand": {
          "nested": {
            "path": "attributeStrings"
          },
          "aggs": {
            "agg_filtered_special": {
              "filter": {
                "query": {
                  "match": {
                    "attributeStrings.facetName": "Brand"
                  }
                }
              },
              "aggs": {
                "facet_value": {
                  "terms": {
                    "size": 1000,
                    "field": "attributeStrings.facetValue"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

这个查询看起来超级大而可怕,但生成这样的查询可以用几十行代码完成.解析查询结果时,需要首先解析一般聚合(使用所有过滤器的聚合)和特殊构面聚合之后.从上面的示例,从第一解析结果agg_attr_strings_filter,但这些结果也将包含聚集值的品牌财产应该由聚合值从被覆盖special_agg_property,并special_agg_brand 另外,这个查询是有效的,因为Elasticsearch确实做好缓存单独的过滤条款,从而适用相同的筛选器在查询的不同部分应该便宜.

但它看起来非常复杂,有点像文章中所描述的那样使模型失败,所以我希望有一个更清晰的解决方案,有人可以提供一些尝试.

实际上,您无需将不同的过滤器应用于不同的方面,同时具有不同的查询过滤器.如果您需要支持"正确的电子商务方面行为",您将有复杂的查询:)

免责声明:我是上述文章的合着者.

  • 非常感谢您的回答和意见.(对于迟到的反馈感到抱歉 - 流感对我来说变得更好.)它确实很有意义,在考虑这个问题时看起来非常冗长,所以非常了解这一点,以及ES中的表现.如果其他人想要这样做,请仔细阅读"hakaa"的答案,因为它提供了创建提示,我希望我一开始就更加小心.:) (3认同)
  • 嗨,我知道这是一个非常古老的线程,但我想知道是否有人可以为我澄清:虽然这个解决方案似乎合理,但我对 ElasticSearch 的了解非常有限,但我正在快速学习:这种多重聚合实际上如何如所描述的那样工作,如果它们是在结果集上计算的?例如,如果我有一个书籍“装订”(平装本、精装本等)的列表并通过“平装本”过滤我的结果,如果结果决定了 aggs,我如何还可以获得其他装订的装订 aggs首先?agg 应该在不同的范围内吗?可以在单个查询中完成吗? (2认同)

Val*_*Val 6

问题来自以下事实:您聚合的内部PropertyOrganic 内部添加了一个过滤器,因此,选择的方面越多,约束条件越多。在那篇文章中,filter他们实际上使用的是a post_filter,两个名称直到最近都被允许使用,但是由于造成歧义而filter 被删除

您需要做的是将过滤器移到聚合之外的post_filter部分中,以使结果能够被选择的任何构面正确地过滤掉,但您的所有构面仍将在整个文档集中正确计算。

{
  "post_filter": {
    "bool": {
      "filter": [
        {
          "nested": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "term": {
                      "attributeStrings.facetName": {
                        "value": "Property"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attributeStrings.facetValue": [
                        "Organic"
                      ]
                    }
                  }
                ]
              }
            },
            "path": "attributeStrings"
          }
        },
        {
          "nested": {
            "query": {
              "bool": {
                "filter": [
                  {
                    "term": {
                      "attributeStrings.facetName": {
                        "value": "Brand"
                      }
                    }
                  },
                  {
                    "terms": {
                      "attributeStrings.facetValue": [
                        "Adidas"
                      ]
                    }
                  }
                ]
              }
            },
            "path": "attributeStrings"
          }
        }
      ]
    }
  },
  "aggs": {
    "agg_attr_strings_full": {
      "nested": {
        "path": "attributeStrings"
      },
      "aggs": {
        "attr_name": {
          "terms": {
            "field": "attributeStrings.facetName"
          },
          "aggs": {
            "attr_value": {
              "terms": {
                "field": "attributeStrings.facetValue",
                "size": 1000,
                "order": [
                  {
                    "_term": "asc"
                  }
                ]
              }
            }
          }
        }
      }
    },
    "agg_attr_strings_filtered": {
      "filter": {
        "bool": {
          "filter": [
            {
              "nested": {
                "path": "attributeStrings",
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Property"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Organic"
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            },
            {
              "nested": {
                "path": "attributeStrings",
                "query": {
                  "bool": {
                    "filter": [
                      {
                        "term": {
                          "attributeStrings.facetName": {
                            "value": "Brand"
                          }
                        }
                      },
                      {
                        "terms": {
                          "attributeStrings.facetValue": [
                            "Adidas"
                          ]
                        }
                      }
                    ]
                  }
                }
              }
            }
          ]
        }
      },
      "aggs": {
        "nested": {
          "path": "attributeStrings"
        },
        "aggs": {
          "attr_name": {
            "terms": {
              "field": "attributeStrings.facetName"
            },
            "aggs": {
              "attr_value": {
                "terms": {
                  "field": "attributeStrings.facetValue",
                  "size": 1000,
                  "order": [
                    {
                      "_term": "asc"
                    }
                  ]
                }
              }
            }
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)