Fråga JSON dokument i Terminalen med GROQ

0
5

JSON dokument finns överallt idag, men de är sällan strukturerad på det sätt du vill ha dem. De innehåller ofta för mycket information, har konstigt namngivna fält, eller placera data i onödiga inkapslade objekt. Grafen-ett Relationellt Objekt Frågor (GROQ) är ett frågespråk (som SQL, men olika) som är utformad för att fungera direkt på JSON dokument. Det i grund och botten kan du skriva frågor som du snabbt kan filtrera och formatera om JSON dokument för att få dem till de mest bekväm form.

GROQ utvecklades av Förnuft.io (där den används som den primära query language). Det är öppen källkod och det ger oss en inbyggd sätt att använda det i JavaScript och kommandoraden på någon JSON källa. Tillsammans ska vi lägga till GROQ till terminalen toolkit, vilket kommer att spara tid när du behöver tvista några JSON-data på en poject.

Låt oss installera GROQ

Liksom de flesta saker vi behöver för att installera GROQ CLI verktyg och kan göra så med med npm (eller Garn) i terminalen:

$ npm install-g groq-cli

För att kunna spela med det, vi måste ha en JSON-fil som finns tillgänglig. Vi kommer att använda curl för att ladda ner ett exempel på dataset på todo-data:

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

Låt oss ta en snabb titt på ett urval objekt i data:

{
“användar-id”: 1,
“id”: 1,
“title”: “delectus aut autem”,
“färdig”: false
},

Ganska enkelt. Vi har ett användar-ID, en todo-objekt-ID, en todo-avdelning och ett booleskt värde som anger om todo objektet är klar eller inte.

Nu ska vi köra en grundläggande GROQ fråga: hitta alla genomförda todos, men bara returnera todo titlar och användar-Id. Det är OK att kopiera/klistra in den här linjen eftersom vi kommer att gå igenom vad det innebär i en bit.

$ cat todos.json | groq ‘*[completed == true]{title userId}’ – ganska

Den groq kommandoraden verktyg acceptera en JSON dokument på standard input. Detta fungerar mycket bra med Unix-filosofin om att “göra en sak och jobba på en text som bild.” För att läsa JSON från en fil som vi kommer att använda kommandot cat. Observera också att groq kommer att utdata en minimal JSON på en enda rad som standard, men genom att passera-ganska, vi får ett fint indragen och markerade syntax.

Att lagra resultatet, kan vi rör den till en ny fil med >:

$ cat todos.json | groq ‘*[completed == true]{title userId}’ > resultat.json

Frågan i sig består av tre delar:

  • * hänvisar till dataset (dvs data i JSON-filen).
  • [completed == true] är ett filter som tar bort objekt som markeras som ofullständig.
  • {title userId} är en prognos som medför att frågan bara returnerar “title” och “användar-id” egenskaper.

Låt oss värma upp med lite övningar

Du kanske inte tror att du skulle behöva övning för att få till det här inlägget! Tja, den goda nyheten är att vi bara tränar sinnet med ett par saker att prova med GROQ innan vi går in på mer detaljer.

  1. Vad händer om du tar bort [completed == true] och/eller {title userId}?
  2. Hur kan du ändra frågan för att hitta alla todos av användaren med ID 2?
  3. Hur kan du ändra frågan för att hitta ofullbordade todos av användaren med ID 2?
  4. Vad händer om filtret i den ursprungliga frågan exempel swappar platser med projektion?
  5. Hur skulle du skriva ett enda kommando (med rör) för att hämta JSON och bearbetar det med GROQ?

Vi ska sätta svaren i slutet av inlägget för dig att referera till.

Fråga nobelpristagare

Todo-data är trevligt för en uppvärmning, men låt oss vara ärliga: Det är inte särskilt motiverande att titta på en lista som använder det latinska som platshållare för innehåll. Men Nobelpriset har ett dataset med alla tidigare pristagare finns för att använda offentligt.

Här är ett exempel på retur:

{
“pristagarna”: [
{
“id”: “1”,
“förnamn”: “Wilhelm Conrad”,
“efternamn”: “Röntgen”,
“född”: “1845-03-27”,
“dog”: “1923-02-10”,
“bornCountry”: “Berlin (nu Tyskland)”,
“bornCountryCode”: “DE”,
“bornCity”: “Lennep (nu Remscheid)”,
“diedCountry”: “Tyskland”,
“diedCountryCode”: “DE”,
“diedCity”: “München”,
“kön”: “manliga”,
“priser”: […],
},
// …
]
}

