mongodb 子文档排序

Mongoose is a library that makes MongoDB easier to use. It does two things:

Mongoose是一个使MongoDB易于使用的库。 它有两件事:

  1. It gives structure to MongoDB Collections
    它为MongoDB集合提供了结构
  2. It gives you helpful methods to use
    它为您提供有用的方法

In this article, we'll go through:

在本文中,我们将介绍:

  1. The basics of using Mongoose
    猫鼬的使用基础
  2. Mongoose subdocuments
    猫鼬子文档
  3. Mongoose population
    猫鼬人口

By the end of the article, you should be able to use Mongoose without problems.

到本文结尾,您应该能够毫无问题地使用Mongoose。

先决条件 (Prerequisites)

I assume you have done the following:

我假设您已执行以下操作:

  1. You have installed MongoDB on your computer
    您已经在计算机上安装了MongoDB
  2. You know how to set up a local MongoDB connection
    您知道如何建立本地MongoDB连接
  3. You know how to see the data you have in your database
    您知道如何查看数据库中的数据
  4. You know what "collections" are in MongoDB
    您知道MongoDB中有哪些“集合”

If you don't know any of these, please read "How to set up a local MongoDB connection" before you continue.

如果您不知道其中任何一个,请在继续之前阅读“如何建立本地MongoDB连接” 。

I also assume you know how to use MongoDB to create a simple CRUD app. If you don't know how to do this, please read "How to build a CRUD app with Node, Express, and MongoDB" before you continue.

我还假设您知道如何使用MongoDB创建简单的CRUD应用程序。 如果您不知道如何执行此操作,请在继续之前阅读“如何使用Node,Express和MongoDB构建CRUD应用程序” 。

猫鼬基础 (Mongoose Basics)

Here, you'll learn how to:

在这里,您将学习如何:

  1. Connect to the database
    连接到数据库
  2. Create a Model
    建立模型
  3. Create a Document
    建立文件
  4. Find a Document
    查找文件
  5. Update a Document
    更新文件
  6. Delete a Document
    删除文件

连接到数据库 (Connecting to a database)

First, you need to download Mongoose.

首先,您需要下载Mongoose。

npm install mongoose --save

You can connect to a database with the connect method. Let's say we want to connect to a database called street-fighters. Here's the code you need:

您可以使用connect方法连接到数据库。 假设我们要连接到名为street-fighters的数据库。 这是您需要的代码:

const mongoose = require('mongoose')
const url = 'mongodb://127.0.0.1:27017/street-fighters'mongoose.connect(url, { useNewUrlParser: true })

We want to know whether our connection has succeeded or failed. This helps us with debugging.

我们想知道我们的连接是成功还是失败。 这有助于我们进行调试。

To check whether the connection has succeeded, we can use the open event. To check whether the connection failed, we use the error event.

要检查连接是否成功,我们可以使用open事件。 为了检查连接是否失败,我们使用error事件。

const db = mongoose.connection
db.once('open', _ => {console.log('Database connected:', url)
})db.on('error', err => {console.error('connection error:', err)
})

Try connecting to the database. You should see a log like this:

尝试连接到数据库。 您应该看到这样的日志:

建立模型 (Creating a Model)

In Mongoose, you need to use models to create, read, update, or delete items from a MongoDB collection.

在Mongoose中,您需要使用模型来创建,读取,更新或删除 MongoDB集合中的项目

To create a Model, you need to create a Schema. A Schema lets you define the structure of an entry in the collection. This entry is also called a document.

要创建模型, 您需要创建一个Schema 。 使用架构可以定义集合中条目的结构 。 该条目也称为文档。

Here's how you create a schema:

创建方式的方法如下:

const mongoose = require('mongoose')
const Schema = mongoose.Schemaconst schema = new Schema({// ...
})

You can use 10 different kinds of values in a Schema. Most of the time, you'll use these six:

您可以在模式中使用10种不同的值 。 大多数时候,您将使用以下六个:

  • String
  • Number
  • Boolean
    布尔型
  • Array
    数组
  • Date
    日期
  • ObjectId
    对象编号

Let's put this into practice.

让我们付诸实践。

Say we want to create characters for our Street Fighter database.

假设我们要为Street Fighter数据库创建角色。

In Mongoose, it's a normal practice to put each model in its own file. So we will create a Character.js file first. This Character.js file will be placed in the models folder.

在Mongoose中,通常的做法是将每个模型放在自己的文件中。 因此,我们将首先创建一个Character.js文件。 此Character.js文件将放置在models文件夹中。

