Search API の基本

2012 年 10 月、Amy Unruh
Google Developer Relations

はじめに

このレッスンでは、Search API の基本的な使用方法である、コンテンツのインデックスへの追加とインデックスに対するクエリについて説明します。次の内容を学習できます。

  • 検索インデックスを作成する
  • インデックス ドキュメントを通じてコンテンツを追加する
  • インデックス追加済みデータに対して単純な全文検索クエリを行う

目標

App Engine Search API の基本的な使用方法を学習します。

要件

インデックス

App Engine の Search API は Index オブジェクトを通じて動作します。このオブジェクトでは、インデックス ドキュメントを通じてデータを保存し、検索クエリを使用してドキュメントの取得、変更、削除を行います。

各インデックスにはインデックス名と、オプションで名前空間があります。この名前は、特定の名前空間内でインデックスを一意に識別します。名前は表示と印刷が可能で、先頭が ! でない ASCII 文字列にする必要があります。空白文字は除外されます。Index オブジェクトは複数作成できますが、同じ名前空間でインデックス名が同じであるオブジェクトは、同じインデックスを参照します。

名前空間とインデックスを使用することで、ドキュメントを整理できます。商品検索アプリケーションの例では、すべての製品ドキュメントが 1 つのインデックス内にあり、別のインデックスに店舗の位置情報が含まれています。たとえば書籍だけを検索する場合は、商品カテゴリでクエリをフィルタリングできます。

コードでは、インデックス名を指定して Index オブジェクトを作成します。

from google.appengine.api import search
index = search.Index(name='productsearch1')

または

index = search.Index(name='yourindex', namespace='yournamespace')

基盤となるドキュメント インデックスがない場合は、最初にアクセスしたときに作成されます。明示的に作成する必要はありません。

次のクラス Python Search API の詳細で説明するように、インデックスからのドキュメントの削除、インデックス全体の削除が可能です。

ドキュメント

ドキュメントにはインデックスの検索可能なコンテンツが記載されています。ドキュメントは、インデックス可能データの構造化を行うコンテナです。技術的な観点では、Document オブジェクトは、一意に識別されるフィールドのコレクションであり、ドキュメント ID によって識別されます。フィールドは名前および型付き値です。ドキュメントには、データストア エンティティと同様に kinds を持ちません。

サンプル アプリケーションの商品カテゴリには、書籍と HD テレビがあります。店舗の商品の種類は限定的です。サンプル アプリケーションの各商品ドキュメントには、docs.Product クラス変数で定義された、以下のコアフィールドが常に含まれています。

  • CATEGORYbooks または hd_televisions に設定)
  • PID(商品 ID)
  • PRODUCT_NAME
  • DESCRIPTION
  • PRICE
  • AVG_RATING
  • UPDATED(最終更新日)
商品ドキュメント フィールド
図 1: 商品ドキュメント フィールド

書籍および HD テレビのカテゴリには、それぞれ独自の追加フィールドがあります。書籍カテゴリには次のような追加フィールドがあります。

  • title
  • author
  • publisher
  • pages
  • isbn

HD テレビカテゴリには次のような追加フィールドがあります。

  • brand
  • tv_type
  • size

アプリケーションでは、各商品タイプのドキュメントに対してアプリケーション レベルのセマンティックな整合性が強制されます。つまり、すべての商品ドキュメントは必ず同じコアフィールドを持ち、すべての書籍は同じ追加フィールドを持ちます。ただし検索インデックスでは、使用するフィールドに対してドキュメント間の計画的な一貫性は求められません。そのため、各「商品」ドキュメントに対するクエリに関しては、明示的なコンセプトは求められません。

フィールド タイプ

各ドキュメント フィールドには一意のフィールド タイプがあります。このタイプは次のいずれかになります。これは Python モジュール search で定義されます。

  • TextField: 書式なしテキストの文字列。
  • HtmlField: HTML 形式のテキスト。Search API では結果のスニペットの作成時とドキュメントのスコアリング時にマークアップが考慮されるため、文字列が HTML である場合はこのフィールド タイプを使用します。
  • AtomField: 単一のトークンとして処理される文字列。完全なフィールド値ではなく部分文字列だけが含まれている場合、クエリは一致しません。
  • NumberField: 数値(整数または浮動小数点)
  • DateField: 時間コンポーネントがない日付
  • GeoField: 緯度と経度の座標を指定する GeoPoint オブジェクトによって示される地理的な位置

テキスト フィールド(TextFieldHtmlFieldAtomField)の場合、値は Unicode 文字列にする必要があります。

例: 商品ドキュメント フィールドをビルドしてドキュメントを作成する

