Dokumente und Indexe

Die Search API bietet ein Modell für die Indexierung von Dokumenten, die strukturierte Daten enthalten. Sie können damit einen Index durchsuchen sowie Suchergebnisse organisieren und präsentieren. Die API unterstützt den Volltextabgleich für Stringfelder. Dokumente und Indexe werden in einem separaten nichtflüchtigen Speicher abgelegt, der für Suchvorgänge optimiert wurde. Die Search API kann eine beliebige Anzahl von Dokumenten indexieren, Der App Engine Datastore eignet sich möglicherweise besser für Anwendungen, die sehr große Ergebnismengen abrufen müssen.

Überblick

Die Search API basiert auf vier Hauptkonzepten: Dokumente, Indexe, Abfragen und Ergebnisse.

Dokumente

Ein Dokument ist ein Objekt mit einer eindeutigen ID und einer Liste von Feldern, die Nutzerdaten enthalten. Jedes Feld enthält einen Namen und einen Typ. Es gibt verschiedene Feldtypen, die durch die Arten von Werten identifiziert werden, die sie enthalten:

  • Atomfeld: Ein unteilbarer Zeichenstring
  • Textfeld: Ein einfacher Textstring, der Wort für Wort durchsucht werden kann
  • HTML-Feld: Ein String, der HTML-Markup-Tags enthält; nur Text außerhalb der Markup-Tags kann durchsucht werden
  • Zahlenfeld: Eine Gleitkommazahl
  • Datumsfeld: Ein Datumsobjekt
  • Geopunktfeld: Ein Datenobjekt mit Längen- und Breitengraden

Die maximale Größe eines Dokuments beträgt 1 MB.

Indexe

Ein Index speichert Dokumente zum Abrufen. Sie können ein einzelnes Dokument anhand seiner ID, eine Reihe von Dokumenten mit fortlaufenden IDs oder alle Dokumente in einem Index abrufen. Außerdem können Sie einen Index durchsuchen, um Dokumente abzurufen, die bestimmte Kriterien für Felder und die zugehörigen Werte erfüllen. Diese Kriterien werden als Abfragestring angegeben. Gruppen von Dokumenten können Sie verwalten, indem Sie sie in separate Indexe einfügen.

Die Anzahl der Dokumente in einem Index oder die Anzahl der Indexe, die Sie verwenden können, unterliegen keiner Beschränkung. Die Gesamtgröße aller Dokumente in einem einzelnen Index ist standardmäßig auf 10 GB beschränkt. Sie können jedoch über die Seite App Engine Search in der Google Cloud Console eine Anfrage senden, um die Begrenzung auf bis zu 200 GB zu erhöhen.

Abfragen

Erstellen Sie eine Abfrage, die einen Abfragestring und gegebenenfalls einige zusätzliche Optionen enthält, um einen Index zu durchsuchen. Ein Abfragestring gibt Bedingungen für die Werte eines oder mehrerer Dokumentfelder an. Wenn Sie einen Index durchsuchen, werden nur die darin enthaltenen Dokumente ausgegeben, deren Felder die Abfragekriterien erfüllen.

Bei der einfachsten Abfrage, die manchmal als "globale Suche" bezeichnet wird, handelt es sich um einen String, der nur Feldwerte enthält. Die folgende Suche verwendet einen String, der nach Dokumenten sucht, die die Wörter "rose" und "water" enthalten:

index.search("rose water");

Dieser String sucht nach Dokumenten mit Datumsfeldern, die das Datum 4. Juli 1776 enthalten, oder Textfeldern, die den String"1776-07-04" enthalten:

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

Ein Abfragestring kann auch spezifischer sein. Er kann einen oder mehrere Begriffe enthalten, die jeweils ein Feld und eine Einschränkung für den Feldwert benennen. Die genaue Form eines Begriffs hängt vom Feldtyp ab. Angenommen, es gibt ein Textfeld mit dem Namen "Product" und ein Zahlenfeld mit dem Namen "Price". Hier ein Abfragestring mit zwei Begriffen:

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

