在線上商店中,分面看似簡單,但一旦選取第一個篩選條件,情況就變得複雜。

在商品目錄中,分面是導覽的一部分。已選取的顏色不應從清單中消失,其他顏色應保持可用:使用者可能想要切換顏色,或擴大選擇範圍。沒有相符產品的選項顯示為不可用會更有用。在單一分面內,通常適用 OR:紅色或藍色。在不同分面之間,則適用 AND:同時依品牌、顏色與尺寸篩選。

難點始於第一次選取之後。想像一個使用者已選取品牌、顏色與尺寸的商品目錄。主要結果集必須保持精確,只顯示符合這三個條件的產品。但篩選面板遵循不同的規則。它需要保留已選取的顏色,同時顯示使用者在相同品牌與尺寸下可以切換的顏色。

如果每個分面都繼承主要查詢的所有篩選條件,很快就會收縮到已選取的值。如果必須手動重建每個分面的篩選條件,最終會產生針對顏色、尺寸、品牌、庫存狀態、賣家以及其他屬性的獨立查詢分支。

Manticore Search 25.12.0 引入了 facet_filter_mode,將此行為移入分面 API。查詢現在可以描述商店篩選面板應有的行為,而不需要應用程式為每個分面手動建立幾乎相同的查詢。

電子商務分面需要什麼

對於篩選面板而言,僅回答「有多少產品具有此值?」是不夠的。它需要介面可直接使用的狀態:

  • 已選取的值保持可見,且可快速清除;
  • 同一分面中的其他值保持可用,因為它們可透過 IN (...) 擴大篩選範圍;
  • 其他分面中的值可指出選取它們是否會傳回至少一項產品;
  • 計數保持可預測:介面知道數字是指目前的結果集還是更廣泛的選擇;
  • 在冗長的品牌清單、類別、價格範圍、SEO 規則與查詢分析中進行搜尋仍然重要;它們是搜尋設計的另一部分。

facet_filter_mode 處理此問題最常見的部分:在使用者已選取多個篩選條件後重新計算分面。

變更內容

分面現在有三種篩選繼承模式:

模式 行為
strict 將主要查詢的所有篩選條件套用至分面。這是先前的行為,維持為預設值。
auto 套用除分面本身篩選條件外的所有篩選條件,並新增 status:已選取的值為 selected,其他值為 available
max 在廣泛的基本結果集上計算儲存貯體,並新增 status 以區分已選取、可用與不可用的值。

您也可以手動控制:

  • ALL FILTERS - 將所有篩選條件套用至分面;
  • FILTERS color_id, size_id - 僅套用列出的篩選條件;
  • EXCLUDE FILTERS color_id - 套用除列出項目外的所有篩選條件;
  • ZEROES - 自 Manticore Search 27.3.0 起,即使目前分面的 count(*)0,仍保留廣泛 max 範圍中的儲存貯體(適用於 SQL max 模式)。

相同的選項也可透過 JSON API 的 facet_filter_modemodefiltersexclude_filterszeroes: true 取得。

簡而言之,strict 回答「目前結果集中有哪些?」,auto 回答「如果變更此特定篩選條件會發生什麼事?」,max 則顯示廣泛的儲存貯體清單,並包含 status 欄位:selectedavailableunavailable

最小範例

考慮一個包含品牌、顏色、尺寸與 SKU 的小型目錄:

CREATE TABLE products(
  title text,
  brand_id int,
  color_id int,
  color_name string,
  size_id int,
  size_name string,
  sku string
);

INSERT INTO products(id,title,brand_id,color_id,color_name,size_id,size_name,sku) VALUES
(1,'p1',7,1,'red',10,'small','sku1'),
(2,'p2',7,1,'red',20,'large','sku2'),
(3,'p3',7,2,'blue',10,'small','sku3'),
(4,'p4',7,3,'green',30,'xlarge','sku4'),
(5,'p5',8,1,'red',10,'small','sku5'),
(6,'p6',8,4,'black',20,'large','sku6'),
(7,'p7',9,5,'white',10,'small','sku7');

Enter fullscreen mode Exit fullscreen mode

使用者已選取:

brand_id=7 AND color_id=1 AND size_id=10

Enter fullscreen mode Exit fullscreen mode

在此條件下,結果集包含一項產品:p1。讓我們看看分面會發生什麼變化。

strict:先前的行為

若無額外選項,Manticore Search 會使用 strict:每個分面都會接收主要查詢的所有篩選條件。