Document オブジェクトを構築するには、フィールドの一覧を作成し、必要に応じてドキュメント ID を定義し、その情報を Document constructor に渡します。

サンプル アプリケーションでは、商品ドキュメント用に TextFieldAtomFieldNumberFieldDateField フィールド タイプを使用します。

商品ドキュメント フィールドを定義する

コア商品フィールド(すべての商品ドキュメントに含まれているフィールド)は次のようになります。以下のコンストラクタの値引数は適切な値に設定されていると仮定します。

from google.appengine.api import search
...
fields = [
      search.TextField(name=docs.Product.PID, value=pid), # the product id
      # The 'updated' field is set to the current date.
      search.DateField(name=docs.Product.UPDATED,
                       value=datetime.datetime.now().date()),
      search.TextField(name=docs.Product.PRODUCT_NAME, value=name),
      search.TextField(name=docs.Product.DESCRIPTION, value=description),
      # The category names are atomic
      search.AtomField(name=docs.Product.CATEGORY, value=category),
      # The average rating starts at 0 for a new product.
      search.NumberField(name=docs.Product.AVG_RATING, value=0.0),
      search.NumberField(name=docs.Product.PRICE, value=price) ]

カテゴリ フィールドには AtomField と入力されています。Atom フィールドは正確な一致が必要になるカテゴリなどに有用です。テキスト フィールドは、タイトルや説明などの文字列に適しています。カテゴリの例としては hd televisions などが挙げられます。televisions だけで検索した場合は一致が得られません(その文字列が別の商品フィールドに含まれていない場合)。ただし完全なフィールド文字列 hd televisions を検索する場合は、カテゴリ フィールドで一致することになります。

サンプル アプリケーションの各商品カテゴリには、固有のフィールドも含まれています。これらは、カテゴリに応じてフィールド一覧にも追加されます。たとえば、テレビカテゴリの場合は size(数値フィールド)、brandtv_type(テキスト フィールド)の追加フィールドがあります。書籍には、これとは異なるフィールドが複数存在します。

ドキュメントを作成する

フィールド一覧に基づいてドキュメント オブジェクトを作成できます。各商品ドキュメントについて、その商品の事前定義された一意の ID となるドキュメント ID を設定します。

d = search.Document(doc_id=product_id, fields=fields)

この設計にはいくつかの利点がありますが(このクラスの後続クラスで説明)、ドキュメント ID を指定しない場合は、インデックスにドキュメントを追加したときに自動的に生成されます。

例: 店舗ロケーション ドキュメントで地理位置情報を使用

Search API では、GeoField タイプのフィールドを含むドキュメントで Geosearch をサポートしています。ドキュメントにそのようなフィールドがある場合は、距離の比較に基づくクエリをインデックスに対して行うことができます。

ロケーションは GeoPoint クラスによって定義され、緯度と経度の座標が保存されます。緯度は角距離を赤道の南北の度数で指定したものです。経度は角距離をグリニッジ子午線の東西の度数で指定したものです。たとえばシドニーのオペラハウスの位置は GeoPoint(-33.857, 151.215) と定義されます。地理位置情報をドキュメントに保存するには、GeoPoint オブジェクトを値として設定した GeoField フィールドを追加します。

商品検索アプリケーションにおける、店舗ロケーション ドキュメントのフィールドの作成方法を示します。

from google.appengine.api import search
...
geopoint = search.GeoPoint(latitude, longitude)
fields = [search.TextField(name=docs.Store.STORE_NAME, value=storename),
             search.TextField(name=docs.Store.STORE_ADDRESS, value=store_address),
             search.GeoField(name=docs.Store.STORE_LOCATION, value=geopoint)  ]

ドキュメントをインデックスに追加する

ドキュメントのコンテンツに対してクエリを行うには、Index オブジェクトの put() メソッドを使用して、ドキュメントをインデックスに追加する必要があります。インデックスへの追加により、Search API のクエリ言語とクエリ オプションを使用してドキュメントを検索できるようになります。

ドキュメントを作成する場合は、独自にドキュメント ID を指定できます。ドキュメント ID は、表示と印刷が可能で、先頭が ! でない ASCII 文字列にする必要があります。空白文字は除外されます(後で説明するように、既存のドキュメントの ID を使用してドキュメントをインデックスに追加すると、その既存のドキュメントは再度インデックス処理されます)。ドキュメント ID を指定しない場合は、ドキュメントがインデックスに追加されると、一意の数値 ID が自動的に生成されます。

