Mongoose 中的 Aggregate 介绍

Mongoose的 aggregate()函数 是您如何将 MongoDB 的Aggregation Framework 与 Mongoose 一起使用。 Mongoose的 aggregate()中工作的任何聚合查询都 MongoDB shell 应该在 Mongoose 中工作,无需任何更改。

什么是 Aggregation Framework ?

从语法上讲,Aggregation Framework 查询是一系列阶段。 阶段 是 MongoDB 如何转换进入阶段的任何文档的对象描述 第一阶段将文档馈送到第二阶段,依此类推,因此您可以使用阶段组合转换。 您传递给的阶段数组 aggregate() 函数称为聚合 管道 。

$match 阶段

$match 阶段过滤掉与给定不匹配的文档 filter 参数,类似于 Mongoose 的 find() 功能

<code class="language-javascript">await Character.create([
{ name: Jean-Luc Picard, age: 59, rank: Captain },
{ name: William Riker, age: 29, rank: Commander },
{ name: Deanna Troi, age: 28, rank: Lieutenant Commander },
{ name: Geordi La Forge, age: 29, rank: Lieutenant },
{ name: Worf, age: 24, rank: Lieutenant }
]);
const filter = { age: { $gte: 30 } };
let docs = await Character.aggregate([
{ $match: filter }
]);
docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59
// `$match` is similar to `find()`
docs = await Character.find(filter);
docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59</code>
<code class="language-javascript">await Character.create([
  { name: Jean-Luc Picard, age: 59, rank: Captain },
  { name: William Riker, age: 29, rank: Commander },
  { name: Deanna Troi, age: 28, rank: Lieutenant Commander },
  { name: Geordi La Forge, age: 29, rank: Lieutenant },
  { name: Worf, age: 24, rank: Lieutenant }
]);

const filter = { age: { $gte: 30 } };
let docs = await Character.aggregate([
  { $match: filter }
]);

docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59

// `$match` is similar to `find()`
docs = await Character.find(filter);
docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59</code>
await Character.create([
{ name: Jean-Luc Picard, age: 59, rank: Captain },
{ name: William Riker, age: 29, rank: Commander },
{ name: Deanna Troi, age: 28, rank: Lieutenant Commander },
{ name: Geordi La Forge, age: 29, rank: Lieutenant },
{ name: Worf, age: 24, rank: Lieutenant }
]);
const filter = { age: { $gte: 30 } };
let docs = await Character.aggregate([
{ $match: filter }
]);
docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59
// `$match` is similar to `find()`
docs = await Character.find(filter);
docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59
await Character.create([
  { name: Jean-Luc Picard, age: 59, rank: Captain },
  { name: William Riker, age: 29, rank: Commander },
  { name: Deanna Troi, age: 28, rank: Lieutenant Commander },
  { name: Geordi La Forge, age: 29, rank: Lieutenant },
  { name: Worf, age: 24, rank: Lieutenant }
]);

const filter = { age: { $gte: 30 } };
let docs = await Character.aggregate([
  { $match: filter }
]);

docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59

// `$match` is similar to `find()`
docs = await Character.find(filter);
docs.length; // 1
docs[0].name; // Jean-Luc Picard
docs[0].age // 59
await Character.create([ { name: Jean-Luc Picard, age: 59, rank: Captain }, { name: William Riker, age: 29, rank: Commander }, { name: Deanna Troi, age: 28, rank: Lieutenant Commander }, { name: Geordi La Forge, age: 29, rank: Lieutenant }, { name: Worf, age: 24, rank: Lieutenant } ]); const filter = { age: { $gte: 30 } }; let docs = await Character.aggregate([ { $match: filter } ]); docs.length; // 1 docs[0].name; // Jean-Luc Picard docs[0].age // 59 // `$match` is similar to `find()` docs = await Character.find(filter); docs.length; // 1 docs[0].name; // Jean-Luc Picard docs[0].age // 59

$group 阶段