Abfrageoptionen sind, wie der Name schon sagt, nicht erforderlich. Sie ermöglichen aber eine Vielzahl von Funktionen:

  • Die Anzahl der Dokumente, die als Suchergebnisse zurückgegeben werden, kann festgelegt werden.
  • Dokumentfelder, die in den Ergebnissen berücksichtigt werden sollen, können angegeben werden. Standardmäßig werden alle Felder aus dem Originaldokument berücksichtigt. Sie können aber auch angeben, dass die Ergebnisse nur eine Teilmenge von Feldern enthalten sollen. Das Originaldokument wird hierbei nicht verändert.
  • Ergebnisse können sortiert werden.
  • "Berechnete Felder" für Dokumente mit FieldExpressions und gekürzten Textfeldern können mithilfe von Snippets erstellt werden.
  • Das Durchblättern von Suchergebnissen kann unterstützt werden. Dazu wird mithilfe von Offsets und Cursors jeweils nur ein Teil der mit einer Abfrage übereinstimmenden Dokumente zurückgegeben.

Suchergebnisse

Ein Aufruf von search() kann nur eine begrenzte Anzahl von übereinstimmenden Dokumenten zurückgeben. Bei der Suche werden möglicherweise viel mehr Dokumente gefunden, als in einem einzigen Aufruf zurückgegeben werden können. Bei jedem Suchaufruf wird eine Instanz der Klasse Results zurückgegeben. Diese Klasse enthält Informationen dazu, wie viele Dokumente gefunden und wie viele zurückgegeben wurden. Außerdem wird eine Liste der zurückgegebenen Dokumente ausgegeben. Sie können dieselbe Suche mit Cursors oder Offsets wiederholen, um alle übereinstimmenden Dokumente abzurufen.

Zusätzliches Schulungsmaterial

Zusätzlich zu dieser Dokumentation können Sie den zweiteiligen Kurs zur Search API in der Google Developer's Academy lesen. In dem Kurs wird zwar die Python API verwendet, die weiterführende Erläuterung der Suchkonzepte kann aber trotzdem sehr hilfreich sein.

Dokumente und Felder

Die Klasse Document stellt Dokumente dar. Jedes Dokument hat eine Dokument-ID und eine Liste von Feldern.

Dokument-ID

Jedes Dokument in einem Index muss eine eindeutige Dokument-ID oder doc_id haben. Die ID kann verwendet werden, um ein Dokument aus einem Index abzurufen, ohne eine Suche durchzuführen. Standardmäßig generiert die Search API bei der Erstellung eines Dokuments automatisch eine doc_id. Sie können die doc_id auch selbst angeben, während Sie ein Dokument erstellen. Eine doc_id darf nur sichtbare, druckbare ASCII-Zeichen (ASCII-Codes 33 bis einschließlich 126) enthalten und darf nicht länger als 500 Zeichen sein. Eine Dokument-ID darf nicht mit einem Ausrufezeichen ("!") beginnen und nicht mit doppelten Unterstrichen ("__") beginnen und enden.

Es ist zwar praktisch, lesbare, aussagekräftige eindeutige Dokument-IDs zu erstellen, Sie können die doc_id jedoch nicht in eine Suche einschließen. Stellen Sie sich das folgende Szenario vor: Sie haben einen Index mit Dokumenten, die Teile darstellen, wobei die Seriennummer des Teils als doc_id verwendet wird. Dadurch ist es ein Kinderspiel, das Dokument für ein einzelnes Teil abzurufen, aber es ist unmöglich, nach einer Reihe von Seriennummern zusammen mit anderen Feldwerten, wie z. B. dem Kaufdatum, zu suchen. Wenn die Seriennummer in einem Atomfeld gespeichert ist, stellt dies kein Problem mehr dar.

Dokumentfelder

