1. JSON-LD and
MongoDB
Powering Linked Web Apps
Gregg Kellogg
gregg@greggkellogg.net
@gkellogg
2. JSON-LD and Mongo
• JSON-LD – graph expression in JSON
• MongoDB – easily query JSON documents
• Together – efficient graph query optimized
for client-side processing
3. Use Case
• WebApps: Single Page Applications (SPAS)
• Use JSON as native object representation
• REST access to web service mirroring
objects
• MongoDB document store maintaining
equivalent object representations
• Data is linked, internally and externally (RDF)
4. JSON-LD
@graph
@context
@id
JSON-based syntax to express linked data
@language
@value
@list
@vocab
@type @set
5. JSON, as expected
{
"@context": "/contexts/wikia.jsonld",
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame",
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ",
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
],
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"],
"name": "Call of Duty: Black Ops",
"publisher": [
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
],
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
6. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame",
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ",
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
],
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"],
"name": "Call of Duty: Black Ops",
"publisher": [
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
],
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
7. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame", Identifier treated as IRI
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ",
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
],
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"],
"name": "Call of Duty: Black Ops",
"publisher": [
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
],
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
8. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame", Identifier treated as IRI
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ", Type same as rdf:type
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
],
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"],
"name": "Call of Duty: Black Ops",
"publisher": [
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
],
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
9. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame", Identifier treated as IRI
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ", Type same as rdf:type
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
], Data-typed values
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"],
"name": "Call of Duty: Black Ops",
"publisher": [
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
],
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
10. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame", Identifier treated as IRI
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ", Type same as rdf:type
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
], Data-typed values
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"], Simple string values
"name": "Call of Duty: Black Ops",
"publisher": [
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
],
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
11. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame", Identifier treated as IRI
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ", Type same as rdf:type
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
], Data-typed values
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"], Simple string values
"name": "Call of Duty: Black Ops",
"publisher": [ (unordered) arrays of values
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
],
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
12. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame", Identifier treated as IRI
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ", Type same as rdf:type
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
], Data-typed values
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"], Simple string values
"name": "Call of Duty: Black Ops",
"publisher": [ (unordered) arrays of values
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
], subject reference links to other resources
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001",
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
13. JSON, as expected
{
"@context": "/contexts/wikia.jsonld", Context defines terms
"@id": "500f1a1b6e7f1827ba000001",
"@type": "wikia:VideoGame", Identifier treated as IRI
"contentRating": [
"ESRB: Mature (DS version is Teen)",
"PEGI: 18+ (DS version is 16+) ", Type same as rdf:type
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"
], Data-typed values
"datePublished": "2011-11-09",
"description": "Primary game entry point",
"genre": ["First-person shooter"], Simple string values
"name": "Call of Duty: Black Ops",
"publisher": [ (unordered) arrays of values
{"@id": "501067246e7f184553000001"},
{"@id": "5010675a6e7f18464d000001"}
], subject reference links to other resources
"event": [
"500f28856e7f187196000001",
"500f44556e7f18f7ef000001", Arrays may be ordered in @context
"500f449e6e7f18f94b000001",
"500f44e26e7f18fa75000001"
],
"wikia:platform": ["Xbox 360","PS3","PC","Wii","DS"]
}
15. Syntactic Conventions
• Keys represent unique properties
• Values may be singular or multiple
• May use strings for most value
representations, with typing information
maintained in a context.
23. In-memory linking
Flattened Form Linked Form:
{
"@id": "500f1a1b6e7f1827ba000001", @id 500f1a1b6e7f1827ba000001
"type": "wikia:VideoGame",
"schema:name": "Call of Duty: Black Ops", type wikia:VideoGame
"wikia:event": [
{"@id": "500f28856e7f187196000001"}, name "Call of Duty: Black Ops"
{"@id": "500f44556e7f18f7ef000001"}, event 500f28856e7f187196000001
{"@id": "500f449e6e7f18f94b000001"},
{"@id": "500f44e26e7f18fa75000001"}
]
},
{ @id 500f28856e7f187196000001
"@id": "500f28856e7f187196000001",
type cod:Mission
"type": "cod:Mission",
"schema:name": "Operation 40", name "Operation 40"
"schema:startDate": "1961-04-17", startDate "1961-04-17"
"wikia:eventIn": [
{"@id": "500f1a1b6e7f1827ba000001"} event 500f1a1b6e7f1827ba000001
]
}
24. Turtle Mapping
{ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
"@context": "/contexts/wikia.jsonld", @prefix schema: <http://schema.org/> .
"@id": "500f1a1b6e7f1827ba000001", @prefix wikia: <http://data.wikia.com/terms#> .
"@type": "wikia:VideoGame", @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
"contentRating": [
"ESRB: Mature (DS version is Teen)", <500f1a1b6e7f1827ba000001> a wikia:VideoGame;
"PEGI: 18+ (DS version is 16+) ", schema:contentRating "ESRB: Mature (DS version is Teen)"@en,
"USK: 18 CERO: Z OFLC: MA15+ (DS version is M)" "PEGI: 18+ (DS version is 16+) "@en,
], "USK: 18 CERO: Z OFLC: MA15+ (DS version is M)"@en;
"datePublished": "2011-11-09", schema:datePublished: "2011-11-09"^^xsd:dateTime;
"name": "Call of Duty: Black Ops", schema:name "Call of Duty: Black Ops"@en;
"publisher": [ schema:publisher <501067246e7f184553000001>,
{"@id": "501067246e7f184553000001"}, <5010675a6e7f18464d000001> .
{"@id": "5010675a6e7f18464d000001"} wikia:event <500f28856e7f187196000001>,
], ...;
"wikia:event": [ .
{"@id": "500f28856e7f187196000001"},
...
]
}
25. More JSON-LD Features*
• @set, @list, • Embedded
Compact IRIs, @context
Unlabeled Nodes definitions
• Language maps • Named Graphs
• Property generators
* http://json-ld.org/spec/latest/json-ld-syntax
26. History
• 2009 – Started as a way to project from the RDFa API
• Developers want solutions to work within HTML
applications
• Desire for JSON-idiomatic way of representing RDF
(Linked Data)
• Adapt existing JSON APIs to Linked Data
27. History
• 2011 – W3C Community Group launched
• Broad Participation
• Separation of Syntax from API
• Proposed to W3C RDF 1.1 Working
Group
28. History
• 2012 – RDF WG abandons other JSON
serialization efforts (RDF/JSON)
• JSON-LD accepted as official work item
• Community Group drafts final report
• RDF WG publishes JSON-LD (Syntax and
API) as FPWD
29. MongoDB
• JSON-like document store
• BSON supports extra datatypes
• Certain key patterns are restricted
• e.g., “.” not allowed in a key
• Query on value = pattern or value includes
pattern
31. Wikia Application
• Express Wiki content as structured data
• Wiki markup doesn’t naturally include
semantic cues (Semantic Media Wiki
aside).
• Add semantic markup to HTML generated
from Wikis in RDFa
• Aggregate structured content through
JSON-LD/MongoDB powered data service
32. Service Architecture
• MongoDB persistence
• JSON document representation
• JSON-LD describedby link header
• Ruby/Sinatra web service
• Content-negotiated access to data
• JSON-LD, HTML, Turtle, ...
33. Document Model
{
• Use aliases for @id and "id": "500f1a1b6e7f1827ba000001",
"type": "wikia:VideoGame",
@type keywords "name": "Call of Duty: Black Ops",
"publisher": [
• Simplifies use within "501067246e7f184553000001",
"5010675a6e7f18464d000001"
client-side MVC ],
"wikia:event": [
frameworks (e.g. "500f28856e7f187196000001"
backbone.js) ]
}
34. Document Model
{
"id": "500f1a1b6e7f1827ba000001",
"type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops",
• Individual subject "publisher": [
"501067246e7f184553000001",
definition as Mongo "5010675a6e7f18464d000001"
document ],
"wikia:event": [
"500f28856e7f187196000001"
]
}
35. Document Model
{
"id": "500f1a1b6e7f1827ba000001",
• Use expanded subject "type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops",
references "publisher": [
{"id": "501067246e7f184553000001"},
{"id": "5010675a6e7f18464d000001"}
➡This allows simple ],
"wikia:event": [
traversal for graphify {"id": "500f28856e7f187196000001"}
]
}
36. Document Model
{
"id": "500f1a1b6e7f1827ba000001",
•
"type": "wikia:VideoGame",
Gather subject "name": "Call of Duty: Black Ops",
"publisher": [
references under a {"id": "501067246e7f184553000001"},
hidden key {"id": "5010675a6e7f18464d000001"}
],
"wikia:event": [
➡This allows easy ],
{"id": "500f28856e7f187196000001"}
querying for objects "_references": [
referencing another "501067246e7f184553000001",
"5010675a6e7f18464d000001",
object "500f28856e7f187196000001"
]
}
37. Document Model
GET http://example.com/collection/
500f1a1b6e7f1827ba000001
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
Link: </contexts/wikia.jsonld>; rel="describedby";
type="application/ld+json"
• Return context as HTTP {
Link Header "id": "500f1a1b6e7f1827ba000001",
"type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops",
"publisher": [
{"id": "501067246e7f184553000001"},
{"id": "5010675a6e7f18464d000001"}
],
"wikia:event": [
{"id": "500f28856e7f187196000001"}
]
}
38. Restful Endpoints
GET http://example.com/collection/?
withType="schema:Corporation"
Accept: application/json
HTTP/1.1 200 OK
• index – query collection Content-Type: application/json;charset=utf-8
Link: </contexts/wikia.jsonld>; rel="describedby";
type="application/ld+json"
• by type – return [
objects having (or {
containing) a type "id": "501067246e7f184553000001",
"type": "schema:Corporation",
"name": "Activision",
"url": "http://callofduty.wikia.com/wiki/Activision"
}, {
"id": "5010675a6e7f18464d000001",
"type": "schema:Corporation",
"name": "Square Enix (Japan)"
}
]
39. Restful Endpoints
•
GET http://example.com/collection/?
index – query collection referencing="500f1a1b6e7f1827ba000001"
Accept: application/json
• by referencing – HTTP/1.1 200 OK
return objects Content-Type: application/json;charset=utf-8
Link: </contexts/wikia.jsonld>; rel="describedby";
referencing another type="application/ld+json"
object [{
"id": "500f28856e7f187196000001",
• Build out space "cod:game": {"id": "500f1a1b6e7f1827ba000001"},
"schema:name": "Operation 40",
around an object "schema:startDate": "1961-04-17T00:00:00",
"wikia:eventIn": [{"id": "500f1a1b6e7f1827ba000001"}],
"wikia:next": {"id": "500f44556e7f18f7ef000001"},
• Similar to SPARQL "wikia:objective": [
{"id": "501445266e7f1847c6000001"},
describe {"id": "50143e1e6e7f18256d000001"},
...
],
}, ...]
40. Restful Endpoints
GET http://example.com/collection/
500f1a1b6e7f1827ba000001
Accept: application/json
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
Link: </contexts/wikia.jsonld>; rel="describedby";
•
type="application/ld+json"
GET – return single
{
object "id": "500f1a1b6e7f1827ba000001",
"type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops",
"publisher": [
{"id": "501067246e7f184553000001"},
{"id": "5010675a6e7f18464d000001"}
]
}
41. Restful Endpoints
POST http://example.com/collection/
Accept: application/json
Content-Type: application/json
{
• POST – Create new "type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops"
object }
HTTP/1.1 201 Created
•
Content-Type: application/json;charset=utf-8
Good practice to Link: </contexts/wikia.jsonld>; rel="describedby";
type="application/ld+json"
maintain dateCreated Location: http://example.com/collection/
and dateModified 500f1a1b6e7f1827ba000001
equivalents {
"id": "500f1a1b6e7f1827ba000001",
"type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops",
"dateCreated": "2012-08-23T14:00:00-08:00",
"dateModified": "2012-08-23T14:00:00-08:00",
}
42. Restful Endpoints
PUT http://example.com/collection/
500f1a1b6e7f1827ba000001
Accept: application/json
Content-Type: application/json
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
• PUT – Update object Link: </contexts/wikia.jsonld>; rel="describedby";
type="application/ld+json"
• Good practice to {
"id": "500f1a1b6e7f1827ba000001",
update dateModified "type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops",
equivalent "publisher": [
{"id": "501067246e7f184553000001"},
{"id": "5010675a6e7f18464d000001"}
],
"dateCreated": "2012-08-23T14:00:00-08:00",
"dateModified": "2012-08-23T14:15:00-08:00",
}
44. Restful Endpoints
GET http://example.com/collection/
500f1a1b6e7f1827ba000001/publisher/
Accept: application/json
HTTP/1.1 200 OK
• path – relational query Content-Type: application/json;charset=utf-8
Link: </contexts/wikia.jsonld>; rel="describedby";
type="application/ld+json"
• Return objects having [
a property {
"id": "501067246e7f184553000001",
relationship with "type": "schema:Corporation",
another object "name": "Activision",
"url": "http://callofduty.wikia.com/wiki/
Activision"
}, {
"id": "5010675a6e7f18464d000001",
"type": "schema:Corporation",
"name": "Square Enix (Japan)"
}
]
45. Restful Endpoints
GET http://example.com/collection/
500f1a1b6e7f1827ba000001
Accept: application/ld+json
HTTP/1.1 200 OK
Content-Type: application/ld+json;charset=utf-8
• GET – as explicit JSON- {
"@context": "/contexts/wikia.jsonld",
LD "id": "500f1a1b6e7f1827ba000001",
"type": "wikia:VideoGame",
"name": "Call of Duty: Black Ops",
"publisher": [
{"id": "501067246e7f184553000001"},
{"id": "5010675a6e7f18464d000001"}
]
}
46. Working with data
• Application Structure
• Ruby/Sinatra REST
service
• JavaScript/Backbone.js
client
{
• Alias JSON-LD "@context": {
"id": "@id",
keywords for
"type": "@type"
convenience
}
}
47. MVC Client
• Natural access from client-side MVC (e.g.,
backbone.js)
Collection
// Object collection, handles objects constructed from OWL classes and properties.
// Initial fetch is only for VideoGame types, as referenced objects are
// fetched, they are added to the collection.
Wikia.ObjectCollection = Backbone.Collection.extend({
initialize: function() { this.model = Wikia.ObjectModel; },
url: function() { return "/api/#{@vers}/#{@name}"; },
comparator: function(model) { return model.get('schema:name') }
...
}
48. MVC Client
• Natural access from client-side MVC (e.g.,
backbone.js)
Model
// OWL model class and sub-classes for dealing with owl:Class,
// owl:DatatypeProperty, owl:ObjectProperty and owl:Datatype
Wikia.ObjectModel Backbone.Model.extend({
referencedObjects: function() { return this.resolve(_.flatten(_.toArray(this.attributes))); },
getModel: function(key, options) { return this._resolve this.get(key)) },
setModel: function(key, value) {...},
_resolve: function(value) {...},
...
}
49. Summary
• JSON-LD is a light-weight layer for representing
linked data in JSON
• The MongoDB document model is a natural fit for
JSON-LD
• Not optimized for SPARQL; consider alternatives
• Single Page Applications benefit from linked data
principles
• RDF with OWL can be powerful when used
appropriately in the client
51. More Information
json-ld.org JavaScript
Ruby
rdf-comments@w3.org Python
PHP
Java
Gregg Kellogg
gregg@greggkellogg.net
http://greggkellogg.net/
@gkellogg
Editor's Notes
\n
\n
\n
JSON-LD brings a standard representation for expressing entity-value relationships using a few standard keywords and a consistent organizational structure for JSON Objects.\nObjects represent entities, with keys acting as properties.\n Properties always expand to full IRIs.\nArrays express a set of values associated with a property, unordered by default.\n Order expressed in @context or as an expanded value representation.\nValues are Object, string or native, with standard XSD representations for native types.\n Expanded form allows for more datatype and language variations.\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
It&#x2019;s important to note that JSON-LD is a product of the W3C RDF Working group, and is as much &#x201C;RDF&#x201D; as any other serialization format.\nIn this case, we show a transformation from the JSON-LD format of a resource description to Turtle, an RDF format specifically intended to be humanly readable.\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
Subject reference identifies an object with @id in the same document, or references an external document (Linked Data).\n
\n
\n
What I've outlined is not really optimized for SPARQL. For performance, you're much better at storing triples as documents, then full-on subject definitions.\n\nThis application is more tuned for returning collections of subject definitions based on a fairly narrow query bounds (resources relevant to a particular point in game play) then general querying. In fact, I've considered that an evolution of the application I'm working on may well go to a generic SPARQL store, with good JSON-LD support, such as Stardog.\n\n