MongoDB i JSON-LD
MongoDB
Informacje wstępne
MongoDB jest bazą danych typu NoSQL do przechowywania
dokumentów (JSON - JavaScript Object Notation). Dane przechowywane są
w zagnieżdżonej formie i mogą być
odpytywane w sposób ad hoc.
MongoDB narzuca brak schematu
danych. Dokumenty mogą opcjonalnie
zawierać pola lub typy, których
nie zawierają inne dokumenty w kolekcji. Mongo jest dobrym
wyborem w przypadku aplikacji webowych, gdzie mamy do czynienia
z dużą skalą danych o elastycznym schemacie. Brak schematu pozwala bazie Mongo na
rozrastanie się i zmienianie wraz
z modelem danych. Mongo posiada mechanizmy
wspierające rozpraszanie danych (map-reduce).
Uruchomienie
Linię komend MongoDB można uruchomić
z konsoli (Linux) poleceniem
mongo.
Linia komend
Aby wybrać istniejącą bazę albo stworzyć
nową, użyj funkcji use z linii komend.
Przykładową bazę dotyczącą muzyki o nazwie music można stworzyć
w następujący sposób (UWAGA:
korzystając ze zdalnego webowego interfejsu, polecenie use
nie jest dostępne, a każda sesja przeglądarki
dostaje własną bazę danych).
> use music
switched to db
music
Następnie stworzymy nową kolekcję. Ponieważ nie jest wymagany
schemat, stworzenie kolekcji jest tak proste jak wstawienie
pierwszego rekordu. Poniższy kod wstawia/tworzy kolekcję albums:
> db.albums.insert({
artist: "U2",
title: "The Joshua Tree",
released: ISODate("1987-09-03"),
genre: ["rock", "folk
rock", "alternative rock"],
recorded : {
period: "from January 1986 to
January 1987",
country: "Ireland"
}
})
Aby wyświetlić listę dostępnych kolekcji dla aktualnej bazy
danych używamy polecenia:
> show
collections
albums
system.indexes
Zawartość kolekcji może zostać wyświetlona za pomocą polecenia
find().
> db.albums.find()
{ "_id" : ObjectId("548b647d8f746f116242bb62"),
"artist: U2", "title" : "The Joshua Tree",
"released" : ISODate("1987-09-03T00:00:00Z"),
"genre" : [ "rock", "folk rock",
"alternative rock" ], "recorded" : { "period" :
"from January 1986 to January 1987", "country" :
"Ireland" } }
Pole _id
oznacza numeryczny klucz główny, który
jest inkrementacyjnie (automatycznie)
zwiększany.
JavaScript
Natywny język Mongo
to JavaScript. Komendy są
funkcjami JavaScript, a baza
i kolekcja są obiektami JavaScript.
> typeof db
object
> typeof db.albums
object
> typeof db.albums.insert
function
Chcąc podejrzeć kod funkcji należy
ją wywołać bez parametrów (lub nawiasów). Stwórzmy swoją własną funkcję do wstawiania albumów do bazy, wprowadzając kod po prostu
z linii komend:
> function insertAlbum(
artist, title, released, genre,
recorded_info
){
db.albums.insert({
artist:artist,
title:title,
released:ISODate(released),
genre:genre,
recorded:recorded_info
});
}
Następnie możemy wywołać funkcję:
> insertAlbum("Michael Jackson",
"Thriller", '1982-11-30', ["pop","R&B","funk","rock"],
{country:"USA"})
> insertAlbum("Metallica","Metallica",
'1991-08-12',["heavy metal"],{period:"Oct
1990-June 1991"})
> insertAlbum("Norah Jones","Come
Away with
Me",'2001-02-26',["jazz"],{period:"2001",country:"USA"})
> insertAlbum("Siekiera","Nowa Aleksandria",'1988-07-27',["punk rock",
"coldwave"],{country:"Poland"})
> insertAlbum("Dżem","Cegła",'1985-10-01',["blues
rock"],{country:"Poland"})
Następnie, możesz użyć funkcji db.albums.find()
aby wyświetlić wprowadzone dane.
W Mongo możliwe
jest wydawanie różnych zapytań, poprzez wyspecyfikowanie nazw pól, zakresów wartości,
bądź też kombinacji kryteriów, np.:
>db.albums.find({"genre":"rock"})
Dozwolone jest użycie operatorów, takich jak np. operatorów zakresu czy też
operatorów wykorzystujących
wyrażenia regularne. Składnia operatorów warunkowych spełnia schemat: { $op : value },
gdzie
$op to operacja jak np. $lte (less or equal
to).
>db.albums.find({released: {$lte:
ISODate('1991-08-12')}, "genre":"rock"})
Jeśli chcemy aby któreś pole nie było zwracane w wynikach to ustawiamy jego wartość na 0.
Zagnieżdżone dane
Mongo jest zaprojektowane
z myślą o zagnieżdżonych danych, które można
odpytywać na
różne sposoby:
* poprzez
podanie dokładnie pasujących wartości:
db.albums.find(
{ genre : 'rock' },
{ _id : 0, title : 1, genre : 1 }
)
* poprzez
podanie częściowo pasujących wartości:
db.albums.find(
{ genre : /metal/ },
{ _id : 0, title : 1, genre : 1 }
)
* poprzez
podanie wszystkich pasujących wartości:
db.albums.find(
{ genre : { $all : ['rock', 'funk']}
},
{ _id : 0, title : 1, genre : 1 }
)
* lub
brak pasujących wartości:
db.albums.find(
{ genre : { $nin
: ['rock', 'jazz']} },
{ _id : 0, title : 1, genre : 1 }
)
Zaletą Mongo jest możliwość odpytywania
zagnieżdżonych danych:
db.albums.find(
{ 'recorded.country' : 'Ireland' },
{ _id : 0, title : 1, recorded : 1
}
)
db.albums.find(
{ 'recorded.country' : { $exists : false } },
{ _id : 0, title : 1, recorded : 1
}
)
Update
Chcielibyśmy teraz zmienić album
„Cegła” i opisać go dodatkowym typem (genre) muzyki. Do tego celu mamy
do dyspozycji funkcję update,
która ma dwa parametry: pierwszy jest kryterium zapytania - obiekt w tym samym
stylu jak podawany na wejście
funkcji find().
Drugi parametr jest albo obiektem, którego pola zastąpią pasujący
dokument albo operacją modyfikacji. W naszym przypadku, chcemy użyć modyfikatora,
który za pomocą $set ustawi wartość pola genre
na [„blues rock”, „rock”]:
>db.albums.update(
{ "title" : "Cegła" }, { $set : { "genre" : ["blues
rock", "rock"] } }
)
Chcąc dodać nowe pole z wartością, można to zrobić w podobny sposób:
>db.albums.update(
{ "title" : "Cegła" }, { $set : { "label" : "Polskie Nagrania Muza" } }
)
JSON-LD
JSON-LD jest standardem
W3C od stycznia 2014.
JSON-LD jest formatem serializacji
Linked Data do JSON.
Zacznijmy od przykładowego dokumentu JSON:
{
"name": "Peter Gabriel",
"homepage":
"http://www.petergabriel.com",
"image":
"http://upload.wikimedia.org/wikipedia/commons/2/2b/Peter_Gabriel_Allan_Warren.jpg"
}
Żeby reprezentować
taki dokument w sieci WWW jako „Linked Data”, dodamy unikalne identyfikatory (IRI).
W tym celu
wykorzystamy słownictwo schema.org.
{
"http://schema.org/name": "Peter Gabriel",
"http://schema.org/url": { "@id":
"http://www.petergabriel.com" },
"http://schema.org/image": { "@id":
"http://upload.wikimedia.org/wikipedia/commons/2/2b/Peter_Gabriel_Allan_Warren.jpg"
}
}
'@id' jest słowem kluczowym, które oznacza, że dana wartość
jest identyfikatorem typu
IRI.
Chociaż dodaliśmy unikalne identyfikatory do własności i wartości, to powyższy zapis jest dosyć rozwlekły. Żeby temu zaradzić w JSON-LD wprowadza się pojęcie
kontekstu (context).
Kontekst
Kontekst pozwala użyć skróconych termów poprzez odwzorowanie termów do IRI. Przykładowo:
{
"@context":
{
"name":
"http://schema.org/name",
"image": {
"@id":
"http://schema.org/image",
"@type": "@id"
},
"homepage": {
"@id":
"http://schema.org/url",
"@type": "@id"
}
}
}
Powyższy przykład definiuje
'name' jako skrót 'http://schema.org/name', 'image' jako skrót 'http://schema.org/image' i
'homepage' jako skrót 'http://schema.org/url'. W powyższym
przykładzie, ”@type”: ”@id” oznacza,
że wartość łańcuchowa związana z 'image' lub 'homepage' powinna być interpretowana jako identyfikator typu IRI. Powyższy kontekst pokazuje, że wartość definicji
termu może być albo: prostym
łańcuchem, odwzorowaniem termu w IRI lub obiektem JSON.
Wstawmy podany wyżej kontekst do nowej kolekcji persons w naszej bazie MongoDB.
>db.persons.insert (
{
"@context":
{
"name":
"http://schema.org/name",
"image": {
"@id":
"http://schema.org/image",
"@type": "@id"
},
"homepage": {
"@id":
"http://schema.org/url",
"@type": "@id"
}
}
}
)
Konteksty mogą być osadzone w dokumentach lub można się
do nich odwołać przez referencję. Zakładając, że dokument kontekstu może być pobrany
z http://json-ld.org/contexts/person.jsonld,
można zapisać do niego referencję w jednej linii. Dzięki
temu dokument JSON-LD może być wyrażony
bardziej zwięźle:
{
"@context":
"http://json-ld.org/contexts/person.jsonld",
"name": "Peter Gabriel",
"homepage":
"http://www.petergabriel.com",
"image":
"http://upload.wikimedia.org/wikipedia/commons/2/2b/Peter_Gabriel_Allan_Warren.jpg"
}
Dodajmy powyższy dokument
do naszej bazy MongoDB (do kolekcji 'persons'):
>db.persons.insert(
{
"@context":
"http://json-ld.org/contexts/person.jsonld",
"name": "Peter Gabriel",
"homepage":
"http://www.petergabriel.com",
"image":
"http://upload.wikimedia.org/wikipedia/commons/2/2b/Peter_Gabriel_Allan_Warren.jpg"
}
)
Kontekst, do którego odnosi się referencja, nie tylko specyfikuje
terminy odwzorowane w IRI w
słownictwie schema.org, ale także
specyfikuje, że wartości łańcuchowe powiązane z własnościami
'homepage' i 'image' mogą być interpretowane jako IRI (”@type”: ”@id”). Taka informacja
pozwala developerom na wzajemne
ponowne wykorzystanie danych bez konieczności
uzgadniania w jaki sposób ich dane
będą interoperacyjne strona po stronie.
IRI
Ponieważ JSON-LD serializuje model 'Powiązanych Danych', większość węzłów i własności jest identyfikowanych poprzez IRI.
Identyfikator węzła
W
JSON-LD, węzeł jest identyfikowany
poprzez słowo kluczowe @id. Przykładowo:
{
"@context":
{
...
"name":
"http://schema.org/name"
},
"@id":
"http://www.petergabriel.com",
"name": "Peter Gabriel",
...
}
Specyfikacja typu
Typ poszczególnego
węzła może być wyspecyfikowany poprzez użycie słowa kluczowego @type. W 'Powiązanych Danych' typy mają
unikalne identyfikatory
(IRI).
{
...
"@id":
"http://en.wikipedia.org/wiki/Peter_Gabriel",
"@type":
"http://schema.org/Person",
...
}
Dodajmy do naszej bazy typ zasobu:
>db.persons.update(
{ "name": "Peter
Gabriel" }, { $set : { "@type" :
"http://schema.org/Person" } }
)
Skrócone IRI
Skrócone IRI to sposób na wyrażenie IRI za pomocą np.
prefiksu. Przykładowo:
{
"@context":
{
"foaf":
"http://xmlns.com/foaf/0.1/"
...
},
"@type": "foaf:Person"
"foaf:name":
"Peter Gabriel",
...
}
Osadzanie
Dzięki możliwości osadzania w JSON-LD można używać obiektów węzłów jako wartości
własności.
Jest to powszechnie wykorzystywany
mechanizm do tworzenia relacji rodzic-dziecko pomiędzy dwoma węzłami. Przykładowo, dwa węzły są
powiązane poprzez własność z pierwszego węzła:
{
...
"name": "Peter Gabriel",
"knows":
{
"@type": "Person",
"name": "Kate Bush",
}
...
}
Zbiory i listy
Zbiór wartości może zostać przedstawiony
w zwięzły sposób poprzez użycie tablic JSON.
Przykładowo (uwaga: kolejność nie ma znaczenia w przypadku JSON-LD):
{
...
"@id":
"http://en.wikipedia.org/wiki/Bono",
"nick": [ "Bono",
"Bono Vox" ],
...
}
Wynikiem będzie wygenerowanie
następujących danych (bez narzuconej kolejności):
Subject |
Property |
Value |
Bono |
||
Bono Vox |
Wiele wartości może także być wyrażonych
w rozszerzonej formie:
{
"@id":
"http://example.org/books/10",
"dc:title":
[
{
"@value": "Ostatnie
życzenie",
"@language": "pl"
},
{
"@value": "The Last Wish",
"@language": "en"
}
]
}
W efekcie zostaną wygenerowane następujące dane:
Subject |
Property |
Value |
Ostatnie życzenie |
||
The Last Wish |
Dodajmy do naszej bazy osoby, które zna
Peter Gabriel:
>db.persons.update(
{ "name": "Peter
Gabriel" },
{ $set :
{
"knows":
[
{
"@type": "Person",
"name": "Kate Bush"
},
{
"@type": "Person",
"name": "Phil Collins"
},
]
}
}
)
Osadzanie
JSON-LD w dokumentach HTML
Znaczniki HTML script mogą być
użyte do osadzenia bloków danych w dokumentach. W ten właśnie sposób, treść JSON-LD może być łatwo
osadzona w HTMLu poprzez umieszczenie jej w elemencie script z atrybutem type ustawionym na application/ld+json.
Przykład:
<script
type="application/ld+json">
{
"@context": "http://json-ld.org/contexts/person.jsonld",
"@id":
"http://dbpedia.org/resource/Marie_Curie",
"name": "Maria Skłodowska-Curie",
"born": "1867-11-07",
"spouse":
"http://dbpedia.org/resource/Pierre_Curie"
}
</script>
Zadania
1 Sformułuj
zapytanie o wszystkie albumy, które reprezentują
gatunek (genre) „rock”.
2 Stwórz
nową kolekcję o nazwie „cds”. Wstaw
do kolekcji produkt, który jest albumem (z metadanymi jak w ćwiczeniach w tutorialu) i który jest dodatkowo
opisany atrybutami: cena, ilość magazynowa,
recenzje (w liczbie 2).
3 Sformułuj zapytanie o ludzi (name), których zna Peter Gabriel.