Ein Dokument enthält Felder mit einem Namen, einem Typ und einem einzelnen Wert dieses Typs. Zwei oder mehr Felder können denselben Namen, aber unterschiedliche Typen haben. Zum Beispiel können Sie zwei Felder mit dem Namen "Alter" definieren: eines mit einem Texttyp (Wert "Zweiundzwanzig"), das andere mit einem Zahlentyp (Wert 22).

Feldnamen

Feldnamen berücksichtigen die Groß- und Kleinschreibung und dürfen nur ASCII-Zeichen enthalten. Sie müssen mit einem Buchstaben beginnen und können Buchstaben, Ziffern oder Unterstriche enthalten. Ein Feldname darf nicht länger als 500 Zeichen sein.

Mehrwertige Felder

Ein Feld darf nur einen einzigen Wert enthalten, der dem Feldtyp entsprechen muss. Feldnamen müssen nicht eindeutig sein. Ein Dokument kann mehrere Felder mit demselben Namen und demselben Typ haben, wodurch ein Feld mit mehreren Werten dargestellt werden kann. Datums- und Zahlenfelder mit demselben Namen dürfen jedoch nicht wiederholt werden. Ein Dokument kann auch mehrere Felder mit demselben Namen und unterschiedlichen Feldtypen enthalten.

Feldtypen

Es gibt drei Arten von Feldern, in denen Zeichenstrings vom Typ java.lang.String gespeichert werden. Diese werden als Stringfelder bezeichnet:

  • Textfeld: ein String mit einer maximalen Länge von 1.024**2 Zeichen
  • HTML-Feld: ein HTML-formatierter String mit einer maximalen Länge von 1.024**2 Zeichen
  • Atomfeld: ein String mit einer maximalen Länge von 500 Zeichen

Außerdem gibt es drei Feldtypen, die nicht textbasierte Daten speichern:

  • Zahlenfeld: ein Gleitkommawert mit doppelter Genauigkeit zwischen -2.147.483.647 und 2.147.483.647
  • Datumsfeld: Ein java.util.Date.
  • Geopunktfeld: ein Punkt auf der Erde, der durch Breiten- und Längengrade beschrieben wird

Angegeben werden die Feldtypen mit den Field.FieldType-Enums TEXT, HTML, ATOM, NUMBER, DATE und GEO_POINT.

Spezielle Behandlung von String- und Datumsfeldern

Wenn ein Dokument mit Datums-, Text- oder HTML-Feldern einem Index hinzugefügt wird, erfordert dies eine spezielle Behandlung. Wenn Sie verstehen, was im Hintergrund passiert, können Sie die Search API effektiver nutzen.

Tokenisierung von Stringfeldern

Wenn ein HTML- oder Textfeld indexiert wird, wird sein Inhalt in Tokens aufgeteilt, also tokenisiert. Der String wird überall dort aufgeteilt, wo er Leerzeichen oder Sonderzeichen (Interpunktionszeichen, Rautezeichen, Backslash usw.) enthält. Der Index enthält für jedes Token einen Eintrag. Auf diese Weise können Sie nach Suchbegriffen und Wortgruppen suchen, die nur einen Teil des Feldwerts umfassen. Zum Beispiel liefert die Suche nach "dark" ein Dokument mit einem Textfeld, das den String "it was a dark and stormy night" enthält. Bei der Suche nach "time" wird ein Dokument mit einem Textfeld ausgegeben, das den String "this is a real-time system" enthält.

In HTML-Feldern wird Text innerhalb von Markup-Tags nicht tokenisiert. Ein Dokument mit einem HTML-Feld, das it was a <strong>dark</strong> night enthält, wird bei einer Suche nach "night" zurückgegeben, aber nicht bei einer Suche nach "strong". Möchten Sie die Suche nach Markup-Text ermöglichen, speichern Sie diesen in einem Textfeld.

Atomfelder werden nicht tokenisiert. Ein Dokument mit einem Atomfeld mit dem Wert "schlechtes Wetter" wird nur bei einer Suche nach dem gesamten String "schlechtes Wetter" zurückgegeben. Es stimmt nicht mit einer Suche nach "schlechtes" oder "Wetter" überein.

