ドキュメントとインデックス

Search API は、構造化データを含むドキュメントにインデックスを作成するためのモデルを提供します。インデックスを検索し、検索結果を整理して表示できます。API は文字列フィールドのフルテキストの突き合わせをサポートします。ドキュメントとインデックスは、検索処理のために最適化された独立した永続ストアに保存されます。Search API は、任意の数のドキュメントのインデックスを作成できます。非常に大きな結果セットを取得する必要があるアプリケーションには、App Engine Datastore のほうが適しています。

概要

Search API は、ドキュメント、インデックス、クエリ、結果という 4 つの主要な概念に基づいています。

ドキュメント

ドキュメントとは、一意の ID を持つオブジェクトであり、ユーザーデータを含むフィールドのリストが含まれています。各フィールドには名前と型があります。フィールドには複数の型があり、フィールドに含まれている値の種類によって指定されます。

  • Atom フィールド - 非表示の文字列
  • テキスト フィールド - ワード単位で検索可能な書式なしテキストの文字列
  • HTML フィールド - HTML マークアップ タグを含む文字列。マークアップ タグの外のテキストのみを検索可能
  • 数値フィールド - 浮動小数点数
  • 日付フィールド - 日付オブジェクト
  • 地理位置情報フィールド - 緯度と経度の座標が指定されたデータ オブジェクト

ドキュメントの最大サイズは 1 MB です。

インデックス

インデックスには取得用にドキュメントを格納できます。ドキュメント ID を 1 つ指定してドキュメントを 1 つ取得したり、連続する ID を指定して一連のドキュメントを取得したり、インデックス内のすべてのドキュメントを取得したりできます。また、フィールドとその値についての条件をクエリ文字列として指定してインデックスを検索し、その条件を満たすドキュメントを取得することもできます。ドキュメントをグループごとに別のインデックスに入れてグループ単位で管理することも可能です。

1 つのインデックスに格納できるドキュメント数と使用できるインデックス数に制限はありません。デフォルトでは 1 つのインデックスに格納できるすべてのドキュメントの合計サイズが 10 GB に制限されていますが、リクエストを送信して最大 200 GB まで増やすことができます。

クエリ

インデックスを検索するには、クエリ文字列といくつかの追加のオプションを使用してクエリを作成します。クエリ文字列は、1 つ以上のドキュメント フィールドの値の条件を指定します。インデックスを検索すると、クエリを満たすフィールドを持つインデックス内のドキュメントのみが戻されます。

「グローバル検索」と呼ばれることがある最もシンプルなクエリは、フィールド値のみを含む文字列です。次の検索では、「rose」と「water」という語を含むドキュメントを検索する文字列を使用しています。

index.search("rose water");

次の例は、1776 年 7 月 4 日の日付を含む日付フィールドまたは文字列「1776-07-04」を含むテキスト フィールドを持つドキュメントを検索します。

index.search("1776-07-04");

さらに絞り込むクエリ文字列を指定することもできます。クエリ文字列に、フィールドとフィールド値の制約を指定した語句を 1 つ以上含めることができます。語句の正確な形式は、フィールドの型によって異なります。たとえば、「product」というテキスト フィールドと「price」という数値フィールドがある場合は、2 つの語句を含む次のクエリ文字列を指定できます。

// search for documents with pianos that cost less than $5000
index.search("product = piano AND price < 5000");

クエリ オプションは、名前が示すとおり必須ではありません。クエリ オプションは次のようなさまざまな機能を有効にします。

  • 検索結果に返すドキュメントの数を制御する。
  • 検索結果に含めるドキュメント フィールドを指定する。デフォルトでは、元のドキュメントのすべてのフィールドが検索結果に含められます。一部のフィールドのみを検索結果に含めるように指定できます(元のドキュメントへの影響はありません)。
  • 検索結果を並べ替える。
  • FieldExpressions を使用してドキュメントの「コンピューティングされたフィールド」を作成し、スニペットを使用して短縮されたテキスト フィールドを作成する。
  • (オフセットとカーソルを使用して)各クエリに一致したドキュメントの一部のみを返すことで、検索結果のページ分割をサポートする

検索結果

