Abfrage von JSON-Dokumenten im Terminal mit GROQ

0
35

JSON-Dokumente sind heute überall, aber Sie sind selten strukturiert die Art und Weise, die Sie Sie sein wünschen. Sie enthalten oft zu viele Daten, haben komischerweise benannten Feldern, oder platzieren Sie die Daten in unnötig verschachtelte Objekte. Graph-Objekt-Relationale Abfragen (GROQ) ist eine Anfragesprache (wie SQL, aber anders), die ausgelegt ist, um direkt auf JSON-Dokumente. Im Grunde können Sie Abfragen schreiben, die Sie können schnell filtern und dann neu formatieren JSON-Dokumente, um Sie in die bequemste Form.

GROQ wurde entwickelt von Vernunft.io (wo es ist verwendet als primäre query language). Es ist open source und es gibt uns eingebauten Möglichkeiten nutzen, um es in JavaScript und die Befehlszeile auf jedem beliebigen JSON-Quelle. Zusammen, wir ‘ ll fügen Sie GROQ, um die terminal-toolkit, mit dem Sie viel Zeit sparen, wenn Sie brauchen, um zu streiten einige JSON-Daten auf einem poject.

Wir installieren GROQ

Wie die meisten Dinge, die wir brauchen, um installieren Sie die GROQ CLI tool, und kann dies mit mit npm (oder Garn) in das terminal:

$ npm install-g groq-cli

Um zu spielen, müssen wir eine JSON-Datei zur Verfügung. Verwenden wir curl, um den download der Beispiel-dataset der todo-Daten:

$ curl -o todos.json https://jsonplaceholder.typicode.com/todos

Werfen wir einen kurzen Blick auf ein Beispiel-item in Daten:

{
“userId”: 1,
“id”: 1,
“title”: “delectus aut autem”,
“abgeschlossen”: false
},

Ziemlich einfach. Wir haben eine Benutzer-ID, ein todo-Element-ID, eine todo-Titel und ein boolean der angibt, ob der todo-Eintrag erledigt ist oder nicht.

Jetzt laufen wir ein basic-GROQ-Abfrage: finden Sie alle erledigten Aufgaben, aber nur die Rückkehr des todo-Titel und Benutzer-IDs. Es ist OK, um zu kopieren/fügen Sie diese Zeile, weil wir zu Fuß durch das, was es bedeutet, in einem bit.

$ cat todo-Liste.json | groq ‘*[fertig == true]{title, userId}’ –ziemlich

Die groq Befehlszeilen-tools, die akzeptieren ein JSON-Dokument auf der standard-Eingabe. Dies funktioniert sehr gut mit der Unix-Philosophie “mache eine Sache und die Arbeit an einem text-stream.” Um zu Lesen JSON aus einer Datei verwenden wir die cat-Befehl. Beachten Sie auch, dass groq-Ausgabe wird eine minimale JSON, in einer einzigen Zeile standardmäßig, sondern durch übergeben von –schön, wir bekommen ein schön eingerückt und farbig hervorgehobene syntax.

Speichern Sie das Ergebnis, wir kann Rohr es in eine neue Datei mit >:

$ cat todo-Liste.json | groq ‘*[fertig == true]{title, userId}’ > Ergebnis.json

Die Abfrage selbst besteht aus drei teilen:

  • * bezieht sich auf den Datensatz (D. H. die Daten in der JSON-Datei).
  • [fertig == true] ist ein filter, der Elemente entfernt, sind als unvollständig gekennzeichnet.
  • {title, userId} ist eine Projektion, die bewirkt, dass die Abfrage nur die Rückkehr der “Titel” und “userId” Eigenschaften”.

Let ‘ s warm up mit einigen übungen

Sie wahrscheinlich nicht denken, Sie brauchen, um übung zu bekommen, durch diesen post! Nun, die gute Nachricht ist, dass wir nur die Ausübung des Geistes mit ein paar Sachen zum ausprobieren mit GROQ bevor wir in weitere details.

  1. Was passiert, wenn Sie entfernen [fertig == true] und/oder {title, userId}?
  2. Wie können Sie die Abfrage ändern, die finden sich auch alle Aufgaben, die durch den Benutzer mit der ID 2?
  3. Wie können Sie die Abfrage ändern, die zu finden nicht erledigten todos von dem Benutzer mit der ID 2?
  4. Was passiert, wenn der filter in der ursprünglichen Abfrage Beispiel swaps Orte mit der Projektion?
  5. Wie würden Sie schreiben einen einzigen Befehl (mit Leitungen) lädt die JSON und verarbeitet es mit GROQ?