Regeln für die Tokenisierung
  • Der Unterstrich (_) sowie das kaufmännische Und-Zeichen (&) unterteilen Wörter nicht in Tokens.

  • Die folgenden Leerraumzeichen unterteilen Wörter immer in Tokens: Leerzeichen, Zeilenumbruch, Zeilenvorschub, horizontaler Tabulator, vertikaler Tabulator, Seitenvorschub und NULL.

  • Die folgenden Zeichen werden als Interpunktion behandelt und unterteilen Wörter in Tokens:

    !"%()
    *,-|/
    []]^`
    :=>?@
    {}~$
  • Die Zeichen in der folgenden Tabelle unterteilen Wörter normalerweise in Tokens, aber sie werden je nach dem Kontext, in dem sie angezeigt werden, unterschiedlich behandelt:

    Zeichen Regel
    < In einem HTML-Feld zeigt das "Kleiner als"-Zeichen den Beginn eines HTML-Tags an, das ignoriert wird.
    + Ein String aus einem oder mehreren Pluszeichen wird als Teil des Wortes behandelt, wenn er am Ende des Wortes steht ("C++").
    # Das Rautezeichen wird als Teil des Wortes behandelt, wenn ihm a, b, c, d, e, f, g, j oder x vorangestellt sind (a# bis g# sind Musiknoten; j# und x# sind Programmiersprachen, c# ist beides). Steht "#" vor einem Begriff ("#google"), wird es als Hashtag behandelt und somit als Teil des Wortes.
    ' Ein Apostroph gilt als Buchstabe, wenn es dem Buchstaben "s" vorangeht, gefolgt von einem Wortumbruch, z. B. im Englischen "John's hat".
    . Wenn ein Punkt zwischen Ziffern steht, ist dieser Teil einer Zahl (z. B. das Tausendertrennzeichen oder der englische Dezimalpunkt). Er kann auch Teil eines Wortes sein, wenn er in einem Akronym verwendet wird (A.B.C).
    - Der Bindestrich ist Teil eines Wortes, wenn er in einem Akronym verwendet wird (I-B-M).
  • Alle anderen 7-Bit-Zeichen außer Buchstaben und Ziffern ("A-Z", "a-z", "0-9") werden als Interpunktion behandelt und unterteilen Wörter in Tokens.

  • Alles andere wird als UTF-8-Zeichen geparst.

Akronyme

Bei der Aufteilung in Tokens werden spezielle Regeln zur Erkennung von Akronymen (Strings wie "I.B.M.", "a-b-c" oder "C I A") angewandt. Ein Akronym ist ein String aus einzelnen Buchstaben mit demselben Trennzeichen zwischen allen Zeichen. Gültige Trennzeichen sind Punkte, Bindestriche oder eine beliebige Anzahl von Leerzeichen. Die Trennzeichen werden bei der Tokenisierung aus dem String entfernt. Daher werden die oben erwähnten Beispielstrings zu den Tokens "ibm", "abc" und "cia". Der ursprüngliche Text im Dokumentfeld wird dabei nicht verändert.

Beachten Sie beim Umgang mit Akronymen Folgendes:

  • Ein Akronym darf nicht mehr als 21 Buchstaben enthalten. Ein gültiger Akronymstring mit mehr als 21 Buchstaben wird in eine Folge von Akronymen mit jeweils 21 Buchstaben oder weniger unterteilt.
  • Wenn die Buchstaben in einem Akronym durch Leerzeichen getrennt sind, müssen alle Buchstaben einheitlich groß- oder kleingeschrieben sein. In Akronymen, die mit Punkt oder Bindestrich geschrieben werden, können Sie Groß- und Kleinbuchstaben gemischt verwenden.
  • Wenn Sie nach einem Akronym suchen, können Sie die kanonische Form des Akronyms eingeben (also den String ohne Trennzeichen) oder das Akronym mit Bindestrich oder Punkt (nicht aber mit beidem) als Trennzeichen zwischen den Buchstaben. So könnte der Text "I.B.M" mit jedem der Suchbegriffe "I-B-M", "I.B.M" oder "IBM" abgerufen werden.

Datumsfeldgenauigkeit

Wenn Sie ein Datumsfeld in einem Dokument erstellen, legen Sie dessen Wert auf ein java.util.Date fest. Wenn ein Datumsfeld indexiert bzw. darin gesucht wird, werden Zeitkomponenten ignoriert und das Datum in die Anzahl der Tage seit dem 1.1.1970 UTC umgerechnet. Das bedeutet, dass ein Datumsfeld zwar einen genauen Uhrzeitwert enthalten kann, bei einer Datumsabfrage aber nur ein Datumsfeldwert im Format yyyy-mm-dd angegeben wird. Es bedeutet auch, dass die sortierte Reihenfolge von Datumsfeldern mit demselben Datum nicht klar definiert ist.

Andere Dokumentattribute

Der Rang eines Dokuments ist eine positive Ganzzahl, mit der die Standardreihenfolge von Dokumenten bestimmt wird, die eine Suche zurückgibt. Standardmäßig wird der Rang bei der Dokumenterstellung auf die Anzahl der Sekunden seit dem 01. Januar 2011 festgelegt. Sie können beim Erstellen eines Dokuments den Rang explizit festlegen. Es ist nicht ratsam, vielen Dokumenten denselben Rang zuzuweisen, und Sie sollten niemals mehr als 10.000 Dokumenten den gleichen Rang erteilen. Wenn Sie Sortieroptionen angeben, können Sie den Rang als Sortierschlüssel verwenden. Ein Rang wird als _rank referenziert, wenn er in einem Sortierausdruck oder einem Feldausdruck verwendet wird.

Das Sprachattribut gibt die Sprache an, in der die Felder codiert sind.

Weitere Informationen zu diesen Attributen finden Sie auf der Referenzseite zur Klasse Document.

Dokument mit anderen Ressourcen verknüpfen

Sie können die doc_id und andere Felder eines Dokuments als Links zu anderen Ressourcen in Ihrer Anwendung verwenden. Wenn Sie beispielsweise Blobstore nutzen, können Sie das Dokument mit einem bestimmten Blob verknüpfen. Dazu geben Sie den Blob-Schlüssel der Daten in doc_id oder einem Wert eines Atomfelds an.

Dokument erstellen

Fordern Sie mit der Methode Document.newBuilder() einen neuen Builder an, um ein Dokument zu erstellen. Sobald die Anwendung Zugriff auf einen Builder hat, kann sie eine optionale Dokument-ID angeben und Felder hinzufügen.

Felder wie Dokumente werden mit einem Builder erstellt. Die Methode Field.newBuilder() gibt einen Feld-Builder zurück, mit dem Sie den Namen und Wert eines Felds angeben können. Der Feldtyp wird durch die Auswahl einer bestimmten set-Methode automatisch angegeben. Wenn Sie z. B. angeben möchten, dass ein Feld nur Text enthält, rufen Sie setText() auf. Mit dem folgenden Code wird ein Dokument mit Feldern erstellt, die einen Gruß in einem Gästebuch darstellen.

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();

Damit Sie auf Felder im Dokument zugreifen können, verwenden Sie 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();

Mit einem Index arbeiten

Dokumente in einen Index einfügen

Wenn Sie ein Dokument in einen Index einfügen, wird das Dokument in den nichtflüchtigen Speicher kopiert und alle darin enthaltenen Felder werden nach Name, Typ und doc_id indexiert.

Das folgende Codebeispiel zeigt, wie Sie auf einen Index zugreifen und ein Dokument einfügen können: Folgende Schritte sind auszuführen:

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;
  }
}
Sie können bis zu 200 Dokumente gleichzeitig an die Methode put() übergeben. Es ist effizienter, die Methode "put" für mehrere Dokumente gleichzeitig anstatt für einzelne Dokumente auszuführen.

Wenn Sie ein Dokument in einen Index einfügen und der Index bereits ein Dokument mit derselben doc_id enthält, ersetzt das neue Dokument das alte. Es wird keine Warnung ausgegeben. Sie können Index.get(id) aufrufen, bevor Sie ein Dokument erstellen oder in einen Index aufnehmen. Dadurch können Sie prüfen, ob eine bestimmte doc_id bereits vorhanden ist.

Beachten Sie, dass durch das Erstellen einer Instanz der Klasse Index nicht garantiert wird, dass tatsächlich ein nichtflüchtiger Index vorhanden ist. Ein nichtflüchtiger Index wird erst erstellt, wenn Sie ihm mit der Methode put zum ersten Mal ein Dokument hinzufügen. Mit der Methode SearchService.getIndexes() können Sie prüfen, ob ein Index vorhanden ist, bevor Sie ihn verwenden.

Dokumente aktualisieren

Ein Dokument kann nicht geändert werden, nachdem Sie es einem Index hinzugefügt haben. Sie können weder Felder einfügen oder entfernen noch den Wert eines Feldes ändern. Sie können das Dokument jedoch durch ein neues Dokument mit der gleichen doc_id ersetzen.

Dokumente nach doc_id abrufen

Es gibt zwei Möglichkeiten, Dokumente über die Dokument-ID aus einem Index abzurufen:
  • Verwenden Sie Index.get() zum Abrufen eines einzelnen Dokuments anhand seiner doc_id.
  • Mit Index.getRange() können Sie eine Gruppe von aufeinanderfolgenden Dokumenten abrufen, die nach doc_id sortiert sind.

Beide Aufrufe werden im folgenden Beispiel dargestellt.

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());

Dokumente anhand ihres Inhalts suchen

Sie können Dokumente aus einem Index abrufen, indem Sie einen Abfragestring erstellen und Index.search() aufrufen. Der Abfragestring kann direkt als Argument übergeben oder in ein Abfrageobjekt aufgenommen werden, das als Argument übergeben wird. Standardmäßig werden übereinstimmende Dokumente von search() in absteigender Rangfolge zurückgegeben. Wenn Sie steuern möchten, wie viele Dokumente in welcher Reihenfolge geliefert werden, oder wenn den Ergebnissen berechnete Felder hinzugefügt werden sollen, müssen Sie das Objekt Query verwenden, das einen Abfragestring enthält und mit dem außerdem weitere Such- und Sortieroptionen angegeben werden können.

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;
}

Index löschen

Jeder Index besteht aus indexierten Dokumenten und einem Indexschema. Sie löschen einen Index, indem Sie alle Dokumente in einem Index und anschließend das Indexschema löschen.

Wenn Sie einzelne Dokumente aus einem Index löschen möchten, geben Sie in der Methode delete() die doc_id von mindestens einem zu löschenden Dokument an. Es ist effizienter, mehrere Dokumente gebündelt zu löschen. Sie können bis zu 200 Dokument-IDs gleichzeitig an die Methode delete() übergeben.

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);
}
Sie können bis zu 200 Dokumente gleichzeitig an die Methode delete() übergeben. Es ist effizienter, Löschvorgänge für mehrere Dokumente gleichzeitig statt einzeln auszuführen.

Eventual Consistency

Wenn Sie ein Dokument in einen Index einfügen oder dort aktualisieren oder löschen, wird die Änderung über mehrere Rechenzentren hinweg übernommen. Dies geschieht normalerweise schnell, die Dauer kann jedoch variieren. Die Search API garantiert Eventual Consistency. Das heißt, dass in manchen Fällen eine Suche oder das Abrufen eines oder mehrerer Dokumente Ergebnisse zurückgeben kann, die nicht den neuesten Änderungen entsprechen.

Größe eines Index ermitteln

Ein Index speichert Dokumente zum Abrufen. Sie können ein einzelnes Dokument anhand seiner ID, eine Reihe von Dokumenten mit fortlaufenden IDs oder alle Dokumente in einem Index abrufen. Außerdem können Sie einen Index durchsuchen, um Dokumente abzurufen, die bestimmte Kriterien für Felder und die zugehörigen Werte erfüllen. Diese Kriterien werden als Abfragestring angegeben. Sie können Gruppen von Dokumenten verwalten, indem Sie sie in separate Indexe einfügen. Die Anzahl der Dokumente in einem Index oder die Anzahl der Indexe, die Sie verwenden können, unterliegt keiner Beschränkung. Die Gesamtgröße aller Dokumente in einem einzelnen Index ist standardmäßig auf 10 GB beschränkt. Sie können jedoch über die Seite App Engine Search in der Google Cloud Console eine Anfrage senden, um die Begrenzung auf bis zu 200 GB zu erhöhen. Die Methode Index.getStorageLimit() gibt die maximal zulässige Größe eines Index zurück.

Die Methode Index.getStorageUsage() ist eine Schätzung des Speicherplatzes, der von einem Index belegt wird. Diese Zahl ist eine Schätzung, da das Indexüberwachungssystem nicht ständig ausgeführt wird. Die tatsächliche Nutzung wird regelmäßig berechnet. Die storage_usage wird zwischen den Probenpunkten angepasst. Dabei werden hinzugefügte Dokumente berücksichtigt, Löschungen jedoch nicht.

Indexschemas

Jeder Index verfügt über ein Schema, das alle Feldnamen und Feldtypen anzeigt, die in den enthaltenen Dokumenten vorkommen. Sie können Schemas nicht selbst definieren. Schemas werden dynamisch verwaltet. Sie werden aktualisiert, wenn einem Index Dokumente hinzugefügt werden. Hier ist ein Beispiel für ein einfaches Schema in JSON-ähnlicher Form:

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

Jeder Schlüssel im Wörterbuch ist der Name eines Dokumentfeldes. Der Wert des Schlüssels ist eine Liste der Feldtypen, die mit diesem Feldnamen verwendet werden. Wenn Sie denselben Feldnamen mit unterschiedlichen Feldtypen verwendet haben, listet das Schema mehrere Feldtypen für einen Feldnamen auf:

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

Sobald ein Feld in einem Schema angezeigt wird, kann es nicht mehr entfernt werden. Ein Feld kann nicht mehr gelöscht werden, selbst wenn der Index keine Dokumente mehr mit diesem speziellen Feldnamen enthält.

Sie können die Schemas für Ihre Indexe auf folgende Weise ansehen:

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());
    }
  }
}
Beim Aufrufen von GetIndexes() können nicht mehr als 1.000 Indexe zurückgegeben werden. Wenn Sie weitere Indexe abrufen möchten, rufen Sie die Methode wiederholt auf. Verwenden Sie dabei setStartIndexName() zusammen mit GetIndexesRequest.Builder.

Ein Schema definiert keine "Klasse" im Sinne der Objektprogrammierung. Für die Search API ist jedes Dokument einmalig und Indexe können verschiedene Arten von Dokumenten enthalten. Wenn Sie Sammlungen von Objekten mit derselben Liste von Feldern als Instanzen einer Klasse behandeln möchten, müssen Sie dies in Ihrem Code erzwingen. Sie können beispielsweise festlegen, dass alle Dokumente mit demselben Satz an Feldern in einem eigenen Index geführt werden. Das Indexschema könnte dabei als Klassendefinition angesehen werden und jedes Dokument im Index wäre dann eine Instanz der Klasse.

Indexe in der Google Cloud Console anzeigen

In der Cloud Console können Sie Informationen zu den Indexen Ihrer Anwendung und den darin enthaltenen Dokumenten einsehen. Durch Klicken auf einen Indexnamen werden die Dokumente angezeigt, die der Index enthält. Es werden alle definierten Schemafelder für den Index angezeigt. Für jedes Dokument mit einem Feld dieses Namens sehen Sie den Wert des Feldes. Sie können die Indexdaten auch direkt über die Console abfragen.

Kontingente für die Search API

Die Search API hat mehrere kostenlose Kontingente:

Ressourcen- oder API-Aufruf Kostenloses Kontingent
Gesamtspeicherplatz (Dokumente und Indexe) 0,25 GB
Abfragen 1.000 Abfragen pro Tag
Dokumente zu Indexen hinzufügen 0,01 GB pro Tag

Die folgenden Grenzwerte werden von der Search API festgelegt, um einen zuverlässigen Dienst zu gewährleisten. Sie gelten für kostenlose und kostenpflichtige Anwendungen:

Ressource Sicherheitskontingent
Maximale Abfrageverwendung 100 aggregierte Minuten Abfrageausführungszeit pro Minute
Maximale Anzahl von hinzugefügten oder gelöschten Dokumenten 15.000 pro Minute
Maximale Größe pro Index (eine unbegrenzte Anzahl von Indexen ist erlaubt) 10 GB

Die API-Nutzung wird je nach Art des Aufrufs unterschiedlich gezählt:

  • Index.search(): Jeder API-Aufruf zählt als eine Abfrage. Die Ausführungszeit entspricht der Latenz des Aufrufs.
  • Index.put(): Wenn Sie Dokumente zu Indexen hinzufügen, werden die Größe jedes Dokuments und die Anzahl der Dokumente zum Indexierungskontingent hinzugerechnet.
  • Alle anderen Search API-Aufrufe werden basierend auf der Anzahl der zugehörigen Vorgänge gezählt:
    • SearchService.getIndexes(): Jede tatsächliche Rückgabe eines Index wird als 1 Vorgang gezählt; ebenso wird als 1 Vorgang gezählt, wenn nichts zurückgegeben wird.
    • Index.get() und Index.getRange(): Jede tatsächliche Rückgabe eines Dokuments wird als 1 Vorgang gezählt; ebenso wird als 1 Vorgang gezählt, wenn nichts zurückgegeben wird.
    • Index.delete(): Jedes Dokument in der Anfrage wird als 1 Vorgang gezählt; ebenso wird als 1 Vorgang gezählt, wenn die Anfrage leer ist.

Das Kontingent für den Abfragedurchsatz ist so festgelegt, dass ein einzelner Nutzer den Suchdienst nicht monopolisieren kann. Da Abfragen gleichzeitig ausgeführt werden können, darf jede Anwendung Abfragen ausführen, die pro Minute Taktzeit bis zu 100 Minuten Ausführungszeit beanspruchen. Wenn Sie viele kurze Abfragen ausführen, werden Sie diesen Grenzwert wahrscheinlich nicht erreichen. Sobald Sie das Kontingent überschritten haben, schlagen nachfolgende Abfragen fehl, bis der nächste Zeitabschnitt beginnt und Ihr Kontingent erneuert wird. Das Kontingent wird nicht streng in einminütigen Abständen durchgesetzt. Es wird eine Variation des Leaky-Bucket-Algorithmus verwendet, um die Suchbandbreite in Fünf-Sekunden-Schritten zu steuern.

Weitere Informationen zu Kontingenten finden Sie auf der Seite "Kontingente". Wenn eine Anwendung versucht, diese Grenzwerte zu überschreiten, wird durch eine Fehlermeldung auf das unzureichende Kontingent hingewiesen.

Diese Grenzwerte werden zwar pro Minute durchgesetzt, die Konsole zeigt jedoch die Gesamtwerte pro Tag an. Kunden mit der Supportstufe Silber, Gold oder Platin können bei dem für sie zuständigen Supportmitarbeiter höhere Durchsatzlimits anfragen.

Preise für Search API

Werden bei der Nutzung die kostenlosen Kontingente überschritten, fallen die folgenden Gebühren an:

Ressource Kosten
Gesamtspeicherplatz (Dokumente und Indexe) 0,18 $ pro GB pro Monat
Abfragen 0,50 $ pro 10.000 Abfragen
Indexierung durchsuchbarer Dokumente 2,00 $ pro GB

Weitere Informationen zur Preisgestaltung finden Sie auf der Seite Preise.