project/|- models/|- Character.js

In Character.js, we create a characterSchema.

Character.js ,我们创建一个characterSchema

const mongoose = require('mongoose')
const Schema = mongoose.Schemaconst characterSchema = new Schema({// ...
})

Let's say we want to save two things into the database:

假设我们要将两件事保存到数据库中:

  1. Name of the character
    角色名称
  2. Name of their ultimate move
    他们最终举动的名字

Both can be represented with Strings.

两者都可以用字符串表示。

const mongoose = require('mongoose')
const Schema = mongoose.Schemaconst characterSchema = new Schema({name: String,ultimate: String
})

Once we've created characterSchema, we can use mongoose's model method to create the model.

一旦创建了characterSchema ,就可以使用猫鼬的model方法创建模型。

module.exports = mongoose.model('Character', characterSchema)

建立文件 (Creating a document)

Let's say you have a file called index.js. This is where we'll perform Mongoose operations for this tutorial.

假设您有一个名为index.js的文件。 这是我们将在本教程中执行Mongoose操作的地方。

project/|- index.js|- models/|- Character.js

First, you need to load the Character model. You can do this with require.

首先,您需要加载角色模型。 您可以使用require做到这一点。

const Character = require('./models/Character')

Let's say you want to create a character called Ryu. Ryu has an ultimate move called "Shinku Hadoken".

假设您要创建一个名为Ryu的角色。 Ryu的终极举动是“ Shinku Hadoken”。

To create Ryu, you use the new, followed by your model. In this case, it's new Character.

要创建Ryu,请使用new ,然后使用模型。 在这种情况下,它是new Character

const ryu = new Character ({name: 'Ryu',ultimate: 'Shinku Hadoken'
})

new Character creates the character in memory. It has not been saved to the database yet. To save to the database, you can run the save method.

new Character角色在内存中创建角色。 它尚未保存到数据库。 要保存到数据库,可以运行save方法

ryu.save(function (error, document) {if (error) console.error(error)console.log(document)
})

If you run the code above, you should see this in the console.

如果运行上面的代码,则应该在控制台中看到它。

承诺和异步/等待 (Promises and Async/await)

Mongoose supports promises. It lets you write nicer code like this:

猫鼬支持诺言。 它使您可以编写更好的代码,如下所示:

// This does the same thing as above
function saveCharacter (character) {const c = new Character(character)return c.save()
}saveCharacter({name: 'Ryu',ultimate: 'Shinku Hadoken'
}).then(doc => { console.log(doc) }).catch(error => { console.error(error) })

You can also use the await keyword if you have an asynchronous function.

如果您具有异步功能,也可以使用await关键字。

If the Promise or Async/Await code looks foreign to you, I recommend reading "JavaScript async and await" before continuing with this tutorial.

如果Promise或Async / Await代码对您来说很陌生,建议您在继续本教程之前阅读“ JavaScript async and await” 。

async function runCode() {const ryu = new Character({name: 'Ryu',ultimate: 'Shinku Hadoken'})const doc = await ryu.save()console.log(doc)
}runCode().catch(error => { console.error(error) })

Note: I'll use the async/await format for the rest of the tutorial.

注意:在本教程的其余部分中,我将使用async / await格式。

独特性 (Uniqueness)

Mongoose adds a new character to the database each time you use new Character and save. If you run the code(s) above three times, you'd expect to see three Ryus in the database.

每次使用new Charactersave时,猫鼬都会向数据库中添加一个新角色。 如果您在上面的代码中运行了3次以上,则可能会在数据库中看到3个Ryu。

We don't want to have three Ryus in the database. We want to have ONE Ryu only. To do this, we can use the unique option.

我们不想在数据库中有三个Ryu。 我们只想有一个龙 。 为此,我们可以使用唯一选项。

const characterSchema = new Schema({name: { type: String, unique: true },ultimate: String
})

The unique option creates a unique index. It ensures that we cannot have two documents with the same value (for name in this case).

unique选项创建一个唯一索引 。 它确保了我们不能拥有两个具有相同值的文档(在这种情况下为name )。

For unique to work properly, you need to clear the Characters collection. To clear the Characters collection, you can use this:

对于unique正常工作,你需要清除的字符集 。 要清除“字符”集合,可以使用以下命令:

await Character.deleteMany({})

Try to add two Ryus into the database now. You'll get an E11000 duplicate key error. You won't be able to save the second Ryu.

