Thinky is a lightweight Node.js ORM for RethinkDB. The library helps you to define models representing the objects you’re using within your code. Further, you can define various relations between each of your models. Primarily, Thinky helps you to CRUD documents in RethinkDB out of your Node.js application.
This guide will show you how to query documents by fields of nested objects. We’ve published further Thinky posts, there might be something interesting for you:
Thinky Series Overview
- Case Insensitive Sorting with RethinkDB and Thinky ORM
- Understanding Virtuals
- How to Query Document(s) by Field of Nested Objects
- How to Add Methods to a Model and Documents
- How to Sort Data in Relations
- How to Join on Foreign Key of Already Joined Table
Query Documents by Field of Nested Object
Let’s set up the context for the practical part. Assume that you’re saving user information and there’s a nested object for the user’s address
that contains the street
, zip
code and city
name. Your User
model will look like this:
var User = thinky.createModel("User", {
id: type.string().default(r.uuid()),
username: type.string(),
password: type.number(),
address: {
street: type.string(),
zip: type.number(),
city: type.string()
}
})
In the following, you’ll compose a query to filter documents on the city
field of the nested address
object. Use Thinky’s filter()
method to query the database table of Users
and further specify a filter to only find users where the related city name matches the given query value.
RethinkDB’s filter(predicate)
method requires a predicate that evaluates to a true or false statement. Only objects in thesequence with a true
predicate are part of the final result. The result of filter()
is an array of values. If nothing was found or matched your data, the result array is empty.
You have three options to be applied using the filter()
method which are shown in the sections below.
Option 1 — filter()
With an Object
The filter(predicate)
method allows you to specify an object that matches the selected keys
of your model and the appropriate values
.
var _ = require('lodash')
var when = require('when')
var User = require('./models/user')
var findUsersByCity = function (city) {
// filter users by city name (in nested address object)
return User.filter({
address: {
city: city
}
}).run()
}
As you can see, you need to pass a nested object to the filter()
method, like filter({ address: { city: 'Magdeburg' } })
, to fetch only users that match your comparison value (predicate).
Option 2 — filter()
With a Function
Besides an object with key-value-pairs that restrict the result, you can pass a callback function that returns the individual documents and you need to apply the actual filter.
var _ = require('lodash')
var when = require('when')
var User = require('./models/user')
var findUsersByCity = function (city) {
// filter users by city name (in nested address object)
return User.filter(function (user) {
return user('address')('city').eq(city)
}).run()
}
The callback returns the user
documents and you need to access the city
field by selecting it like this: user('address')('city')
. Afterwards, you need to compare the given value with your restriction so that the result is either true or false. Remember: a predicate is required.
Option 3 — filter()
With Document Reference
There’s a third option: the document reference using RethinkDB’s row
command. The row
command returns the currently visited document and you can further apply your desired filters.
var _ = require('lodash')
var when = require('when')
var User = require('./models/user')
var findUsersByCity = function (city) {
// filter users by city name (in nested address object)
return User.filter(
r.row('address')('city').eq(city)
).run()
}
This option is kind of close to the show solution above using a callback function. Anyway, it’s a convenient way to have more complex filters that just the { key: value }
-predicate object (presented as option 1).
A Complete Example
The following code snippet presents a more complete example by also evaluating the result set of the query.
var _ = require('lodash')
var when = require('when')
var User = require('./models/user')
var findUsersByCity = function (city) {
// filter users by city name (in nested address object)
return User.filter({
address: {
city: city
}
}).run().then(function (users) {
if (_.isEmpty(users)) {
// nothing found
return when.reject('No user found for city: ' + city)
}
// return or perform desired operations on document(s)
// we just return in a resolving Promise :)
return when.resolve(users)
})
}
As you can see, within the code snippet above you can just to pass a nested object to the filter()
method, like filter({ address: { city: city } })
and there’s no need for a more complex design to fetch only users that match your comparison value. The result of filter()
is an array of values that match the given criteria.
Outlook
You’ve learned how to query documents by fields of nested objects using Thinky. Next week, we’ll continue on Thinky and show you how to add custom methods to your documents by extending the foundational model.
If you’ve questions or ideas on how to improve this post, leave them in the comments below or shout out @futurestud_io.
Make it rock & enjoy coding!