如何解决MongoDB - 多次获取第一个匹配的文档 - 哪个更好?
想象一个集合是这样的:
{
created_at: Date,color: string,// 'blue' | 'red' | 'yellow'
}
我在一个集合中有数千或数百万个文档,其中包含随机 created_at 日期时间和不同颜色。 现在我想拥有最新创建的颜色“蓝色”的文档和最新创建的颜色“红色”的文档。
哪种方法最好? (随意推荐另一个)
A) 多个并行查询
const latest = await Promise.all([
collection.findOne({ color: 'blue' },{ sort: { created_at: -1 } }),collection.findOne({ color: 'red' },]);
B) 一个聚合查询 - 组
const latest = await collection.aggregate([
{ $match: { color: { $in: ['blue','red'] } } },{ $sort: { created_at: -1 } },{
$group: {
_id: "$color",latest: { $first: "$$ROOT" }
}
},]).toArray();
C) 一个聚合查询 - 方面
const latest = await collection.aggregate([
{ $match: { color: { $in: ['blue',{
$facet: {
blue: [
{ $match: { color: 'blue' } },{ $limit: 1 },],red: [
{ $match: { color: 'red' } },}
},]).toArray();
哪个性能最好?如果我想为超过 2 种颜色执行此操作怎么办?
附带问题:实际上,对于 $facet 操作,我很想知道这个问题。由于 $facet 不能使用索引,因此在某些情况下并行执行多个查询似乎更好。如果每组有很多文档(在本例中为“颜色”),使用索引似乎很有用。所以我想选项C不是很好。对于选项 B,我想知道 MongoDB 是否首先需要获取所有符合通用条件的文档,对所有文档进行排序,将所有文档分组,然后只取第一个...
提前致谢!
解决方法
您的解决方案都没有使用索引(我猜是因为您还没有创建它)。
创建以下索引:
{ color: 1,created_at: -1 }
然后您可以对查询运行解释计划并查看它们的行为。
我“认为”第一个会是最快的。
,似乎选项 A 是最好的,至少在我测试的情况下是这样。
我做了如下性能测试:
我创建了一个包含 3.000.000 个文档和 2 个字段的集合:
-
created_at
:2018-01-01T00:00:00.000Z 和 2030-01-01T00:00:00.000Z 之间的随机日期 -
color
:随机字符串,31 个不同的选项
我做了一个查询,为 17 种不同颜色的每一种获取最新创建的文档。
结果
没有索引 | { color: 1,created_at: -1 } | { created_at: -1,color: 1 } | |
---|---|---|---|
A1) 一个 FindOne (shell) | 70 毫秒 | 60 毫秒 | 60 毫秒 |
A2) FindOnes 并行 (JS) | 21944 毫秒 | 543 毫秒 | 572 毫秒 |
B) 聚合 - 组 - 首先 | QueryExceededMemoryLimitNoDiskUseAllowed | 9120 毫秒 | 10200 毫秒 |
C) 聚合 - 方面 | QueryExceededMemoryLimitNoDiskUseAllowed | 3860 毫秒 | 4770 毫秒 |
选项 A1 是最快的,但这仅适用于一种颜色(我无法通过外壳并行执行)。所以真正的赢家是选项A2!另请注意,A2 是通过连接到外部数据库的本地运行 JS 脚本完成的。它仍然比直接通过 shell 完成的其他选项要快得多。
我检查了并行执行 5 个 findOnes 而不是 17 个的影响:
A3) FindOnes 并行 (JS) | 6656 毫秒 | 334 毫秒 | 339 毫秒
这明显更快。我怀疑如果您需要超过 17 个案例,其他选择可能会更好。它与我的需求无关,所以我没有对此进行测试。
为了完整性,确切的查询:
A1)
db.getCollection('colors').findOne({ color: 'Ivory' },{ sort: { created_at: -1 } })
A2)
const latest = await Promise.all(colors.map((c) => mongodb.collection('colors').findOne({ color: c },{ sort: { created_at: -1 } })));
B)
db.getCollection('colors').aggregate([
{ $match: { color: { $in: [
'Ivory','Teal','Silver','Purple','Navy blue','Pea green','Gray','Orange','Maroon','Charcoal','Aquamarine','Coral','Fuchsia','Wheat','Lime','Crimson','Khaki'
] } } },{ $sort: { created_at: -1 } },{
$group: {
_id: "$color",latest: { $first: "$$ROOT" }
}
},]);
C)
db.getCollection('colors').aggregate([
{ $match: { color: { $in: [
'Ivory',{
$facet: {
Ivory: [
{ $match: { color: 'Ivory' } },{ $limit: 1 },],Teal: [
{ $match: { color: 'Teal' } },Silver: [
{ $match: { color: 'Silver' } },Purple: [
{ $match: { color: 'Purple' } },Navyblue: [
{ $match: { color: 'Navy blue' } },Peagreen: [
{ $match: { color: 'Pea green' } },Gray: [
{ $match: { color: 'Gray' } },Orange: [
{ $match: { color: 'Orange' } },Maroon: [
{ $match: { color: 'Maroon' } },Charcoal: [
{ $match: { color: 'Charcoal' } },Aquamarine: [
{ $match: { color: 'Aquamarine' } },Coral: [
{ $match: { color: 'Coral' } },Fuchsia: [
{ $match: { color: 'Fuchsia' } },Wheat: [
{ $match: { color: 'Wheat' } },Lime: [
{ $match: { color: 'Lime' } },Crimson: [
{ $match: { color: 'Crimson' } },Khaki: [
{ $match: { color: 'Khaki' } },}
},]);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。