Wir werden die Antworten am Ende der post für Sie zu verweisen.

Abfragen der Nobel-Preis-Gewinner

Die todo-Daten ist ganz nett für ein warm-up, aber lassen Sie uns ehrlich sein: Es ist nicht sehr motivierend, zu sehen in einer Liste, die lateinische als Platzhalter Inhalt. Aber der Nobelpreis hat einen dataset, der alle bisherigen Preisträger zur Verfügung, ist öffentlich.

Hier ist ein Beispiel zurück:

{
“Preisträger”: [
{
“id”: “1”,
“firstname”: “Wilhelm-Conrad”,
“Nachname”: “Röntgen”,
“geboren”: “1845-03-27”,
“gestorben”: “1923-02-10”,
“bornCountry”: “Preußen (jetzt Deutschland)”,
“bornCountryCode”: “DE”,
“bornCity”: “Lennep (heute Remscheid)”,
“diedCountry”: “Deutschland”,
“diedCountryCode”: “DE”,
“diedCity”: “München”,
“gender”: “male”,
“Preise”: […],
},
// …
]
}

Ah! Das ist viel interessanter! Lassen Sie uns downloaden Sie das dataset und finden Sie die Vornamen aller Norwegischen Preisträger. Hier verwenden wir –output Option für curl, um die Daten speichern zu einer Datei.

$ curl –output-Preisträger.json http://api.nobelprize.org/v1/laureate.json
$ cat Preisträger.json | groq ‘*.Preisträger[bornCountryCode == “NEIN”]{Vorname}’ –ziemlich

Was bekommen Sie zurück? Ich erhielt 12 Norwegische Nobel-Preisträger. Nicht schlecht!

Beachten Sie, dass diese Abfrage nicht ganz wie die erste Frage, die wir geschrieben haben. Wir haben ein extra .Preisträger in diesem. Wenn wir * in der todo-dataset, es repräsentiert die gesamte JSON-Dokument, das enthalten ist in einem array auf der obersten Ebene der todo-dataset. Auf der anderen Seite, die Preisträger-Datei benutzt ein Objekt der obersten Ebene, wo die Liste der Preisträger ist gespeichert in der “Preisträger” – Eigenschaft.

Um Zugriff auf ein bestimmtes Element, können wir die filter [0] und gibt nur den ersten Namen. Das sollte uns sagen, wer der erste Norweger war, um den Nobelpreis zu gewinnen.

$ cat Preisträger.json | groq ‘*.Preisträger[bornCountryCode == “NEIN”]{Vorname}[0]’ –ziemlich

// Zurückgegebene Objekt
{
“firstname”: “Ivar”
}
Mehr übungen!

Wir wären nachlässig, nicht zu spielen, um mit dieser neuen dataset ein bit, um zu sehen, wie die Abfragen funktionieren.

  1. Schreiben Sie eine Abfrage aller Nobelpreisträger aus dem eigenen Land.
  2. Schreiben Sie eine Abfrage um die letzten Norwegischen Nobelpreisträger. Hinweis: -1 bezieht sich auf das Letzte Element.
  3. Was passiert, wenn Sie versuchen, den filter direkt auf das root-Objekt? *[bornCountryCode == “NO”]?
  4. Was ist der Unterschied zwischen *.Preisträger[bornCountryCode == “NO”][0] und *.Preisträger[0][bornCountryCode == “NO”]?

Wie beim letzten mal, Antworten werden am Ende von diesem post.

Arbeiten mit filtern

Jetzt wissen wir, dass in Summe gab es 12 Norwegische Nobel-Preisträger, wie viele von Ihnen wurden nach 1950 geboren wurden? Das ist kein problem, herauszufinden, mit GROQ:

$ cat Preisträger.json | groq ‘*.Preisträger[bornCountryCode == “NO” && jahrgang >= “1950-01-01”]{Vorname}’ –ziemlich

