Relationships
Introduction
In MongoDB, documents can have relationships with other documents (see MongoDB Relationships documentation section).
RESTHeart allows to declare the existing relationships in a collection, so that it automatically adds the links to related documents in its representation.
As discussed in the Representation Format section, RESTHeart uses the HAL+json hypermedia format. HAL builds up on 2 simple concepts: Resources and Links
- Resources have state (plain JSON), embedded resources and links
- Links have target (href URI) and relations (aka
rel
)
Let’s see the following simple example, a GET on a document resource:
$ http -a a:a GET 127.0.0.1:8080/test/coll/doc
HTTP/1.1 200 OK
...
{
"_etag": {
"$oid": "55e84f5ac2e66d1e0a8e46b8"
},
"_id": "doc",
"descr": "a document for testing but modified"
}
Declaring a relationship will add to the _links
property the related
documents links.
The rels collection metadata
In RESTHeart, not only documents but also dbs and collections have properties. Some properties are metadata, i.e. they have a special meaning for RESTheart that influences its behavior.
The collection metadata property rels
allows to declare the existing
relationship so that the representation of the collection’s documents
auto-magically include the links to the referenced documents between the
HAL _link
property
rels
is an array of rel
objects having the following format:
{
"rel": "<relid>",
"type": "<type>",
"role": "<role>",
"target-db": "<dname>",
"target-coll": "<collname>",
"ref-field": "<reffieldname>"
}
Property
|
Description
|
Mandatory
|
---|---|---|
rel |
Name of the relationship. |
Yes |
type |
Type of relationship: ONE_TO_ONE, MANY_TO_ONE, ONE_TO_MANY or MANY_TO_MANY | Yes |
role |
Role of the relationship: OWNING or INVERSE. This says where the reference field is. OWNING means it is in this document, INVERSE means that it is on the target document. | Yes |
target-db |
Database name of the referenced document(s) | No. If omitted the target db is supposed to be the current one |
target-coll |
Collection's name of the referenced document(s) | Yes |
ref-field |
Name of the first level field storing the id(s) of the referenced document(s) or the json path expression that resolves to the ids of the referenced documents (to reference nested fields). The value of the first level field must be either the id string (in case the multiplicity of other side of the relationship is ONE) or an array of id strings (in case the multiplicity of other side of the relationship is MANY) Note: json path expression must start with
|
Yes |
Examples
Tree of documents
In this example, we create a collection to store documents organized in a tree (each document has a single parent document).
Let’s create a collection, declaring a many-to-one relationship
called parent
, so that documents can refer a parent document in
the collection itself.
$ http -a a:a PUT 127.0.0.1:8080/test/parentcoll rels:='[{"rel":"parent","type":"MANY_TO_ONE","role":"OWNING","target-coll":"parentcoll","ref-field":"parent"}]'
HTTP/1.1 201 CREATED
...
Let’s now create few documents, specifying the parent
property:
$ http -a a:a PUT 127.0.0.1:8080/test/parentcoll/root parent=root
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/parentcoll/1 parent=root
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/parentcoll/1.1 parent=1
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/parentcoll/1.2 parent=1
HTTP/1.1 201 CREATED
...
If we now get the document /test/parentcoll/1.2
, the _links
property
includes parent
with the correct URI of the
document /test/parentcoll/1
$ http -a a:a GET 127.0.0.1:8080/test/parentcoll/1.2
HTTP/1.1 200 OK
...
{
"_etag": {
"$oid": "55f15b43c2e65448b566d18b"
},
"_id": "1.2",
"_lastupdated_on": "2015-09-10T10:28:19Z",
"_links": {
"curies": [],
"self": {
"href": "/test/parentcoll/1.2"
}
},
"parent": "1"
}
One-to-Many, owning
In this example, we create two collections: bands
and albums
; of
course, each band has a 1:N relationship to albums.
$ http -a a:a PUT 127.0.0.1:8080/test/bands rels:='[{"rel":"albums","type":"ONE_TO_MANY","role":"OWNING","target-coll":"albums","ref-field":"albums"}]' descr="music bands"
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/albums descr="albums published by music bands"
HTTP/1.1 201 CREATED
...
Let’s now create few albums:
$ http -a a:a PUT 127.0.0.1:8080/test/albums/Disintegration year:=1989
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/albums/Wish year:=1992
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/albums/Bloodflowers year:=2000
HTTP/1.1 201 CREATED
...
Now we create the band referring these albums:
$ http -a a:a PUT "127.0.0.1:8080/test/bands/The Cure" albums:='["Disintegration","Wish","Bloodflowers"]'
HTTP/1.1 201 CREATED
...
If we now get The Cure document, we can notice the albums
link:
/test/albums?filter={'_id':{'$in':['Disintegration','Wish','Bloodflowers']}}"
Since the other side of the relationship has cardinality N, the albums
link is a collection resource URI with a filter query parameter.
$ http -a a:a GET "127.0.0.1:8080/test/bands/The Cure"
HTTP/1.1 200 OK
...
"_embedded": {},
"_etag": {
"$oid": "55f16029c2e65448b566d191"
},
"_id": "The Cure",
"_lastupdated_on": "2015-09-10T10:49:13Z",
"_links": {
"albums": {
"href": "/test/albums?filter={'_id':{'$in':['Disintegration','Wish','Bloodflowers']}}"
},
"curies": [],
"self": {
"href": "/test/bands/The Cure"
}
},
"albums": [
"Disintegration",
"Three Imaginary Boys",
"Seventeen Seconds"
]
}
If we want to get the referenced document with httpie (or curl) we need to issue the following command:
http -a a:a GET 127.0.0.1:8080/test/albums?filter="{'_id':{'\$in':['Disintegration','Wish','Bloodflowers']}}"
Note the “\” char prefixing the operator $in
. This prevents the
command line interpreter replacing $in
with an (not existing)
environment variable.
One-to-Many, inverse
We’ll resemble the previous example, but using an inverse relationship, i.e. the filed storing the relationship will be stored in the album documents.
$ http -a a:a PUT 127.0.0.1:8080/test/bandsi rels:='[{"rel":"albums","type":"ONE_TO_MANY","role":"INVERSE","target-coll":"albums","ref-field":"band"}]' descr="music bands"
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/albumsi descr="albums published by music bands"
HTTP/1.1 201 CREATED
...
Let’s now create few albums:
$ http -a a:a PUT 127.0.0.1:8080/test/albumsi/Disintegration year:=1989 band="The Cure"
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/albumsi/Wish year:=1992 band="The Cure"
HTTP/1.1 201 CREATED
...
$ http -a a:a PUT 127.0.0.1:8080/test/albumsi/Bloodflowers year:=2000 band="The Cure"
HTTP/1.1 201 CREATED
...
Now we create the band referred by these albums:
$ http -a a:a PUT "127.0.0.1:8080/test/bandsi/The Cure" descr="The Cure are an English rock band formed in Crawley, West Sussex, in 1976"
HTTP/1.1 201 CREATED
...
If we now get “The Cure” document, we can notice the albums
link:
/test/albumsi?filter={'band':'The Cure'}
$ http -a a:a GET "127.0.0.1:8080/test/bandsi/The Cure"
HTTP/1.1 200 OK
...
{
"_etag": {
"$oid": "55f19409b8e449c1e1304ec5"
},
"_id": "The Cure",
"_links": {
"albums": {
"href": "/test/albums?filter={'band':'The Cure'}"
},
"curies": [],
"self": {
"href": "/test/bandsi/The Cure"
}
},
"descr": "The Cure are an English rock band formed in Crawley, West Sussex, in 1976"
}