Auf einen Blick: ElasticSearch nutzt Eltern-Kind-Beziehungen zur Verwaltung von Dokumentassoziationen, ähnlich wie relationale Datenbank-Joins. Der Leitfaden erklärt, wie man Zuordnungen und Abfragen für diese Beziehungen erstellt, wobei die geschwindigkeitsorientierte Architektur von ElasticSearch betont wird, die traditionelle Joins einschränkt. Wichtige Beispiele sind die Erstellung von Daten für Universitäten und Studierende, die Demonstration der Einrichtung von Eltern- und Kinddokumenten sowie das Abfragen von Kinderdatensätze nach Eltern-ID.
- ElasticSearch ist keine relationale Datenbank und vermeidet traditionelle Joins.
- Eltern-Kind-Beziehungen werden durch spezifische Zuordnungen und Abfragen hergestellt.
Erneut gehen wir mit der Komplexität und manchmal widersprüchlichen Dokumentation von ElasticSearch zu und versuchen, sie verständlicher zu machen. Hier betrachten wir, wie Eltern-Kind-Beziehungen zwischen Dokumenten entstehen.
Die Konzepte Eltern, Kind und Beitritt
In einer relationalen Datenbank wird eine Eltern-Kind-Beziehung als Join bezeichnet. Ein Mathematiker würde dies als den Schnitt zweier Mengen bezeichnen. Zum Beispiel können wir den Schnitt einer Menge ungerade und primzahlen finden und so eine Menge ungerade Zahlen erzeugen, die keine Primzahlen sind.
Ein Datenbankmitarbeiter würde das in einer Eltern-Kind-Beziehung ausdrücken. Dafür sind einige gemeinsame Elemente erforderlich.
Zur Veranschaulichung: Angenommen, wir haben diese beiden Mengen einige ungerade Zahlen und einige Primzahlen. Dann machen wir den Durchschnitt der beiden Mengen, also die Menge der Elemente, die in beiden liegen. Ein Datenbank-Join ist dasselbe.
Parent (einige ungerade Zahlen) |
{9,21}
|
Kinder (eine Primzahl) |
{7,11, 21}
|
Schnitt (ungerade Zahlen ∩ Primzahlen) |
{21}
|
In diesem Fall spielt es keine Rolle, welche Menge wir als Elternteil oder Kind wählen, da es sich nur um Zahlen handelt.
Wir werden dies in ElasticSearch anhand von Universitäten und Studierenden veranschaulichen. Wir werden eine Gruppe von Universitäten und Studierenden erstellen und dann jeweils Studierenden zuweisen.
Wir haben also etwas wie:
Elternteil (Universitäten) |
{Harvard, Clemson}
|
Kinder (Schüler) |
{{"student:" Walker",
"university": "Clemson},
{student: Stephen",
"university": "Harvard"}}
|
Intersection (Schüler, die nach Clemson gehen) |
{"student:" Walker",
"university": "Clemson}
|
Das Kind zeigt auf das Elternelement, weil der Universitätsname im chilenen Datensatz steht. Das ist das gemeinsame Element. Im Hinblick auf ElasticSearch geben Sie dies in der Indexabbildung so an:
"universities_students" : {
"type" : "join",
"eager_global_ordinals" : true,
"relations" : {
"universities" : "students"
}
}
Dadurch entsteht ein neues Feld mit dem Namen universities_students des Typs join. Der Elternteil ist Universitäten und das Kind ist Studierende.
Wenn Sie einen Schüler- (Kind-)Datensatz schreiben, weisen Sie ihn mit dem _id, also 'Elternteil': 'e2cfb3b015d4be50a466aa593ab4d9f490982ede' des Elterndokuments, auf den Elternteil und geben an, dass es sich um ein Dokument von Typschülern handelt:
'universities_students': {
'name': 'students',
'parent': 'e2cfb3b015d4be50a466aa593ab4d9f490982ede'
}
Und wenn du einen Universitäts-(Eltern-)Datensatz schreibst, gibst du an, dass es sich um ein Dokument vom Typ Universitäten handelt. Du gibst keine _id an, das wäre nicht logisch. Die Abfrage findet diese Beziehung heraus.
'universities_students': {
'name': 'universities'
}
ElasticSearch ist keine Datenbank
ElasticSearch weist darauf hin, dass es sich nicht um eine relationale Datenbank handelt. Es erlaubt also keine traditionellen Joins, weil diese zu langsam laufen würden, und ES dreht sich ganz um Geschwindigkeit. Sie verlangen also, dass beide Dokumenttypen im selben Shard im selben Index liegen müssen. Das bringt sie nahe beieinander auf derselben Festplatte und verkürzt so die Suchzeit. Sie weisen auch darauf hin, dass diese Abfrage mit dieser Regel langsamer läuft, je mehr Dokumente man hinzufügt.
Erstellen Sie zuerst einen Mapping-Typ
Nun veranschaulichen wir das mit einem Beispiel. Erstellen Sie zunächst einen Mapping-Typ. Beachten Sie, dass dieser Mapping-Typ sowohl Universitäten als auch Studierende enthalten kann. Das liegt daran, dass diese Liste der Felder die Obermenge aller gemeinsamen Felder ist. Das heißt, Studierende haben Kurse und Noten, Universitäten jedoch nicht. Ebenso haben Schulen eine Admin-Email, einen Schulnamen usw.
Beachten Sie, dass wir am Ende das Feld universities_students auflisten, das vom Typ join ist. Dies drückt die Beziehung zwischen Dokumenten aus.
(Wir werden nur den Schulnamen in den Universitätsdatensatz eintragen. Sie können all diese anderen Felder ignorieren, wie zum Beispiel die Telefonnummer. Wir haben sie in diesem vorherigen Beispiel verwendet, um verschachtelte Abfragen durchzuführen, was ein verwandtes, wenn auch anderes Thema ist.)
curl -XPUT --header 'Content-Type: application/json' http://parisx:9200/universities -d '{
"settings" : {
"index" : {
"number_of_shards" : 1,
"number_of_replicas" : 1
}
}, "mappings" : {
"doc" : {
"properties" : {
"Address" : { "type" : "text"},
"AdminEmail" : { "type" : "text"},
"AdminName" : { "type" : "text" },
"AdminPhone" : { "type" : "text" },
"DapipId" : { "type" : "text" },
"Fax" : { "type" : "text" },
"GeneralPhone" : { "type" : "text" },
"LocationName" : { "type" : "text" },
"LocationType" : { "type" : "text" },
"OpeId" : { "type" : "text" },
"ParentDapipId" : { "type" : "text" },
"ParentName" : { "type" : "text" },
"UpdateDate" : { "type" : "text" },
"classes" : {
"type" : "nested",
"properties" : {
"name" : { "type" : "text"},
"grades" : { "type" : "integer" }
}
},
"firstName" : { "type" : "text" },
"lastName" : { "type" : "text" },
"school" : { "type" : "text" },
"universities_students" : {
"type" : "join",
"eager_global_ordinals" : true,
"relations" : {
"universities" : "students"
}
}
}
}
}
}'
Daten erstellen
Laden Sie diesen Code herunter und führen Sie ihn aus. Um es einfach zu machen, erstellen wir nur eine Schule und einen Schüler. Wenn Sie damit experimentieren und viel mehr Daten erstellen möchten, ändern Sie einfach den Bereich (1,2) in etwas wie Range (1,400).
Man könnte Daten mit Curl erstellen, wie in den ES-Beispielen in deren Dokumentation. Aber es ist viel einfacher, Python zu verwenden, da es einfacher ist, mit Python-Wörterbüchern als mit JSON-Objekten zu arbeiten, besonders wenn die ES-Dokumente lang sind und viele Objekte innerhalb der Objekte enthalten.
Schau dir unten die Universitäts- und Studentenakten an. Beachte, dass beim Ausführen verschiedene Daten generiert werden, da die Schulen zufällig ausgewählt werden. Du musst die _id des Universitätsdatensatzes notieren, damit du ihn in die untenstehende Abfrage eingeben kannst, die Kinderunterlagen findet.
Elternteil (Universität)
{'school': 'University of South Carolina - Columbia', 'universities_students': {'name': 'universities'}}
{'_version': 1, '_type': 'doc', '_id': 'e2cfb3b015d4be50a466aa593ab4d9f490982ede', '_primary_term': 1, 'result': 'created', '_index': 'universities', '_seq_no': 0, '_shards': {'failed': 0, 'total': 2, 'successful': 2}}
Kind (Schüler)
{'school': 'University of North Carolina at Chapel Hill', 'universities_students': {'name': 'students', 'parent': 'e2cfb3b015d4be50a466aa593ab4d9f490982ede'}, 'lastName': 'Mann', 'classes': [{'name': 'French', 'grades': 6}, {'name': 'French', 'grades': 3}, {'name': 'French', 'grades': 2}, {'name': 'math', 'grades': 4}], 'firstName': 'Walker'}
Hier ist der vollständige Code. Darunter erklären wir jene Teile, die nicht selbstverständlich sind. Es wählt einfach eine von drei Schulen basierend auf einer Zufallszahl. Es wählt Namen und Kurse mit demselben Ansatz. Dann fügt es die Universitäts- und Studierendendaten hinzu, wie wir oben erklärt haben.
Der grundlegende Ansatz besteht darin, ein Wörterbuch {} zu erstellen und dann Felder hinzuzufügen. Wir können Wörterbücher zu Wörterbüchern hinzufügen, um verschachtelte JSON-Objekte zu erstellen. Das ist also einfacher als die Arbeit mit langen JSON-String-Feldern.
import requests
import random
import hashlib
import json
url="http://parisx:9200/universities/doc/"
def assignClasses():
classes=[]
for x in range(random.randrange(1,6)):
classes.append(genclass())
return classes
schools = ("Arizona State University", "University of South Carolina - Columbia", "University of North Carolina at Chapel Hill")
firstNames = ("Walker", "Stephen", "Julie", "George")
lastNames = ("Rowe", "Shakespeare", "Mann", "Sarte")
courses= ("math", "physics", "French", "logic")
def genclass():
classes={}
classes["name"]=courses[random.randrange(0,3)]
classes["grades"] = random.randrange(1,7)
return classes
def genParent(id):
child={}
child['name'] = 'students'
child['parent'] = id
return child
def genSchool():
school={}
school["universities_students"] = {'name': 'universities'}
school["school"]=schools[random.randrange(0,3)]
m = hashlib.sha1()
m.update(bytes(json.dumps(school), 'utf-8'))
id = m.hexdigest()
print(school)
response = requests.post(url + id, json=school)
print (response.json())
return id
def genStudent():
id = genSchool()
students={}
students["universities_students"] = genParent(id)
students["school"]=schools[random.randrange(0,3)]
students["firstName"] = firstNames[random.randrange(0,3)]
students["lastName"] = lastNames[random.randrange(0,3)]
students["classes"] = assignClasses()
m = hashlib.sha1()
m.update(bytes(json.dumps(students), 'utf-8'))
sid = m.hexdigest()
print(students)
response = requests.post(url + sid + "?routing=1&refresh", json=students)
print (response.json())
for r in range(1,2):
genStudent()
URL="http://parisx:9200 /Universitäten/doc/" |
Der Index/Typ bleibt einer der verwirrendsten Teile von ElasticSearch. Sie sagen, sie hätten das Dokumenttypfeld weggelassen, aber wenn man Daten schreibt, wird man gebeten, sie im Index/Typ zu verwenden. Das ist ein Widerspruch. In diesem Fall ist das Doc an Universitäten/Doc. |
m = hashlib.sha1() m.update(bytes(json. dumps (school),'utf-8')) id = m.hexdigest() |
Jedes ElasticSearch-Dokument muss eine eindeutige ID haben. Wenn man dieselbe Dokument-ID zweimal schreibt, wird das eine das andere gelöscht. Der gängige Ansatz ist, einen SHA1-Digest über das gesamte JSON-Dokument zu berechnen. Natürlich könnten zwei Dokumente dieselbe ID haben. Man könnte auch etwas wie UUID verwenden. UUID. |
response = requests.post (URL + sid + "?routing=1&refresh", json=students) |
Wenn wir das Kinddokument hinzufügen, müssen wir ihm sagen, welchen Shard er verwenden soll, da er auf demselben Shard und dem Elterndokument liegen muss. (Ein Shard ist eine Division eines Index. Er ist darauf ausgelegt, Daten über Knoten in einem Cluster zu verteilen. Das ist keine Begrenzung. Wir können immer noch mehr als einen Shard haben. Man müsste nur das Routing platzieren
Befehl auf dem Parent und verwenden Sie dieselbe Shard-Nummer, während wir eine Liste davon rotieren.) Hat ES gesagt, wie viele Shards er erstellen soll, als wir die Indexkarte erstellen:"index" : { "number_of_shards" : 1, "number_of_replicas" : 1 }
|
Kinder finden
Jetzt müssen wir einige Daten erstellen. Damit wir einige Abfragen ausführen können.
Der erste findet Kinderunterlagen, die einem Elternteil gegeben werden. Das Schlüsselwort hier ist parent_id. Die Art des Objekts, den wir suchen, ist "Typ": "Schüler". Und wir sagen ihr, nach welcher Schule sie suchen soll, indem wir die _id der Schule in "id" eintragen: "e2cfb3b015d4be50a466aa593ab4d9f490982ede".
curl -XGET --header 'Content-Type: application/json' http://parisx:9200/universities/_search?pretty -d '{
"query": {
"parent_id": {
"type": "students",
"id": "e2cfb3b015d4be50a466aa593ab4d9f490982ede"
}
}
}'
Diese Abfrage listet diesen Schüler auf (denken Sie daran, wir haben nur einen Schüler hinzugefügt):
{
"_index" : "universities",
"_type" : "doc",
"_id" : "185fa8736085fd07ce6f5fb8f1c66fce64fa42e3",
"_score" : 0.18232156,
"_routing" : "1",
"_source" : {
"classes" : [
{
"name" : "French",
"grades" : 1
}
],
"school" : "University of South Carolina - Columbia",
"lastName" : "Mann",
"universities_students" : {
"name" : "students",
"parent" : "e2cfb3b015d4be50a466aa593ab4d9f490982ede"
},
"firstName" : "Walker"
}
}
Eltern finden
Im Elternteil gibt es kein Feld, das auf die Kinder verweist, daher können wir nicht einfach die Kinder-ID in die Abfrage eingeben. Das wäre nicht logisch, denn wenn wir die Kinder-IDs kennen, warum sollte man sich dann überhaupt auf den Elternteil beziehen. Stattdessen können wir alle Elterndokumente auflisten, die ein Kind haben.
Wir verwenden den existierenden Abfrageparameter, da wir etwas in den Abfrageparameter eingeben müssen.
curl -XGET --header 'Content-Type: application/json' http://parisx:9200/universities/_search?pretty -d '{
"query": {
"has_child" : {
"type" : "students",
"query" : {
"exists" : {"field": "firstName"}
}
}
}
}'
Dies führt dazu, dass der Schul- (Eltern-)Datensatz wie folgt aufgeführt wird:
{
"_index" : "universities",
"_type" : "doc",
"_id" : "e2cfb3b015d4be50a466aa593ab4d9f490982ede",
"_score" : 1.0,
"_source" : {
"school" : "University of South Carolina - Columbia",
"universities_students" : {
"name" : "universities"
}
}
ElasticSearch-Leitfaden
- ElasticSearch Tutorial für Anfänger: ElasticSearch Grundlagen
- Verwendung von Apache Hive mit ElasticSearch
- Verwendung von ElasticSearch mit Apache Spark
- Curl ElasticSearch Befehle Spickzettel
- Spark ElasticSearch Hadoop-Update und Upsert Beispiel und Erklärung
- Wie man Apache Spark-Daten mit Python in ElasticSearch schreibt
- ElasticSearch-Suchsyntax sowie boolesche und Aggregationssuchen
- ElasticSearch verschachtelte Abfragen: Wie man nach eingebetteten Dokumenten sucht
- ElasticSearch Joins: Has_Child, Has_parent Abfrage
- Wie man einen elastischen Cluster der Version 7 einrichtet
- Wie man Filebeat für nginx und ElasticSearch konfiguriert
- Beats und Logstash verwenden, um Logs an ElasticSearch zu senden
- Wie man eine CSV-Datei mit Logstash in ElasticSearch lädt
- ElasticSearch-Aggregationen erklärt
- Was ist der ELK-Stack?
- Logstash 101: Verwendung von Logstash in einer Datenverarbeitungspipeline
- Verwendung von Kibana zur Ausführung von Abfragen in ElasticSearch mit Lucene und Kibana Query Language
- ElasticSearch Maschinelles Lernen
- Wie man Elastic Enterprise Search installiert
- Wie man Elastic Enterprise Search mit GitHub verwendet
Diese Beiträge sind meine eigenen und spiegeln nicht unbedingt die Position, Strategien oder Meinung von BMC wider.