Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

buckless/requelize

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

40 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Requelize

Installation

yarn add requelize joi

joi is a peer dependency to create schemas

Configuration

const requelize = require('requelize')({ host: 'localhost', db: 'myApp' })

Options are passed to rethinkdbdash options

Debugging

requelize uses debug to debug your apps. You can use the DEBUG environment variable as follows:

DEBUG=requelize:* yarn start

Model definition

const joi = require('joi')

const User = requelize.model('User', {
  email: joi.string()
  firstname: joi.string(),
  lastname: joi.string()
})

User.index('email')

You might also pass a third argument to requelize.model that looks like this: { primaryKey: string } Primary keys allowed are: guid and integers

Querying

requelize.sync()
  .then(() => User.getAll('[email protected]', { index: 'email' }))
  .then((users) => {
    console.log(users)
  })

First you need to sync database with requelize.sync. To query documents, you can use any rql commands. User is a sugar syntax for r.db('...').table('User').

Create / Update / Delete

You can instanciate models to create documents

const user = new User({
  email: '[email protected]',
  firstname: 'John',
  lastname: 'Doe'
})

user
  .save()
  .then(() => console.log(user.id))

You can update users with subsequent save calls

user.email = '[email protected]'

user.save()

You can delete documents with the delete model method.

user
  .delete()
  .then(() => assert.equal(null, user.id))

Relationships

hasOne

User.hasOne('Role', 'role', 'User_id')

Arguments are:

  1. Relation Model name (the first argument of requelize.model)
  2. The virutal field on the instance (so that you have user.role.id)
  3. The foreign key to use. requelize defaults to SelfModel_id

hasMany

User.hasMany('Post', 'posts', 'User_id')

Arguments are:

  1. Relation Model name (the first argument of requelize.model)
  2. The virutal field on the instance (so that you have user.posts[0].id)
  3. The foreign key to use. requelize defaults to SelfModel_id

The foreign key have to be equal to the local key used on belongsTo method

belongsTo

Role.belongsTo('User', 'user', 'User_id')
Post.belongsTo('User', 'user', 'User_id')

Arguments are:

  1. Relation Model name (the first argument of requelize.model)
  2. The virutal field on the instance (so that you have role.user.id or post.user.id)
  3. The local key to use (usually: model_id or modelId or Model_id). requelize defaults to Model_id

belongsToMany

User.belongsToMany('Right', 'rights')
Right.belongsToMany('User', 'users')

Arguments are:

  1. Relation Model name (the first argument of requelize.model)
  2. The virutal field on the instance (so that you have right.users[0].id or user.rights[0].id)
  3. An optional join table name. Default is ModelA_ModelB with ModelA and ModelB being sorted

embed and saveAll

To query an instance with its relationships, you can use embed:

User
  .get('someId')
  .embed({
    role: true,
    rights: true,
    group: {
      admin: true
    }
  })
  .then((user) => {
    console.log(user.role.id)
    assert.equal(true, user.role instanceof Role)
  })

You may have nested relations with sub object (as in group: { admin: true })

To save an instance with its relationships, you can use saveAll:

user.role = roleA
user.rights = [ rightA, rightB ]

user.saveAll({
  role: true,
  rights: true
})

You can also save an array of ids with hasMany or belongsToMany:

user.role = roleA
user.rights = [ rightA.id, rightB.id ]

user.saveAll({
  role: true,
  rights: true
})

Note: be careful that user.rights will be repopulated with rightA and rightB, but those objects will not be the same as the original rightA and rightB. That means rightA.User_id is undefined (because saveAll knows nothing about it), but user.rights[0].User_id will be set.

Tree has the same possibilities that you have in embed

pivot data

If you want to save pivot data (data you store in a belongsToMany relationship), use setPivot and getPivot

user.rights = [ rightA, rightB ]

rightA.setPivot('User', { period: 'infinity' })
rightB.setPivot('User', { period: 'this month' })

user.saveAll({ rights: true })

