ファセット検索

ファセット検索により、ドキュメントにカテゴリ情報を追加できます。ファセットは属性と値のペアです。たとえば、「size」というファセットが「small」、「medium」、「large」などの値を持つとします。

検索でファセットを使用すると、一連のステップでクエリを絞り込み、結果に「ドリルダウン」するために役立つ概要情報を取得できます。

これはショッピング サイトのような用途で有用な機能であり、購入者が見たい商品を絞り込めるように一連のフィルタを提供できます。

ファセットの集計データは、ファセットの値の分布状態を示します。たとえば、ファセット「size」が結果セットで多くのドキュメントに出現しているとします。そのファセットの集計データには、「small」の値が 100 回出現し、「medium」が 300 回、「large」が 250 回出現したことが示されます。各ファセットと値のペアは、クエリ結果内のドキュメントのサブセットを表します。各ペアには refinement というキーが関連付けられています。クエリに絞り込みを含めると、クエリ文字列に一致し、1 つ以上の絞り込みに対応するファセット値のあるドキュメントを取得できます。

検索を実行する場合、収集し、結果で表示するファセットを選択し、ファセット検出を有効にして、ドキュメントで最も頻繁に出現するファセットを自動的に選択できます。

ドキュメントにファセットを追加する

インデックスにドキュメントを追加する前に、ドキュメントにファセットを追加します。これはドキュメントのフィールドを指定するときに同時に行います。

package app

import (
    "appengine"
    "appengine/search"
    "net/http"
)

type ComputerDoc struct {
    Name      search.Atom
    Type      search.Atom `search:",facet"`
    RAMSizeGB float64     `search:",facet"`
}

func handlePut(w http.ResponseWriter, r *http.Request) {
    c := appengine.NewContext(r)
    index, err := search.Open("products")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    _, err = index.Put(c, "doc1", &ComputerDoc{
        Name:      "x86",
        Type:      "computer",
        RAMSizeGB: 8.0,
    })
    // Handle err and write HTTP response.
}

ファセットはドキュメント フィールドに似ており、名前と 1 つの値を持ちます。

ファセット名はドキュメント フィールドと同じルールに従います。名前は大文字と小文字が区別され、ASCII 文字のみ含めることができます。それらは文字で始まり、文字、数字、またはアンダースコアを含めることができます。名前は 500 文字を超えることはできません。

ファセットの値は、アトミック文字列(500 文字以内)または数値(-2147483647 から 2147483647 までの倍精度浮動小数点値)のいずれかです。

1 つのドキュメントのファセットに複数の値を割り当てるには、毎回異なる値を使用して、同じ名前と型のファセットを何回も追加します。

ファセットに指定できる値の数に上限はありません。ドキュメントに追加できるファセット数やインデックス内の一意の名前が付けられたファセット数にも上限はありません。

ファセットを使用するときは毎回、アトミック値または数値のどちらでも指定できます。「size」の名前を持つファセットは、文字列値「small」で 1 つのドキュメントに追加し、数値 8 で別のドキュメントに追加できます。実際に、同じファセットが両方の種類の値で、同じドキュメントに複数回出現できます。可能であっても同じファセットにアトミック値と数値の両方を使用することはおすすめできません。

ファセットはドキュメントに追加する際に特定の型を持ちますが、検索結果は、その値をすべてまとめて収集します。たとえば、ファセット「size」に、値「small」の 100 インスタンス、「medium」の 150 インスタンス、および範囲 [4, 8) の数値の 135 インスタンスがあったことが示されます。正確な数値とそれらの頻度分布は表示されません。

クエリを使用してドキュメントを取得する場合、そのファセットや値に直接アクセスすることはできません。次のセクションで説明するように、クエリでファセット情報が返されるようにリクエストする必要があります。

ファセット検索を使用してファセット情報を取得する

検索バックエンドに、最も頻繁に使用されているファセットの検出を要求できます。これは自動ファセット検出と呼ばれます。名前または名前と値でファセットを選択して、ファセット情報を明示的に取得することもできます。1 つのクエリで 3 種類すべてのファセット取得を混在させ、照合させることができます。

ファセット情報の要求は、クエリで返されるドキュメントに影響しません。それはパフォーマンスに影響する可能性があります。デフォルトの深さ 1,000 のファセット検索を実行することは、並べ替えオプション スコア上限を 1,000 に設定するのと同じ効果があります。

自動ファセット検出

自動ファセット検出はドキュメントの集計で最も頻繁に出現するファセットを探します。たとえば、クエリに一致するドキュメントに、値「red」で 5 回、値「white」で 5 回、値「blue」で 5 回出現する「color」ファセットが含まれているとします。このファセットの合計カウントは 15 になります。検出の目的で、これは同じ一致するドキュメントに、値「dark」で 6 回および値「light」で 7 回出現する別のファセット「shade」より上の順位になります。

クエリにファセット検出を設定して有効にする必要があります。

func handleSearch(w http.ResponseWriter, r *http.Request) {
    index, err := search.Open("products")
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    it := index.Search(c, "name:x86", &search.SearchOptions{
        Facets: {
            search.AutoFacetDiscovery(0, 0),
        },
    })
    facets, err := it.Facets()
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    for _, results := range facets {
        for i, facet := range result {
            // The facet results are grouped by facet name.
            // Print the name of each group before its values.
            if i == 0 {
                fmt.Fprintf(w, "Facet %s:\n", facet.Name)
            }
            fmt.Fprintf(w, "    %v: count=%d", facet.Value, facet.Count)
        }
    }
}