search() を呼び出して、一致するドキュメントのうち限られた数のみを返すことができます。1 回の呼び出しで返すことができる数より多くのドキュメントが検索で見つかる場合があります。検索呼び出しごとに Results クラスのインスタンスが返されます。これには、見つかったドキュメント数と返されたドキュメント数の情報が、返されたドキュメントのリストとともに含まれています。カーソルオフセットを使用して同じ検索を繰り返すことで、一致するドキュメントをすべて取得できます。

追加のトレーニング資料

このドキュメントに加えて、Search API に関する 2 部構成のトレーニング クラスが Google Developers Academy に用意されています(クラスでは Python API を使用しますが、Search の概念についての追加の説明は役に立ちます)。

ドキュメントとフィールド

Document クラスはドキュメントを表します。各ドキュメントは、ドキュメント IDフィールドのリストを持っています。

ドキュメント ID

インデックス内のドキュメントはそれぞれ一意のドキュメント ID(doc_id)を持っています。検索を実行しなくても、この ID を使用してインデックスからドキュメントを取得できます。デフォルトでは、ドキュメントが作成されたときに Search API が自動的に doc_id を生成します。ユーザーがドキュメントを作成するときに自分で doc_id を指定することもできます。doc_id は、表示可能で印刷可能な ASCII 文字(ASCII コード 33 から 126 まで)で構成され、かつ 500 字以内でなければなりません。ドキュメント ID の最初の文字を感嘆符(!)にすることはできず、最初と最後の文字を 2 つのアンダースコア(__)にすることもできません。

読み取りやすく意味のある一意のドキュメント ID を作成すると便利ですが、検索に doc_id を含めることはできません。次のシナリオを考えてみましょう。部品を表すドキュメントに、部品のシリアル番号を doc_id として使用したインデックスがあるとします。この場合、非常に効率的に任意の 1 つの部品のドキュメントを取得できますが、他のフィールド値(購入日など)とともに一定範囲のシリアル番号を使用して検索することはできません。この問題は、シリアル番号を Atom フィールドに格納することで解決できます。

ドキュメントのフィールド

ドキュメントには、名前、型、その型の単一の値を持つフィールドが含まれています。2 つ以上の値を同じ名前にすることは可能ですが、その場合は型が異なっていなければなりません。たとえば、「age」という名前の 2 つのフィールドを、テキスト型(値は「twenty-two」)と、数値型(値は「22」)を指定して定義できます。

フィールド名

フィールド名では大文字と小文字が区別され、ASCII 文字のみを含めることができます。先頭は文字でなければならず、文字、数字、アンダースコアを含めることができます。フィールド名は 500 文字以下にする必要があります。

複数の値を持つフィールド

1 つのフィールドには 1 つの値だけを含めることができ、その値はフィールドの型と一致している必要があります。フィールド名を一意にする必要はありません。ドキュメントには同じ名前と同じ型の複数のフィールドを含めることができ、これによって 1 つのフィールドを複数の値で表すことができます(ただし、日付フィールドと数値フィールドについては同じ名前のものが複数含まれていてはいけません)。また、名前が同じでフィールド型が異なる複数のフィールドをドキュメントに含めることもできます。

フィールドの型

java.lang.String 文字列を格納するフィールドには次の 3 種類があり、それらをまとめて「文字列フィールド」と呼びます。

  • テキスト フィールド: 最大 1024**2 文字の文字列。
  • HTML フィールド: 最大 1024**2 文字の HTML 形式の文字列。
  • Atom フィールド: 最大 500 文字の文字列。

さらに、非テキスト データを格納する 3 つのフィールド型があります。

  • 数値フィールド: -2,147,483,647 から 2,147,483,647 までの範囲の倍精度浮動小数点値。
  • 日付フィールド: java.util.Date
  • 地理的位置フィールド: 緯度と経度の座標で表される地点。

フィールド型は、Field.FieldType 列挙型の TEXTHTMLATOMNUMBERDATEGEO_POINT によって指定されます。

文字列フィールドと日付フィールドの特別な処理

日付フィールド、テキスト フィールド、または HTML フィールドを持つドキュメントがインデックスに追加されるときには、いくつかの特別な処理が行われます。内部で何が行われているかを理解することは、Search API を効果的に使用するのに役立ちます。

文字列フィールドのトークン化