// Sample return
[
{
“firstname”: “Mai-Britt”
},
{
“firstname”: “Edvard I.”
}
]

In der Tat, GROQ hat eine reiche Reihe von Operatoren, die wir verwenden können innerhalb einer filter. Wir können vergleichen, zahlen und strings mit gleichen (==), ungleich (!=), größer als (>), größer gleich ( >=), kleiner als ( < ) und kleiner oder gleich (<=). Plus, Vergleiche kann kombiniert werden mit UND (&&), ODER (||) und NICHT (!). Der in-operator ermöglicht es sogar, um zu überprüfen, für viele Fälle auf einmal (z.B. bornCountryCode in [“NEIN”, “SE”, “DK”]). Und der definierten Funktion ermöglicht es uns, zu sehen, wenn ein Feld vorhanden ist (z.B. definiert(diedCountry)).

Noch mehr übungen!

Sie kennen das: spielen Sie mit dem Filter ein wenig, um zu sehen, wie Sie arbeiten mit dem dataset. Antworten werden am Ende natürlich.

  1. Schreiben Sie eine Abfrage zurückgibt lebenden Preisträger.
  2. Gibt es einen Unterschied zwischen den filtern [bornCountryCode == “NO”][geboren >= “1950-01-01”] und [bornCountryCode == “NO” && jahrgang >= “1950-01-01”]?
  3. Finden Sie alle Preisträger, die Preisträger im Jahr 1973?

Arbeit mit Projektionen

Der Nobelpreis dataset trennt den vor-und Nachnamen für jeden Preisträger, aber was ist, wenn wir wollen, kombinieren Sie zusammen in einem Feld? Projektionen in GROQ können genau das tun!

*.Preisträger[bornCountryCode == “NO” && jahrgang >= “1950-01-01”]{
“name”: firstname + “” + Nachname,
geboren,
“prizeCount”: count(Preise),
}

Die Ausführung dieser Abfrage sagt uns, dass May-Britt Moser und Edvard Moser erhalten einen Preis (das war in der Tat, den gleichen Preis):

[
{
“name”: “Mai-Britt Moser”,
“geboren”: “1963-01-04”,
“prizeCount”: 1
},
{
“name”: “Edvard I. Moser”,
“geboren”: “1962-04-27”,
“prizeCount”: 1
}
]

Was ist hier passiert? Gut, wenn wir schreiben eine Projektion in GROQ, was wir wirklich schreiben ist ein JSON-Objekt. Vorher hatten wir einfache Projektionen (wie {firstname}), aber diese ist eine Abkürzung der Schreibweise {“firstname”: firstname}. Durch Verwendung der erweiterten Objekt-syntax, wir können beide benennen Sie die Schlüssel und die Transformation der Werte.