现在尝试将两个Ryus添加到数据库中。 您将收到E11000 duplicate key error 。 您将无法保存第二个Ryu。

Let's add another character into the database before we continue the rest of the tutorial.

在继续本教程的其余部分之前,让我们向数据库中添加另一个字符。

const ken = new Character({name: 'Ken',ultimate: 'Guren Enjinkyaku'
})await ken.save()

查找文件 (Finding a document)

Mongoose gives you two methods to find stuff from MongoDB.

Mongoose为您提供了两种从MongoDB中查找内容的方法。

  1. findOne: Gets one document.

    findOne :获取一个文档。

  2. find: Gets an array of documents

    find :获取文档数组

找一个 (findOne)

findOne returns the first document it finds. You can specify any property to search for. Let's search for Ryu:

findOne 返回找到的第一个文档 。 您可以指定要搜索的任何属性。 让我们搜索Ryu

const ryu = await Character.findOne({ name: 'Ryu' })
console.log(ryu)

(find)

find returns an array of documents. If you specify a property to search for, it'll return documents that match your query.

find 返回一个文档数组 。 如果您指定要搜索的属性,它将返回与您的查询匹配的文档。

const chars = await Character.find({ name: 'Ryu' })
console.log(chars)

If you did not specify any properties to search for, it'll return an array that contains all documents in the collection.

如果您未指定要搜索的任何属性,它将返回一个包含集合中所有文档的数组。

const chars = await Character.find()
console.log(chars)

更新文件 (Updating a document)

Let's say Ryu has three special moves:

假设Ryu有以下三个特殊举动:

  1. Hadoken
    哈多肯
  2. Shoryuken
    Shoryuken
  3. Tatsumaki Senpukyaku
    辰m千pu阁

We want to add these special moves into the database. First, we need to update our CharacterSchema.

我们想将这些特殊动作添加到数据库中。 首先,我们需要更新我们的CharacterSchema

const characterSchema = new Schema({name: { type: String, unique: true },specials: Array,ultimate: String
})

Then, we use one of these two ways to update a character:

然后,我们使用以下两种方法之一来更新字符:

  1. Use findOne, then use save

    使用findOne ,然后使用save

  2. Use findOneAndUpdate

    使用findOneAndUpdate

findOne并保存 (findOne and save)

First, we use findOne to get Ryu.

首先,我们使用findOne来获取Ryu。

const ryu = await Character.findOne({ name: 'Ryu' })
console.log(ryu)

Then, we update Ryu to include his special moves.

然后,我们更新Ryu,以包括他的特殊举动。

const ryu = await Character.findOne({ name: 'Ryu' })
ryu.specials = ['Hadoken','Shoryuken','Tatsumaki Senpukyaku'
]

After we modified ryu, we run save.

修改ryu ,我们运行save

const ryu = await Character.findOne({ name: 'Ryu' })
ryu.specials = ['Hadoken','Shoryuken','Tatsumaki Senpukyaku'
]const doc = await ryu.save()
console.log(doc)

findOneAndUpdate (findOneAndUpdate)

findOneAndUpdate is the same as MongoDB's findOneAndModify method.

findOneAndUpdate与MongoDB的findOneAndModify方法相同。

Here, you search for Ryu and pass the fields you want to update at the same time.

在这里,您搜索Ryu并同时传递要更新的字段。

// Syntax
await findOneAndUpdate(filter, update)
// Usage
const doc = await Character.findOneAndUpdate({ name: 'Ryu' },{specials: ['Hadoken','Shoryuken','Tatsumaki Senpukyaku']})console.log(doc)

findOne + save与findOneAndUpdate之间的区别 (Difference between findOne + save vs findOneAndUpdate)

Two major differences.

两个主要区别。

First, the syntax for findOne` + `save is easier to read than findOneAndUpdate.

首先, findOne` + `save语法findOneAndUpdate 更易于阅读

Second, findOneAndUpdate does not trigger the save middleware.

其次, findOneAndUpdate不会触发save中间件。

I'll choose findOne + save over findOneAndUpdate anytime because of these two differences.

由于这两个差异, 我会随时选择findOne + save不是findOneAndUpdate

删除文件 (Deleting a document)

There are two ways to delete a character:

有两种删除字符的方法:

  1. findOne + remove

    findOne + remove

  2. findOneAndDelete

    findOneAndDelete

使用findOne +删除 (Using findOne + remove)

const ryu = await Character.findOne({ name: 'Ryu' })
const deleted = await ryu.remove()