聚合可以做的不仅仅是过滤文档。您还可以使用Aggregation Framework 来转换文档。 例如 $group 舞台表现得像 reduce() 功能 。 例如 $group stage 让您计算给定的字符数 age

<code class="language-javascript">let docs = await Character.aggregate([
{
$group: {
// Each `_id` must be unique, so if there are multiple
// documents with the same age, MongoDB will increment `count`.
_id: $age,
count: { $sum: 1 }
}
}
]);
docs.length; // 4
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
docs[3]; // { _id: 59, count: 1 }</code>
<code class="language-javascript">let docs = await Character.aggregate([
  {
    $group: {
      // Each `_id` must be unique, so if there are multiple
      // documents with the same age, MongoDB will increment `count`.
      _id: $age,
      count: { $sum: 1 }
    }
  }
]);

docs.length; // 4
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
docs[3]; // { _id: 59, count: 1 }</code>
let docs = await Character.aggregate([
{
$group: {
// Each `_id` must be unique, so if there are multiple
// documents with the same age, MongoDB will increment `count`.
_id: $age,
count: { $sum: 1 }
}
}
]);
docs.length; // 4
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
docs[3]; // { _id: 59, count: 1 }
let docs = await Character.aggregate([
  {
    $group: {
      // Each `_id` must be unique, so if there are multiple
      // documents with the same age, MongoDB will increment `count`.
      _id: $age,
      count: { $sum: 1 }
    }
  }
]);

docs.length; // 4
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
docs[3]; // { _id: 59, count: 1 }
let docs = await Character.aggregate([ { $group: { // Each `_id` must be unique, so if there are multiple // documents with the same age, MongoDB will increment `count`. _id: $age, count: { $sum: 1 } } } ]); docs.length; // 4 docs.sort((d1, d2) => d1._id - d2._id); docs[0]; // { _id: 24, count: 1 } docs[1]; // { _id: 28, count: 1 } docs[2]; // { _id: 29, count: 2 } docs[3]; // { _id: 59, count: 1 }

结合多个阶段

聚合管道的优势在于它的可组合性。 例如您可以结合前面的两个示例,仅按以下方式对字符进行分组 age 如果他们的 age< 30

<code class="language-javascript">let docs = await Character.aggregate([
{ $match: { age: { $lt: 30 } } },
{
$group: {
_id: $age,
count: { $sum: 1 }
}
}
]);
docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }</code>
<code class="language-javascript">let docs = await Character.aggregate([
  { $match: { age: { $lt: 30 } } },
  {
    $group: {
      _id: $age,
      count: { $sum: 1 }
    }
  }
]);

docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }</code>
let docs = await Character.aggregate([
{ $match: { age: { $lt: 30 } } },
{
$group: {
_id: $age,
count: { $sum: 1 }
}
}
]);
docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
let docs = await Character.aggregate([
  { $match: { age: { $lt: 30 } } },
  {
    $group: {
      _id: $age,
      count: { $sum: 1 }
    }
  }
]);

docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
let docs = await Character.aggregate([ { $match: { age: { $lt: 30 } } }, { $group: { _id: $age, count: { $sum: 1 } } } ]); docs.length; // 3 docs.sort((d1, d2) => d1._id - d2._id); docs[0]; // { _id: 24, count: 1 } docs[1]; // { _id: 28, count: 1 } docs[2]; // { _id: 29, count: 2 }

Mongoose Aggregate Class

Mongoose 的 aggregate() 函数返回一个 Mongoose 的实例 AggregateAggregate实例是 thenable ,因此您可以将它们与 await承诺链接

Aggregate 类还支持用于构建聚合管道的链接接口。 例如,下面的代码显示了构建聚合管道的另一种语法 $match 其次是 $group

<code class="language-javascript">let docs = await Character.aggregate().
match({ age: { $lt: 30 } }).
group({ _id: $age, count: { $sum: 1 } });
docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }</code>
<code class="language-javascript">let docs = await Character.aggregate().
  match({ age: { $lt: 30 } }).
  group({ _id: $age, count: { $sum: 1 } });

docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }</code>
let docs = await Character.aggregate().
match({ age: { $lt: 30 } }).
group({ _id: $age, count: { $sum: 1 } });
docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
let docs = await Character.aggregate().
  match({ age: { $lt: 30 } }).
  group({ _id: $age, count: { $sum: 1 } });

docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
let docs = await Character.aggregate(). match({ age: { $lt: 30 } }). group({ _id: $age, count: { $sum: 1 } }); docs.length; // 3 docs.sort((d1, d2) => d1._id - d2._id); docs[0]; // { _id: 24, count: 1 } docs[1]; // { _id: 28, count: 1 } docs[2]; // { _id: 29, count: 2 }

Mongoose 中间件 也支持 pre(aggregate)post(aggregate)钩子。 您可以使用聚合中间件来转换聚合管道。

<code class="language-javascript">const characterSchema = Schema({ name: String, age: Number });
characterSchema.pre(aggregate, function() {
// Add a `$match` to the beginning of the pipeline
this.pipeline().unshift({ $match: { age: { $lt: 30 } } });
});
const Character = mongoose.model(Character, characterSchema);
// The `pre(aggregate)` adds a `$match` to the pipeline.
let docs = await Character.aggregate().
group({ _id: $age, count: { $sum: 1 } });
docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }</code>
<code class="language-javascript">const characterSchema = Schema({ name: String, age: Number });
characterSchema.pre(aggregate, function() {
  // Add a `$match` to the beginning of the pipeline
  this.pipeline().unshift({ $match: { age: { $lt: 30 } } });
});
const Character = mongoose.model(Character, characterSchema);

// The `pre(aggregate)` adds a `$match` to the pipeline.
let docs = await Character.aggregate().
  group({ _id: $age, count: { $sum: 1 } });

docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }</code>
const characterSchema = Schema({ name: String, age: Number });
characterSchema.pre(aggregate, function() {
// Add a `$match` to the beginning of the pipeline
this.pipeline().unshift({ $match: { age: { $lt: 30 } } });
});
const Character = mongoose.model(Character, characterSchema);
// The `pre(aggregate)` adds a `$match` to the pipeline.
let docs = await Character.aggregate().
group({ _id: $age, count: { $sum: 1 } });
docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
const characterSchema = Schema({ name: String, age: Number });
characterSchema.pre(aggregate, function() {
  // Add a `$match` to the beginning of the pipeline
  this.pipeline().unshift({ $match: { age: { $lt: 30 } } });
});
const Character = mongoose.model(Character, characterSchema);

// The `pre(aggregate)` adds a `$match` to the pipeline.
let docs = await Character.aggregate().
  group({ _id: $age, count: { $sum: 1 } });

docs.length; // 3
docs.sort((d1, d2) => d1._id - d2._id);
docs[0]; // { _id: 24, count: 1 }
docs[1]; // { _id: 28, count: 1 }
docs[2]; // { _id: 29, count: 2 }
const characterSchema = Schema({ name: String, age: Number }); characterSchema.pre(aggregate, function() { // Add a `$match` to the beginning of the pipeline this.pipeline().unshift({ $match: { age: { $lt: 30 } } }); }); const Character = mongoose.model(Character, characterSchema); // The `pre(aggregate)` adds a `$match` to the pipeline. let docs = await Character.aggregate(). group({ _id: $age, count: { $sum: 1 } }); docs.length; // 3 docs.sort((d1, d2) => d1._id - d2._id); docs[0]; // { _id: 24, count: 1 } docs[1]; // { _id: 28, count: 1 } docs[2]; // { _id: 29, count: 2 }
© 版权声明
THE END
喜欢就支持一下吧
点赞312 分享
The sacrifices you make today will pay dividends in the future.
今天的牺牲和努力未来都会有回报
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容