Using relationships
What are relations in Cozy-Client?¶
CouchDB is a document store. It does not materialize relations between documents. We do, however, have a standardized way of describing relations between cozy documents. This allows Cozy-Client to give us some automation.
In CouchDB¶
Relations between documents are materialized under a relationships
object at the root of the document. Relations are referenced by their names, e.g. authors
.
Each relation is an object with a data
property, containing either one reference, or an array of references.
A reference is an object containing at least:
* _type
: the name of the referenced doctype, e.g. io.cozy.contacts
* _id
: the id of the referenced document.
For instance, a book -> authors relationship might be represented like this:
{ "_id": "mobydick", "relationships": { "authors": { "data": [{ "_id": "hermanmelville", "_type": "io.cozy.contacts" }] } } }
Please see the cozy-doctypes documentation for more insights about the syntax.
In the Cozy-Client schema¶
Cozy-client knows how to handle these relations thanks to the schema you provide at initialization:
const schema = { books: { doctype: 'io.cozy.books', attributes: {}, relationships: { authors: { doctype: 'io.cozy.contacts', type: 'has-many' } } } } const client = new CozyClient({ uri: 'http://cozy.tools:8080', token: '...', schema })
We explain below what are the different types of relations.
Relation types¶
Cozy-Client predefines two basic relation types that must be specified in the schema:
'has-one'
: the relation has a unique reference.'has-many'
: the relation can have several references.
Files relations¶
The files implements a special type of relation: 'io.cozy.files:has-many'
. This relation name is referenced_by
, which is an array of references, just like the 'has-many'
relation.
The stack implements routes to handle this kind of relation on the /files
endpoint. See the stack documentation.
Note specific view index are defined on referenced_by
relationships, allowing fast queries on it.
Customize relations¶
You are free to create your own relation type if you need a special behaviour.
For instance, Banks doctypes implements HasManyBills
or HasManyReimbursements
.
Old relation types¶
Note there are two others basic relations, that are here for backward compatibility:
'has-one-in-place'
'has-many-in-place'
⚠️ For new doctypes, you should not use those relations, prefer 'has-one'
or 'has-many'
.
With these relations, instead of using the relationships
attribute, you reference directly the ids of linked documents at the root of your data:
const schema = { books: { doctype: 'io.cozy.books', attributes: {}, relationships: { authors: { doctype: 'io.cozy.contacts', type: 'has-many-in-place' } } } }
const book = { "_id": "mobydick", "authors": [ "hermanmelville" ] }
Metadata¶
It is possible to assign metadatas to relationships. You need to modify the content of the document relationships somehow and save the modified document.
const doc = { _id: "mobydick", relationships: { authors: { data: [{ _id: "hermanmelville", _type: "io.cozy.contacts" }] } } } doc.relationships.authors.data[0].metadata = { addressId: "123" } await client.save(doc)
Usage¶
Include relations in your query¶
Relations are not loaded eagerly by default. If you want your query to load your relations for you, you will have to name them in an include()
request.
const query = Q('io.cozy.books') .include(['authors']) .limitBy(20)
You will then find your relations under the data
attribute:
const response = await client.query(query) const docs = response.data const firstDoc = docs[0] const firstAuthors = firstDoc.authors.data
Add a relation to an existing document¶
When the relationship is a has-many
type, you can call the add(docs)
to create the relationship:
const otherAuthors = [{_id: 'Rivest'}, {_id: 'Shamir'}] const response = await client.query(query) const docs = response.data const firstDoc = docs[0] firstDoc.authors.add(otherAuthors)
add
also accepts single document:
firstDoc.authors.add({_id: 'Adleman'})
Likewise, when the relationship is a has-one
, use add(doc)
:
const printer = { _id: 'abc123', _type: 'io.cozy.company', name: 'Harper & Brothers' } const response = await client.query(query) const docs = response.data const firstDoc = docs[0] firstDoc.printingCompany.add(printer)
Remove a relation to an existing document¶
For has-many
relationships, use the remove(docs)
method:
const wrongAuthors = [{_id: 'James Wrong' }, {_id: 'Henry Mistake'}] const response = await client.query(query) const docs = response.data const firstDoc = docs[0] firstDoc.authors.remove(wrongAuthors)
Just like add
, remove
accepts a single document.
For has-one
relationships, just use remove()
, with no argument:
const response = await client.query(query) const docs = response.data const firstDoc = docs[0] firstDoc.printingCompany.remove()
Create a new file with existing relations¶
Simply pass the reference to your file as a third parameter to create()
:
const photo = { _id: "sunset.jpg" } const reference = { _id: "1324", _type: "io.cozy.photos.albums" } const albumReference = { albums: [ albumReference ] } await client.create('io.cozy.files', photo, albumReference)