SELECT count(*)
FROM products
WHERE brand_id=7 AND color_id=1 AND size_id=10
LIMIT 0
FACET color_id ORDER BY color_id ASC
FACET size_id ORDER BY size_id ASC;

Enter fullscreen mode Exit fullscreen mode

回應:

+----------+----------+
| color_id | count(*) |
+----------+----------+
|        1 |        1 |
+----------+----------+
+---------+----------+
| size_id | count(*) |
+---------+----------+
|      10 |        1 |
+---------+----------+

Enter fullscreen mode Exit fullscreen mode

這是準確的 SQL 回應,但對於線上商店而言通常過於狹隘。使用者只會看到已選取的 color_id=1size_id=10。看起來似乎沒有其他選項,儘管資料包含具有相同品牌與尺寸但不同顏色的產品(color_id=2),以及具有相同品牌與顏色但不同尺寸的產品(size_id=20)。

auto:分面忽略自身篩選條件

auto 模式會保留除目前計算分面的篩選條件外的所有篩選條件。

SELECT count(*)
FROM products
WHERE brand_id=7 AND color_id=1 AND size_id=10
LIMIT 0
OPTION facet_filter_mode='auto'
FACET color_id ORDER BY color_id ASC
FACET size_id ORDER BY size_id ASC;

Enter fullscreen mode Exit fullscreen mode

回應:

+----------+----------+-----------+
| color_id | count(*) | status    |
+----------+----------+-----------+
|        1 |        1 | selected  |
|        2 |        1 | available |
+----------+----------+-----------+
+---------+----------+-----------+
| size_id | count(*) | status    |
+---------+----------+-----------+
|      10 |        1 | selected  |
|      20 |        1 | available |
+---------+----------+-----------+

Enter fullscreen mode Exit fullscreen mode

發生了什麼事:

  • FACET color_id 套用了 brand_id=7 AND size_id=10,但未套用 color_id=1
  • FACET size_id 套用了 brand_id=7 AND color_id=1,但未套用 size_id=10

這讓介面在不為每個分面建立獨立查詢的情況下取得替代值。已選取的儲存貯體標記為 selected,而同一分面中的其他值則標記為 available:它們可以透過 IN (...) 加入目前篩選條件,作為更廣泛的選擇。

max:具有狀態的廣泛儲存貯體清單

auto 僅顯示特定分面目前篩選範圍內的值。max 更進一步:它在基本結果集上計算儲存貯體,並為介面分別標記其狀態。

SELECT count(*)
FROM products
WHERE brand_id=7 AND color_id=1 AND size_id=10
LIMIT 0
OPTION facet_filter_mode='max'
FACET color_id ORDER BY color_id ASC
FACET size_id ORDER BY size_id ASC;

Enter fullscreen mode Exit fullscreen mode

回應:

+----------+----------+-----------+
| color_id | count(*) | status    |
+----------+----------+-----------+
|        1 |        3 | selected  |
|        2 |        1 | available |
|        3 |        1 | available |
|        4 |        1 | available |
|        5 |        1 | available |
+----------+----------+-----------+
+---------+----------+-----------+
| size_id | count(*) | status    |
+---------+----------+-----------+
|      10 |        4 | selected  |
|      20 |        2 | available |
|      30 |        1 | available |
+---------+----------+-----------+

Enter fullscreen mode Exit fullscreen mode

status 直接來自 Manticore Search:

  • selected - 此值已存在於此分面的篩選條件中;
  • available - 此值可以選取;對於同一分面中的另一個值,這會透過 IN (...) 擴大篩選範圍;
  • unavailable - 此值存在於分面的廣泛結果集中,但選取它在目前篩選條件下不會傳回任何文件。

在此範例中,color_id=1 出現在目錄中的三項產品中,因此其在 max 中的計數為 3。它被標記為 selected,因為它已參與篩選。其他顏色被標記為 available:如果使用者選取其中一個,顏色篩選條件會變得更廣泛,例如 color_id IN (1,2)。當廣泛結果集中的儲存貯體在目前篩選條件下無法傳回文件時,就會出現不可用的值;下面的 sku 範例展示了這種情況。

手動設定篩選範圍

全域 facet_filter_mode 涵蓋大多數常見情況,但有時不同分面需要不同的行為。例如,顏色可以保持嚴格受所有篩選條件限制,尺寸可以使用 max,SKU 可以僅使用顏色與尺寸計算,而品牌可以在沒有顏色篩選條件的情況下計算。