HTML フィールドまたはテキスト フィールドのインデックスを作成するときには、その内容がトークン化されます。空白や特殊文字(句読点やハッシュ記号など)がある場所で文字列がトークンに分割されます。インデックスには各トークンのエントリが含められます。これにより、フィールドの値の一部のみを含むキーワードやフレーズを検索することが可能になります。たとえば、「dark」を検索すると「it was a dark and stormy night」という文字列を含むテキスト フィールドを持つドキュメントに一致し、「time」を検索すると「this is a real-time system」という文字列を含むテキスト フィールドを持つドキュメントに一致します。

HTML フィールドでは、マークアップ タグ内のテキストはトークン化されないため、「it was a <strong>dark</strong> night」を含む HTML フィールドは「night」の検索とは一致しますが、「strong」の検索とは一致しません。マークアップ テキストを検索できるようにしたい場合は、そのテキストをテキスト フィールドに格納してください。

Atom フィールドはトークン化されません。「bad weather」という値を持つ Atom フィールドを含むドキュメントは、「bad weather」の文字列全体を検索した場合にのみ一致します。「bad」や「weather」だけを検索しても一致しません。

トークン化のルール
  • アンダースコア(_)文字とアンパサンド(&)文字では語句はトークンに分割されません。

  • 空白文字(スペース、復帰、改行、水平タブ、垂直タブ、フォーム フィード、NULL)がある場合は必ず語句がトークンに分割されます。

  • 以下の文字は句読点として処理され、語句がトークンに分割されます。

