Grundlagen zur Search API

Amy Unruh, Okt. 2012
Google Developer Relations

Einführung

In dieser Lektion werden die Grundlagen der Verwendung der Search API besprochen: Inhalte indexieren und Abfragen an einem Index ausführen. Folgende Themen werden behandelt:

  • Suchindex erstellen
  • Dem Suchindex Inhalte über ein Indexdokument hinzufügen
  • Einfache Volltextsuchanfragen an diesen indexierten Daten ausführen

Ziele

Grundlagen zur Verwendung der App Engine Search API vermitteln.

Voraussetzungen

Indexe

Die Search API von App Engine wird über das Objekt Index ausgeführt. Mit diesem Objekt können Sie Daten über ein Indexdokument speichern, Dokumente mithilfe von Suchanfragen abrufen, Dokumente bearbeiten und Dokumente löschen.

Jeder Index hat einen Indexnamen und optional einen Namespace. Der Name identifiziert eindeutig den Index innerhalb eines bestimmten Namespace. Er muss ein sichtbarer, druckbarer ASCII-String sein, der nicht mit einem "!" beginnen darf. Leerzeichen sind ausgeschlossen. Sie können mehrere Objekte vom Typ Index erstellen, aber zwei Objekte dieses Typs mit demselben Indexnamen im selben Namespace verweisen auf denselben Index.

Sie können mit Namespaces und Indexen Ihre Dokumente organisieren. In der Beispielanwendung für die Produktsuche befinden sich alle Produktdokumente in einem Index und ein weiterer Index enthält Informationen zu den Filialstandorten. Wir können eine Suchanfrage nach der Produktkategorie filtern, wenn wir beispielsweise nur nach Büchern suchen möchten.

Erstellen Sie in Ihrem Code das Objekt Index. Geben Sie dazu den Indexnamen an:

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

oder

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

Der zugrunde liegende Dokumentindex wird beim ersten Zugriff erstellt, wenn er nicht bereits vorhanden ist. Sie müssen ihn nicht explizit erstellen.

Sie können Dokumente aus einem Index oder den gesamten Index löschen. Darauf wird im nächsten Kurs Ausführliche Informationen zur Python Search API näher eingegangen.

Dokumente

Dokumente enthalten den durchsuchbaren Inhalt eines Index. Ein Dokument ist ein Container zum Strukturieren von indexierbaren Daten. Aus technischer Sicht stellt das Objekt Document eine eindeutig identifizierte Sammlung von Feldern dar, die durch eine Dokument-ID gekennzeichnet ist. Felder sind benannte, typisierte Werte. Dokumente haben keine kinds im selben Sinne wie Datastore-Entitäten.

In unserer Beispielanwendung haben wir die Produktkategorien "books" und "HD televisions". Der Händler hat eine eher begrenzte Auswahl an Produkten. Jedes Produktdokument in der Beispielanwendung enthält immer die folgenden Kernfelder, die durch die Klassenvariablen docs.Product definiert sind:

  • CATEGORY (entweder books oder hd_televisions)
  • PID (Produkt-ID)
  • PRODUCT_NAME
  • DESCRIPTION
  • PRICE
  • AVG_RATING
  • UPDATED (Datum der letzten Aktualisierung)
Produktdokumentfelder
Abbildung 1: Produktdokumentfelder

Die Kategorien "books" und "HD televisions" haben jeweils eigene zusätzliche Felder. Für die Kategorie "books" sind dies:

  • title
  • author
  • publisher
  • pages
  • isbn

Für die Kategorie "HD televisions" sind dies:

  • brand
  • tv_type
  • size

Die Anwendung selbst erzwingt eine semantische Konsistenz auf Anwendungsebene für Dokumente jedes Produkttyps. Das heißt, alle Produktdokumente enthalten immer die gleichen Kernfelder, alle Bücher haben die gleichen zusätzlichen Felder und so weiter. Ein Suchindex erzwingt keine dokumentübergreifende schematische Konsistenz für die verwendeten Felder. Es gibt daher kein explizites Konzept für die spezifische Abfrage von "Produkt"-Dokumenten.

Feldtypen

Jedes Dokumentfeld hat einen eindeutigen Feldtyp. Es gibt folgende Typen, die im Python-Modul search definiert sind:

  • TextField: Ein einfacher Textstring.
  • HtmlField: HTML-formatierter Text. Verwenden Sie diesen Feldtyp, wenn Ihr String HTML ist. Die Search API kann dadurch das Markup beim Erstellen von Ergebnisausschnitten und beim Bewerten von Dokumenten berücksichtigen.
  • AtomField: Ein String, der als einzelnes Token behandelt wird. Eine Suchanfrage ergibt keine Ergebnisse, wenn sie nur einen Teilstring und nicht den vollständigen Feldwert enthält.
  • NumberField: Ein numerischer Wert (Ganzzahl oder Gleitkomma).
  • DateField: Ein Datum ohne Uhrzeitkomponente.
  • GeoField: Ein Standort, der durch das Objekt GeoPoint mit Koordinaten für Längen- und Breitengrad bezeichnet wird.