使用findOneAndDelete (Using findOneAndDelete)

const deleted = await Character.findOneAndDelete({ name: 'Ken' })

子文件 (Subdocuments)

In Mongoose, subdocuments are documents that are nested in other documents. You can spot a subdocument when a schema is nested in another schema.

在Mongoose中, 文档是嵌套在其他文档中的文档 。 当一个模式嵌套在另一个模式中时,您可以发现一个子文档。

Note: MongoDB calls subdocuments embedded documents.

注意:MongoDB调用子文档嵌入的文档

const childSchema = new Schema({name: String
});const parentSchema = new Schema({// Single subdocumentchild: childSchema,// Array of subdocumentschildren: [ childSchema ]
});

In practice, you don't have to create a separate childSchema like the example above. Mongoose helps you create nested schemas when you nest an object in another object.

实际上,您不必像上面的示例一样创建单独的childSchema 。 当您将一个对象嵌套在另一个对象中时,Mongoose可帮助您创建嵌套模式。

// This code is the same as above
const parentSchema = new Schema({// Single subdocumentchild: { name: String },// Array of subdocumentschildren: [{name: String }]
});

In this section, you will learn to:

在本节中,您将学习:

  1. Create a schema that includes a subdocument
    创建一个包含子文档的架构
  2. Create documents that contain subdocuments
    创建包含子文档的文档
  3. Update subdocuments that are arrays
    更新属于数组的子文档
  4. Update a single subdocument
    更新单个子文档

更新characterSchema (Updating characterSchema)

Let's say we want to create a character called Ryu. Ryu has three special moves.

假设我们要创建一个名为Ryu的角色。 Ryu有三个特殊举动。

  1. Hadoken
    哈多肯
  2. Shinryuken
    新龙园
  3. Tatsumaki Senpukyaku
    辰m千pu阁

Ryu also has one ultimate move called:

Ryu还采取了一项终极举措:

  1. Shinku Hadoken
    新宿长拳

We want to save the names of each move. We also want to save the keys required to execute that move.

我们要保存每个动作的名称。 我们还希望保存执行该移动所需的密钥。

Here, each move is a subdocument.

在这里,每个步骤都是一个子文档。

const characterSchema = new Schema({name: { type: String, unique: true },// Array of subdocumentsspecials: [{name: String,keys: String}]// Single subdocumentultimate: {name: String,keys: String}
})

You can also use the childSchema syntax if you wish to. It makes the Character schema easier to understand.

如果愿意,还可以使用childSchema语法。 它使角色架构更易于理解。

const moveSchema = new Schema({name: String,keys: String
})const characterSchema = new Schema({name: { type: String, unique: true },// Array of subdocumentsspecials: [moveSchema],// Single subdocumentultimate: moveSchema
})

创建包含子文档的文档 (Creating documents that contain subdocuments)

There are two ways to create documents that contain subdocuments:

有两种方法可以创建包含子文档的文档:

  1. Pass a nested object into new Model

    将嵌套对象传递到new Model

  2. Add properties into the created document.
    将属性添加到创建的文档中。

方法1:传递整个对象 (Method 1: Passing the entire object)

For this method, we construct a nested object that contains both Ryu's name and his moves.

对于此方法,我们构造了一个包含Ryu的名字和他的举动的嵌套对象。

const ryu = {name: 'Ryu',specials: [{name: 'Hadoken',keys: '↓ ↘ → P'}, {name: 'Shoryuken',keys: '→ ↓ ↘ → P'}, {name: 'Tatsumaki Senpukyaku',keys: '↓ ↙ ← K'}],ultimate: {name: 'Shinku Hadoken',keys: '↓ ↘ → ↓ ↘ → P'}
}

Then, we pass this object into new Character.

然后,我们将此对象传递给new Character

const char = new Character(ryu)
const doc = await char.save()
console.log(doc)

方法2:以后添加子文档 (Method 2: Adding subdocuments later)

For this method, we create a character with new Character first.

对于此方法,我们首先创建一个具有new Character

const ryu = new Character({ name: 'Ryu' })

Then, we edit the character to add special moves:

然后,我们编辑角色以添加特殊动作:

const ryu = new Character({ name: 'Ryu' })
const ryu.specials = [{name: 'Hadoken',keys: '↓ ↘ → P'
}, {name: 'Shoryuken',keys: '→ ↓ ↘ → P'
}, {name: 'Tatsumaki Senpukyaku',keys: '↓ ↙ ← K'
}]