The data will be stored in the joint table. It is shared between the user instance and the right instance

Right
  .get('rightAId')
  .embed({ users: true })
  .then((rightA) => {
    console.log(rightA.users[0].getPivot()) // should be equal
  })

User
  .get('userId')
  .embed({ rights: true })
  .then((user) => {
    console.log(user.rights[0].getPivot()) // should be equal
  })

Note: a race condition exists here because of users[0] and rights[0]. A proper way to do it would be to use: rights.find(right => right.id === rightA.id)

Custom joins tables - three-way relationships

You can provide your own model to join tables:

User = requelize.model('User', { name: Joi.string() })
Role = requelize.model('Role', { name: Joi.string() })
Period = requelize.model('Period', { name: Joi.string() })
UserRole = requelize.model('UserRole')

// This is important (creates indexes for relation fields)
UserRole.customJoinTable('User', 'Role')

Period.hasMany('UserRole', 'userroles')
UserRole.belongsTo('Period', 'period')

User.belongsToMany('Role', 'roles', 'UserRole')
Role.belongsToMany('User', 'users', 'UserRole')

To save a three-way document:

user = new User({ name: 'John Doe' })
role = new Role({ name: 'Admin' })
period = new Period({ name: 'Infinity' })
userRole = new UserRole()

user.roles = [
  {
    // this is the related document
    document: role,
    // this is the join document
    through: userRole,
    // this will be used when saving the join document
    saveAll: { period: true }
  }
]

userRole.period = period

return user.saveAll({
  roles: true
})

To retrieve a three-way document:

User
  .embed({ roles: { _through: { period: true } } })
  .nth(0)
  .then((res) => {
    console.log(res.roles[0]._through.period)
  })

Changefeeds

requelize provide a changefeed structure with the library rxjs and Observables. You are free to use original cursors if you want (by using changes((err, cursor) => { ... }) and not .feed())

let feed = User.feed()

feed
  .filter(event => event.type === 'create')
  .subscribe(
    (event) => console.log(event),
    (err) => console.error(err)
  )

event is a simple object that contains:

  • type (string): 'create', 'update' or 'delete'
  • from (Object): the original value (null when event.type === 'create')
  • to (Object): the target value (null when event.type === 'delete')

You should unsubscribe when you do not have any use of it by using the custom Observable method close():

let feed = User.feed()

let subscription = feed
  .filter(event => event.type === 'create')
  .subscribe(
    (event) => console.log(event),
    (err) => console.error(err)
  )

somePromise()
  .then(() => {
    feed.close()
    subscription.unsubscribe()
  })

Note: in the example above, the subscription is also closed

Hooks

A few hooks are available in requelize: validating, validated, saving, saved, creating, created, updating and updated.

User.on('saving', (user) => {
  user.editedAt = new Date()
})

User.on('creating', (user) => {
  user.createdAt = new Date()
})

Hooks also support promises

User.on('saving', (user) => {
  return somePromise()
    .then(() => {
      user.foo = 'bar'
    })
})

Parse and validation

If you plan to use queries that do not resolve to a Model instance, but starts from a model, use parse() :

User
  .parse(false)
  .getAll()
  .map((user) => 1)
  .reduce((a, b) => a.add(b))
  .run()
  .then((res) => {
    assert.equal('number', typeof res)
  })

Note: you need to call run()

You can also disable validation when saving a document. Documents are only validated at insert, not at retrieval

user.someInvalidField = 'foo'

user.validate(false).save()

Virtuals

If you need virtual fields, you can use virtual method from model:

const User = requelize.model('User', {
  email: joi.string()
  firstname: joi.string(),
  lastname: joi.string()
})

User.virtual('fullname', user => `${user.firstname} ${user.lastname}`)

let user = new User({ firstname: 'John', lastname: 'Doe' })

assert.equal('John Doe', user.fullname)

Access to requelize

If you need r for any use (r.row, etc.) you can find it under requelize: requelize.r