Die Werte in den Textfeldern (TextField, HtmlField und AtomField) müssen Unicode-Strings sein.

Beispiel: Produktdokumentfelder erzeugen und Dokument erstellen

Wenn Sie das Objekt Document erzeugen möchten, erstellen Sie eine Liste der Felder, definieren Sie bei Bedarf die Dokument-ID und übergeben Sie diese Informationen an den Document constructor.

In der Beispielanwendung werden die Feldtypen TextField, AtomField, NumberField und DateField für Produktdokumente verwendet.

Produktdokumentfelder definieren

Die Kernproduktfelder (die in allen Produktdokumenten enthalten sind) sehen so aus, wobei angenommen wird, dass die Wertargumente der nachfolgenden Konstruktoren auf geeignete Werte festgelegt sind:

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) ]

Beachten Sie, dass das Kategoriefeld als AtomField eingegeben wurde. Atomfelder sind nützlich für Dinge wie Kategorien, bei denen genaue Übereinstimmungen erwünscht sind. Textfelder eignen sich besser für Strings wie Titel oder Beschreibungen. Eine Kategorie aus unserem Beispiel ist hd televisions. Wenn wir nur nach televisions suchen, erhalten wir keine Übereinstimmung (vorausgesetzt, dass dieser String nicht in einem anderen Produktfeld enthalten ist). Wenn wir aber nach dem vollständigen Feldstring hd televisions suchen, treffen wir auf eine Übereinstimmung im Kategoriefeld.

Die Beispielanwendung enthält auch spezifische Felder für einzelne Produktkategorien. Diese werden je nach Kategorie ebenfalls der Feldliste hinzugefügt. Für die Kategorie "HD televisions" gibt es beispielsweise zusätzliche Felder für size (Zahlenfeld), brand und tv_type (Textfelder). Die Kategorie "books" hat andere Felder.

Dokumente erstellen

Anhand der Feldliste können wir das Objekt "Document" erstellen. Für jedes Produktdokument legen wir die Dokument-ID auf die vordefinierte eindeutige ID dieses Produkts fest:

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

Dieser Entwurf hat einige Vorteile für uns (die wir im Folgekurs zu diesem Thema besprechen werden). Wenn wir die Dokument-ID allerdings nicht angeben, wird eine ID automatisch für uns erzeugt, sobald das Dokument einem Index hinzugefügt wird.

Beispiel: Geopunkte in Filialstandortdokumenten verwenden

Die Search API unterstützt Geosearch bei Dokumenten, die Felder vom Typ GeoField haben. Wenn Ihre Dokumente solche Felder enthalten, können Sie anhand von Vergleichen der Entfernung einen Index nach Übereinstimmungen abfragen.

Ein Standort wird durch die Klasse GeoPoint definiert, die Koordinaten für Längen- und Breitengrad speichert. Der Breitengrad gibt den Winkelabstand in Grad nördlich oder südlich des Äquators an. Der Längengrad gibt den Winkelabstand in Grad östlich oder westlich des Nullmeridians an. Beispiel: Der Standort des Opernhauses in Sydney wird durch GeoPoint(-33.857, 151.215) definiert. Wenn ein Geopunkt in einem Dokument gespeichert werden soll, müssen Sie das Feld GeoField mit dem Objekt GeoPoint als Wert hinzufügen.

So werden die Felder für die Filialstandortdokumente in der Anwendung für die Produktsuche erstellt:

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)  ]

Dokumente indexieren

Bevor Sie den Inhalt eines Dokuments abfragen können, müssen Sie das Dokument einem Index hinzufügen. Verwenden Sie dazu die Methode put() des Objekts Index. Anhand der Indexierung kann das Dokument mit der Abfragesprache und den Abfrageoptionen der Search API durchsucht werden.

Sie können beim Erstellen eines Dokuments Ihre eigene Dokument-ID angeben. Die Dokument-ID muss ein sichtbarer, druckbarer ASCII-String sein, der nicht mit einem "!" beginnen darf. Leerzeichen sind ausgeschlossen. (Wie Sie später sehen werden, wird ein vorhandenes Dokument neu indexiert, wenn Sie ein Dokument mit der ID des vorhandenen Dokuments indexieren.) Wenn Sie keine Dokument-ID angeben, wird automatisch eine eindeutige numerische ID generiert, sobald das Dokument dem Index hinzugefügt wird.

Sie können Dokumente entweder einzeln hinzufügen oder alternativ eine Liste mit Dokumenten hinzufügen, was weitaus effizienter ist. So erstellen Sie ein Dokument anhand einer Feldliste und fügen es einem Index hinzu:

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:
  # ...

