一尘不染

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

elasticsearch

在我们的一个新项目中,我们受本文http://project-a.github.io/on-site-search-design-patterns-
for-e-commerce/#generic-faceted-search的启发而做“构面”结构。虽然在本文所述的范围内可以使它正常工作,但是在选择构面时却遇到了使其无法工作的问题。我希望有人可以提示您尝试一下,所以我不必再次将所有聚合重做为单独的聚合计算。

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

查看https://www.contorion.de/search/Metabo_Fein/ou1-ou2?q=Winkelschleifer&c=bovy(上面文章中描述的网站),我选择了“
Metabo”和“ Fein”制造商(
Hersteller),然后展开Hersteller菜单,它会显示所有制造商,而不仅仅是选定的制造商。因此,我知道这是有可能的,我希望那里的某个人对如何编写聚合/过滤器有所提示,以便获得“正确的电子商务方面行为”。

在ES中的产品上,我具有以下结构:(与原始文章中的相同,尽管命名中为“ C#”化)

"attributeStrings": [
    {
        "facetName": "Property",
        "facetValue": "Organic"
    },
    {
        "facetName": "Property",
        "facetValue": "Without parfume"
    },
    {
        "facetName": "Brand",
        "facetValue": "Adidas"
    }
]

因此,上述产品具有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"
                      }
                    ]
   } } } } } } } }

现在,如果我选择属性“有机”和品牌“阿迪达斯”,我将建立相同的聚合,但是要应用这两个约束条件的过滤器(确实有点不对劲…):

  "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"
                      }
                    ]
   } } } } } } } }

我可以用此模型向前看的唯一方法是计算每个选定方面的聚合并以某种方式合并结果。但是,这似乎非常复杂并且有点像文章中描述的那样具有模型的要点,因此我希望有一个更简洁的解决方案,并且有人可以提供一些尝试的提示。


阅读 316

收藏
2020-06-22

共1个答案

一尘不染

我可以用此模型向前看的唯一方法是计算每个选定方面的聚合并以某种方式合并结果。

这是完全正确的。如果选择了一个构面(例如 brand ),则如果您还想获取其他品牌进行多选,则无法使用全局品牌过滤器。您可以做的是将所有 其他
过滤器应用于选定的方面,并将 所有 过滤器
应用于未选定的方面。结果,您将为选定的过滤器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"
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

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

但是,这似乎非常复杂并且有点像文章中描述的那样具有模型的要点,因此我希望有一个更简洁的解决方案,并且有人可以提供一些尝试的提示。

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

免责声明 :我是上述文章的合著者。

2020-06-22