Query JSON dokumenter i Terminal med GROQ

0
4

JSON dokumenter er overalt i dag, men de er sjelden strukturert slik du vil ha dem til å være. De inneholder ofte for mye data, har nifs kalt felt, eller plassere data i unødvendig nestede objekter. Graf-Relasjons Objekt-Spørringer (GROQ) er en query language (som SQL, men forskjellige) som er designet for å jobbe direkte på JSON dokumenter. Det er i utgangspunktet lar deg skrive spørringer kan du raskt filtrere og deretter formatere JSON dokumenter for å få dem inn i den mest praktiske form.

GROQ ble utviklet av mental helse.io (hvor det brukes som det primære query language). Det er åpen kildekode, og det gir oss innebygde måter å bruke det i JavaScript og kommando-linje på noen JSON-kilde. Sammen, vil vi legge til GROQ til terminalen toolkit, noe som vil spare deg for tid når du trenger å argumentere noen JSON-dataene på en poject.

La oss installere GROQ

Som de fleste ting, vi trenger å installere GROQ CLI-verktøy, og kan gjøre det med med npm (eller Garn) i terminal:

$ npm installere -g groq-cli

For å kunne spille med det, må vi ha en JSON-filer. Vi vil bruke curl for å laste ned et eksempel dataset av todo data:

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

La oss ta en rask titt på et eksempel element i dataene:

{
“bruker-id”: 1,
“id”: 1,
“title”: “delectus aut autem”,
“fullført”: false
},

Ganske grei. Vi har en bruker-ID, et eneste element-ID, en todo-tittel og en boolsk angi om todo elementet er fullført eller ikke.

Nå la oss kjøre en grunnleggende GROQ spørring: å finne alle fullførte todos, men bare gå tilbake todo titler og bruker-Id-er. Det er OK å kopiere/lime inn denne linjen fordi vi vil gå gjennom hva det betyr i en bit.

$ cat todos.json | groq ‘*[fullførte == true]{tittel, bruker-id}’ – ganske

Den groq kommando linje verktøy godta en JSON-dokument på standard input. Dette fungerer veldig fint med Unix-filosofi “gjør en ting og arbeide med en tekst strømmen.” For å lese JSON fra en fil, vil vi bruke cat-kommandoen. Merk også at groq vil sende ut en minimal JSON på en enkelt linje som standard, men ved å sende –pen, får vi et pent med innrykk og syntaks uthevet.

For å lagre resultatet, kan vi rør det til en ny fil ved hjelp av >:

$ cat todos.json | groq ‘*[fullførte == true]{tittel, bruker-id}’ > resultat.json

Spørringen i seg selv består av tre deler:

  • * refererer til datasett (dvs. data i JSON-fil).
  • [avsluttet == true] er et filter som fjerner elementer som er merket som ufullstendig.
  • {tittel, bruker-id} er en projeksjon som fører til spørringen for å bare gå tilbake “tittel” og “bruker-id” egenskaper.

La oss varme opp med noen øvelser

Du har sannsynligvis ikke trodde du hadde behov for å trene på å komme gjennom dette innlegget! Vel, den gode nyheten er at vi er bare trene sinnet med et par ting å prøve ut med GROQ før vi får inn mer informasjon.

  1. Hva skjer hvis du fjerner [fullførte == true] og/eller {tittel, bruker-id}?
  2. Hvordan kan du endre spørringen for å finne alle todos av brukeren med en ID av 2?
  3. Hvordan kan du endre spørringen for å finne uferdige todos av brukeren med en ID av 2?
  4. Hva skjer hvis filteret i den opprinnelige spørringen eksempel bytteavtaler steder med projeksjon?
  5. Hvordan ville du skrive en enkelt kommando (med rør) som laster ned JSON, og behandler dem med GROQ?

Vi vil sette svar på slutten av innlegget for deg å referere.

Spørring Nobels fredspris vinnere

Todo data er hyggelig for en oppvarming, men la oss være ærlige: Det er ikke veldig motiverende å se på en liste som bruker latinske som plassholder innhold. Men Nobels Fredspris har et datasett med alle tidligere prisvinnere er tilgjengelig for å bruke offentlig.

Her er et eksempel på en retur:

{
“vinnerne”: [
{
“id”: “1”,
“firstname”: “Wilhelm Conrad”,
“etternavn”: “Röntgen”,
“født”: “1845-03-27”,
“døde”: “1923-02-10”,
“bornCountry”: “Preussen (nå Tyskland)”,
“bornCountryCode”: “DE”,
“bornCity”: “Lennep (nå Remscheid)”,
“diedCountry”: “Tyskland”,
“diedCountryCode”: “DE”,
“diedCity”: “München”,
“kjønn”: “mannlig”,
“premier”: […],
},
// …
]
}