Sie sollten alle Ausnahmen erfassen und verarbeiten, die sich aus der Methode put() ergeben und den Typ search.Error haben.

Wenn Sie die Dokument-ID angeben möchten, übergeben Sie sie so an den Konstruktor Document:

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

Sie können die IDs der Dokumente, die hinzugefügt wurden, über das Attribut id der Liste mit den Objekten search.AddResult, die von der Operation put() zurückgegeben wurden, abrufen:

doc_id = add_result[0].id

Einfache Suchanfragen

Durch das Hinzufügen von Dokumenten zu einem Index kann der Inhalt des Dokuments durchsucht werden. Sie können dann Volltextsuchanfragen an den Dokumenten im Index durchführen.

Es gibt zwei Möglichkeiten, eine Suchanfrage einzugeben. Die einfachste Möglichkeit ist, einen Abfragestring an die Methode search() des Objekts Index zu übergeben. Alternativ können Sie das Objekt Query erstellen und dieses an die Methode search() übergeben. Durch das Erstellen des Objekts "Query" können Sie Abfrage-, Sortier- und Ergebnisdarstellungsoptionen für Ihre Suche angeben.

In dieser Lektion erfahren Sie, wie einfache Suchanfragen mit beiden Ansätzen erstellt werden. Wie bereits erwähnt, werden einige Suchanfragen auf dem Entwicklungs-Webserver (der lokal ausgeführt wird) nicht vollständig unterstützt. Sie müssen diese daher mithilfe einer bereitgestellten Anwendung ausführen.

Suche mit Abfragestring

Ein Abfragestring ist ein beliebiger Unicode-String, der von der Abfragesprache der Search API geparst werden kann. Nachdem Sie einen Abfragestring erzeugt haben, übergeben Sie ihn an die Methode Index.search(). Beispiel:

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:
  # ...

Suche mit dem Objekt "Query"

Das Objekt Query gibt Ihnen im Gegensatz zum Abfragestring mehr Kontrolle über Ihre Abfrageoptionen. In diesem Beispiel erzeugen wir zuerst das Objekt QueryOptions. Dessen Argumente geben an, dass die Abfrage die Anzahl doc_limit der Ergebnisse zurückgeben soll. (Wenn Sie sich den Anwendungscode für die Produktsuche angesehen haben, sehen Sie komplexere Objekte vom Typ QueryOption. Diese betrachten wir uns im Folgekurs Ausführliche Informationen zur Python Search API etwas genauer.) Als Nächstes erstellen wir das Objekt Query. Dazu verwenden wir den Abfragestring und das Objekt QueryOptions. Wir übergeben das Objekt Query dann an die Methode Index.search(), genau wie oben beim Abfragestring.

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:
  # ...

Abfrageergebnisse verarbeiten

Nachdem Sie eine Suchanfrage eingegeben haben, werden übereinstimmende Suchergebnisse in dem iterierbaren Objekt SearchResults an die Anwendung zurückgegeben. Dieses Objekt enthält die Anzahl der gefundenen Ergebnisse, die tatsächlich zurückgegebenen Ergebnisse und das optionale Objekt Abfrage-Cursor.

Auf die zurückgegebenen Dokumente kann durch Iteration des Objekts SearchResults zugegriffen werden. Die Anzahl der zurückgegebenen Ergebnisse entspricht der Länge des Attributs results des Objekts. Das Attribut number_found ist auf die Anzahl der gefundenen Treffer festgelegt. Durch die Iteration des zurückgegebenen Objekts erhalten Sie die zurückgegebenen Dokumente, die Sie beliebig verarbeiten können:

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:
  # ...

Zusammenfassung

In dieser Lektion haben wir die Grundlagen zum Erstellen von indexierten Dokumenten und zum Abfragen ihrer Inhalte kennengelernt. Testen Sie Ihr Wissen und versuchen Sie, diese Schritte in Ihrer eigenen einfachen Anwendung zu rekonstruieren:

  • Erstellen Sie das Objekt Index.
  • Erzeugen Sie eine Liste mit Dokumentfeldern (z. B. vom Typ TextField) und erstellen Sie das Objekt Document anhand dieser Feldliste. Fügen Sie das Dokument dem Index hinzu.
  • Durchsuchen Sie den Index mithilfe eines Suchstrings, der aus einem Begriff in einem Ihrer Feldwerte besteht. Wird das von Ihnen erstellte Dokument als Treffer zurückgegeben?

In der nächsten Lektion sehen wir uns die Search API-Indexe etwas genauer an.

Hat Ihnen diese Seite weitergeholfen? Teilen Sie uns Ihr Feedback mit:

Feedback geben zu...

App Engine-Standardumgebung für Python 2