SELECT count(*)
FROM products
WHERE brand_id=7 AND color_id=1 AND size_id=10
LIMIT 0
OPTION facet_filter_mode='max'
FACET color_id ALL FILTERS ORDER BY color_id ASC
FACET size_id ORDER BY size_id ASC
FACET sku FILTERS color_id, size_id ORDER BY sku ASC
FACET brand_id EXCLUDE FILTERS color_id ORDER BY brand_id ASC;

Enter fullscreen mode Exit fullscreen mode

回應:

+----------+----------+-----------+
| color_id | count(*) | status    |
+----------+----------+-----------+
|        1 |        1 | selected  |
+----------+----------+-----------+
+---------+----------+-----------+
| size_id | count(*) | status    |
+---------+----------+-----------+
|      10 |        4 | selected  |
|      20 |        2 | available |
|      30 |        1 | available |
+---------+----------+-----------+
+------+----------+-------------+
| sku  | count(*) | status      |
+------+----------+-------------+
| sku1 |        1 | available   |
| sku5 |        1 | unavailable |
+------+----------+-------------+
+----------+----------+----------+
| brand_id | count(*) | status   |
+----------+----------+----------+
|        7 |        2 | selected |
+----------+----------+----------+

Enter fullscreen mode Exit fullscreen mode

如何閱讀此查詢:

  • FACET color_id ALL FILTERS 套用所有篩選條件並僅傳回已選取的顏色;
  • FACET size_id 繼承查詢層級的 max 模式;
  • FACET sku FILTERS color_id, size_id 僅套用顏色與尺寸篩選條件;
  • FACET brand_id EXCLUDE FILTERS color_id 套用除顏色外的所有篩選條件。

當篩選面板包含一般與技術分面,且某些值需要根據特殊規則計算時,此模式很有用。

ZEROES:max 中的零計數儲存貯體

當需要保持嚴格的計數,但又需要保持廣泛的值清單時,ZEROES 很有用。它可與 max 搭配使用,無論是透過 OPTION facet_filter_mode='max',還是透過個別分面的 MODE max

若無 ZEROES,具有 ALL FILTERS 的分面僅顯示通過所有篩選條件的儲存貯體:

SELECT count(*)
FROM products
WHERE brand_id=7 AND color_id=1 AND size_id=10
LIMIT 0
OPTION facet_filter_mode='max'
FACET size_id ALL FILTERS ORDER BY size_id ASC;

Enter fullscreen mode Exit fullscreen mode

回應:

+---------+----------+----------+
| size_id | count(*) | status   |
+---------+----------+----------+
|      10 |        1 | selected |
+---------+----------+----------+

Enter fullscreen mode Exit fullscreen mode

使用 ZEROES 時,Manticore Search 會保持相同的可見計數,但會傳回來自廣泛 max 範圍的其餘儲存貯體,並將計數設為零:

SELECT count(*)
FROM products
WHERE brand_id=7 AND color_id=1 AND size_id=10
LIMIT 0
OPTION facet_filter_mode='max'
FACET size_id ALL FILTERS ZEROES ORDER BY size_id ASC;

Enter fullscreen mode Exit fullscreen mode

回應:

+---------+----------+-----------+
| size_id | count(*) | status    |
+---------+----------+-----------+
|      10 |        1 | selected  |
|      20 |        0 | available |
|      30 |        0 | available |
+---------+----------+-----------+

Enter fullscreen mode Exit fullscreen mode

介面隨後可以在已選取的 small 旁邊顯示 largexlarge:數字是指目前的嚴格結果集,而 status 則顯示這些值仍然可以選取以擴大篩選範圍。

透過 JSON API 的相同查詢

SQL 在此處較短,且更清楚地顯示機制,但 JSON API 支援相同的方法:

