MongoDB
Support for MongoDB is provided in Feathers via the
@feathersjs/mongodb
database adapter which uses the
MongoDB Client for Node.js
. The adapter uses the
MongoDB Aggregation Framework
, internally, and enables using Feathers' friendly syntax with the full power of
aggregation operators
. The adapter automatically uses the
MongoDB Query API
when you need features like
Collation
.
tip
The MongoDB adapter implements the common database adapter API and querying syntax .
API
MongoDBService(options)
new MongoDBService(options)
returns a new service instance initialized with the given options. The following example extends the
MongoDBService
and then uses the
mongodbClient
from the app configuration and provides it to the
Model
option, which is passed to the new
MessagesService
.
Here's an overview of the
options
object:
Options
MongoDB adapter specific options are:
-
Model {Promise<MongoDBCollection>}
( required ) - A Promise that resolves with the MongoDB collection instance. This can also be the return value of anasync
function withoutawait
-
disableObjectify {boolean}
( optional , defaultfalse
) - This will disable conversion of the id field to a MongoDB ObjectID if you want to e.g. use normal strings -
useEstimatedDocumentCount {boolean}
( optional , defaultfalse
) - Iftrue
document counting will rely onestimatedDocumentCount
instead ofcountDocuments
The common API options are:
-
id {string}
( optional , default:'_id'
) - The name of the id field property. By design, MongoDB will always add an_id
property. -
id {string}
( optional ) - The name of the id field property (usually set by default toid
or_id
). -
paginate {Object}
( optional ) - A pagination object containing adefault
andmax
page size -
multi {string[]|boolean}
( optional , default:false
) - Allowcreate
with arrays andpatch
andremove
with idnull
to change multiple items. Can betrue
for all methods or an array of allowed methods (e.g.[ 'remove', 'create' ]
)
There are additionally several legacy options in the common API options
getModel()
getModel([params])
returns a Promise that resolves with the MongoDB collection object. The optional
params
is the service parameters which may allow to override the collection via
params.adapter
.
aggregateRaw(params)
The
find
method has been split into separate utilities for converting params into different types of MongoDB requests. By default, requests are processed by this method and are run through the MongoDB Aggregation Pipeline. This method returns a raw MongoDB Cursor object, which can be used to perform custom pagination or in custom server scripts, if desired.
findRaw(params)
findRaw(params)
is used when
params.mongodb
is set to retrieve data using
params.mongodb
as the
FindOptions
object. This method returns a raw MongoDB Cursor object, which can be used to perform custom pagination or in custom server scripts, if desired.
makeFeathersPipeline(params)
makeFeathersPipeline(params)
takes a set of Feathers params and converts them to a pipeline array, ready to pass to
collection.aggregate
. This utility comprises the bulk of the
aggregateRaw
functionality, but does not use
params.pipeline
.
Custom Params
The
@feathersjs/mongodb
adapter utilizes two custom params which control adapter-specific features:
params.pipeline
and
params.mongodb
.
params.adapter
Allows to dynamically set the
adapter options
(like the
Model
collection) for a service method call.
params.pipeline
Used for aggregation pipelines .
params.mongodb
When making a
service method
call,
params
can contain an
mongodb
property (for example,
{upsert: true}
) which allows modifying the options used to run the MongoDB query. The adapter will use the
collection.find
method and not the
aggregation pipeline
when you use
params.mongodb
.
Transactions
MongoDB Transactions
can be used by passing a
session
in
params.mongodb
. For example in a
hook
:
Indexes
Indexes and unique constraints can be added to the
Model
Promise, usually in the
getOptions
in
<service>.class
:
info
Note that creating indexes for an existing collection with many entries should be done as a separate operation instead. See the MongoDB createIndex documentation for more information.
Querying
Additionally to the
common querying mechanism
this adapter also supports
MongoDB's query syntax
and the
update
method also supports MongoDB
update operators
.
Search
Important
Note that in a normal application all MongoDB specific operators have to explicitly be added to the TypeBox query schema or JSON query schema .
There are two ways to perform search queries with MongoDB:
-
Perform basic Regular Expression matches using the
$regex
filter. -
Perform full-text search using the
$search
filter.
Basic Regex Search
You can perform basic search using regular expressions with the
$regex
operator. Here's an example query.
Full-Text Search
See the MongoDB documentation for instructions on performing full-text search using the
$search
operator:
- Perform full-text queries on self-hosted MongoDB .
- Perform full-text queries on MongoDB Atlas (MongoDB's first-party hosted database).
- Perform full-text queries with the MongoDB Pipeline
Aggregation Pipeline
In Feathers v5 Dove, we added support for the full power of MongoDB's Aggregation Framework and blends it seamlessly with the familiar Feathers Query syntax. All
find
queries now use the Aggregation Framework, by default.
The Aggregation Framework is accessed through the mongoClient's
collection.aggregate
method, which accepts an array of "stages". Each stage contains an operator which describes an operation to apply to the previous step's data. Each stage applies the operation to the results of the previous step. It’s now possible to perform any of the
Aggregation Stages
like
$lookup
and
$unwind
, integration with the normal Feathers queries.
Here's how it works with the operators that match the Feathers Query syntax. Let's convert the following Feathers query:
The above query looks like this when converted to aggregation pipeline stages:
Pipeline Queries
You can use the
params.pipeline
array to append additional stages to the query. This next example uses the
$lookup
operator together with the
$unwind
operator to populate a
user
attribute onto each message based on the message's
userId
property.
Aggregation Stages
In the example, above, the
query
is added to the pipeline, first. Then additional stages are added in the
pipeline
option:
-
The
$lookup
stage creates an array calleduser
which contains any matches inmessage.userId
, so ifuserId
were an array of ids, any matches would be in theusers
array. However, in this example, theuserId
is a single id, so... -
The
$unwind
stage turns the array into a singleuser
object.
The above is like doing a join, but without the data transforming overhead like you'd get with an SQL JOIN. If you have properly applied index to your MongoDB collections, the operation will typically execute extremely fast for a reasonable amount of data.
A couple of other notable query stages:
-
$graphLookup
lets you recursively pull in a tree of data from a single collection. -
$search
lets you do full-text search on fields
All stages of the pipeline happen directly on the MongoDB server.
Read through the full list of supported stages in the MongoDB documentation .
The
$feathers
Stage
The previous section showed how to append stages to a query using
params.pipeline
. Well,
params.pipeline
also supports a custom
$feathers
operator/stage which allows you to specify exactly where in the pipeline the Feathers Query gets injected.
Example: Proxy Permissions
Imagine a scenario where you want to query the
pages
a user can edit by referencing a
permissions
collection to find out which pages the user can actually edit. Each record in the
permissions
record has a
userId
and a
pageId
. So we need to find and return only the pages to which the user has access by calling
GET /pages
from the client.
We could put the following query in a hook to pull the correct
pages
from the database in a single query THROUGH the permissions collection. Remember, the request is coming in on the
pages
service, but we're going to query for pages
through
the permissions collection. Assume we've already authenticated the user, so the user will be found at
context.params.user
.
Notice the
$feathers
stage in the above example. It will apply the query to that stage in the pipeline, which allows the query to apply to pages even though we had to make the query through the
permissions
service.
If we were to express the above query with JavaScript, the final result would the same as with the following example:
Both examples look a bit complex, but te one using aggregation stages will be much quicker because all stages run in the database server. It will also be quicker because it all happens in a single database query!
One more obstacle for using JavaScript this way is that if the user's query changed (from the front end), we would likely be required to edit multiple different parts of the JS logic in order to correctly display results. With the pipeline example, above, the query is very cleanly applied.
Collation
This adapter includes support for
collation and case insensitive indexes available in MongoDB v3.4
. Collation parameters may be passed using the special
collation
parameter to the
find()
,
remove()
and
patch()
methods.
Example: Patch records with case-insensitive alphabetical ordering
The example below would patch all student records with grades of
'c'
or
'C'
and above (a natural language ordering). Without collations this would not be as simple, since the comparison
{ $gt: 'c' }
would not include uppercase grades of
'C'
because the code point of
'C'
is less than that of
'c'
.
Example: Find records with a case-insensitive search
Similar to the above example, this would find students with a grade of
'c'
or greater, in a case-insensitive manner.
For more information on MongoDB's collation feature, visit the collation reference page .
ObjectIds
MongoDB uses ObjectId object as primary keys. To store them in the right format they have to be converted from and to strings.
AJV keyword
To validate and convert strings to an object id using AJV, the
keywordObjectId
AJV keyword
helper can be used. It is set up automatically in a generated application using MongoDB.
ObjectIdSchema
Both,
@feathersjs/typebox
and
@feathersjs/schema
export an
ObjectIdSchema
helper that creates a schema which can be both, a MongoDB ObjectId or a string that will be converted with the
objectid
keyword:
Important
The
ObjectIdSchema
helper will only work when the
objectid
AJV keyword
is registered.
ObjectId resolvers
While the AJV format checks if an object id is valid, it still needs to be converted to the right type. An alternative the the AJV converter is to use Feathers resolvers . The following property resolver helpers can be used.
Important
ObjectId resolvers do not need to be used when using the AJV keyword . They are useful however when using another JSON schema validation library.
resolveObjectId
resolveObjectId
resolves a property as an object id. It can be used as a direct property resolver or called with the original value.
resolveQueryObjectId
resolveQueryObjectId
allows to query for object ids. It supports conversion from a string to an object id as well as conversion for values from the
$in, $nin and $ne query syntax
.