model.find

Find document(s) in a collection, and call the model hook: afterFind

Arguments

options (object)

  • query (object|id): MongoDB query document, or id
  • [blacklist] (array|string|false): augment definition.findBL. false will remove all blacklisting
  • [getSignedUrls] (boolean): get signed urls for all image objects
  • [noDefaults] (boolean|string|array): dont add defaults for any matching paths, e.g. [‘pet.name’]]
  • [populate] (array)
  • [project] (string|array|object): return only these fields, ignores blacklisting
  • [sort] (string|array|object): same as the mongodb option, but allows string parsing e.g. ‘name’, ‘name:1’
  • [any mongodb option] (any)

Returns

A promise

Example

await user.find({ query: "5ebdd6677466b95109aa278e" })
// {..}

await user.find({ query: { name: "Martin Luther" }})
// [{..}]

await user.find({ query: { name: "Martin Luther" }, limit: 100 })
// [{..}]

Blacklisting

You can augment the model’s definition.findBL blacklist by passing a custom blacklist:

// Prevents `name` and `pets.$.name` (array) from being returned.
user.find({ query: {}, blacklist: ['name', 'pets.name'] })
// You can also whitelist any blacklisted fields found in definition.findBL
user.find({ query: {}, blacklist: ['-name', '-pet'] })

Populate

You are able to populate document references to other collections. Behind the scenes this uses mongodb’s $lookup aggregation operator which can also be passed full $lookup syntax for more control.

Populate is first enabled by including a reference to another document in the field-type via model. The value of the model reference should be an ID, e.g. myBook = id

{
  fields: {
    myBook: { model: 'book' },
    myBooks: [{ model: 'books' }], // you can even populate arrays
  }
}

You are then able to easily populate the returned results in the find method:

user.find({ query: {...}, populate: ['myBook', 'myBooks', ...] })

You can also populate within embedded document fields.

user.find({ query: {...}, populate: ['myBooks.book'] })

Custom Populate Query

If you would like more control you can either use Mongo’s aggregate method via model._aggregate, or simply pass a MongoDB lookup object to populate. When passing a lookup object, the populated field still needs to be defined in definition.fields if you want Monastery to process any related hooks, field blacklisting and default values. See the example belows,

Populate a single document

(Below is the same as using populate: ['myBook'].)

user.find({
  query: {...},
  populate: [{
    as: 'myBook',
    from: 'book',
    foreignField: '_id',
    localField: 'myBook',
  }]
})

Populate multiple documents into virtual fields

Below populates all books into user.myBooks with a bookOwnerId equal to the user._id. Since myBooks isn’t stored on the user, you will need define it as virtual field.

db.model('user', {
  fields: {
    name: { type: 'string' },
    myBooks: [{ model: 'book', virtual: true }],
  },
})
db.model('book', {
  fields: {
    bookTitle: { type: 'string' },
    bookOwnerId: { model: 'user' },
  },
})

// books and user inserted here...

user.find({
  query: {...},
  populate: [{
    as: 'myBooks',
    from: 'book',
    let: { userId: '$_id' },
    pipeline: [{
      $match: {
        $expr: {
          $eq: ['$bookOwnerId', '$$userId']
        }
      }
    }]
  }]
})