在 $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"}]
           }
        }
    }]
}}