検出によってファセットを取得する場合、デフォルトでは、ファセットの最も頻繁に出現する 10 個の値のみが返されます。AutoFacetDiscovery パラメータを使用すると、この上限を 100 まで増やすことができます。

自動ファセット検出では、可能性のあるすべてのファセットとその値が返されるわけではない点に注意してください。検出で返されるファセットは、実行ごとに異なる可能性があります。固定された一連のファセットが必要な場合は、クエリで return_facets パラメータを使用します。

文字列値は個別に返されます。検出されたファセットの数値は 1 つの範囲 [min max) で返されます。この範囲を調査して、後のクエリ用に小さな部分範囲を作成できます。

名前でファセットを選択する

名前のみでファセットに関する情報を取得するには、ファセットの名前を指定した FacetDiscovery をクエリの検索オプションに追加します。

it := index.Search(c, "name:x86", &search.SearchOptions{
    Facets: {
        FacetDiscovery("Type"),
        FacetDiscovery("RAMSizeGB"),
    },
})

名前でファセットを取得すると、ファセットの最も頻繁に出現する 10 個の値のみが返されます。

名前と値でファセットを選択する

ファセットの特定の値に関する情報だけを取得するには、ファセットの名前と対象となる値の両方を指定した FacetDiscovery を追加します。

it := index.Search(c, "name:x86", &search.SearchOptions{
    Facets: {
        // Fetch the "Type" facet with Values "computer" and "printer"
        FacetDiscovery("Type",
            search.Atom("computer"),
            search.Atom("printer"),
        ),
        // Fetch the "RAMSizeGB" facet with values in the ranges [0, 4), [4, 8), and [8, max]
        FacetDiscovery("RAMSizeGB",
            search.Range{Start: 0, End: 4},
            search.Range{Start: 4, End: 8},
            search.AtLeast(8),
        ),
    },
})

1 つの FacetDiscovery 内の値はすべて、search.Atom 値のリストまたは search.Range のリスト(数値の場合)のいずれか同じ型でなければなりません。数値の場合は、左側(開始)はその値を含み、右側(終了)はその値を含まない区間で指定します。ファセットに文字列値と数値が混在している場合は、それぞれに個別の FacetDisovery オプションを追加します。

オプション

ファセット情報の収集で評価されるドキュメントの最小数を制御するには、クエリの SearchOptionsFacetDocumentDepth オプションを追加します。指定しない場合、深度はデフォルトで 1,000 になります。

ファセットの深さは通常クエリ制限よりはるかに大きいことに注意してください。ファセット結果は、ドキュメントの深さ数以上に計算されます。並べ替えオプションを深さより大きいスコア制限に設定している場合、スコア上限が代わりに使われます。

ファセット結果の取得

クエリでファセット検索パラメータを使用すると、集計されたファセット情報がクエリ結果自体に付属します。

検索の Iterator には Facets というメソッドが用意されており、集計されたファセット情報を [][]FacetResult として返します。結果は、クエリに一致するドキュメントに出現するファセットごとに、ファセット結果のスライスが 1 つだけ含まれるように編成されます。各結果で、次を取得します。

  • ファセット名
  • 最も頻繁な値のリストで構成されたファセットの 1 つの値
  • その値が出現するおおよその回数

値のリストには、ファセットの文字列と数値が含まれることに注意してください。ファセットが自動検出された場合、その数値が 1 つの間隔 [min max) として返されます。クエリで 1 つ以上の範囲を持つ数値ファセットを明示的に要求した場合、リストには範囲ごとに 1 つの closed-open 間隔 [start end) が含まれます。

クエリ オプションによって、調査するドキュメント数や返される値の数が指定されているため、ファセット値のリストに、ドキュメントで見つかったすべての値が含まれないことがあります。

各ファセットの集計された情報は、イテレータから読み取ることができます。

it := index.Search(...)
facets, err := it.Facets()  // And check err != nil.
for _, results := range facets {
    for _, facet := range results {
        ...
    }
}

たとえば、クエリによって文字列値と数値の「size」ファセットを含むドキュメントが見つかったとします。このファセットの結果は、次のように構築されます。

[][]search.FacetResult{
    {
        {Name: "size", Value: search.Range{Start: 8, End: 10}, Count: 22},
        {Name: "size", Value: search.Atom("small"), Count: 100},
        {Name: "size", Value: search.Atom("medium"), Count: 300},
        {Name: "size", Value: search.Atom("large"), Count: 250},
    },
}

ファセットを使用してクエリを絞り込み / フィルタする

FacetResult を使用すると、これらのファセット値を持つドキュメントのみを結果に含めることができます。このようなキーを 1 つ以上使用してクエリを絞り込むには、検索オプションとしてキーを渡します。

it := index.Search(c, "...", &search.SearchOptions{
    Refinements: []search.Facet{
        facetResult1.Facet,
        facetResult2.Facet,
    },
})

同じリクエストで、1 つ以上の異なるファセットの絞り込みを組み合わせることができます。同じファセットに属するすべての絞り込みは OR で結合します。異なるファセットの絞り込みは AND で組み合わせます。

また、カスタムの Facet を手動で作成し、絞り込みで使用することもできます。詳細については、リファレンスをご覧ください。