以前のバンドル サービス用の Search API

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

概要

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

ドキュメント

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

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

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

インデックス

インデックスにはユーザーが取得できるようにドキュメントが格納されます。ID を直接指定して単一ドキュメントを取得する、連続した ID を指定して一連のドキュメントを取得する、インデックス内の全ドキュメントを取得するなど、複数の取得方法がサポートされています。また、フィールドとその値についての条件をクエリ文字列として指定してインデックスを検索し、その条件を満たすドキュメントを取得することもできます。ドキュメントをグループごとに別のインデックスに入れてグループ単位で管理することも可能です。

1 つのインデックスに格納できるドキュメント数と使用できるインデックス数に上限はありません。デフォルトでは、1 つのインデックスに格納できるすべてのドキュメントの合計サイズは 10 GB に制限されています。App Engine 管理者のロールを持つユーザーは、Google Cloud コンソールの App Engine Search ページからリクエストを送信することで、最大 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 回につき、返すことができる一致ドキュメントの数には制限があります。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 フィールド: 最大 1,024**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 を使用している場合、ドキュメントを特定の blob と関連付けるには、doc_id または Atom フィールドの値をデータの BlobKey に設定します。

ドキュメントの作成

ドキュメントを作成するには、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 に従って作成されます。

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

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() メソッドに指定します。効率を高めるには、複数のドキュメントを一括で削除します。一度に最大で 200 個のドキュメント 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 が保証するのは結果整合性です。つまり、1 つ以上のドキュメントを検索または取得した場合、最新の変更が反映されていない結果が返されることがあります。

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

インデックスにはユーザーが取得できるようにドキュメントが格納されます。ID を直接指定して単一ドキュメントを取得する、連続した ID を指定して一連のドキュメントを取得する、インデックス内の全ドキュメントを取得するなど、複数の取得方法がサポートされています。また、フィールドとその値についての条件をクエリ文字列として指定してインデックスを検索し、その条件を満たすドキュメントを取得することもできます。ドキュメントをグループごとに別のインデックスに入れてグループ単位で管理することも可能です。1 つのインデックスに格納できるドキュメント数と使用できるインデックス数に上限はありません。1 つのインデックスに格納できる全ドキュメントの合計サイズはデフォルトで 10 GB に制限されていますが、Google Cloud コンソールの [App Engine] > [検索] ページからリクエストを送信することで、最大 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 コンソールでのインデックスの表示

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

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

料金に関する詳細については、料金のページをご覧ください。