Queries查询
Mongoose models provide several static helper functions for CRUD operations. Mongoose模型为CRUD操作提供了几个静态帮助函数。Each of these functions returns a mongoose 这些函数中的每一个都返回一个mongoose Query
object.Query
对象。
Model.deleteMany()
Model.deleteOne()
Model.find()
Model.findById()
Model.findByIdAndDelete()
Model.findByIdAndRemove()
Model.findByIdAndUpdate()
Model.findOne()
Model.findOneAndDelete()
Model.findOneAndRemove()
Model.findOneAndReplace()
Model.findOneAndUpdate()
Model.replaceOne()
Model.updateMany()
Model.updateOne()
A mongoose query can be executed in one of two ways. 猫鼬查询可以通过以下两种方式之一执行。First, if you pass in a 首先,如果传入callback
function, Mongoose will execute the query asynchronously and pass the results to the callback
.callback
函数,Mongoose将异步执行查询,并将结果传递给回调。
A query also has a 查询还有一个.then()
function, and thus can be used as a promise..then()
函数,因此可以用作promise。
Executing执行Queries are Not Promises查询不是承诺References to other documents参考其他文件Streaming流Versus Aggregation对比聚合
Executing执行
When executing a query with a 当使用callback
function, you specify your query as a JSON document. callback
函数执行查询时,您将查询指定为JSON文档。The JSON document's syntax is the same as the MongoDB shell的语法相同。
const Person = mongoose.model('Person', yourSchema);
// find each person with a last name matching 'Ghost', selecting the `name` and `occupation` fields找到每个姓氏与“Ghost”匹配的人,选择`name`(姓名)和`occupation`(职业)字段
Person.findOne({ 'name.last': 'Ghost' }, 'name occupation', function(err, person) {
if (err) return handleError(err);
// Prints "Space Ghost is a talk show host".
console.log('%s %s is a %s.', person.name.first, person.name.last,
person.occupation);
});
Mongoose executed the query and passed the results to Mongoose执行了查询并将结果传递给callback
. callback
。All callbacks in Mongoose use the pattern: Mongoose中的所有回调都使用以下模式:callback(error, result)
. callback(error, result)
。If an error occurs executing the query, the 如果执行查询时发生错误,error
parameter will contain an error document, and result
will be null. error
参数将包含错误文档,result
将为null
。If the query is successful, the 如果查询成功,则error
parameter will be null, and the result
will be populated with the results of the query.error
参数将为null
,并且result
将填充查询的结果。
Anywhere a callback is passed to a query in Mongoose, the callback follows the pattern 在Mongoose中将回调传递给查询的任何地方,回调都遵循模式callback(error, results)
. callback(error, results)
。What 结果取决于操作:对于results
is depends on the operation: For findOne()
it is a potentially-null single document, find()
a list of documents, count()
the number of documents, update()
the number of documents affected, etc. findOne()
,它是一个可能空的单个文档,对于find()
,它是一个文档列表,对于count()
,它是文档数量,对于update()
,它是受影响的文档数量,等等。The API docs for Models provide more detail on what is passed to the callbacks.模型的API文档提供了传递给回调的内容的更多详细信息。
Now let's look at what happens when no 现在,让我们看看当没有传递callback
is passed:callback
时会发生什么:
// find each person with a last name matching 'Ghost'找到每个姓氏匹配“Ghost”的人
const query = Person.findOne({ 'name.last': 'Ghost' });
// selecting the `name` and `occupation` fields选择`name`和`occupation`字段
query.select('name occupation');
// execute the query at a later time
query.exec(function(err, person) {
if (err) return handleError(err);
// Prints "Space Ghost is a talk show host."
console.log('%s %s is a %s.', person.name.first, person.name.last,
person.occupation);
});
In the above code, the 在上面的代码中,query
variable is of type Query. query
变量的类型是查询。A Query
enables you to build up a query using chaining syntax, rather than specifying a JSON object. The below 2 examples are equivalent.Query
使您能够使用链接语法而不是指定JSON对象来构建查询。以下两个示例是等效的。
// With a JSON doc
Person.
find({
occupation: /host/,
'name.last': 'Ghost',
age: { $gt: 17, $lt: 66 },
likes: { $in: ['vaporizing', 'talking'] }
}).
limit(10).
sort({ occupation: -1 }).
select({ name: 1, occupation: 1 }).
exec(callback);
// Using query builder
Person.
find({ occupation: /host/ }).
where('name.last').equals('Ghost').
where('age').gt(17).lt(66).
where('likes').in(['vaporizing', 'talking']).
limit(10).
sort('-occupation').
select('name occupation').
exec(callback);
A full list of Query helper functions can be found in the API docs.查询助手函数的完整列表可以在API文档中找到。
Queries are Not Promises查询不是承诺
Mongoose queries are not promises. Mongoose查询不是承诺。They have a 为了方便起见,它们有一个用于co和.then()
function for co and async/await as a convenience. async
/await
的.then()
函数。However, unlike promises, calling a query's 然而,与promise不同,调用查询的.then()
can execute the query multiple times..then()
可以多次执行查询。
For example, the below code will execute 3 例如,下面的代码将执行3个updateMany()
calls, one because of the callback, and two because .then()
is called twice.updateMany()
调用,一个是因为回调,两个是因为调用了两次then()
。
const q = MyModel.updateMany({}, { isDeleted: true }, function() {
console.log('Update 1');
});
q.then(() => console.log('Update 2'));
q.then(() => console.log('Update 3'));
Don't mix using callbacks and promises with queries, or you may end up with duplicate operations. 不要将回调和promise与查询混合使用,否则可能会导致重复的操作。That's because passing a callback to a query function immediately executes the query, and calling then() executes the query again.这是因为将回调传递给查询函数会立即执行查询,然后调用then()
会再次执行查询。
Mixing promises and callbacks can lead to duplicate entries in arrays. 混合promise和callback可能会导致数组中的条目重复。For example, the below code inserts 2 entries into the 例如,下面的代码在tags
array, not just 1.tags
数组中插入2个条目,而不仅仅是1个。
const BlogPost = mongoose.model('BlogPost', new Schema({
title: String,
tags: [String]
}));
// Because there's both `await` **and** a callback, this `updateOne()` executes twice
// and thus pushes the same string into `tags` twice.
const update = { $push: { tags: ['javascript'] } };
await BlogPost.updateOne({ title: 'Introduction to Promises' }, update, (err, res) => {
console.log(res);
});
References to other documents参考其他文件
There are no joins in MongoDB but sometimes we still want references to documents in other collections. MongoDB中没有联接,但有时我们仍然希望引用其他集合中的文档。This is where population comes in. 这就是人口的来源。Read more about how to include documents from other collections in your query results here.请在此处阅读有关如何在查询结果中包含其他集合中的文档的详细信息。
Streaming流
You can stream query results from MongoDB. 您可以从MongoDB流化查询结果。You need to call the Query#cursor() function to return an instance of QueryCursor.您需要调用Query#cursor()
函数来返回QueryCursor
的实例。
const cursor = Person.find({ occupation: /host/ }).cursor();
for (let doc = await cursor.next(); doc != null; doc = await cursor.next()) {
console.log(doc); // Prints documents one at a time一次打印一个文档
}
Iterating through a Mongoose query using async iterators also creates a cursor.使用异步迭代器遍历Mongoose查询也会创建一个游标。
for await (const doc of Person.find()) {
console.log(doc); // Prints documents one at a time一次打印一个文档
}
Cursors are subject to cursor timeouts. 游标服从于游标超时。By default, MongoDB will close your cursor after 10 minutes and subsequent 默认情况下,MongoDB将在10分钟后关闭游标,随后的next()
calls will result in a MongoServerError: cursor id 123 not found
error. next()
调用将导致MongoServerError: cursor id 123 not found
错误。To override this, set the 要覆盖此项,请在游标上设置noCursorTimeout
option on your cursor.noCursorTimeout
选项。
// MongoDB won't automatically close this cursor after 10 minutes.MongoDB不会在10分钟后自动关闭此游标。
const cursor = Person.find().cursor().addCursorFlag('noCursorTimeout', true);
However, cursors can still time out because of session idle timeouts. 但是,由于会话空闲超时,游标仍然可能超时。So even a cursor with 因此,即使是设置了noCursorTimeout
set will still time out after 30 minutes of inactivity. noCursorTimeout
的游标,在30分钟的不活动后仍会超时。You can read more about working around session idle timeouts in the MongoDB documentation.您可以在MongoDB文档中阅读更多关于处理会话空闲超时的信息。
Versus Aggregation对比聚合
Aggregation can do many of the same things that queries can. 聚合可以做许多与查询相同的事情。For example, below is how you can use 例如,以下是如何使用aggregate()
to find docs where name.last = 'Ghost'
:aggregate()
查找name.last = 'Ghost'
的文档:
const docs = await Person.aggregate([{ $match: { 'name.last': 'Ghost' } }]);
However, just because you can use 然而,仅仅因为你可以使用aggregate()
doesn't mean you should. aggregate()
并不意味着你应该这样做。In general, you should use queries where possible, and only use 一般来说,您应该尽可能使用查询,并且只有在绝对需要时才使用aggregate()
when you absolutely need to.aggregate()
。
Unlike query results, Mongoose does not hydrate() aggregation results. 与查询结果不同,Mongoose不会hydrate()
聚合结果。Aggregation results are always POJOs, not Mongoose documents.聚合结果总是POJO,而不是Mongoose文档。
const docs = await Person.aggregate([{ $match: { 'name.last': 'Ghost' } }]);
docs[0] instanceof mongoose.Document; // false
Also, unlike query filters, Mongoose also doesn't cast aggregation pipelines. 此外,与查询筛选器不同,Mongoose也不强制转换聚合管道。That means you're responsible for ensuring the values you pass in to an aggregation pipeline have the correct type.这意味着您有责任确保传递到聚合管道的值具有正确的类型。
const doc = await Person.findOne();
const idString = doc._id.toString();
// Finds the `Person`, because Mongoose casts `idString` to an ObjectId
const queryRes = await Person.findOne({ _id: idString });
// Does **not** find the `Person`, because Mongoose doesn't cast aggregation
// pipelines.
const aggRes = await Person.aggregate([{ $match: { _id: idString } }]);
Sorting排序
Sorting排序 is how you can ensure you query results come back in the desired order.是如何确保查询结果按所需顺序返回的。
const personSchema = new mongoose.Schema({
age: Number
});
const Person = mongoose.model('Person', personSchema);
for (let i = 0; i < 10; i++) {
await Person.create({ age: i });
}
await Person.find().sort({ age: -1 }); // returns age starting from 10 as the first entry返回从10岁开始的年龄作为第一个条目
await Person.find().sort({ age: 1 }); // returns age starting from 0 as the first entry返回从0开始的年龄作为第一个条目
When sorting with mutiple fields, the order of the sort keys determines what key MongoDB server sorts by first.当使用多个字段进行排序时,排序键的顺序决定了MongoDB服务器首先按哪个键进行排序。
const personSchema = new mongoose.Schema({
age: Number,
name: String,
weight: Number
});
const Person = mongoose.model('Person', personSchema);
const iterations = 5;
for (let i = 0; i < iterations; i++) {
await Person.create({
age: Math.abs(2 - i),
name: 'Test' + i,
weight: Math.floor(Math.random() * 100) + 1
});
}
await Person.find().sort({ age: 1, weight: -1 }); // returns age starting from 0, but while keeping that order will then sort by weight.返回从0开始的年龄,但在保持该顺序的同时,将按重量排序。
You can view the output of a single run of this block below. 您可以在下面查看此块的单次运行的输出。As you can see, age is sorted from 0 to 2 but when age is equal, sorts by weight.正如您所看到的,年龄是从0到2排序的,但当年龄相等时,则按权重排序。
[
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb37'),
age: 0,
name: 'Test2',
weight: 67,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb35'),
age: 1,
name: 'Test1',
weight: 99,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb39'),
age: 1,
name: 'Test3',
weight: 73,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb33'),
age: 2,
name: 'Test0',
weight: 65,
__v: 0
},
{
_id: new ObjectId('63a335a6b9b6a7bfc186cb3b'),
age: 2,
name: 'Test4',
weight: 62,
__v: 0
}
];
Next Up下一步
Now that we've covered 现在我们已经介绍了Queries
, let's take a look at Validation.Queries
,让我们来看看验证。