GROQ hat einen reichen Satz an Operatoren und Funktionen, die für die Umwandlung von Daten, einschließlich string-Verkettung, arithmetische Operatoren (+, -, *, /, %, **), zählen arrays (count(Preise)), und Runden von zahlen (round(num, <Menge von Nachkommastellen>).

Übungen

Hoffentlich, Sie bekommen ein gutes Gefühl für die Dinge zu diesem Punkt, aber hier sind ein paar weitere Wege, um die Praxis arbeitet mit Projektionen:

  1. Finden Sie alle Preisträger, die nicht gewonnen haben zwei oder mehr Preise.
  2. Wie viele Preise wurden gewonnen durch Frauen.
  3. Format a fullname Schlüssel, kombiniert lastname und firstname in das Ergebnis.

Tun Sie mehr auf einmal

Watch this:

$ cat Preisträger.json | groq-ziemlich’
{
“count”: count(*.Preisträger),
“Norweger”: *.Preisträger[bornCountryCode == “NEIN”]{Vorname},
}

Das Ergebnis:

{
“count”: 928,
“Norweger”: [
{
“firstname”: “Ivar”
},
{
“firstname”: “Lars”
},

]
}

Fangen Sie das? Ein GROQ-Abfrage nicht haben, um mit zu beginnen *. In dieser Anfrage, wir erstellen Ihnen ein JSON-Objekt, in dem die Werte sind Ergebnisse von getrennten Abfragen. Dies bietet viel Flexibilität in dem, was wir produzieren können, mit GROQ. Vielleicht möchten Sie die Gesamtzahl der unvollständigen todo-Liste zusammen mit einer Liste der fünf letzten. Oder vielleicht möchten Sie teilen Sie die Aufgaben in zwei getrennten Listen: eine für abgeschlossen und eine für unvollständig. Oder vielleicht brauchen Sie zum wickeln alles in einem Objekt, denn das ist was anderes tool/library/Frameworks erwartet. Was auch immer der Fall, GROQ hat Sie bedeckt.

Lassen Sie uns versuchen, eine Letzte Aufgabe. Können Sie Projekt ein Objekt, in dem die Preisträger enthält ein array mit einer abgerundeten Prozentsatz der die Gesamtzahl der Preise, die jeder Preisträger hat ausgeführt, gibt die Preisträger der’ ersten Namen? Versuchen Sie anschließend, ausgeben der Gesamtzahl ausgehändigt.

Zusammenfassung

Es gibt nicht viel, was Sie lernen müssen, bevor wir einige gute out-of-GROQ. Wenn Sie befolgt haben, die übungen, du bist auf einem tollen Weg zu einem GROQ-guru. Natürlich ist diese Einleitung nicht berühren all die unterschiedlichen Funktionen und Aspekte von GROQ, so fühlen sich frei zu erkunden, die Spezifikation und das Projekt selbst auf GitHub. Und fühlen Sie sich frei zu erreichen, um das team an die Vernunft.io wenn Sie Fragen zu den Daten, die Auseinandersetzung mit GROQ.

Antworten für die übungen

Übung 1
Frage 1

Wenn Sie [fertig == true] erhalten Sie alle todos, nicht nur diejenigen, die abgeschlossen sind. Wenn Sie entfernen {title, userId} erhalten Sie alle Eigenschaften.

Frage 2
*[Benutzer-id == 2]
Frage 3
*[userId == 2 && fertig == false] oder *[userId == 2 && !abgeschlossen]
Frage 4

Wenn Sie ändern Sie die Reihenfolge der filter und die Projektion, die Sie tun, die Projektion und erst dann den filter anwenden. Das bedeutet, dass man das filtern auf eine Liste der todo-Liste, die nur Titel enthalten und Benutzer-id, und fertig == true ” kann niemals wahr sein.

Frage 5
curl https://jsonplaceholder.typicode.com/todos | groq ‘*[fertig == true]{title, userId}’ > Ergebnis.json

Übung 2
Frage 1
*.Preisträger[bornCountryCode == “EINFÜGEN-IHREM-LAND-HIER”]
Frage 2
*.Preisträger[bornCountryCode == “NO”][-1]
Frage 3

*[bornCountryCode == “NO”] wird versuchen, die filter auf ein Objekt. Dies macht keinen Sinn, so erhalten Sie null als Antwort.

Frage 4

*.Preisträger[0][bornCountryCode == “NO”] funktioniert nicht, wie Sie vielleicht denken. Dieser wird zuerst die erste Preisträger (in diesem Fall Wilhelm-Conrad) und dann wird es versuchen, Sie zu “filter” das Objekt. Das macht keinen Sinn, die Antwort ist also null.

Übung 3
Frage 1
*.Preisträger[gestorben == “0000-00-00”]
Frage 2

Es gibt keinen Unterschied zwischen den filtern [bornCountryCode == “NO”][geboren >= “1950-01-01”] und [bornCountryCode == “NO” && jahrgang >= “1950-01-01”]. Der erste funktioniert die Filterung in zwei “Durchgänge”, aber das Endergebnis ist das gleiche.

Frage 3
*.Preisträger[“1973” in Preisen[].Jahr]

Übung 4
Frage 1
*.Preisträger[count(Preise) >= 2]
Frage 2
count(*.Preisträger[gender == “female”])
Frage 3
*.Preisträger{“fullname”: Nachname + “, ” + firstname}

Übung 5
*.Preisträger{“Preisträger”: {“firstname”, “percentage”: round(count(Gewinne) / count(*.Preisträger[].Preise), 3) * 100}, “total”: count(*.Preisträger[].Preise)}