Ah! Detta är mycket mer intressant! Låt oss hämta datan och hitta det första namnet på alla norska pristagare. Här kommer vi att använda –output flagga för curl för att spara data till en fil.

$ curl –output-pristagare.json http://api.nobelprize.org/v1/laureate.json
$ cat pristagare.json | groq ‘*.pristagare[bornCountryCode == “NO”]{förnamn}’ – ganska

Vad får du tillbaka? Jag fick 12 norska Nobel-pristagare. Inte dåligt!

Observera att denna fråga är inte riktigt som den första frågan som vi skrev. Vi har en extra .pristagarna i denna. När vi använde * i todo-datasetet, det representerar hela JSON dokument som finns i en array på den högsta nivån av todo-dataset. Å andra sidan, nobelpristagaren filen använder ett objekt på översta nivån där listan över pristagare är lagrade i “pristagare” egendom.

För att få tillgång till en viss artikel, kan vi använda filter [0] och återgå bara förnamnet. Som borde tala om oss som det första norrman att vinna ett nobelpris.

$ cat pristagare.json | groq ‘*.pristagare[bornCountryCode == “NO”]{förnamn}[0]’ –ganska

// Tillbaka objekt
{
“förnamn”: “Ivar”
}
Fler övningar!

Vi skulle vara försumligt att inte leka med denna nya dataset lite för att se hur frågorna fungerar.

  1. Skriv en fråga för att hitta alla nobelpristagare från ditt eget land.
  2. Skriv en fråga för att återgå till den förra norska pristagare. Tips: -1 hänvisar till det sista objektet.
  3. Vad händer om du försöker filter direkt på roten objekt? *[bornCountryCode == “NO”]?
  4. Vad är skillnaden mellan *.pristagare[bornCountryCode == “NO”][0] och sedan på *.pristagare[0][bornCountryCode == “NO”]?

Precis som förra gången, svar kommer att vara i slutet av detta inlägg.

Arbeta med filter

Nu vet vi att totalt har det blivit 12 norska Nobel pristagare, hur många av dem som är födda efter 1950? Det är inga problem att räkna ut med GROQ:

$ cat pristagare.json | groq ‘*.pristagare[bornCountryCode == “NEJ” && born >= “1950-01-01”]{förnamn}’ – ganska

// Exempel tillbaka
[
{
“förnamn”: “Maj-Britt”
},
{
“förnamn”: “Edvard I.”
}
]

I själva verket, GROQ har en rik uppsättning av aktörer som vi kan använda inne i ett filter. Vi kan jämföra tal och strängar med likhetstecken (==), inte lika med (!=), större än (>), större än eller lika med ( >=), mindre än (<), mindre än eller lika med (<=). Plus, jämförelser kan kombineras med AND (&&), ELLER (||) och INTE (!). Den operatör som även gör det möjligt att kontrollera för många fall på en gång (t ex bornCountryCode i [“INGEN”, “SE”, “DK”]). Och den definierade funktionen gör det möjligt för oss att se om ett fält finns (t ex definieras(diedCountry)).

Ännu fler övningar!

Du vet hur det går: försök att spela med filter lite för att se hur de arbetar med dataset. Svar finns i slutet av kursen.

  1. Skriv en fråga som returnerar levande pristagare.
  2. Finns det en skillnad mellan filter [bornCountryCode == “NO”][född >= “1950-01-01”] och [bornCountryCode == “NEJ” && born >= “1950-01-01”]?
  3. Du kan hitta alla nobelpristagare som vann ett pris i 1973?

Att arbeta med prognoser

Nobelpriset dataset skiljer förnamn och efternamn för varje pristagare, men vad händer om vi vill kombinera ihop dem till ett område? Beräkningarna i GROQ kan göra just det!

*.pristagare[bornCountryCode == “NEJ” && born >= “1950-01-01”]{
“namn”: förnamn + “” + efternamn,
född,
“prizeCount”: count(priser),
}

Kör denna fråga säger oss att Maj-Britt Moser och Edvard Moser fick ett pris (som var, i själva verket, samma pris):

[
{
“name”: “Maj-Britt Moser”,
“född”: “1963-01-04”,
“prizeCount”: 1
},
{
“name”: “Edvard I. Moser”,
“född”: “1962-04-27”,
“prizeCount”: 1
}
]

Vad har hänt här? Jo, när vi skriver en projektion i GROQ vad vi egentligen skriver är en JSON-objekt. Tidigare hade vi enkla beräkningar (som {förnamn}), men det här är en genväg sätt att skriva {“förnamn”: förnamn}. Med hjälp av den utökade objekt syntax, vi kan både byta namn på knappar och förändra värderingar.

