Revision ec4191ee9c3b641ab4005b119c6949002088bcb1 authored by Valeri Karpov on 09 January 2024, 16:35:42 UTC, committed by Valeri Karpov on 09 January 2024, 16:35:42 UTC
1 parent 3bc4482
Raw File
models.md
# Models

[Models](api/model.html) are fancy constructors compiled from
`Schema` definitions. An instance of a model is called a
[document](documents.html). Models are responsible for creating and
reading documents from the underlying MongoDB database.

* [Compiling your first model](#compiling)
* [Constructing Documents](#constructing-documents)
* [Querying](#querying)
* [Deleting](#deleting)
* [Updating](#updating)
* [Change Streams](#change-streams)
* [Views](#views)

<h2 id="compiling"><a href="#compiling">Compiling your first model</a></h2>

When you call `mongoose.model()` on a schema, Mongoose *compiles* a model
for you.

```javascript
const schema = new mongoose.Schema({ name: String, size: String });
const Tank = mongoose.model('Tank', schema);
```

The first argument is the *singular* name of the collection your model is
for. **Mongoose automatically looks for the plural, lowercased version of your model name.**
Thus, for the example above, the model Tank is for the **tanks** collection
in the database.

**Note:** The `.model()` function makes a copy of `schema`. Make sure that
you've added everything you want to `schema`, including hooks,
before calling `.model()`!

## Constructing Documents

An instance of a model is called a [document](documents.html). Creating
them and saving to the database is easy.

```javascript
const Tank = mongoose.model('Tank', yourSchema);

const small = new Tank({ size: 'small' });
await small.save();

// or

await Tank.create({ size: 'small' });

// or, for inserting large batches of documents
await Tank.insertMany([{ size: 'small' }]);
```

Note that no tanks will be created/removed until the connection your model
uses is open. Every model has an associated connection. When you use
`mongoose.model()`, your model will use the default mongoose connection.

```javascript
await mongoose.connect('mongodb://127.0.0.1/gettingstarted');
```

If you create a custom connection, use that connection's `model()` function
instead.

```javascript
const connection = mongoose.createConnection('mongodb://127.0.0.1:27017/test');
const Tank = connection.model('Tank', yourSchema);
```

## Querying

Finding documents is easy with Mongoose, which supports the [rich](https://www.mongodb.com/docs/manual/reference/method/js-cursor/) query syntax of MongoDB.
Documents can be retrieved using a `model`'s [find](api/model.html#model_Model-find), [findById](api/model.html#model_Model-findById), [findOne](api/model.html#model_Model-findOne), or [where](api/model.html#model_Model-where) static functions.

```javascript
await Tank.find({ size: 'small' }).where('createdDate').gt(oneYearAgo).exec();
```

See the chapter on [queries](queries.html) for more details on how to use the [Query](api/query.html) api.

## Deleting

Models have static `deleteOne()` and `deleteMany()` functions
for removing all documents matching the given `filter`.

```javascript
await Tank.deleteOne({ size: 'large' });
```

## Updating

Each `model` has its own `update` method for modifying documents in the
database without returning them to your application. See the
[API](api/model.html#model_Model-updateOne) docs for more detail.

```javascript
// Updated at most one doc, `res.nModified` contains the number
// of docs that MongoDB updated
await Tank.updateOne({ size: 'large' }, { name: 'T-90' });
```

*If you want to update a single document in the db and return it to your
application, use [findOneAndUpdate](api/model.html#model_Model-findOneAndUpdate)
instead.*

## Change Streams

[Change streams](https://www.mongodb.com/docs/manual/changeStreams/) provide
a way for you to listen to all inserts and updates going through your
MongoDB database. Note that change streams do **not** work unless you're
connected to a [MongoDB replica set](https://www.mongodb.com/docs/manual/replication/).

```javascript
async function run() {
  // Create a new mongoose model
  const personSchema = new mongoose.Schema({
    name: String
  });
  const Person = mongoose.model('Person', personSchema);

  // Create a change stream. The 'change' event gets emitted when there's a
  // change in the database
  Person.watch().
    on('change', data => console.log(new Date(), data));

  // Insert a doc, will trigger the change stream handler above
  console.log(new Date(), 'Inserting doc');
  await Person.create({ name: 'Axl Rose' });
}
```

The output from the above [async function](http://thecodebarbarian.com/80-20-guide-to-async-await-in-node.js.html) will look like what you see below.

```no-highlight
2018-05-11T15:05:35.467Z 'Inserting doc'
2018-05-11T15:05:35.487Z 'Inserted doc'
2018-05-11T15:05:35.491Z { _id: { _data: ... },
  operationType: 'insert',
  fullDocument: { _id: 5af5b13fe526027666c6bf83, name: 'Axl Rose', __v: 0 },
  ns: { db: 'test', coll: 'Person' },
  documentKey: { _id: 5af5b13fe526027666c6bf83 } }
```

You can read more about [change streams in mongoose in this blog post](http://thecodebarbarian.com/a-nodejs-perspective-on-mongodb-36-change-streams.html#change-streams-in-mongoose).

## Views

[MongoDB Views](https://www.mongodb.com/docs/manual/core/views) are essentially read-only collections that contain data computed from other collections using [aggregations](api/aggregate.html).
In Mongoose, you should define a separate Model for each of your Views.
You can also create a View using [`createCollection()`](api/model.html#model_Model-createCollection).

The following example shows how you can create a new `RedactedUser` View on a `User` Model that hides potentially sensitive information, like name and email.

```javascript
// Make sure to disable `autoCreate` and `autoIndex` for Views,
// because you want to create the collection manually.
const userSchema = new Schema({
  name: String,
  email: String,
  roles: [String]
}, { autoCreate: false, autoIndex: false });
const User = mongoose.model('User', userSchema);

const RedactedUser = mongoose.model('RedactedUser', userSchema);

// First, create the User model's underlying collection...
await User.createCollection();
// Then create the `RedactedUser` model's underlying collection
// as a View.
await RedactedUser.createCollection({
  viewOn: 'users', // Set `viewOn` to the collection name, **not** model name.
  pipeline: [
    {
      $set: {
        name: { $concat: [{ $substr: ['$name', 0, 3] }, '...'] },
        email: { $concat: [{ $substr: ['$email', 0, 3] }, '...'] }
      }
    }
  ]
});

await User.create([
  { name: 'John Smith', email: 'john.smith@gmail.com', roles: ['user'] },
  { name: 'Bill James', email: 'bill@acme.co', roles: ['user', 'admin'] }
]);

// [{ _id: ..., name: 'Bil...', email: 'bil...', roles: ['user', 'admin'] }]
console.log(await RedactedUser.find({ roles: 'admin' }));
```

Note that Mongoose does **not** currently enforce that Views are read-only.
If you attempt to `save()` a document from a View, you will get an error from the MongoDB server.

## Yet more

The [API docs](api/model.html#model_Model) cover many additional methods available like [count](api/model.html#model_Model-count), [mapReduce](api/model.html#model_Model-mapReduce), [aggregate](api/model.html#model_Model-aggregate), and more.

## Next Up

Now that we've covered `Models`, let's take a look at [Documents](documents.html).
back to top