Ah! Dette er mye mer interessant! La oss ned datasettet og finne den første navnet på alle norske prisvinnarar. Her, vi kommer til å bruke –output flagg for curl for å lagre dataene til en fil.

$ curl –output prisvinner.json http://api.nobelprize.org/v1/laureate.json
$ cat prisvinner.json | groq ‘*.vinnerne[bornCountryCode == “NO”]{fornavn}’ – ganske

Hva får du tilbake? Jeg fikk 12 norske nobelprisvinnere. Ikke verst!

Vær oppmerksom på at dette spørsmålet er ikke helt som den første spørringen vi skrev. Vi har en ekstra .vinnerne i dette. Når vi brukt * i todo-datasettet, det representerer hele JSON dokumenter som er inneholdt i en rekke på det øverste nivået av todo-datasettet. På den annen side, vinneren-filen bruker et objekt på topp-nivå-der listen over prisvinnere er lagret i “vinnerne” eiendom.

For å få tilgang til et bestemt punkt, kan vi bruke filter [0] og gå tilbake bare fornavn. Det burde fortelle oss som den første norske var til å vinne Nobels fredspris.

$ cat prisvinner.json | groq ‘*.vinnerne[bornCountryCode == “NO”]{fornavn}[0]’ – ganske

// Returnerte objektet
{
“firstname”: “Ivar”
}
Flere øvelser!

Vi ville være remiss ikke å spille rundt med denne nye datasettet litt for å se hvordan søk fungerer.

  1. Skriv en spørring for å finne alle nobelprisvinnere fra ditt eget land.
  2. Skriv en spørring for å gå tilbake til den siste norske vinneren. Hint: -1 refererer til det siste elementet.
  3. Hva skjer hvis du prøver å filtrere direkte på de underliggende objekt? *[bornCountryCode == “NO”]?
  4. Hva er forskjellen mellom *.vinnerne[bornCountryCode == “NEI”][0] og *.vinnerne[0][bornCountryCode == “NO”]?

Som forrige gang, svar vil være på slutten av dette innlegget.

Arbeide med filtre

Nå vet vi at det totalt har vært 12 norske nobelprisvinnere, hvor mange av dem var født etter 1950? Det er ikke noe problem å finne ut av med GROQ:

$ cat prisvinner.json | groq ‘*.vinnerne[bornCountryCode == “NEI” && født >= “1950-01-01”]{fornavn}’ – ganske

// Eksempel gå tilbake
[
{
“firstname”: “May-Britt”
},
{
“firstname”: “Edvard I.”
}
]

Faktisk, GROQ har et rikt sett av operatorer vi kan bruke inne i et filter. Vi kan sammenligne tall og strenger med lik (==), ikke lik (!=), større enn (>), som er større enn eller lik ( >=), mindre enn (<), og mindre enn eller lik (<=). Plus, sammenligninger kan kombineres med OG (&&), ELLER (||) og IKKE (!). I operatør selv gjør det mulig å kontrollere for mange saker på en gang (f.eks. bornCountryCode i [“NEI”, “SE”, “DK”]). Og det definert funksjon gjør det mulig for oss å se om et felt som finnes (f.eks. definert(diedCountry)).

Enda flere øvelser!

Du vet drillen: prøv å spille med filtre litt for å se hvordan de jobber med datasettet. Svarene er på slutten av kurset.

  1. Skriv en spørring som returnerer levende prisvinnerne.
  2. Er det en forskjell mellom filtrene [bornCountryCode == “NO”][født >= “1950-01-01”] og [bornCountryCode == “NEI” && født >= “1950-01-01”]?
  3. Kan du finne alle vinnerne som vant en premie i 1973?

Arbeider med anslag

Nobels Fredspris dataset skiller mellom fornavn og etternavn for hver vinner, men hva hvis vi ønsker å kombinere dem sammen til ett felt? Anslagene i GROQ kan gjøre akkurat det!

*.vinnerne[bornCountryCode == “NEI” && født >= “1950-01-01”]{
“name”: fornavn + “” + etternavn,
født,
“prizeCount”: count(premier),
}

Kjører denne spørringen forteller oss at May-Britt Moser og Edvard Moser har fått en premie (som var, faktisk, den samme prisen):

[
{
“name”: “May-Britt Moser”,
“født”: “1963-01-04”,
“prizeCount”: 1
},
{
“name”: “Edvard I. Moser”,
“født”: “1962-04-27”,
“prizeCount”: 1
}
]

Hva skjedde her? Vel, når vi skriver en projeksjon i GROQ hva vi virkelig skriving er et JSON-objekt. Tidligere hadde vi enkle anslag (som {fornavn}), men dette er en snarvei måte å skrive {“firstname”: fornavn}. Ved å bruke den utvidede objektet syntaksen, vi kan både endre navn på taster og transformere verdiene.

