如何在mongoDB中编写联合查询

Pun*_*nit 0 mongodb spring-mongo

是否可以使用2个或更多个类似于SQL查询的集合在Mongo DB中编写联合查询?

我正在使用spring mongo模板,用例是我需要根据某些条件从3-4个集合中获取数据。我们可以一次完成此操作吗?

例如,我有一个名为“ circuitId”的字段,该字段存在于所有4个集合中。现在,在搜索操作中,我需要从该字段与给定值匹配的所有4个集合中获取所有记录。

Xav*_*hot 7

开始Mongo 4.4,聚合框架提供了一个新$unionWith阶段,执行两个集合的并集(将两个集合的组合管道结果合并为一个结果集)。

因此,为了组合来自 3 个集合的文档:

// > db.collection1.find()
//   { "circuitId" : 12, "a" : "1" }
//   { "circuitId" : 17, "a" : "2" }
//   { "circuitId" : 12, "a" : "5" }
// > db.collection2.find()
//   { "circuitId" : 12, "b" : "x" }
//   { "circuitId" : 12, "b" : "y" }
// > db.collection3.find()
//   { "circuitId" : 12, "c" : "i" }
//   { "circuitId" : 32, "c" : "j" }
db.collection1.aggregate([
  { $match: { circuitId: 12 } },
  { $unionWith: { coll: "collection2", pipeline: [{ $match: { circuitId: 12 } }] } },
  { $unionWith: { coll: "collection3", pipeline: [{ $match: { circuitId: 12 } }] } }
])
// { "circuitId" : 12, "a" : "1" }
// { "circuitId" : 12, "a" : "5" }
// { "circuitId" : 12, "b" : "x" }
// { "circuitId" : 12, "b" : "y" }
// { "circuitId" : 12, "c" : "i" }
Run Code Online (Sandbox Code Playgroud)

这个:

  • 首先过滤文档 collection1
  • 然后将文档collection2从新$unionWith阶段包含到管道中。该pipeline参数是一个可选的聚合管道,在合并发生之前应用于正在合并的集合中的文档。
  • 并且还包括从collection3同一$unionWith阶段进入管道的文档。


sbo*_*sse 5

在单个查询中,可以使用聚合和查找以“ SQL UNION”方式在MongoDB中进行联合。

像这样:

    db.getCollection("AnyCollectionThatContainsAtLeastOneDocument").aggregate(
    [
      { $limit: 1 }, // Reduce the result set to a single document.
      { $project: { _id: 1 } }, // Strip all fields except the Id.
      { $project: { _id: 0 } }, // Strip the id. The document is now empty.

      // Lookup all collections to union together.
      { $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
      { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
      { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } },

      // Merge the collections together.
      {
        $project:
        {
          Union: { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
        }
      },

      { $unwind: "$Union" }, // Unwind the union collection into a result set.
      { $replaceRoot: { newRoot: "$Union" } } // Replace the root to cleanup the resulting documents.
    ]);
Run Code Online (Sandbox Code Playgroud)

以下是其工作原理的说明:

  1. 实例化数据库中至少包含一个文档aggregate任何集合。如果不能保证数据库的任何集合都不为空,则可以通过在数据库中创建某种“虚拟”集合来解决此问题,该“虚拟”集合中仅包含一个空文档,专门用于进行联合查询。

  2. 使管道的第一阶段成为{ $limit: 1 }。这将删除集合中除第一个文档外的所有文档。

  3. 通过使用$project阶段来剥离剩余文档的所有字段:

    { $project: { _id: 1 } },
    { $project: { _id: 0 } }
    
    Run Code Online (Sandbox Code Playgroud)
  4. 您的汇总现在包含一个空文档。现在该为要合并在一起的每个集合添加查找。您可以使用该pipeline字段进行某些特定的过滤,也可以使用离开localFieldforeignField设为null来匹配整个集合。

    { $lookup: { from: 'collectionToUnion1', pipeline: [...], as: 'Collection1' } },
    { $lookup: { from: 'collectionToUnion2', pipeline: [...], as: 'Collection2' } },
    { $lookup: { from: 'collectionToUnion3', pipeline: [...], as: 'Collection3' } }
    
    Run Code Online (Sandbox Code Playgroud)
  5. 现在,您有一个包含单个文档的聚合,该文档包含3个数组,如下所示:

    {
        Collection1: [...],
        Collection2: [...],
        Collection3: [...]
    }
    
    Run Code Online (Sandbox Code Playgroud)

    然后,您可以使用一个$project阶段以及$concatArrays聚合运算符将它们合并到一个数组中:

    {
      "$project" :
      {
        "Union" : { $concatArrays: ["$Collection1", "$Collection2", "$Collection3"] }
      }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  6. 现在,您有了一个包含单个文档的聚合,其中包含一个包含集合的并存的数组。剩下要做的是添加$unwind$replaceRoot阶段,以将数组拆分为单独的文档:

    { $unwind: "$Union" },
    { $replaceRoot: { newRoot: "$Union" } }
    
    Run Code Online (Sandbox Code Playgroud)
  7. Voilà。您知道有一个结果集,其中包含要合并在一起的集合。然后,您可以添加更多阶段以对其进行进一步过滤,排序,应用skip()和limit()。您想要的几乎任何东西。

  • 我们没有对其进行分析,但我们在生产环境中使用了这种方法,并且性能看起来不成问题。 (2认同)
  • 在我的项目中这是一个问题,因为 $lookup 超出了其最大容量。因此,对于使用它的人来说,如果您期望在您的集合中写入更多文档,请考虑未来的问题。 (2认同)

Pub*_*ana 2

不幸的是,基于文档的 MongoDB 不支持关系数据库引擎中的 JOINS/Union。MongoDB 的关键设计原则之一是根据应用程序的数据获取模式防止使用嵌入文档进行连接。话虽如此,如果您确实需要使用 4 个集合,则需要管理应用程序端的逻辑,或者您可以根据 MongoDB 最佳实践重新设计数据库设计。

欲了解更多信息:https://docs.mongodb.com/master/core/data-model-design/