ドキュメントは 1 つずつ追加することもできますが、ドキュメントの一覧を一括で追加する効率的な方法もあります。ドキュメントを作成し、フィールド一覧を使用してインデックスに追加する方法を示します。

from google.appengine.api import search

# Here we do not specify a document ID, so one will be auto-generated on put.
d = search.Document(fields=fields)
try:
  add_result = search.Index(name=INDEX_NAME).put(d)
except search.Error:
  # ...

put() による例外を検出して対応する必要があります。これはタイプ search.Error になります。

ドキュメント ID を指定するには、次のように Document コンストラクタに渡します。

d = search.Document(doc_id=doc_id, fields=fields)

追加されたドキュメントの ID を取得するには、id プロパティを使用します。これは search.AddResult オブジェクトの一覧から put() オペレーションによって返されたプロパティです。

doc_id = add_result[0].id

基本的な検索クエリ

ドキュメントをインデックスに追加することで、ドキュメントのコンテンツが検索可能になります。さらに、インデックス内のドキュメントに対する全文検索クエリができるようになります。

検索クエリを送信する方法は 2 つあります。簡単な方法としては、クエリ文字列を Index オブジェクトの search() メソッドに渡します。もう 1 つは、Query オブジェクトを作成して search() メソッドに渡す方法です。クエリ オブジェクトを作成することで、検索のクエリ、並べ替え、結果の表示オプションを指定できます。

このレッスンでは、両方のアプローチを通じてシンプルなクエリを作成する方法を確認します。ローカルで実行される開発用ウェブサーバーではサポートされない検索クエリもあるため、クエリはデプロイされたアプリケーションを使用して実行する必要があります。

クエリ文字列を使用した検索

クエリ文字列には、Search API のクエリ言語によって解析できる任意の Unicode 文字列を使用できます。クエリ文字列を作成したら、Index.search() メソッドに渡します。次に例を示します。

from google.appengine.api import search

# a query string like this comes from the client
query = "stories"
try:
  index = search.Index(INDEX_NAME)
  search_results = index.search(query)
  for doc in search_results:
    # process doc ..
except search.Error:
  # ...

クエリ オブジェクトを使用した検索

Query オブジェクトによって、クエリ文字列を使用するよりもさらにクエリ オプションを制御できます。この例では、最初に QueryOptions オブジェクトを作成します。この引数は、doc_limit で指定された数の結果がクエリによって返されるように指定します(商品検索アプリケーション コードを見ると、さらに複雑な QueryOption オブジェクトがあります。それについては次のクラス Python Search API の詳細で取り上げます)。次にクエリ文字列と QueryOptions オブジェクトを使用して Query オブジェクトを作成します。続いて、先ほどクエリ文字列について行ったように、Query オブジェクトを Index.search() メソッドに渡します。

from google.appengine.api import search

# a query string like this comes from the client
querystring = “stories”
try:
  index = search.Index(INDEX_NAME)
  search_query = search.Query(
      query_string=querystring,
      options=search.QueryOptions(
          limit=doc_limit))
  search_results = index.search(search_query)
except search.Error:
  # ...

クエリ結果の処理

クエリを送信すると、一致する検索結果が反復可能な SearchResults オブジェクトに含まれてアプリケーションに返されます。このオブジェクトには、検索結果の数、実際に返された結果の数、オプションのクエリカーソル オブジェクトが含まれています。

返されたドキュメントには、SearchResults オブジェクトを反復処理することでアクセスできます。返される結果数は、オブジェクトの results プロパティの長さです。number_found プロパティは検索のヒット数に設定されています。返されたオブジェクトを反復処理することで、返されたドキュメントを取得し、次のように任意に処理できるようになります。

try:
  search_results = index.search("stories")
  returned_count = len(search_results.results)
  number_found = search_results.number_found
  for doc in search_results:
    doc_id = doc.doc_id
    fields = doc.fields
    # etc.
except search.Error:
  # ...

まとめと確認

このレッスンでは、ドキュメントを作成してインデックスに追加し、コンテンツに対するクエリを行う基本を学習しました。知識を確認するには、自分のシンプルなアプリケーションで、次のステップを再現してみてください。

  • Index オブジェクトを作成します。
  • ドキュメント フィールドの一覧を(たとえば TextField タイプを使用して)作成し、そのフィールド一覧を使用して Document オブジェクトを作成します。インデックスにドキュメントを追加します。
  • いずれかのフィールド値の条件で構成された検索文字列を使用して、インデックスを検索します。作成したドキュメントが検索結果として返されるでしょうか。

次のレッスンでは、Search API のインデックスの詳細を確認します。

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Python 2 の App Engine スタンダード環境