有什么事
如何在网上商店中进行多面搜索?多面搜索过滤器中的值是如何生成的?在过滤器中选择一个值如何影响相邻过滤器中的值?为了寻找答案,我进入了Google搜索结果的第五页。我没有找到详尽的信息,我不得不自己弄清楚。本文介绍:
- 用户使用过滤器时,UI的反应如何;
- 生成过滤器值的算法;
- ElasticSearch查询模板和索引结构及其说明。
这里没有现成的解决方案。您无法复制和粘贴。为了解决自己的问题,您必须深入研究。
明确概念
全文搜索-按单词或短语搜索产品。对于用户而言,这是一个用于使用“查找”按钮输入文本的字段,该字段可在网站的任何页面上使用。
多面搜索-通过以下几种特征搜索产品:颜色,大小,内存大小,价格等。对于用户来说,它是一组过滤器。每个滤波器仅与一个特征相关,反之亦然。过滤器值是特征的所有可能值。用户可以在带有全文搜索结果的页面的部分页面,类别中看到过滤器。当用户选择一个值时,该过滤器被认为是活动的。
多面过滤器行为
简而言之,一个过滤器过滤产品,并在其他过滤器中过滤选择选项。
过滤产品
这很容易。用户选择了:
- 一个价值,看到与价值相匹配的产品;
- 在一个过滤器中有多个值,可以看到至少匹配一个的产品;
- 几个过滤器中的值,查看与每个过滤器中的值相匹配的产品。
用布尔代数来说:过滤器之间有一个逻辑``与'',过滤器中的值之间有一个逻辑``或''。简单的逻辑。
过滤其他过滤器中的选择
“嗯...那里有什么选项-显示,没有-隐藏什么”-这就是企业描述过滤器行为的方式。听起来合乎逻辑。实际上,它的工作方式如下:
- 转到“电话”部分,按特征查看过滤器:品牌,对角线,记忆。每个过滤器都包含值。
- . . 1.
- . , . , 2.
- . . , 3.
- «» . 3 ..
过滤器值的数量取决于产品的数量:具有不同特征值的产品越多,过滤器中的值就越多。当用户选择品牌时,用户会减少其余过滤器的选择产品数量。这导致值列表的更新。
这引起了一个通用规则:从产品的选择中检索过滤器值,这是由其余的有源过滤器形成的。
每个有源滤波器都有自己的产品选择。
如果我们有N个过滤器,并且:
- 不活跃,则样本是常规的。所有过滤器都相同,并且匹配搜索结果;
- M是活动的,并且M <N,则样本数为M +1,其中1是应用了所有有源滤波器的样本。所有无效过滤器均相同,并且与搜索结果一致;
- 激活M,并且N = M,则样本数为N。每个过滤器都有自己的样本。
最终,当用户选择构面过滤器值时,会发生以下情况:
- 形成对商品的搜索选择;
- 从搜索选择中检索无效过滤器的值;
- 对于每个有源滤波器,形成一个新样本并从中提取有源滤波器的新值。
出现了问题-如何在实践中实施?
Elasticsearch(ES)实施
产品特性不是通用的,因此在这里您将找不到用于存储产品或现成查询的现成索引结构。相反,将有指向文档的链接,这些文档解释了如何构建“正确的”索引并自行查询。“正确”-根据我的经验和知识。
文本框的“正确”类型
在ES中,我们对2种数据类型感兴趣:
ES解析文本字段中的值并构建用于全文本搜索的字典。关键字字段中的值被索引为已接收。汇总和排序仅适用于关键字字段。
两种情况下,用户都使用特征:全文搜索和通过过滤器。ES不允许指定2种类型单场,但提供其他解决方案:
领域
PUT my_index
{
«mappings»: {
«properties»: {
«some_property»: {
«type»: «text», // 1
«fields»: { // 2
«raw»: {
«type»: «keyword»
}
}
}
}
}
}
因此,对于每个特征。
在进行精确比较,排序和聚合操作的查询中,必须使用类型为keyword的子虚拟字段。在示例中,这是some_property.raw。对于文本搜索-父级。
copy_to。
PUT my_index
{
«mappings»: {
«properties»: {
«all_properties»: { // 1
«type»: «text»
}, «some_property_1»: {
«type»: «keyword»,
«copy_to»: «all_properties» // 2
},
«some_property_2»: {
«type»: «keyword»,
«copy_to»: «all_properties»
}
}
}
为了进行精确比较,排序和汇总操作,您需要使用特征字段进行文本搜索-一个具有所有特征值的字段。
两种方法都在索引中创建原始文档结构中不存在的其他字段。因此,要创建查询,您需要知道索引的结构。
我更喜欢copy_to选项。然后,要构建全文搜索查询,只需知道一个具有所有特征值的副本的字段即可。
询价
搜索产品
假设索引结构与copy_to变体中的相同。对于ES中的全文搜索,使用match结构,用于与多面过滤器的值进行比较-术语查询。 布尔查询将构造合并为一个查询。将会是这样的:
{
«query» : {
«bool»: {
«must»: {
«match»: {
«virtual_field_for_fulltext_searching»: {
«query»: «some text»
}
}
},
«filter»: {
«must»: [
{«property_1»: [ «value_1_1», …, «value_1_n»]},
…
{«property_n»: [ «value_n_1», …, «value_n_m»]}
]
}
}
}
}
query.bool.must.match主要全文搜索
查询query.bool.filter过滤器可优化主查询。必须在内部意味着过滤器之间的逻辑“与”。每个过滤器中的值数组是布尔值或。
对于过滤器值
该术语聚集子句基团的产物通过特性值,并计算每个组中的数量。此操作称为聚合。困难在于,对于每个有源滤波器, 术语聚合必须在其他有源滤波器形成的产品选择上执行。对于无效过滤器-选择与搜索结果匹配的过滤器。筛选器聚合构造使您可以为每个聚合创建单独的选择,并将操作“打包”到一个查询中。
请求结构将如下所示:
{
«size»: 0,
«query» : {
«bool»: {
«must»: {
«match»: {
«field_for_fulltext_searching»: {
«fuzziness»: 2,
«query»: «some text»
}
}
},
«filter»: {
}
}
},
«aggs» : {
«inavtive_filter_agg» : {
«filter» : { …
},
«aggs»: {
«some_inavtive_filter_subagg»: {
«terms» : {
«field» : «some_property»
}
},
...
«some_other_inavtive_filter_subagg»: {
«terms» : {
«field» : «some_other_property»
}
}
}
},
«active_filter_1_agg» : {
«filter»: {
… },
«aggs»: {
«active_filter_1_subagg»: {
«terms» : {
«field»: «property_1»
}
}
}
},
…,
«active_filter_N_agg» : {
«filter»: {
…
},
«aggs»: {
«active_filter_N_subagg»: {
«terms» : {
«field»: «property_N»
}
}
}
}
}
}
query.bool-主查询,过滤操作在其上下文中执行。它包括:
- 匹配-请求全文搜索;
- 过滤器-按与方面过滤器无关的特征进行过滤,并且必须存在于任何子集中。如果您始终只想显示库存产品或仅显示可见产品,则可以按in_stock,is_visible进行过滤。
aggs.inavtive_filter_agg-非活动构面过滤器的聚合包括:
- 过滤器- 由活动的构面过滤器形成的特征条件。与主要查询一起,形成了商品选择,在该商品上执行了本节的子汇总;
- aggs是每个不活动过滤器的命名聚合的对象。
aggs.active_filter_1_agg-汇总第一个活动构面过滤器的值。每种设计都与一个方面过滤器相关联。由组成:
- 过滤器-由主动小平面过滤器形成的特征所构成的条件,当前的除外。它与主查询一起形成一个商品选择,在此商品上执行本节的子汇总;
- aggs-根据当前活动的构面滤镜的特征来自一个聚合的对象。
重要的是指定“ size”:0,否则您将获得与主查询匹配且没有聚合的产品列表。
最终
收到两个请求:
- 对于搜索结果,返回要显示给用户的产品;
- 对于过滤器值,执行汇总,返回过滤器值以及具有该值的产品数量。
每个请求都是独立的,因此最好异步执行它们。
PS我承认,有更多“正确”的方法和工具可以解决分面搜索的问题。谢谢您提供其他信息和评论中的示例。