-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Webcom's backend implements a RethinkDB ODM. In order to accomplish that, the codebase implements some complex RethinkDB queries that are worth mentioning here. This particular ODM, by default, does not delete entities. It archives them. This can be configurable one day.
Objects stored in RethinkDB are flattened objects with each key being either a relationship
or attribute
. For example, the following is how a video
might be stored.
{
name: 'Jurassic Park',
year: 1993,
director: {
id: '20',
archived: false,
},
customers: [
{
id: '425',
archived: false,
},
{
id: '247',
archived: true,
},
],
}
From the above structure, we have immediate insight regarding what the attributes are, what types of relationships are which (i.e. belongsTo
or hasMany
) and which relationships are currently pointing to archived entities.
The ODM archives hasMany
relationships. This is so the client can easily filter out the 'deleted/archived' records.
// example RethinkDB query
r.table('enterprise').get('100').update(row => ({
'listings': row('listings').map(data => (
r.branch(data('id').eq('1111'),
{
id: data('id'),
archived: true,
}, {
id: data('id'),
archived: data('archived'),
})
)),
}))
// relationship before archive
{
id: '100',
name: 'Company 1',
meta: {
archived: true,
},
relationships: {
listings: [
{
id: '1111',
archived: false,
},
{
id: '2222',
archived: false,
},
],
...
},
}
// relationship after archive
{
id: '100',
name: 'Company 1',
meta: {
archived: true,
},
relationships: {
listings: [
{
id: '1111',
archived: true,
},
{
id: '2222',
archived: false,
},
],
...
},
}
The ODM archives belongsTo
or hasOne
relationships. This is so the client can know to filter out the 'deleted/archived' record.
// example RethinkDB query
r.table('listing').get('1111').update({
'company': {
id: '100'
archived: true,
},
})
// relationship before archive
{
id: '1111',
name: 'Listing 1',
meta: {
archived: true,
},
relationships: {
company: {
id: '100',
archived: false,
},
...
},
}
// relationship after archive
{
id: '1111',
name: 'Listing 1',
meta: {
archived: true,
},
relationships: {
company: {
id: '100',
archived: true,
},
...
},
}
The ODM merges relationships when sending data back to the client. This is so that the client can easily normalize entity data.
r.table('users').get(1)
.merge(function(user) {
return r({}).merge(r.args([
r.branch(user.hasFields('pets'), {
pets: r.table('animals').getAll(r.args(user('pets').map(function(pet) { return pet('id') }))
.filter(function(pet) { return r.not(pet('meta')('archived')) })
.coerceTo('array'),
}, {}),
r.branch(user.hasFields('company').and(user('company')('archived')), {
company: r.table('companies').get(user('company')('id'))
}, {})
]))
})
Assume that some entity has a relationship array with ids ['6', '7', '8']
. The client sends a PATCH
with the ids ['5', '7']
. We need to determine which ids need to be appended and which ids need to be spliced:
r(['6', '7', '8']).setUnion(['5', '7']).difference(r(['6', '7', '8'])); // ADD [5]
r(['6', '7', '8']).setUnion(['5', '7']).difference(r(['5', '7'])); // DELETE [6, 8]