Then, we edit the character to add the ultimate move:

然后,我们编辑角色以添加最终动作:

const ryu = new Character({ name: 'Ryu' })// Adds specials
const ryu.specials = [{name: 'Hadoken',keys: '↓ ↘ → P'
}, {name: 'Shoryuken',keys: '→ ↓ ↘ → P'
}, {name: 'Tatsumaki Senpukyaku',keys: '↓ ↙ ← K'
}]// Adds ultimate
ryu.ultimate = {name: 'Shinku Hadoken',keys: '↓ ↘ → ↓ ↘ → P'
}

Once we're satisfied with ryu, we run save.

一旦我们对ryu满意,就运行save

const ryu = new Character({ name: 'Ryu' })// Adds specials
const ryu.specials = [{name: 'Hadoken',keys: '↓ ↘ → P'
}, {name: 'Shoryuken',keys: '→ ↓ ↘ → P'
}, {name: 'Tatsumaki Senpukyaku',keys: '↓ ↙ ← K'
}]// Adds ultimate
ryu.ultimate = {name: 'Shinku Hadoken',keys: '↓ ↘ → ↓ ↘ → P'
}const doc = await ryu.save()
console.log(doc)

更新数组子文档 (Updating array subdocuments)

The easiest way to update subdocuments is:

更新子文档的最简单方法是:

  1. Use findOne to find the document

    使用findOne查找文档

  2. Get the array
    获取数组
  3. Change the array
    更改数组
  4. Run save

    运行save

For example, let's say we want to add Jodan Sokutou Geri to Ryu's special moves. The keys for Jodan Sokutou Geri are ↓ ↘ → K.

例如,假设我们要将Jodan Sokutou Geri添加到Ryu的特殊举动中。 Jodan Sokutou Geri的键是↓ ↘ â†' K

First, we find Ryu with findOne.

首先,我们使用findOne查找Ryu。

const ryu = await Characters.findOne({ name: 'Ryu' })

Mongoose documents behave like regular JavaScript objects. We can get the specials array by writing ryu.specials.

猫鼬文档的行为类似于常规JavaScript对象。 我们可以通过编写ryu.specials获得specials数组。

const ryu = await Characters.findOne({ name: 'Ryu' })
const specials = ryu.specials
console.log(specials)

This specials array is a normal JavaScript array.

这个specials数组是普通JavaScript数组。

const ryu = await Characters.findOne({ name: 'Ryu' })
const specials = ryu.specials
console.log(Array.isArray(specials)) // true

We can use the push method to add a new item into specials,

我们可以使用push方法将新商品添加到specials

const ryu = await Characters.findOne({ name: 'Ryu' })
ryu.specials.push({name: 'Jodan Sokutou Geri',keys: '↓ ↘ → K'
})

After updating specials, we run save to save Ryu to the database.

更新specials ,我们运行save将Ryu save到数据库中。

const ryu = await Characters.findOne({ name: 'Ryu' })
ryu.specials.push({name: 'Jodan Sokutou Geri',keys: '↓ ↘ → K'
})const updated = await ryu.save()
console.log(updated)

更新单个子文档 (Updating a single subdocument)

It's even easier to update single subdocuments. You can edit the document directly like a normal object.

更新单个子文档甚至更加容易。 您可以像普通对象一样直接编辑文档。

Let's say we want to change Ryu's ultimate name from Shinku Hadoken to Dejin Hadoken. What we do is:

假设我们想将Ryu的终极名字从Shinku Hadoken更改为Dejin Hadoken。 我们要做的是:

  1. Use findOne to get Ryu.

    使用findOne获取Ryu。

  2. Change the name in ultimate

    ultimate更改name

  3. Run save

    运行save

const ryu = await Characters.findOne({ name: 'Ryu' })
ryu.ultimate.name = 'Dejin Hadoken'const updated = await ryu.save()
console.log(updated)

人口 (Population)

MongoDB documents have a size limit of 16MB. This means you can use subdocuments (or embedded documents) if they are small in number.

MongoDB文档的大小限制为16MB。 这意味着如果子文档(或嵌入式文档)数量很少,则可以使用它们。

For example, Street Fighter characters have a limited number of moves. Ryu only has 4 special moves. In this case, it's okay to use embed moves directly into Ryu's character document.

例如,《街头霸王》角色的动作数量有限。 Ryu只有4个特殊举动。 在这种情况下,可以直接在Ryu的角色文档中使用嵌入动作。

