在 $lookup 管道中正确使用 MongoDB 复合主键
Correctly use MongoDB composite primary key in $lookup pipeline
我有一个包含复合主键的集合
_id: {
a: 'a',
b: 'b'
}
解释以下查询,表明它执行 COLLSCAN 并且未使用索引
db.collection.find({'_id.a': '1', '_id.b': '2'}).explain()
将该查询更改为以下内容,成功使用 IDHACK。
db.collection.find({_id: {a: '1', b: '2'}}).explain()
问题是,如果在聚合中 $lookup
的管道中使用,这似乎不起作用。
这 return 没有任何结果:
$lookup: {
from: 'collection',
let: {
pid: '1',
sid: '2',
},
pipeline: [
{
$match: {
_id: {
a: '$$pid',
b: '$$sid',
},
},
},
],
as: 'results',
},
而未使用索引 returns 的格式按预期结果:
$lookup: {
from: 'collection',
let: {
pid: '1',
sid: '2',
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
'$_id.a', '$$pid',
],
}, {
$eq: [
'$_id.b', '$$sid',
],
},
],
},
},
},
],
as: 'results',
},
所以我的问题是,如何修改查找管道才能使用复合主键的索引?
这个问题似乎分为两部分:为什么两个 _id 查询都不使用索引?为什么不都查找 return 数据?
为什么两个查询都没有使用索引?
当MongoDB 比较两个对象时,字段的顺序很重要。这意味着所有这些都可以存储在同一个集合中,并且每个 _id 值都是唯一的:
{_id:{ a:'1',b:'2'}},
{_id:{ b:'2',a:'1'}},
{_id:{ a:'1',b:'2',c:'3'}},
{_id:{ a:'1',c:'3',b:'2'}},
{_id:{ c:'3',a:'1',b:'2'}}
当索引包含 {_id: 1}
等字段时,在构建索引键时会使用该字段的整个值。鉴于上述文档,{"_id.a":'1'}
上的查询无法由 {_id:1}
上的索引提供服务。
查询
db.collection.find({_id: {a: '1', b: '2'}})
将只匹配上面列表中的第一个文档,并且由于它只查询整个 _id
字段,因此索引可以正确地为它提供服务。
查询
db.collection.find({'_id.a': '1', '_id.b': '2'})
匹配 _id
字段中包含的子文档中的字段值,而不是整个字段,因此 {_id:
上的索引无法正确提供它}`
此查询将匹配上面列表中的 每个 文档。
为什么不同时查找 return 数据?
MongoDB查询语言和聚合语言不同。
其中一个区别是聚合语言会扩展字符串中的变量值,而查询语言不会。
有时会有点混乱,因为查询语言是在 $match
聚合阶段内部使用的,除非您使用 $expr
运算符将聚合表达式括起来。
此匹配阶段正在检查 _id
字段恰好是文档 {a: '$$pid', b: '$$sid'}
的文档(即 _id.a
字段包含字符串“$$pid”等。 )
{
$match: {
_id: {
a: '$$pid',
b: '$$sid',
},
},
},
要使用变量,您还需要像在其他查询中一样使用 $expr
运算符。
{$lookup:{
let: {pid:'1', sid:'2'},
as:"lookedup",
from:"collection",
pipeline:[{
$match:{
$expr:{
$eq:[ "$_id", {a:"$$pid", b:"$$sid"}]
}
}
}]
}}
我有一个包含复合主键的集合
_id: {
a: 'a',
b: 'b'
}
解释以下查询,表明它执行 COLLSCAN 并且未使用索引
db.collection.find({'_id.a': '1', '_id.b': '2'}).explain()
将该查询更改为以下内容,成功使用 IDHACK。
db.collection.find({_id: {a: '1', b: '2'}}).explain()
问题是,如果在聚合中 $lookup
的管道中使用,这似乎不起作用。
这 return 没有任何结果:
$lookup: {
from: 'collection',
let: {
pid: '1',
sid: '2',
},
pipeline: [
{
$match: {
_id: {
a: '$$pid',
b: '$$sid',
},
},
},
],
as: 'results',
},
而未使用索引 returns 的格式按预期结果:
$lookup: {
from: 'collection',
let: {
pid: '1',
sid: '2',
},
pipeline: [
{
$match: {
$expr: {
$and: [
{
$eq: [
'$_id.a', '$$pid',
],
}, {
$eq: [
'$_id.b', '$$sid',
],
},
],
},
},
},
],
as: 'results',
},
所以我的问题是,如何修改查找管道才能使用复合主键的索引?
这个问题似乎分为两部分:为什么两个 _id 查询都不使用索引?为什么不都查找 return 数据?
为什么两个查询都没有使用索引?
当MongoDB 比较两个对象时,字段的顺序很重要。这意味着所有这些都可以存储在同一个集合中,并且每个 _id 值都是唯一的:
{_id:{ a:'1',b:'2'}},
{_id:{ b:'2',a:'1'}},
{_id:{ a:'1',b:'2',c:'3'}},
{_id:{ a:'1',c:'3',b:'2'}},
{_id:{ c:'3',a:'1',b:'2'}}
当索引包含 {_id: 1}
等字段时,在构建索引键时会使用该字段的整个值。鉴于上述文档,{"_id.a":'1'}
上的查询无法由 {_id:1}
上的索引提供服务。
查询
db.collection.find({_id: {a: '1', b: '2'}})
将只匹配上面列表中的第一个文档,并且由于它只查询整个 _id
字段,因此索引可以正确地为它提供服务。
查询
db.collection.find({'_id.a': '1', '_id.b': '2'})
匹配 _id
字段中包含的子文档中的字段值,而不是整个字段,因此 {_id:
上的索引无法正确提供它}`
此查询将匹配上面列表中的 每个 文档。
为什么不同时查找 return 数据?
MongoDB查询语言和聚合语言不同。
其中一个区别是聚合语言会扩展字符串中的变量值,而查询语言不会。
有时会有点混乱,因为查询语言是在 $match
聚合阶段内部使用的,除非您使用 $expr
运算符将聚合表达式括起来。
此匹配阶段正在检查 _id
字段恰好是文档 {a: '$$pid', b: '$$sid'}
的文档(即 _id.a
字段包含字符串“$$pid”等。 )
{
$match: {
_id: {
a: '$$pid',
b: '$$sid',
},
},
},
要使用变量,您还需要像在其他查询中一样使用 $expr
运算符。
{$lookup:{
let: {pid:'1', sid:'2'},
as:"lookedup",
from:"collection",
pipeline:[{
$match:{
$expr:{
$eq:[ "$_id", {a:"$$pid", b:"$$sid"}]
}
}
}]
}}