GROQ har en rik uppsättning operatorer och funktioner för att omvandla data, inklusive sträng sammanslagning, aritmetiska operatorer (+, -, *, /, %, **), räkna matriser (count(priser)), och avrundning nummer (round(num, <mängd decimaler>).

Övningar

Förhoppningsvis kan du få en bra känsla för saker på denna punkt, men här är några fler sätt att öva på att arbeta med prognoser:

  1. Hitta alla pristagare som har vunnit två eller fler priser.
  2. Ta reda på hur många vinster som har vunnits av kvinnor.
  3. Formatera en fullname nyckel som kombinerar efternamn och förnamn i resultatet.

Man kan göra mer på en gång

Titta på den här:

$ cat pristagare.json | groq –ganska’
{
“count”: count(*.pristagare),
“norrmän”: *.pristagare[bornCountryCode == “NO”]{förnamn},
}

Resultatet:

{
“count”: 928,
“norrmän”: [
{
“förnamn”: “Ivar”
},
{
“förnamn”: “Lars”
},

]
}

Fånga det? En GROQ fråga inte har till att börja med *. I denna fråga, vi skapar ett JSON-objekt där värdena är resultat från olika frågor. Detta ger en stor flexibilitet i vad vi kan producera med GROQ. Kanske du vill att det totala antalet ofullständiga todos tillsammans med en lista över de fem senaste och kära. Eller kanske du vill dela todos i två separata listor: en för avslutat och en för ofullständig. Eller du kanske behöver avsluta allt inuti ett objekt, eftersom det är vad ett annat verktyg/library/ram förväntar sig. Hur som helst, GROQ har du omfattas.

Låt oss försöka en sista övning. Kan du projicera ett objekt där pristagarna innehåller en array med en rundad procent av det totala antalet priser som varje pristagare har kört, återvänder pristagarnas första namn? Försök sedan mata det totala antalet delade ut.

Sammanfattning

Det finns inte mycket du behöver för att lära sig innan det blir några bra användning av GROQ. Om du har följt övningar, du är på en bra väg till att bli en GROQ guru. Naturligtvis, denna introduktion inte gå in på alla de olika funktioner och aspekter av GROQ, så känn dig fri att utforska specifikation och själva projektet på GitHub. Och känn dig fri att nå ut till teamet på Sanity.io om du har frågor om uppgifterna gräl med GROQ.

Motion svar

Övning 1
Fråga 1

Om du tar bort [completed == true] du kommer att få alla todos, inte bara de som är avslutade. Om du ta bort {title userId} du kommer att få alla egenskaper.

Fråga 2
*[userId == 2]
Fråga 3
*[userId == 2 && avslutade == false] eller *[userId == 2 && !avslutad]
Fråga 4

Om du ändrar ordning för filtret och projektion du kommer att göra projektion först och sedan tillämpa filtret på. Detta innebär att du filtrera på en lista över todos som bara innehåller titeln och användar-id, och avslutade == true kan aldrig vara sant.

Fråga 5
curl https://jsonplaceholder.typicode.com/todos | groq ‘*[completed == true]{title userId}’ > resultat.json

Övning 2
Fråga 1
*.pristagare[bornCountryCode == “SÄTTA-DITT-LAND-HÄR”]
Fråga 2
*.pristagare[bornCountryCode == “NO”][-1]
Fråga 3

*[bornCountryCode == “NO”] kommer att försöka att filtrera på ett objekt. Detta är inte klokt, så får du null svar.

Fråga 4

*.pristagare[0][bornCountryCode == “NO”] fungerar inte som du kanske tror. Detta kommer först att hitta den första pristagare (som råkar vara Wilhelm Conrad) och sedan kommer den att försöka “filter” – objekt. Det är ingen mening så är svaret noll.

Motion 3
Fråga 1
*.pristagare[dog == “0000-00-00”]
Fråga 2

Det är ingen skillnad mellan filtren [bornCountryCode == “NO”][född >= “1950-01-01”] och [bornCountryCode == “NEJ” && born >= “1950-01-01”]. Det första man gör filtreringen i två “pass”, men slutresultatet är detsamma.

Fråga 3
*.pristagare[“1973” i priser[].år]

Övning 4
Fråga 1
*.pristagare[count(priser) >= 2]
Fråga 2
count(*.pristagare[gender == “kvinnliga”])
Fråga 3
*.pristagare{“fullname”: efternamn + “, ” + förnamn}

Motion 5
*.pristagare{“pristagare”: {förnamn, “procent”: round(count(vinster) / count(*.pristagare[].- priser), 3) * 100}, “total”: count(*.pristagare[].priser)}