使用MongoDB关联查询

  • 使用MongoDB关联查询
    • Mongo shell 关联查询
      • $lookup 简单教程
      • $lookup 示例
        • 使用$lookup执行单个相等连接
        • 使用$lookup与一个数组
        • 使用$lookup与\$mergeObjects
        • 使用$lookup指定多个连接条件
        • 不相关子查询
    • MongoTemplate 关联查询

使用MongoDB关联查询

在工作中,我们有时会使用MongoDB来存储一些复杂数据(有层级关系),一般情况下我们是直接单集和(表)进行CRUD,特殊情况下我们也需要进行关联查询。
下面,我们首先介绍在Mongo Shell 中进行关联查询,然后再使用Java代码利用MongoTemplate 框架进行关联查询:

  1. Mongo Shell(Mongo命令行)
  2. MongoTemplate(spring-data-mongodb框架)

注意:MongoDB虽然支持关联查询,但版本需要至少3.2及以上,且关联查询不如关系型数据库那样多表关联查询简单。

Mongo shell 关联查询

官网介绍$lookup

$lookup 简单教程

我们需要先简单了解下 $lookup,下图是官网给的定义:

$lookup 语法:

{$lookup:{from: <collection to join>,localField: <field from the input documents>,foreignField: <field from the documents of the "from" collection>,as: <output array field>}
}
ffield 作用
from Specifies the collection in the same database to perform the join with. The from collection cannot be sharded. For details, see Sharded Collection Restrictions.

指定同一数据库中用于执行联接的集合。从集合中不能分片。有关详细信息,请参阅分片收集限制。

localField Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection. If an input document does not contain the localField, the $lookup treats the field as having a value of null for matching purposes.

指定从文档输入到 查找工作台的字段。查找工作台的字段。查找工作台的字段。查找从 from 集合的文档中对 localField 与 foreignField 执行相等匹配。如果输入文档不包含 localField,则 $lookup 将该字段视为具有 null 值,以便进行匹配。

foreignField Specifies the field from the documents in the from collection. $lookup performs an equality match on the foreignField to the localField from the input documents. If a document in the from collection does not contain the foreignField, the $lookup treats the value as null for matching purposes.

指定 from 集合中文档的字段。$lookup在 foreignField 上执行与输入文档中的 localField 相等的匹配。如果 from 集合中的文档不包含 foreignField,则为了进行匹配,$查找将该值视为 null。

as Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection. If the specified name already exists in the input document, the existing field is overwritten.

指定要添加到输入文档中的新数组字段的名称。新的数组字段包含来自 from 集合的匹配文档。如果输入文档中已经存在指定的名称,则会覆盖现有字段。

该操作将对应于以下伪 sql 语句:

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (SELECT *FROM <collection to join>WHERE <foreignField>= <collection.localField>);

$lookup 示例

以下内用均出自MongoDB官方文档

使用$lookup执行单个相等连接

  1. 使用以下文档创建一个收集订单(orders):
db.orders.insert([{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 },{ "_id" : 3  }
])
  1. 使用以下文档创建另一个库存集合(inventory):
db.inventory.insert([{ "_id" : 1, "sku" : "almonds", "description": "product 1", "instock" : 120 },{ "_id" : 2, "sku" : "bread", "description": "product 2", "instock" : 80 },{ "_id" : 3, "sku" : "cashews", "description": "product 3", "instock" : 60 },{ "_id" : 4, "sku" : "pecans", "description": "product 4", "instock" : 70 },{ "_id" : 5, "sku": null, "description": "Incomplete" },{ "_id" : 6 }
])
  1. 以下订单集合(orders)上的聚合操作使用来自订单集合(orders)的item字段和来自库存集合(inventory)的sku字段将来自订单集合的文档与来自库存集合的文档连接起来:
db.orders.aggregate([{$lookup:{from: "inventory",localField: "item",foreignField: "sku",as: "inventory_docs"}}
])
  1. 该操作返回以下文档:
{"_id" : 1,"item" : "almonds","price" : 12,"quantity" : 2,"inventory_docs" : [{ "_id" : 1, "sku" : "almonds", "description" : "product 1", "instock" : 120 }]
}
{"_id" : 2,"item" : "pecans","price" : 20,"quantity" : 1,"inventory_docs" : [{ "_id" : 4, "sku" : "pecans", "description" : "product 4", "instock" : 70 }]
}
{"_id" : 3,"inventory_docs" : [{ "_id" : 5, "sku" : null, "description" : "Incomplete" },{ "_id" : 6 }]
}
  1. 该操作将对应于下面的伪sql语句
SELECT *, inventory_docs
FROM orders
WHERE inventory_docs IN (SELECT *
FROM inventory
WHERE sku= orders.item);

使用$lookup与一个数组

启动MongoDB 3.4,如果localField是一个数组,你可以匹配一个标量foreignField的数组元素,而不需要$unwind阶段。

  1. 例如,使用以下文档创建一个示例集合(classes):
db.classes.insert( [{ _id: 1, title: "Reading is ...", enrollmentlist: [ "giraffe2", "pandabear", "artie" ], days: ["M", "W", "F"] },{ _id: 2, title: "But Writing ...", enrollmentlist: [ "giraffe1", "artie" ], days: ["T", "F"] }
])
  1. 使用以下文档创建另一个集合(members):
db.members.insert( [{ _id: 1, name: "artie", joined: new Date("2016-05-01"), status: "A" },{ _id: 2, name: "giraffe", joined: new Date("2017-05-01"), status: "D" },{ _id: 3, name: "giraffe1", joined: new Date("2017-10-01"), status: "A" },{ _id: 4, name: "panda", joined: new Date("2018-10-11"), status: "A" },{ _id: 5, name: "pandabear", joined: new Date("2018-12-01"), status: "A" },{ _id: 6, name: "giraffe2", joined: new Date("2018-12-01"), status: "D" }
])
  1. 下面的聚合操作将classes集合中的文档与members集合连接起来,classes集和的enrollee_info字段与members的字段name之间进行匹配:
db.classes.aggregate([{$lookup:{from: "members",localField: "enrollmentlist",foreignField: "name",as: "enrollee_info"}}
])
  1. 该操作返回以下结果:
{"_id" : 1,"title" : "Reading is ...","enrollmentlist" : [ "giraffe2", "pandabear", "artie" ],"days" : [ "M", "W", "F" ],"enrollee_info" : [{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },{ "_id" : 5, "name" : "pandabear", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "A" },{ "_id" : 6, "name" : "giraffe2", "joined" : ISODate("2018-12-01T00:00:00Z"), "status" : "D" }]
}
{"_id" : 2,"title" : "But Writing ...","enrollmentlist" : [ "giraffe1", "artie" ],"days" : [ "T", "F" ],"enrollee_info" : [{ "_id" : 1, "name" : "artie", "joined" : ISODate("2016-05-01T00:00:00Z"), "status" : "A" },{ "_id" : 3, "name" : "giraffe1", "joined" : ISODate("2017-10-01T00:00:00Z"), "status" : "A" }]
}

使用$lookup与$mergeObjects

在3.6版更改:MongoDB 3.6添加了$mergeObjects操作符来将多个文档合并为一个文档

  1. 使用以下文档重新创建订单集和(orders):
db.orders.insert([{ "_id" : 1, "item" : "almonds", "price" : 12, "quantity" : 2 },{ "_id" : 2, "item" : "pecans", "price" : 20, "quantity" : 1 }
])
  1. 使用以下文档创建另一个集合(items):
db.items.insert([{ "_id" : 1, "item" : "almonds", description: "almond clusters", "instock" : 120 },{ "_id" : 2, "item" : "bread", description: "raisin and nut bread", "instock" : 80 },{ "_id" : 3, "item" : "pecans", description: "candied pecans", "instock" : 60 }
])
  1. 下面的操作首先使用$lookup阶段通过item字段来连接两个集合,然后使用$replaceRoot中的$mergeObjects来合并item和orders中的已连接文档:
db.orders.aggregate([{$lookup: {from: "items",localField: "item",    // field in the orders collectionforeignField: "item",  // field in the items collectionas: "fromItems"}},{$replaceRoot: { newRoot: { $mergeObjects: [ { $arrayElemAt: [ "$fromItems", 0 ] }, "$$ROOT" ] } }},{ $project: { fromItems: 0 } }
])

该操作返回以下文档:

{ "_id" : 1, "item" : "almonds", "description" : "almond clusters", "instock" : 120, "price" : 12, "quantity" : 2 }
{ "_id" : 2, "item" : "pecans", "description" : "candied pecans", "instock" : 60, "price" : 20, "quantity" : 1 }

使用$lookup指定多个连接条件

在3.6版更改:MongoDB 3.6增加了对已连接集合执行管道的支持,这允许指定多个连接条件以及不相关的子查询。

  1. 使用以下文档创建一个订单集和(orders):
db.orders.insert([{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
])
  1. 使用以下文档创建另一个仓库集合(warehouses):
db.warehouses.insert([{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120 },{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80 },{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60 },{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40 },{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80 }
])
  1. 以下操作将订单集合与仓库集合结合起来,根据项目以及库存数量是否足以涵盖订购数量:
db.orders.aggregate([{$lookup:{from: "warehouses",let: { order_item: "$item", order_qty: "$ordered" },pipeline: [{ $match:{ $expr:{ $and:[{ $eq: [ "$stock_item",  "$$order_item" ] },{ $gte: [ "$instock", "$$order_qty" ] }]}}},{ $project: { stock_item: 0, _id: 0 } }],as: "stockdata"}}
])
  1. 该操作返回以下文档:
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2,"stockdata" : [ { "warehouse" : "A", "instock" : 120 },{ "warehouse" : "B", "instock" : 60 } ] }
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1,"stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60,"stockdata" : [ { "warehouse" : "A", "instock" : 80 } ] }
  1. 该操作对应于以下伪sql语句:
SELECT *, stockdata
FROM orders
WHERE stockdata IN ( SELECT warehouse, instockFROM warehousesWHERE stock_item = orders.itemAND instock >= orders.ordered );

The $expr operator only uses indexes on the from collection for equality matches. For example, if the index { stock_item: 1, instock: 1 } exists on the warehouses collection:
The equality match on the warehouses.stock_item field uses the index.
The range part of the query on the warehouses.instock field does not use the indexed field in the compound index.

不相关子查询

在3.6版更改:MongoDB 3.6增加了对已连接集合执行管道的支持,这允许指定多个连接条件以及不相关的子查询。

  1. 根据以下文档创建一个集合(absences):
db.absences.insert([{ "_id" : 1, "student" : "Ann Aardvark", sickdays: [ new Date ("2018-05-01"),new Date ("2018-08-23") ] },{ "_id" : 2, "student" : "Zoe Zebra", sickdays: [ new Date ("2018-02-01"),new Date ("2018-05-23") ] },
])
  1. 使用以下文档创建另一个集合(holidays):
db.holidays.insert([{ "_id" : 1, year: 2018, name: "New Years", date: new Date("2018-01-01") },{ "_id" : 2, year: 2018, name: "Pi Day", date: new Date("2018-03-14") },{ "_id" : 3, year: 2018, name: "Ice Cream Day", date: new Date("2018-07-15") },{ "_id" : 4, year: 2017, name: "New Years", date: new Date("2017-01-01") },{ "_id" : 5, year: 2017, name: "Ice Cream Day", date: new Date("2017-07-16") }
])
  1. 以下操作将 absences集和与holidays集和中的2018年假期信息加入到absences集和中:
db.absences.aggregate([{$lookup:{from: "holidays",pipeline: [{ $match: { year: 2018 } },{ $project: { _id: 0, date: { name: "$name", date: "$date" } } },{ $replaceRoot: { newRoot: "$date" } }],as: "holidays"}}
])

该操作返回以下结果:

{ "_id" : 1, "student" : "Ann Aardvark", "sickdays" : [ ISODate("2018-05-01T00:00:00Z"), ISODate("2018-08-23T00:00:00Z") ],"holidays" : [ { "name" : "New Years", "date" : ISODate("2018-01-01T00:00:00Z") }, { "name" : "Pi Day", "date" : ISODate("2018-03-14T00:00:00Z") }, { "name" : "Ice Cream Day", "date" : ISODate("2018-07-15T00:00:00Z") } ] }
{ "_id" : 2, "student" : "Zoe Zebra", "sickdays" : [ ISODate("2018-02-01T00:00:00Z"), ISODate("2018-05-23T00:00:00Z") ],"holidays" : [ { "name" : "New Years", "date" : ISODate("2018-01-01T00:00:00Z") }, { "name" : "Pi Day", "date" : ISODate("2018-03-14T00:00:00Z") }, { "name" : "Ice Cream Day", "date" : ISODate("2018-07-15T00:00:00Z") } ] }

该操作将对应于以下伪sql语句:

SELECT *, holidays
FROM absences
WHERE holidays IN (SELECT name, dateFROM holidaysWHERE year = 2018);

MongoTemplate 关联查询

需要引入 spring-boot-starter-data-mongodb

下面是Java代码

LookupOperation lookupOperation = LookupOperation.newLookup().from(ExamMongoCollectionConstant.TOPIC)    // 被关联的表 topic.localField(ExamMongoColumnConstant.ID) // 自己的字段.foreignField(ExamMongoColumnConstant.TOPIC_FATHER_ID)  // 被关联的表 topic 中的字段.as("result"); // 关联查询后的结果Criteria criteria = Criteria.where(ExamMongoColumnConstant.RELATION_ID).is(relationId); // 条件查询Aggregation aggregation = Aggregation.newAggregation(// 条件查询过滤Aggregation.match(criteria),// 关联查询lookupOperation);/*其中 TopicJoinTopicFather2DoTopicDTO 是定义的一个结果返回类,需要跟上面result返回的格式一样ExamMongoCollectionConstant.TOPIC_FATHER 表示关联查询的表,TopicJoinTopicFather2DoTopicDTO.class:返回的数据使用该类接收*/// 执行上面的关联查询,并将结果返回成 List<TopicJoinTopicFather2DoTopicDTO> 格式List<TopicJoinTopicFather2DoTopicDTO> list =this.mongoTemplate.aggregate(aggregation, ExamMongoCollectionConstant.TOPIC_FATHER, TopicJoinTopicFather2DoTopicDTO.class).getMappedResults();

上面代码中的 TopicJoinTopicFather2DoTopicDTO 类其样子如下:

注意的是字段上面要加上MongoDB的注解,标明要与哪个字段进行一一对应即可

@Data
public class TopicJoinTopicFather2DoTopicDTO extends Topic {/*** 主键标识,该属性的值会自动对应mongodb的主键字段"_id",如果该属性名就叫“id”,则该注解可以省略,否则必须写*/@Idprivate String id;/*** 题干对象*/@Field("question_detail")private TopicDetailDO questionDetail;/*** 题型样式*/@Field("question_type")private String questionType;/*** 大题的题型名称*/@Field("question_type_name")private String questionTypeName;/*** 课节或试卷uuid*/@Field("relation_id")private String relationId;@Field("result")private List<DoTopicDTO> doTopicDTOS;
}

使用MongoDB关联查询相关推荐

  1. mongodb关联查询

    文章目录 1.mongodb关联查询基本操作 2.关联字段类型不一致时 3.其他 4.DBRef在Lookup中的使用 5.从ISODate中获取年月日等 1.mongodb关联查询基本操作 mong ...

  2. MongoDb 关联查询

    刚接触MongoDB,有个需求是进行两张表的关联查询的. 需求:把订单表(PrepurchaseOrder)和用户表(User)通过邮箱(emaiL)进行关联,查找订单用户对应的钱包地址. 订单表结构 ...

  3. mongodb关联查询 和spring data mongodb

    GITHUB:https://github.com/peterowang/Springdata-mongo 使用DBRefs DBRefs中有三个字段 - $ref - 此字段指定引用文档的集合 $i ...

  4. NoSQLBooster for MongoDB 中跨库关联查询

    ​ 使用 MongoDB 是我们常常会遇到一些特殊的需求需要跨库关联查询,比如订单明细缺商品重量需要补商品重量,而商品重量数据又在商品库中,这事就需要跨库关联操作,示例代码如下: // 使用 orde ...

  5. springboot集成mongoDB高级聚合查询,关联查询

    目录 mongoDB的常用操作符 mongoDB的聚合管道符号 比较操作符 逻辑运算符? 注意注意注意? 数学运算符 mongoDB案例 ? ? ? ? 插入测试数据 ?mongodb的阶段操作符号 ...

  6. java mongodb 多表关联查询,多条件查询,分页,排序

    前言: 由于最近项目赶,版本迭代快,不知道大BOSS从哪里听别人说MongoDB用来做关系型数据库好,而且速度快,性能高:听到这话的我,立马就反驳了回去:"MongoDB不支持事物" ...

  7. springboot集成mongoDB高级聚合查询,关联查询,lookup.let多条件关联查询。

    目录 mongoDB的常用操作符 mongoDB的聚合管道符号 比较操作符 逻辑运算符 注意坑 数学运算符 mongoDB案例 插入测试数据 mongodb的阶段操作符号 $match: $count ...

  8. MongoDB中的聚合管道($lookup多表关联查询、$unwind、$match、$project)

    MongoDB中的聚合管道($lookup多表关联查询.$unwind.$match.$project) 管道的概念 聚合框架 $lookup的功能及语法 主要功能 基本语法 例子 说明 $unwin ...

  9. MongoDB之多表关联查询

    最近在看同事代码的时候,注意到了$lookup多表关联查询,不太清楚类比MySQL中是哪种连接查询,因此就有了此次的测试总结.接下来,我会用测试结果来介绍MySQL中的内连接.左外连接.右外连接,介绍 ...

最新文章

  1. 【问题收录】Eclipse Type Access restriction问题解决
  2. ISA2006之工作组环境下的阵列部署
  3. Eclipse+Tomcat7.0+MySQL 连接池设置
  4. 新建Web网站与新建Web应用程序的区别
  5. android 制作的精美闹钟
  6. 看出每个应用程序最高可用内存是多少
  7. mysql自增长id为null_主键设为自增字段了, 在添加数据时经常报null id是怎么回事?...
  8. 使用python通过odbc处理*.dbf数据表
  9. 从request中获取上一个请求的url
  10. python系列教程_python基础学习系列教程2-进阶之路中绕不开的知识点。
  11. 随手记_科研攻略_常犯错误
  12. win10修改服务器IP,Win10系统更改本地连接ip地址的方法
  13. 不一样的摊余成本法债基—终篇
  14. matlab判断传递函数的稳定性,基于Matlab的控制系统稳定性判定.pdf
  15. 桌面美化 Python tkinter倒计时工具
  16. Angular真有React开发人员讲得那么差劲?
  17. 风云滚滚唱英雄,云的江湖谁做主?
  18. android 状态栏wifi,【技术贴】教你修改状态栏里的wifi、信号、电量图标(转自论坛)...
  19. 人脸识别行业应用状况及发展前景模式分析报告
  20. c语言在测绘工程中的作用,测绘C程序设计实习报告

热门文章

  1. 登录密码规则复杂度:8-20位,必须包含大写字母,小写字母,数字组合,必须包含下面一位特殊字符(#@$%*~)
  2. MySQL快速数据迁移
  3. 老牌激活工具– Microsoft Toolkit 2.4.3 + 详细图文教程【转】
  4. k线顶分型 python_K线战法之『顶底分型』高手懂的!
  5. 计算机和数学 论文参考文献,数学与应用数学专业论文参考文献
  6. 西南大学计算机博士好毕业论文,学位学术论文论文,关于西南大学授予博士学位学术成果相关参考文献资料-免费论文范文...
  7. combox绑定总结
  8. STC89C52RC单片机额外篇 | 02 - 认识串行通信、波特率以及数据包
  9. php做钓鱼视频教程,钓鱼人作品--我做珠珠的一些过程希望对大家有用
  10. window 10 专业版激活 及去掉 window defender