But if you have data that can contain an unlimited number of subdocuments, you need to design your database differently.

但是,如果您的数据可以包含无限数量的子文档,则需要以不同的方式设计数据库。

One way is to create two separate models and combine them with populate.

一种方法是创建两个单独的模型,然后将它们与填充组合。

创建模型 (Creating the models)

Let's say you want to create a blog. And you want to store the blog content with MongoDB. Each blog has a title, content, and comments.

假设您要创建一个博客。 并且您想将博客内容存储在MongoDB中。 每个博客都有标题,内容和评论。

Your first schema might look like this:

您的第一个架构可能如下所示:

const blogPostSchema = new Schema({title: String,content: String,comments: [{comment: String}]
})module.exports = mongoose.model('BlogPost', blogPostSchema)

There's a problem with this schema.

该架构存在问题。

A blog post can have an unlimited number of comments. If a blog post explodes in popularity and comments swell up, the document might exceed the 16MB limit imposed by MongoDB.

博客文章可以有无限数量的评论。 如果博客文章的人气激增且评论激增,则该文档可能会超出MongoDB规定的16MB限制。

This means we should not embed comments in blog posts. We should create a separate collection for comments.

这意味着我们不应该在博客文章中嵌入评论。 我们应该为评论创建一个单独的集合。

const comments = new Schema({comment: String
})module.exports = mongoose.model('Comment', commentSchema)

In Mongoose, we can link up the two models with Population.

在猫鼬中,我们可以将两种模型与人口联系起来。

To use Population, we need to:

要使用人口,我们需要:

  1. Set type of a property to Schema.Types.ObjectId

    将属性的type设置为Schema.Types.ObjectId

  2. Set ref to the model we want to link too.

    ref设置为我们也要链接的模型。

Here, we want comments in blogPostSchema to link to the Comment collection. This is the schema we'll use:

在这里,我们想commentsblogPostSchema链接到评论的集合。 这是我们将使用的架构:

const blogPostSchema = new Schema({title: String,content: String,comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
})module.exports = mongoose.model('BlogPost', blogPostSchema)

创建博客文章 (Creating a blog post)

Let's say you want to create a blog post. To create the blog post, you use new BlogPost.

假设您要创建一个博客文章。 要创建博客文章,请使用new BlogPost

const blogPost = new BlogPost({title: 'Weather',content: `How's the weather today?`
})

A blog post can have zero comments. We can save this blog post with save.

博客文章的评论数为零。 我们可以保存这个博客帖子与save

const doc = await blogPost.save()
console.log(doc)

创建评论 (Creating comments)

Now let's say we want to create a comment for the blog post. To do this, we create and save the comment.

现在,我们要为博客文章创建评论。 为此,我们创建并保存评论。

const comment = new Comment({comment: `It's damn hot today`
})const savedComment = await comment.save()
console.log(savedComment)

Notice the saved comment has an _id attribute. We need to add this _id attribute into the blog post's comments array. This creates the link.

请注意,已保存的注释具有_id属性。 我们需要将此_id属性添加到博客文章的comments数组中。 这将创建链接。

// Saves comment to Database
const savedComment = await comment.save()// Adds comment to blog post
// Then saves blog post to database
const blogPost = await BlogPost.findOne({ title: 'Weather' })
blogPost.comments.push(savedComment._id)
const savedPost = await blogPost.save()
console.log(savedPost)
Blog post with comments.
带有评论的博客文章。

搜索博客文章及其评论 (Searching blog posts and their comments)

If you tried to search for the blog post, you'll see the blog post has an array of comment IDs.

如果您尝试搜索该博客文章,则会看到该博客文章具有一系列注释ID。

const blogPost = await BlogPost.findOne({ title: 'Weather' })
console.log(blogPost)

There are four ways to get comments.

有四种获取评论的方法。

  1. Mongoose population
    猫鼬人口
  2. Manual way #1
    手动方式#1
  3. Manual way #2
    手动方式2
  4. Manual way #3
    手动方式3

猫鼬人口 (Mongoose Population)

Mongoose allows you to fetch linked documents with the populate method. What you need to do is call .populate when you execute with findOne.

猫鼬允许您使用populate方法获取链接的文档。 您需要做的是在使用findOne执行时调用.populate

When you call populate, you need to pass in the key of the property you want to populate. In this case, the key is comments. (Note: Mongoose calls this key a "path").