GROQ har et rikt sett av operatorer og funksjoner for å transformere data, inkludert string sammensetting, aritmetiske operatorer (+, -, *, /, %, **), telling av matriser (count(premier)), og runde tall (round(tall, <mengden av desimaler>).

Øvelser

Forhåpentligvis, du får en god følelse av ting på dette punktet, men her er noen flere måter å praktisere som arbeider med projeksjoner:

  1. Finn alle prisvinnerne som har vunnet to eller flere premier.
  2. Finn ut hvor mange premier har blitt vunnet av kvinner.
  3. Formatere et fullname nøkkelen som kombinerer etternavn og fornavn i resultatet.

Å gjøre flere ting på én gang

Se på dette:

$ cat prisvinner.json | groq-ganske’
{
“count”: count(*.vinnerne),
“nordmenn”: *.vinnerne[bornCountryCode == “NO”]{fornavn},
}

Resultatet:

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

]
}

Ta det? En GROQ spørring ikke har, til å begynne med *. I dette søket, vi holder på å lage et JSON-objekt hvor verdier er resultater fra separate spørsmål. Dette gir en stor fleksibilitet i hva vi kan produsere med GROQ. Kanskje du vil vise det totale antall ufullstendige todos sammen med en liste over de fem siste de. Eller kanskje du ønsker å dele todos i to separate lister: én for fullført og en for ufullstendig. Eller kanskje du trenger å bryte alt inne i et objekt, fordi det er hva annet verktøy/bibliotek/framework forventer. Uansett tilfelle, GROQ har du dekket.

La oss prøve en siste øvelse. Kan du projisere et objekt der prisvinnere inneholder en matrise med en avrundet prosentandel av det totale antall premier som hver vinner har kjørt, tilbake prisvinnerne’ fornavn? Deretter prøver du å gi ut det totale antallet levert ut.

Oppsummering

Det er ikke mye du trenger å vite før du får noen gode ut av GROQ. Hvis du har fulgt øvelser, du er på god vei til å bli en GROQ guru. Naturligvis, denne introduksjonen ikke røre på alle de forskjellige funksjoner og aspekter av GROQ, så føl deg fri til å utforske spesifikasjon og selve prosjektet på GitHub. Og føl deg fri til å nå ut til det laget som på Fornuft.io hvis du har spørsmål om data krangel med GROQ.

Øvelse svar

Øvelse 1
Spørsmål 1

Hvis du fjerner [fullførte == true] du vil få alle todos, ikke bare de som er fullført. Hvis du vil fjerne {tittel, bruker-id} du vil få alle egenskaper.

Spørsmål 2
*[userId == 2]
Spørsmål 3
*[userId == 2 && ferdig == false] eller *[userId == 2 && !fullført]
Spørsmål 4

Hvis du vil endre rekkefølgen på filter og projeksjon vil du gjøre projeksjon først, og deretter bruke filteret. Dette betyr at du filtrering på en liste over todos som bare inneholde tittel og bruker-id, og fullførte == true aldri kan være sant.

Spørsmål 5
curl https://jsonplaceholder.typicode.com/todos | groq ‘*[fullførte == true]{tittel, bruker-id}’ > resultat.json

Øvelse 2
Spørsmål 1
*.vinnerne[bornCountryCode == “SETT inn-DIN-LAND-HER”]
Spørsmål 2
*.vinnerne[bornCountryCode == “NEI”][-1]
Spørsmål 3

*[bornCountryCode == “NO”] vil prøve å filtrere på et objekt. Dette gjør ikke noe fornuftig, så vil du få null i svaret.

Spørsmål 4

*.vinnerne[0][bornCountryCode == “NO”] virker ikke som du kanskje tror. Dette vil først finne den første prisvinner (som skjer for å være Wilhelm Conrad) og deretter vil det prøve å “filtrere” objektet. Dette gir ingen mening så svaret er null.

Øvelse 3
Spørsmål 1
*.vinnerne[døde == “0000-00-00”]
Spørsmål 2

Det er ingen forskjell mellom filtre [bornCountryCode == “NEI”][født >= “1950-01-01”] og [bornCountryCode == “NEI” && født >= “1950-01-01”]. Det første man gjør filtrering i to “går over”, men resultatet er det samme.

Spørsmål 3
*.prisvinnere, [“1973” i premier[].året]

Øvelse 4
Spørsmål 1
*.vinnerne[teller(premier) >= 2]
Spørsmål 2
count(*.vinnerne[gender == “kvinnelige”])
Spørsmål 3
*.prisvinnere, {“fullname”: etternavn + “, ” + fornavn}

Øvelse 5
*.prisvinnere, {“vinnerne”: {fornavn, “prosent”: runde(count(gevinster) / count(*.vinnerne[].premier), 3) * 100}, “total”: count(*.vinnerne[].premier)}