Elasticsearch – Ein praktischer Einstieg

Pipeline Aggregationen

Elasticsearch bietet mit den Aggregationen einen mächtigen Mechanismus, um Inhalte anhand unterschiedlicher Kriterien zu gruppieren und zu kategorisieren. Beispielsweise werden Aggregationen genutzt, um klassische Facettierungen zu realisieren, wie sie beispielsweise in Online-Shops üblich sind. Einzelne Aggregationen entsprechen dabei einer Facette, beispielsweise um alle Terme zu einem Kategorie-Feld anzuzeigen oder um Produkte in einen Preisbereich einzuordnen. Durch die Möglichkeit, einzelne Aggregationen zu verschachteln, sind jedoch auch noch weitere nützliche Abfragen möglich. Beispielsweise kann so für eine Reporting-Lösung der Durchschnittspreis eines Einkaufskorbs pro Stunde berechnet werden.

Seit Elasticsearch 2.0 gibt es mit den Pipeline-Aggregationen eine Erweiterung der Aggregationen, mittels der noch weitere Anwendungsfälle abgedeckt werden können. Sie können genutzt werden, um auf den Ergebnissen von Aggregationen weitere Berechnungen durchzuführen. Schauen wir uns ein Beispiel dazu an, das ein bisschen von den Inhalten im Buch abweicht.

Reporting

Elasticsearch wird häufig genutzt, um Reporting-Daten zu speichern. Dabei werden beispielsweise durch Nutzer-Interaktionen Events generiert, die dann in Elasticsearch gespeichert werden. Ein sehr einfaches Beispiel zeigt das folgende Mapping.

{
  "_timestamp": {
    "enabled": true
  },
  "properties": {
    "eventType": {
      "type": "string",
      "index": "not_analyzed"
    },
    "userId": {
      "type": "long"
    }
  }
}

Wir speichern einen Timestamp mit jedem Dokument, der automatisch angelegt wird. Zusätzlich besteht ein Event in diesem einfachen Beispiel noch aus einem Typ, der als String abgelegt ist, und der Id des Benutzers als long. Wir können nun mehrere Events der folgenden Art indizieren.

POST event-test/event/
{
  "eventType": "login",
  "userId": 1
}

POST event-test/event/
{
  "eventType": "login",
  "userId": 2
}

POST event-test/event
{
  "eventType": "search",
  "userId": 1
}

POST event-test/event
{
  "eventType": "logout",
  "userId": 1
}

Aggregationen

Schon mit den normlen Aggregationen hat man nun unterschiedliche Möglichkeiten, um Informationen aus diesen Daten zu ziehen. Beispielsweise können wir ein Histogramm der häufigsten Aktionen pro Minute oder die Aktionen eines bestimmten Benutzers aggregieren. Mit der folgenden Abfrage werden die Aktionen eines Benutzers pro Minute gezählt.

POST event-test/event/_search
{
  "aggs": {
    "minutes": {
      "date_histogram": {
	"field": "_timestamp",
	"interval": "minute",
	"format": "hh:mm",
	"time_zone": "+02:00"
      },
      "aggs": {
	"user": {
	  "terms": {
	    "field": "userId",
	    "size": 10
	  }
	}
      }
    }
  },
  "size": 0
}

Die Anfrage besteht aus einer Date-Histogram-Aggregation, die für das _timestamp-Feld und die aktuelle deutsche Zeitzone konfiguriert ist. Darin verschachtelt ist eine Terms-Aggregation auf dem Feld userId. Das Ergebnis enthält dann für jede Minute die Anzahl der von einzelnen Nutzern durchgeführten Aktionen.

    {
      "key_as_string": "05:51",
      "key": 1473263460000,
      "doc_count": 3,
      "user": {
        "doc_count_error_upper_bound": 0,
        "sum_other_doc_count": 0,
        "buckets": [
          {
            "key": 1,
            "doc_count": 2
          },
          {
            "key": 2,
            "doc_count": 1
          }
        ]
      }
    }

Pipeline-Aggregationen

Pipeline-Aggregationen können nun genutzt werden, um auf den Ergebnissen vorheriger Aggregationen weitere Berechnungen durchzuführen. Beispielsweise könnten wir daran interessiert sein, wie viel Zeit für einen Nutzer zwischen erster und letzter Aktion liegt. Dazu können wir über eine bestimmte Zeitspanne (z.B. für einen Tag) die erste und letzte Aktion über die min- und max-Aggregation ermitteln. Eine Bucket-Script-Aggregation kann sich dann auf diese Werte beziehen und die Differenz berechnen.

{
  "aggs": {
    "minutes": {
      "date_histogram": {
	"field": "_timestamp",
	"interval": "minute",
	"format": "hh:mm",
	"time_zone": "+02:00"
      },
      "aggs": {
	"user": {
	  "terms": {
	    "field": "userId",
	    "size": 10
	  },
	  "aggs": {
	    "first": {
	      "min": {
		"field": "_timestamp"
	      }
	    },
	    "last": {
	      "max": {
		"field": "_timestamp"
	      }
	    },
	    "duration": {
	      "bucket_script": {
		"buckets_path": {
		  "first": "first",
		  "last": "last"
		},
		"script": "last - first"
	      }
	    }
	  }
	}
      }
    }
  },
  "size": 0
}

Als Ergebnis erhalten wir nun für jeden Nutzer noch ein duration-Feld, das die Dauer zwischen erster und letzter Interaktion für die Zeitspanne angibt.

Neben der sehr flexiblen Bucket-Script-Aggregation stehen noch viele weitere Pipeline-Aggregationen zur Verfügung. Neben einfacheren Berechnungen wie min, max und avg stehen auch komplexere Funktionen wie die Derivate Aggregation, die Moving Average Aggregation oder der Bucket Selector zur Auswahl.

Weitere Informationen finden sich wie immer in der zugehörigen Dokumentation.