调用填充时,您需要传递要填充的属性的key 。 在这种情况下, keycomments 。 (注意:猫鼬称此key为“路径”)。

const blogPost = await BlogPost.findOne({ title: 'Weather' }).populate('comments')
console.log(blogPost)

手动方式(方法1) (Manual way (method 1))

Without Mongoose Populate, you need to find the comments manually. First, you need to get the array of comments.

如果没有猫鼬填充,则需要手动查找注释。 首先,您需要获取评论数组。

const blogPost = await BlogPost.findOne({ title: 'Weather' }).populate('comments')
const commentIDs = blogPost.comments

Then, you loop through commentIDs to find each comment. If you go with this method, it's slightly faster to use Promise.all.

然后,您循环浏览commentIDs以查找每个注释。 如果使用此方法,则使用Promise.all更快。

const commentPromises = commentIDs.map(_id => {return Comment.findOne({ _id })
})
const comments = await Promise.all(commentPromises)
console.log(comments)

手动方式(方法2) (Manual way (method 2))

Mongoose gives you an $in operator. You can use this $in operator to find all comments within an array. This syntax takes a little effort to get used to.

猫鼬给您一个$in运算符。 您可以使用此$in运算符查找数组中的所有注释。 这种语法需要一点努力来习惯。

If I had to do the manual way, I'd prefer Manual #1 over this.

如果必须采用手动方式,则我更希望使用手册#1。

const commentIDs = blogPost.comments
const comments = await Comment.find({'_id': { $in: commentIDs }
})console.log(comments)

手动方式(方法3) (Manual way (method 3))

For the third method, we need to change the schema. When we save a comment, we link the comment to the blog post.

对于第三种方法,我们需要更改架构。 保存评论后,我们会将评论链接到博客文章。

// Linking comments to blog post
const commentSchema = new Schema({comment: StringblogPost: [{ type: Schema.Types.ObjectId, ref: 'BlogPost' }]
})module.exports = mongoose.model('Comment', commentSchema)

You need to save the comment into the blog post, and the blog post id into the comment.

您需要将评论保存到博客文章中,并将博客文章ID保存到评论中。

const blogPost = await BlogPost.findOne({ title: 'Weather' })// Saves comment
const comment = new Comment({comment: `It's damn hot today`,blogPost: blogPost._id
})
const savedComment = comment.save()// Links blog post to comment
blogPost.comments.push(savedComment._id)
await blogPost.save()

Once you do this, you can search the Comments collection for comments that match your blog post's id.

完成此操作后,您可以在评论集合中搜索与博客文章ID相匹配的评论。

// Searches for comments
const blogPost = await BlogPost.findOne({ title: 'Weather' })
const comments = await Comment.find({ _id: blogPost._id })
console.log(comments)

I'd prefer Manual #3 over Manual #1 and Manual #2.

与手册1和手册2相比,我更喜欢手册3。

And Population beats all three manual methods.

人口胜过所有三种手动方法。

快速总结 (Quick Summary)

You learned to use Mongoose on three different levels in this article:

在本文中,您学会了在三个不同级别上使用Mongoose:

  1. Basic Mongoose
    基本猫鼬
  2. Mongoose subdocuments
    猫鼬子文档
  3. Mongoose population
    猫鼬人口

That's it!

而已!



Thanks for reading. This article was originally posted on my blog. Sign up for my newsletter if you want more articles to help you become a better frontend developer.

谢谢阅读。 本文最初发布在我的博客上 。 如果您想要更多的文章来帮助您成为更好的前端开发人员,请注册我的时事通讯。

翻译自: https://www.freecodecamp.org/news/mongoose101/

mongodb 子文档排序