POST /search
{
  "table": "products",
  "limit": 0,
  "query": {
    "bool": {
      "must": [
        { "equals": { "brand_id": 7 } },
        { "equals": { "color_id": 1 } },
        { "equals": { "size_id": 10 } }
      ]
    }
  },
  "facet_filter_mode": "max",
  "aggs": {
    "colors": {
      "terms": { "field": "color_id", "size": 10 },
      "sort": [ { "color_id": { "order": "asc" } } ]
    },
    "sizes": {
      "terms": { "field": "size_id", "size": 10 },
      "sort": [ { "size_id": { "order": "asc" } } ]
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

回應:

{
  "took": 0,
  "timed_out": false,
  "hits": {
    "total": 1,
    "total_relation": "eq",
    "hits": []
  },
  "aggregations": {
    "colors": {
      "buckets": [
        { "key": 1, "doc_count": 3, "status": "selected" },
        { "key": 2, "doc_count": 1, "status": "available" },
        { "key": 3, "doc_count": 1, "status": "available" },
        { "key": 4, "doc_count": 1, "status": "available" },
        { "key": 5, "doc_count": 1, "status": "available" }
      ]
    },
    "sizes": {
      "buckets": [
        { "key": 10, "doc_count": 4, "status": "selected" },
        { "key": 20, "doc_count": 2, "status": "available" },
        { "key": 30, "doc_count": 1, "status": "available" }
      ]
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

在 JSON 聚合中,modefiltersexclude_filterszeroes 也可以針對每個個別聚合進行設定。

與 Meilisearch、Elasticsearch 與 OpenSearch 的差異

我們在 Manticore Search、Meilisearch、Elasticsearch 與 OpenSearch 中測試了相同的場景:brand_id=7color_id=1size_id=10 篩選條件處於作用中;結果集保持嚴格,而分面需要顯示排除自身篩選條件時出現的選項。

引擎 在單一查詢中排除自身篩選條件 原生儲存貯體狀態 應用程式仍需處理的事項
Manticore Search 是,透過 auto/max 是,在 auto/max 中;unavailable 僅在 max 呈現介面、SEO、分析與自訂規則。
Meilisearch 非此形式 執行額外查詢並在應用程式中組裝分面。
OpenSearch 是,但需透過 global + filter 聚合手動操作 複製篩選分支並在應用程式中計算 status
Elasticsearch 是,但需透過 global + filter 聚合手動操作 相同:維護獨立的聚合分支與用戶端邏輯。

在 Meilisearch 中使用基本分面很容易,但像這樣的查詢:

{
  "filter": ["brand_id = 7", "color_id = 1", "size_id = 10"],
  "facets": ["color_id", "size_id"]
}

Enter fullscreen mode Exit fullscreen mode

傳回已篩選結果集的計數。在我們的資料集中,這意味著只有 color_id=1size_id=10。替代的 color_id=2size_id=20 值不會出現在此回應中。如果介面需要它們,應用程式必須發出額外查詢並合併結果。

在 Elasticsearch 與 OpenSearch 中,類似的行為可以在單一查詢中建立,但每個分面都需要自己的明確聚合分支。對於 color,您保留 brand_idsize_id;對於 size,您保留 brand_idcolor_id;以此類推。這可行,但查詢會快速成長,且儲存貯體狀態仍需在應用程式中計算。

主要差異在於此邏輯存在的位置。在 Manticore Search 中,它直接在分面 API 中設定;在其他系統中,它通常由多個相似的篩選樹組裝而成。

效能與限制

strict 維持為預設值,並保留先前的行為。如果您只需要目前結果集內的分面,則無需進行任何變更。

auto 通常更適合電子商務篩選條件:它顯示每個分面中的替代值,將已選取的值標記為 selected,並將其他值標記為 available

當介面需要比目前結果集更廣泛的值清單,並需要顯示不可用選項時,請使用 max。此模式成本較高:Manticore Search 會在廣泛範圍上計算儲存貯體,然後分別判斷其 status。在大型資料集與許多分面的情況下,值得考慮。

還有一些限制:

  • 分面的本機篩選條件重寫僅支援與 AND 結合的屬性篩選條件;
  • 複雜的 AND/OR 樹不會自動為個別分面重寫;
  • 目前僅能可靠地比對明確值篩選條件(如 =IN)的已選取值;
  • 尚未自動計算價格等範圍的狀態。

對於價格、折扣與評分,最好明確定義獨立的範圍。例如,保留用於排序與滑桿的數值欄位,並為分面新增 price_banddiscount_band。這讓介面可以為預定義範圍顯示明確的狀態。

分面 URL 的 SEO、A/B 測試、銷售規劃規則與查詢分析仍然是搜尋周圍應用程式或平台的責任。

實際上,當您需要替代值時,請從 auto 開始;當介面需要具有 selectedavailableunavailable 狀態的廣泛儲存貯體清單時,請改用 max

進一步閱讀

若要介紹 Manticore Search 中的分面搜尋,請從較早的 Faceted search 文章開始。它涵蓋基本的 FACET 查詢、排序、限制與運算式的分面。

請參閱 FACET 文件 以取得詳細資訊。

若要以互動方式介紹分面,請參加 Manticore Faceting 課程。