多面向 (Facet) 搜尋可讓您將類別資訊附加至文件。Facet 是指屬性/值組合,例如名為「size」的 Facet,可能擁有「small」、「medium」及「large」等值。
使用 Facet 搭配搜尋可擷取匯總資訊,協助您透過一系列步驟修正查詢及「細查」結果。
這非常適合用於購物網站等應用,您可於其中提供一組篩選條件,協助客戶縮小範圍,找出希望瀏覽的產品。
Facet 匯總資料可顯示 Facet 值的分佈情形。舉例來說,「size」Facet 可能出現在結果集的許多文件之中。該 Facet 的匯總資料可能顯示「small」值出現 100 次、「medium」值出現 300 次,以及「large」值出現 250 次。每個 Facet/值組合都代表查詢結果之中的子集。名為「分類標籤」的鍵與各個組合有關。您可在查詢之中納入分類標籤,擷取符合查詢字串,且具有對應至一項以上分類標籤 Facet 值的文件。
您在執行搜尋時,可選擇用於收集的 Facet 以顯示於結果,或是可以啟用 Facet 探索功能,自動選擇最常出現在文件中的 Facet。
新增 Facet 至文件
請在將文件新增至索引之前,先將 Facet 新增至文件。在指定文件欄位時,請同時執行這項作業:
package com.google.test.facet;
import java.io.IOException;
import javax.servlet.http.*;
import com.google.appengine.api.search.*;
public class FacetsearchjavaServlet extends HttpServlet {
public void doPut(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Document doc1 = Document.newBuilder()
.setId("doc1")
.addField(Field.newBuilder().setName("name").setAtom("x86"))
.addFacet(Facet.withAtom("type", "computer"))
.addFacet(Facet.withNumber("ram_size_gb", 8.0))
.build();
IndexSpec indexSpec = IndexSpec.newBuilder().setName("products").build();
Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);
index.put(doc1);
}
}
Facet 類似於文件欄位,其中具有名稱,並取用一個值。
Facet 名稱遵循與文件欄位相同的規則:名稱會區分大小寫,並且只能包含 ASCII 字元。名稱必須以字母為開頭,可包含字母、數字或底線。名稱長度不得超過 500 個字元。
Facet 值可為不可分割字串 (不得超過 500 個字元) 或數字 (介於 -2,147,483,647 及2,147,483,647 之間的雙精度浮點值)。
您可在單一文件指派多個值至單一 Facet,方法為新增相同名稱的 Facet 並輸入多次,每次使用不同的值。
Facet 可擁有無限數量的值。您可以新增至文件的 Facet 數量,或是索引之中不重複名稱 Facet 的數量,都不會受到限制。
請注意,每次使用 Facet 時,Facet 可能取得不可分割值或數值。名為「size」的 Facet,可利用「small」字串值附加至一份文件,並以數值 8 附加至另一份文件。事實上,相同的 Facet 可利用這兩種值,在同一份文件出現多次。雖然允許使用,但我們不建議針對相同的 Facet 同時使用 atom 及數值。
雖然 Facet 新增至文件時具有特定類型,但搜尋結果會將 Facet 的「所有」值收集在一起。例如,「size」Facet 可能顯示「small」值有 100 個執行個體、「medium」值有 150 個執行個體,以及範圍 [4, 8) 數值的 135 個執行個體。其中並不會顯示確切數值及其頻率分佈。
您使用查詢功能擷取文件時,無法直接存取其 Facet 與值。您必須要求查詢傳回該 Facet 資訊,作法將於下節說明。
使用多面向 (Facet) 搜尋擷取 Facet 資訊
您可要求搜尋後端探索最常用的 Facet;這稱為自動 Facet 探索。您也可以依據名稱或是名稱與值,明確地擷取 Facet 資訊。您可在單一查詢之中混搭使用所有三種 Facet 擷取。
要求 Facet 資訊不會影響查詢傳回的文件,這可能會影響效能。以預設深度 1000 執行多面向 (Facet) 搜尋,與設定排序選項計分器限制為 1000 的效果相同。
自動 Facet 探索
自動 Facet 探索會尋找文件「匯總」之中最常出現的 Facet。舉例來說,假設符合查詢的文件包含「color」Facet,以「red」值出現五次、「white」值出現五次,以及「blue」顏色出現五次。此 Facet 的總數為 15。就探索目的而言,相較於在同一個符合文件之中以「dark」值出現 6 次及以「light」值出現 7 次的 Facet「shade」,「color」Facet 的排名可能較高。
您必須在查詢之中設定,以啟用 Facet 探索功能:
package com.google.test.facet;
import java.io.IOException;
import javax.servlet.http.*;
import com.google.appengine.api.search.*;
public class FacetsearchjavaServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
IndexSpec indexSpec = IndexSpec.newBuilder().setName("products").build();
Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);
Results<ScoredDocument> result = index.search(
Query.newBuilder().setEnableFacetDiscovery(true) // enable discovery
.build("name:x86"));
for(FacetResult facetResult : result.getFacets()) {
resp.getWriter().printf("Facet %s:\n", facetResult.getName());
for (FacetResultValue facetValue : facetResult.getValues()) {
resp.getWriter().printf(" %s: Count=%s, RefinementKey=%s\n",
facetValue.getLabel(),
facetValue.getCount(),
facetValue.getRefinementToken());
}
}
}
}
您以探索功能擷取 Facet 時,依據預設只會傳回 10 個最常產生的 Facet 值。您可以使用 FacetOptions.Builder.setDiscoveryValueLimit()
將這個限制提高至 100。
請注意,自動 Facet 探索未必能傳回所有可能的 Facet 及其值。探索功能傳回的 Facet 值在每次執行之間可能不同。如果需要固定的 Facet 組合,請在查詢使用 return_facets
參數。
字串值將會個別傳回。探索 Facet 的數值將以單一範圍傳回 [最小值 最大值)。您可檢驗此項範圍,並建立更小的子範圍供後續查詢使用。
依名稱選擇 Facet
如果只要依名稱擷取 Facet 資訊,請將ReturnFacet
物件新增至查詢以指定 Facet 名稱:
package com.google.test.facet;
import java.io.IOException;
import javax.servlet.http.*;
import com.google.appengine.api.search.*;
public class FacetsearchjavaServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
IndexSpec indexSpec = IndexSpec.newBuilder().setName("products").build();
Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);
Results<ScoredDocument> result = index.search(Query.newBuilder()
.addReturnFacet("type")
.addReturnFacet("ram_size_gb")
.build("name:x86"));
for(FacetResult facetResult : result.getFacets()) {
resp.getWriter().printf("Facet %s:\n", facetResult.getName());
for (FacetResultValue facetValue : facetResult.getValues()) {
resp.getWriter().printf(" %s: Count=%s, RefinementKey=%s\n",
facetValue.getLabel(),
facetValue.getCount(),
facetValue.getRefinementToken());
}
}
}
}
當您以名稱擷取 Facet 時,依據預設只會傳回 10 個最常產生的 Facet 值。您可以使用 FacetOptions.Builder.setDiscoveryValueLimit()
將這個限制提高至 20。
依名稱與值選擇 Facet
如要擷取具特定值的 Facet,請新增包含FacetRequest
的 ReturnFacet
物件:
package com.google.test.facet;
import java.io.IOException;
import javax.servlet.http.*;
import com.google.appengine.api.search.*;
public class FacetsearchjavaServlet extends HttpServlet {
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
IndexSpec indexSpec = IndexSpec.newBuilder().setName("products").build();
Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);
// Fetch the "type" facet with values "computer and "printer"
// along with the "ram_size_gb" facet with values in the ranges [0,4), [4, 8), and [8, max]
Results<ScoredDocument> result = index.search(Query.newBuilder()
.addReturnFacet(FacetRequest.newBuilder()
.setName("type")
.addValueConstraint("computer")
.addValueConstraint("printer"))
.addReturnFacet(FacetRequest.newBuilder()
.setName("ram_size_gb")
.addRange(FacetRange.withEnd(4.0))
.addRange(FacetRange.withStartEnd(4.0, 8.0))
.addRange(FacetRange.withStart(8.0)))
.build("name:x86"));
for(FacetResult facetResult : result.getFacets()) {
resp.getWriter().printf("Facet %s:\n", facetResult.getName());
for (FacetResultValue facetValue : facetResult.getValues()) {
resp.getWriter().printf(" %s: Count=%s, RefinementKey=%s\n",
facetValue.getLabel(),
facetValue.getCount(),
facetValue.getRefinementToken());
}
}
}
}
單一 FacetRequest
的值都必須屬於相同類型,可為字串值清單或 FacetRanges
清單 (若為數字),也就是在左側 (開始) 關閉,右側 (結束) 開啟的間隔。如果 Facet 混合字串值與數值,請針對每個類型的值新增獨立的 FacetRequests。
選項
您可以將FacetOptions
參數新增至 Query 呼叫來控制多面向 (Facet) 搜尋。此參數會取用 FacetOptions
的單一例項。您可以使用此參數覆寫多面向 (Facet) 搜尋的預設行為。
Results<ScoredDocument> results = index.search(Query.newBuilder()
.addReturnFacet(FacetRequest.newBuilder()
.setName("type")
.addValueConstraint("computer")
.addValueConstraint("printer"))
.addReturnFacet(FacetRequest.newBuilder()
.setName("ram_size_gb")
.addRange(FacetRange.withEnd(4.0))
.addRange(FacetRange.withStartEnd(4.0, 8.0))
.addRange(FacetRange.withStart(8.0)))
.setFacetOptions(FacetOptions.newBuilder()
.setDiscoveryLimit(5)
.setDiscoveryValueLimit(10)
.setDepth(6000).build());
.build("some_query");
參數 | 說明 | 預設 |
---|---|---|
DiscoveryLimit |
在已開啟 Facet 探索的情況下,可探索的 Facet 數量。如果設為 0,將會停用探索 Facet。 | 10 |
DiscoveryValueLimit |
針對每個已探索 Facet 數量最高的項目,可傳回的值數量。 | 10 |
Depth |
查詢結果中的文件數量下限,可用來評估收集 Facet 資訊。 | 1000 |
Depth
選項適用於全部三種 Facet 匯總:依名稱、依名稱和值,以及自動探索。其他選項則僅適用於自動探索。
請注意,該 Facet 深度通常遠高於查詢限制。Facet 結果至少會運算至文件的深度數。如果您讓排序選項計分限制設定高於深度,就會改為使用計分限制。
擷取 Facet 結果
您在查詢中使用多面向 (Facet) 搜尋參數時,匯總 Facet 資訊會隨著查詢結果本身提供。
查詢將會有 FacetResult
清單。對於出現在符合查詢的文件中的各個 Facet,清單中各會有一個結果。您可以在各項結果中獲得下列資訊:
- Facet 名稱
- 最常出現的 Facet 值清單。每個值都會有一個近似計數,計算值出現的次數,並且會有一個修正鍵,可用來擷取符合這項查詢和 Facet 值的文件。
請注意,值清單包含 Facet 字串值「及」數值。若 Facet 為自動探索,其數值將以單一間隔 [最小值 最大值) 傳回。若您在查詢之中明確要求一個或多個範圍的數值 Facet,清單將包含各個範圍的關閉開啟間隔 [開始 結束)。
由於查詢選項會判斷待檢驗的文件數量及傳回的值數量,因此 Facet 值清單可能並未包含文件內找到的所有值。
您可以從搜尋結果讀取各個 Facet 的匯總資訊:
Results<ScoredDocument> results = index.search(...);
for (FacetResult facetInfo : results.getFacets()) {
...
}
例如,查詢可能已經找到文件,其中具有含字串值和數值的「size」Facet。這個 Facet 的 FacetResult 結構如下:
FacetResult.newBuilder()
.setName("size")
.addValue(FacetResultValue.create("[8, 10)", 22, refinement_key)
.addValue(FacetResultValue.create("small", 100, refinement_key)
.addValue(FacetResultValue.create("medium", 300, refinement_key)
.addValue(FacetResultValue.create("large", 250, refinement_key).build());
FacetResultValue.label 是由 Facet 值所建構。數值會以表示範圍的「stringified」傳回。
refinement_key
是可用於後續查詢的網路/網址安全字串,可擷取符合結果的 Facet 名稱和值的文件。
使用 Facet 修正/篩選查詢
Query query = Query.newBuilder()
.addFacetRefinementFromToken(refinement_key1)
.addFacetRefinementFromToken(refinement_key2)
.addFacetRefinementFromToken(refinement_key3)
.build("some_query");
您可在同一個要求中,將用於一或多個不同 Facet 的分類標籤加以合併。屬於相同 Facet 的所有分類標籤會以 OR 彙整;屬於不同 Facet 的分類標籤會以 AND 合併。
您也可以手動建立自訂 FacetRefinement
鍵。詳情請參閱類別說明文件。