mongodb 子文档排序_猫鼬101:基础知识,子文档和人口简介相关推荐

  1. MongoDB技术文章合辑1:基础知识

    近期请社区内几位成员整理了下号内MongoDB技术文章,分为基础知识.性能优化.原理解读.运维监控和最佳实践5个主题,感兴趣的朋友可以直接进入查看微信服务号上的归类整理https://mp.weixi ...

  2. html5基础知识文档,HTML5基础知识(1)

    原标题:HTML5基础知识(1) html5是万维网的核心语言.标准通用标记语言下的一个应用超文本标记语言(HTML)的第五次重大修改.2014年10月29日,万维网联盟宣布,经过接近8年的艰苦努力, ...

  3. mysql 算子 谓词_[SQL] SQL 基础知识梳理(六)- 函数、谓词、CASE 表达式

    SQL 基础知识梳理(六)-  函数.谓词.CASE 表达式 目录 函数 谓词 CASE 表达式 一.函数 1.函数:输入某一值得到相应输出结果的功能,输入值称为"参数",输出值称 ...

  4. 电脑的基础知识_电脑的基础知识大全,你确定都知道?

    电脑的基础知识大全,你确定都知道? 一.软件系统 软件系统包括:操作系统.应用软件等.应用软件中电脑行业的管理软件,IT电脑行业的发展必备利器,电脑行业的erp软件. 二.硬件系统 硬件系统包括:机箱 ...

  5. oracle基础知识文档,Oracle 基础知识分享PPT

    因测试组需求,所以把Oracle基础知识整理成了PPT,并讲解了一下(PPT无风格,简约派吐舌头). Oracle 是以高级结构化查询语言(SQL)为基础的大型关系数据库,通俗地讲它是用方便逻辑管理的 ...

  6. 计算机文档培训讲座,计算机的基础知识培训讲座.ppt

    (7)运算速度.运算速度是指计算机每秒所能执行的指令条数,一般用MIPS为单位.它是计算机的主要技术指标之一. (8)主频.主频是指计算机的时钟频率,单位用MHz表示.它是计算机的主要技术指标之一. ...

  7. 串口发送tcp数据 源端口号_三分钟基础知识:用动画给面试官解释 TCP 三次握手过程...

    作者 |  小鹿 来源 |  小鹿动画学编程 写在前边 TCP 三次握手过程对于面试是必考的一个,所以不但要掌握 TCP 整个握手的过程,其中有些小细节也更受到面试官的青睐. 对于这部分掌握以及 TC ...

  8. 计算机基础知识应用文档,计算机基础知识与应用

    计算机基础知识与应用Tag内容描述: 1.计算机应用基础知识计算机应用基础知识点计算机应用基础知识话题计算机应用基础知识计算机基础知识电子商务单击第一章计算机基础知识1.1946年2月15日世界上第一 ...

  9. 电脑的基础知识_电脑入门基础知识

    电脑入门基础知识 学习电脑应该先了解电脑的基本的组件,然后学习操作,包括键盘.鼠标的使用,能基本使用操作系统,再学习打字. 作为一个电脑小白,想要学习电脑,首先要知道如何操作,学习打字,接着就是熟练使 ...

最新文章

  1. 概率论—随机变量的数字特征、大数定律及中心极限定理
  2. linux root权限_深入了解 Linux 权限
  3. XStream 用法汇总
  4. linux修改时间指令,Linux 修改时间的指令
  5. ITK:计算梯度各向异性扩散
  6. 自定义repeater带分页功能的DataGrid(仿PetShop)
  7. table tr省略后鼠标移入显示相应信息_中考来了,人机对话、信息技术考试要求看过来...
  8. iOS 获取当前对象所在的VC
  9. C语言数组学完学啥,我的c语言学习-数组专题
  10. java script中==和===_Java Script 中 ==(Equal) 和 === (Identity Equal) 的区别和比较算法逻辑...
  11. 性能调优第一篇-SQL格式化
  12. 将 DataDirect ODBC 驱动程序与 Python 和 pyodbc 一起使用
  13. Scintilla教程(7): 多选以及滚动
  14. 世界上最大的计算机硬盘,三星:2.5英寸16TB,我是史上最大SSD硬盘-消费电子-与非网...
  15. 人才缺口30万,市场需求每年涨20% ,这一 IT 岗位你知道嘛
  16. 关键词词云怎么做_做关键词分析,我有4款免费词云工具
  17. 长安大学转计算机专业,长安大学本科学生转换专业管理办法
  18. 科技人文丨爱上陌生人的36个问题
  19. 我写了一套SpringBoot微信小程序电商全栈就业实战课程,免费分享给CSDN的朋友们
  20. 论文成功写作技巧之行之有效的写作从“结果”开始(下)

热门文章

  1. 妙啊,阻塞到底是个啥?黄袍加身,亦能谈古说今
  2. .net 压缩、解压文件
  3. Unity协程那些事儿
  4. 总结二十二:如何在公司建立成功的技术文化(3)
  5. 为海红征集祝福···
  6. listview 争夺焦点_浏览器趋势2015年6月:争夺第二名
  7. 2012年12月6日
  8. [rospack] Warning: error while crawling /home/dereck: boost::filesystem::status: Permission denied:
  9. Android 一个应用启动另外一个应用
  10. Java 、Scala的下载