!"%(
*,-|/
[]]^`
:=>?@
{}~$

  • 次の表の文字がある場合、通常は語句がトークンに分割されますが、出現する文脈によっては異なる処理が行われる可能性があります。
文字 ルール
< HTML フィールドでは、「小なり」記号は HTML タグの開始を表すので無視されます。
+ 1 つ以上の「+」記号からなる文字列が語の最後に現れる場合(C++ など)、その文字列は語の一部として扱われます。
# 「ハッシュ」記号は、a、b、c、d、e、f、g、j、x が前に付く場合、語の一部として扱われます(a# から g# は音楽記号を表し、j# と x# はプログラミング言語を表します。c# はその両方を表します)。前に「#」が付く語(#google など)はハッシュタグとして扱われ、ハッシュは語の一部になります。
' アポストロフィは、「s」の文字の前に付き、「s」の後に語の区切りが来る場合(例、「John's hat」)、文字として扱われます。
. 数字の間に現れる小数点は、数値の一部(小数区切り記号)です。頭字語(A.B.C. など)の中で使用される場合も語の一部になります。
- ダッシュは、頭字語で使用される場合(I-B-M など)、語の一部です。
  • 文字と数字(A から Z、a から z、0 から 9)以外のすべての 7 ビット文字は、句読点として処理され、語句がトークンに分割されます。

  • それ以外のすべては、UTF-8 文字として解析されます。

頭字語

トークン化では、頭字語(「I.B.M.」、「a-b-c」、「C I A」など)を認識するための特別なルールが使用されています。頭字語とは、同じ区切り文字を使用してアルファベット文字を 1 つずつつなげた文字列です。使用可能な区切り文字は、ピリオド、ダッシュ、任意の数のスペースです。頭字語がトークン化されるときには、文字列から区切り文字が削除されます。そのため、前述の文字列の例の場合は「ibm」、「abc」、「cia」というトークンになります。元のテキストはドキュメント フィールドにそのまま残されます。

頭字語を扱う際には、以下の点に注意してください。

  • 頭字語には 21 以上の文字を含めることができません。有効な頭字語の文字列が 21 文字を超えている場合、その頭字語は 21 文字以下の複数の頭字語に分割されます。
  • 頭字語内の文字をスペースで区切る場合は、すべての文字が大文字または小文字で統一されている必要があります。ピリオドまたはダッシュを使用する頭字語は、大文字と小文字が混在していてもかまいません。
  • 頭字語を検索するときは、頭字語の正規形(区切り文字なしの文字列)あるいは文字の間をダッシュまたはドット(併用不可)で区切った頭字語を入力できます。したがって、「I.B.M」のテキストは「I-B-M」、「I.B.M」、「IBM」のどの検索語を使用しても取得できます。

日付フィールドの精度

ドキュメントに日付フィールドを作成するときは、その値を java.util.Date に設定します。日付フィールドのインデックス作成と検索では、時刻コンポーネントはすべて無視され、日付は 1970 年 1 月 1 日 UTC からの経過日数に変換されます。つまり、日付フィールドに正確な時刻値を含めることは可能ですが、日付クエリには yyyy-mm-dd の形式の日付フィールド値しか指定できません。またこれは、日付が同じ日付フィールドの並び順を明確に定義できないことも意味します。

その他のドキュメント プロパティ

ドキュメントの「ランク」とは、検索からドキュメントを返すデフォルトの順序を決定する正の整数です。デフォルトでは、ランクはドキュメントの作成時刻(2011 年 1 月 1 日からの経過秒数)に設定されます。ドキュメントを作成するときに、ランクを明示的に設定することができます(多数のドキュメントに同じランクを割り当てるのは良くありません。10,000 個を超えるドキュメントに同じランクを決して指定しないでください)。並べ替えオプションを指定する場合に、ランクをソートキーとして使用できます。並べ替え式またはフィールド式でランクを使用するときには、_rank と指定します。

言語プロパティは、フィールドをエンコードする言語を指定します。

上記の属性について詳しくは、Document クラスのリファレンス ページをご覧ください。

ドキュメントから他のリソースへのリンクの作成

ドキュメントの doc_id と他のフィールドを、アプリケーションで他のリソースへのリンクとして使用できます。たとえば、Blobstore を使用している場合、doc_id や Atom フィールドの値をデータの BlobKey に設定することで、ドキュメントを特定の Blob と関連付けることができます。

ドキュメントの作成

ドキュメントを作成するには、Document.newBuilder() メソッドを使用して新しいビルダーを要求します。ビルダーを利用できるようになると、アプリケーションでは、オプションでドキュメント ID を指定し、フィールドを追加できます。

フィールドも、ドキュメントと同様にビルダーを使用して作成します。Field.newBuilder() メソッドは、フィールドの名前と値を指定できるフィールド ビルダーを返します。フィールドの型は、特定の set メソッドを選択することによって自動的に指定されます。たとえば、プレーン テキストを保持するフィールドを指定するには、setText() を呼び出します。次のコードは、ゲストブックのメッセージを表すフィールドを含むドキュメントを作成します。

User currentUser = UserServiceFactory.getUserService().getCurrentUser();
String userEmail = currentUser == null ? "" : currentUser.getEmail();
String userDomain = currentUser == null ? "" : currentUser.getAuthDomain();
String myDocId = "PA6-5000";
Document doc = Document.newBuilder()
    // Setting the document identifer is optional.
    // If omitted, the search service will create an identifier.
    .setId(myDocId)
    .addField(Field.newBuilder().setName("content").setText("the rain in spain"))
    .addField(Field.newBuilder().setName("email").setText(userEmail))
    .addField(Field.newBuilder().setName("domain").setAtom(userDomain))
    .addField(Field.newBuilder().setName("published").setDate(new Date()))
    .build();

ドキュメント内のフィールドを利用するには、getOnlyField() を使用します。

String coverLetter = document.getOnlyField("coverLetter").getText();
String resume = document.getOnlyField("resume").getHTML();
String fullName = document.getOnlyField("fullName").getAtom();
Date submissionDate = document.getOnlyField("submissionDate").getDate();

インデックスの操作

インデックスへのドキュメントの挿入

インデックスにドキュメントを挿入すると、そのドキュメントは永続ストレージにコピーされ、ドキュメントの各フィールドのインデックスが名前、型、doc_id にしたがって作成されます。

次のコード例は、Index にアクセスし、その中にドキュメントを挿入する方法を示しています。ステップは次のとおりです。

public static void indexADocument(String indexName, Document document)
    throws InterruptedException {
  IndexSpec indexSpec = IndexSpec.newBuilder().setName(indexName).build();
  Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);

  final int maxRetry = 3;
  int attempts = 0;
  int delay = 2;
  while (true) {
    try {
      index.put(document);
    } catch (PutException e) {
      if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())
          && ++attempts < maxRetry) { // retrying
        Thread.sleep(delay * 1000);
        delay *= 2; // easy exponential backoff
        continue;
      } else {
        throw e; // otherwise throw
      }
    }
    break;
  }
}
一度に最大で 200 個のドキュメントを put() メソッドに渡すことができます。ドキュメントを一度に 1 つずつ追加するよりも、一括で put を実行するほうが効率的です。

インデックスにドキュメントを挿入したときに、そのインデックスにすでに同じ doc_id のドキュメントが含まれている場合、古いドキュメントは新しいドキュメントで置き換えられます。警告は表示されません。ドキュメントを作成したりインデックスに追加したりする前に Index.get(id) を呼び出して、特定の doc_id がすでに存在するかどうかを確認することができます。

Index クラスのインスタンスを作成しても永続インデックスが実際に存在することにはならないので注意してください。永続インデックスは、put メソッドを使用して初めてインデックスにドキュメントを追加したときに作成されます。インデックスを使用し始める前にインデックスが実際に存在するかどうかを確認する場合は、SearchService.getIndexes() メソッドを使用します。

ドキュメントの更新

インデックスに追加した後にドキュメントを変更することはできません。フィールドの追加や削除、フィールド値の変更もできません。ただし、ドキュメントを同じ doc_id を持つ新しいドキュメントに置き換えることはできます 。

doc_id によるドキュメントの取得

ドキュメント ID を使用してインデックスからドキュメントを取得するには、次の 2 通りの方法があります。
  • Index.get()doc_id を使用して 1 つのドキュメントを取得する。
  • Index.getRange() を使用して doc_id の順序で並べられた一連のドキュメントのグループを取得する。

それぞれの呼び出しの例を以下に示します。

IndexSpec indexSpec = IndexSpec.newBuilder().setName(INDEX).build();
Index index = SearchServiceFactory.getSearchService().getIndex(indexSpec);

// Fetch a single document by its  doc_id
Document doc = index.get("AZ125");

// Fetch a range of documents by their doc_ids
GetResponse<Document> docs = index.getRange(
    GetRequest.newBuilder().setStartId("AZ125").setLimit(100).build());

内容によるドキュメントの検索

インデックスからドキュメントを取得するには、クエリ文字列を作成して Index.search() を呼び出します。クエリ文字列は、引数として直接渡すことも、引数として渡される Query オブジェクトに含めることもできます。デフォルトでは、search() は一致するドキュメントをランクの降順に並び替えて返します。返されるドキュメントの数や並べ替え方法を制御したり、結果にコンピューティングされたフィールドを追加したりするには、Query オブジェクトを使用する必要があります。このオブジェクトにはクエリ文字列が含まれ、他の検索オプションや並べ替えオプションを指定することもできます。

final int maxRetry = 3;
int attempts = 0;
int delay = 2;
while (true) {
  try {
    String queryString = "product = piano AND price < 5000";
    Results<ScoredDocument> results = getIndex().search(queryString);

    // Iterate over the documents in the results
    for (ScoredDocument document : results) {
      // handle results
      out.print("maker: " + document.getOnlyField("maker").getText());
      out.println(", price: " + document.getOnlyField("price").getNumber());
    }
  } catch (SearchException e) {
    if (StatusCode.TRANSIENT_ERROR.equals(e.getOperationResult().getCode())
        && ++attempts < maxRetry) {
      // retry
      try {
        Thread.sleep(delay * 1000);
      } catch (InterruptedException e1) {
        // ignore
      }
      delay *= 2; // easy exponential backoff
      continue;
    } else {
      throw e;
    }
  }
  break;
}

インデックスからのドキュメントの削除

削除したい 1 つ以上のドキュメントの doc_iddelete() メソッドに指定してインデックス内のドキュメントを削除することができます。インデックス内のドキュメント ID を取得するには、GetRequest.BuildersetReturningIdsOnly() メソッドを呼び出し、これが getRange() メソッドに渡されます。このメソッドを呼び出すと、API は doc_id のみが指定されたドキュメント オブジェクトを返します。その後、それらのドキュメント ID を delete() メソッドに渡して、該当する複数のドキュメントを削除できます。

try {
  // looping because getRange by default returns up to 100 documents at a time
  while (true) {
    List<String> docIds = new ArrayList<>();
    // Return a set of doc_ids.
    GetRequest request = GetRequest.newBuilder().setReturningIdsOnly(true).build();
    GetResponse<Document> response = getIndex().getRange(request);
    if (response.getResults().isEmpty()) {
      break;
    }
    for (Document doc : response) {
      docIds.add(doc.getId());
    }
    getIndex().delete(docIds);
  }
} catch (RuntimeException e) {
  LOG.log(Level.SEVERE, "Failed to delete documents", e);
}
一度に最大で 200 個のドキュメントを delete() メソッドに渡すことができます。一度に 1 つずつ削除するよりも、一括で削除するほうが効率的です。

結果整合性

インデックスにドキュメントを挿入したり、インデックス内のドキュメントを更新したり削除したりすると、その変更が複数のデータセンターに伝播されます。通常この処理はごく短時間で行われますが、所要時間は一定ではありません。Search API が保証するのは結果整合性です。これは、ID を使用して検索したり 1 つ以上のドキュメントを取得したりする場合に、最新の変更が結果に反映されていないことがあることを意味します。

インデックスのサイズの確認

インデックスには取得用にドキュメントを格納できます。ドキュメント ID を 1 つ指定してドキュメントを 1 つ取得したり、連続する ID を指定して一連のドキュメントを取得したり、インデックス内のすべてのドキュメントを取得したりできます。また、フィールドとその値についての条件をクエリ文字列として指定してインデックスを検索し、その条件を満たすドキュメントを取得することもできます。ドキュメントをグループごとに別のインデックスに入れてグループ単位で管理することも可能です。1 つのインデックスに格納できるドキュメント数と使用できるインデックス数に制限はありません。デフォルトでは、1 つのインデックスに格納できるすべてのドキュメントの合計サイズが 10 GB に制限されていますが、リクエストを送信して最大 200 GB に増やすことができます(メソッド Index.getStorageLimit() によって、インデックスの最大許容サイズが返されます)。

メソッド Index.getStorageUsage() はインデックスで使用されているストレージ スペースの推定容量です。インデックスのモニタリング システムは継続的に実行されてはいないため、この数は推定値です。実際の使用量は定期的にコンピューティングされます。storage_usage は、ドキュメントの削除ではなく追加を考慮してサンプリング ポイント間で調整されます。

インデックス スキーマ

すべてのインデックスは、そのインデックス中に含まれているドキュメントに出現するすべてのフィールド名とフィールド型を示すスキーマを持っています。スキーマを自分で定義することはできません。スキーマは動的に保守され、ドキュメントがインデックスに追加されると更新されます。シンプルなスキーマは、次のような JSON に似た形式です。

{'comment': ['TEXT'], 'date': ['DATE'], 'author': ['TEXT'], 'count': ['NUMBER']}

ディクショナリ内の各キーは、ドキュメント フィールドの名前です。キーの値は、そのフィールド名で使用されているフィールド型のリストです。複数の異なるフィールド型で同じフィールド名を使用している場合は、スキーマに次のように 1 つのフィールド名について複数のフィールド型がリストされます。

{'ambiguous-integer': ['TEXT', 'NUMBER', 'ATOM']}

一度スキーマに現れたフィールドを削除することはできません。あるフィールド名を含むドキュメントがインデックスに存在しなくなった場合でも、そのフィールドを削除する方法はありません。

自分のインデックスのスキーマは、次のようにして表示できます。

GetResponse<Index> response = SearchServiceFactory.getSearchService().getIndexes(
    GetIndexesRequest.newBuilder().setSchemaFetched(true).build());

// List out elements of each Schema
for (Index index : response) {
  Schema schema = index.getSchema();
  for (String fieldName : schema.getFieldNames()) {
    List<FieldType> typesForField = schema.getFieldTypes(fieldName);
    // Just printing out the field names and types
    for (FieldType type : typesForField) {
      out.println(index.getName() + ":" + fieldName + ":" + type.name());
    }
  }
}
GetIndexes() の呼び出しでは、1,000 を超えるインデックスは返せないので注意してください。それ以上のインデックスを取得するには、setStartIndexName()GetIndexesRequest.Builder とともに使用してメソッドを繰り返し呼び出してください。

スキーマは、オブジェクト プログラミングでいう「クラス」を定義するものではありません。Search API に関する限り、すべてのドキュメントは一意であり、インデックスにはさまざまな種類のドキュメントを含めることができます。フィールド リストが同じオブジェクトの集合をクラスのインスタンスとして扱うためには、コード内で抽象化を行う必要があります。たとえば、同じセットのフィールドを持つドキュメントをすべて専用のインデックスに入れることができます。インデックス スキーマはクラス定義と見なされ、インデックス内の各ドキュメントはクラスのインスタンスになります。

Google Cloud Platform Console でのインデックスの表示

Cloud Platform Console で、アプリケーションのインデックスとインデックスに含まれているドキュメントに関する情報を表示することができます。インデックス名をクリックすると、インデックスに含まれているドキュメントが表示されます。インデックスのすべての定義済みスキーマ フィールドが表示され、その名前のフィールドを持つドキュメントごとにフィールドの値が表示されます。また、インデックス データに対するクエリをコンソールから直接発行することができます。

Search API の割り当て

Search API には次のようにいくつかの無料の割り当てがあります。

リソースまたは API 呼び出し 無料の割り当て
ストレージの合計(ドキュメントとインデックス) 0.25 GB
クエリ 1,000 クエリ/日
インデックスへのドキュメントの追加 0.01 GB/日

サービスの信頼性を維持するため、Search API には次の制限が設定されています。これらの制限は無料割り当てと有料割り当ての両方に適用されます。

リソース 安全上の割り当て
クエリの最大使用量 クエリ実行時間の合計 100 分/分
追加または削除されるドキュメントの最大数 15,000/分
インデックス 1 つあたりの最大サイズ(許可されるインデックスの数は無制限) 10 GB

API の使用量は、呼び出しのタイプに応じて異なる方法でカウントされます。

  • Index.search(): 各 API 呼び出しが 1 つのクエリとしてカウントされます。実行時間は呼び出しのレイテンシに相当します。
  • Index.put(): インデックスにドキュメントを追加すると、各ドキュメントのサイズとドキュメントの数が、インデックス作成割り当て量に近づきます。
  • その他のすべての Search API 呼び出しは、呼び出しに必要なオペレーションの数に基づいてカウントされます。
    • SearchService.getIndexes(): 実際にインデックスが返されるたびに 1 オペレーションがカウントされます。また、何も返されなくても 1 オペレーションがカウントされます。
    • Index.get()Index.getRange(): 実際にドキュメントが返されるたびに 1 オペレーションがカウントされます。また、何も返されなくても 1 オペレーションがカウントされます。
    • Index.delete(): リクエスト内のドキュメントごとに 1 オペレーションがカウントされます。また、リクエストが空の場合も 1 オペレーションがカウントされます。

1 人のユーザーが検索サービスを独占できないように、クエリのスループットに割り当てが設定されています。クエリは同時に実行できるので、アプリケーションごとに、クロック時間 1 分あたりのクエリの実行時間の合計が最大 100 分になるまでクエリを実行することが許可されています。短いクエリを多数実行している場合、この限界に達することはまずありません。この割り当てを超えた場合は、割り当てが元に戻される次のタイムスライスまで、それ以降のクエリは失敗します。厳密に 1 分間のタイムスライスで割り当てを適用しているわけではなく、リーキー バケット アルゴリズムの一種を使用して、5 秒間隔で検索帯域幅を制御しています。

割り当てについて詳しくは、割り当てのページをご覧ください。アプリがこれらの量を超えると、割り当て不足エラーが返されます。

これらの制限は分単位で適用されますが、コンソールには各制限についての日単位の合計数が表示されることに注意してください。シルバー、ゴールド、プラチナ サポートをご契約の方は、サポート担当者に連絡して、スループットの上限を引き上げることができます。

Search API 料金

アプリの課金を有効にした場合は、無料の割り当て分を超えた追加使用分に対して課金されます。以下の料金が請求対象のアプリに適用されます。

リソース 料金
ストレージの合計(ドキュメントとインデックス) 月額 $0.18 / GB
クエリ $0.50 / 1 万クエリ
検索可能なドキュメントのインデックス作成 $2.00 / GB

料金に関する追加情報は料金のページにあります。

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

Java の App Engine スタンダード環境