MongoDB 聚合 -- 连表查询
<p>在使用MongoDB存储数据的时候,我们查询的时候,有时候难免会需要进行连表查询。但是MongoDB本身是非关系性数据库,连表查询,很多时候,需要我们自己在代码里手工操作。但是从 MongoDB 3.2 版本过后,我们可以使用 $lookup 进行连表查询。下面就简单介绍一下 MongoDB 的 $lookup 的简单使用。</p><p> 比如现在我们有两张表, user 和 order 表。其中 user 表中的字段有 _id、uid、name、age;order 表中的字段有:_id、uid、product、money; 两张表存储的数据为:</p>
<div class="cnblogs_code">
<p>users = [{<br>_id: ObjectId("5af2b2c6b138c267e414c072"),<br>uid: "uid000",<br>name: "小红",<br>age: 26<br>}, {<br>_id: ObjectId("5af2b2c6b138c267e414c073"),<br>uid: "uid001",<br>name: "小芳",<br>age: 27<br>}]</p>
</div>
<p> </p>
<div class="cnblogs_code">
<pre>orders =<span style="color: rgba(0, 0, 0, 1)"> [{
_id: ObjectId(</span>"4af2b2c6b138c267e414c071"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>100<span style="color: rgba(0, 0, 0, 1)">
}, {
_id: ObjectId(</span>"4af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品2"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>200<span style="color: rgba(0, 0, 0, 1)">
}, {
_id: ObjectId(</span>"4af2b2c6b138c267e414c073"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid001"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>100<span style="color: rgba(0, 0, 0, 1)">
}, {
_id: ObjectId(</span>"4af2b2c6b138c267e414c074"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid001"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品2"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>200<span style="color: rgba(0, 0, 0, 1)">
}]</span></pre>
</div>
<p> </p>
<p><br>假如现在有两个需求:</p>
<p>查询用户信息并且显示该用户的总消费金额(用户名、年龄、总消费金额)<br>查询用户的订单信息(订单id、产品、价格、用户名)<br>1. 首先来看第一个需求:<br> 这个需求如果我们不考虑连表,只考虑关联的话,应该是</p>
<p>先查询出用户表所有的数据<br>在订单表中求出每一个用户的消费总金额<br>遍历用户和订单数据,然后一一通过 uid 进行匹配对应。<br> 如果按照我们的数据库连表来说:那应该是我们查询 user 表关联到 order 表,然后分组根据 uid 统计求和;下面来看一看具体的实现方式。</p>
<p>1.1 连表查询</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">db.user.aggregate([{
$lookup: { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 左连接</span>
from: "order", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 关联到order表</span>
localField: "uid", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> user 表关联的字段</span>
foreignField: "uid", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> order 表关联的字段</span>
as: "orders"<span style="color: rgba(0, 0, 0, 1)">
}
}]);</span></pre>
</div>
<p> </p>
<p><br>这个时候出来的结果应该为:</p>
<div class="cnblogs_code">
<pre>users =<span style="color: rgba(0, 0, 0, 1)"> [{
_id: ObjectId(</span>"5af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
name: </span>"小红"<span style="color: rgba(0, 0, 0, 1)">,
age: </span>26<span style="color: rgba(0, 0, 0, 1)">,
orders: [{
_id: ObjectId(</span>"4af2b2c6b138c267e414c071"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>100<span style="color: rgba(0, 0, 0, 1)">
}, {
_id: ObjectId(</span>"4af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品2"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>200<span style="color: rgba(0, 0, 0, 1)">
}]
}, {
_id: ObjectId(</span>"5af2b2c6b138c267e414c073"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid001"<span style="color: rgba(0, 0, 0, 1)">,
name: </span>"小芳"<span style="color: rgba(0, 0, 0, 1)">,
age: </span>27<span style="color: rgba(0, 0, 0, 1)">,
orders: [{
_id: ObjectId(</span>"4af2b2c6b138c267e414c073"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid001"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>100<span style="color: rgba(0, 0, 0, 1)">
}, {
_id: ObjectId(</span>"4af2b2c6b138c267e414c073"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid001"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>200<span style="color: rgba(0, 0, 0, 1)">
}]
}]</span></pre>
</div>
<p> </p>
<p><br>1.2 拆分 orders 数组</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
$unwind: { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 拆分子数组</span>
path: "$orders"<span style="color: rgba(0, 0, 0, 1)">,
preserveNullAndEmptyArrays: </span><span style="color: rgba(0, 0, 255, 1)">true</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空的数组也拆分</span>
<span style="color: rgba(0, 0, 0, 1)">}
}</span></pre>
</div>
<p> </p>
<p><br>这个时候的数据结果应该是这样的</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">[{
_id: ObjectId(</span>"5af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
name: </span>"小红"<span style="color: rgba(0, 0, 0, 1)">,
age: </span>26<span style="color: rgba(0, 0, 0, 1)">,
orders: {
_id: ObjectId(</span>"4af2b2c6b138c267e414c071"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>100<span style="color: rgba(0, 0, 0, 1)">
}
}, {
_id: ObjectId(</span>"5af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
name: </span>"小红"<span style="color: rgba(0, 0, 0, 1)">,
age: </span>26<span style="color: rgba(0, 0, 0, 1)">,
orders: {
_id: ObjectId(</span>"4af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品2"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>200<span style="color: rgba(0, 0, 0, 1)">
}
} …… ]</span></pre>
</div>
<p> </p>
<p><br>1.3 分组求和并返回字段数据</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
$group: { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分组查询</span>
_id: "$_id"<span style="color: rgba(0, 0, 0, 1)">,
name: { $first: </span>"$name"<span style="color: rgba(0, 0, 0, 1)"> },
age: { $first: </span>"$age"<span style="color: rgba(0, 0, 0, 1)"> },
money: {$sum: </span>"$orders.money"<span style="color: rgba(0, 0, 0, 1)">}
}
}</span></pre>
</div>
<p> </p>
<p><br>这样就查询出了我们所需要的数据。将代码总结一下为:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">db.user.aggregate([{
$lookup: { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 左连接</span>
from: "order", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 关联到order表</span>
localField: "uid", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> user 表关联的字段</span>
foreignField: "uid", <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> order 表关联的字段</span>
as: "orders"<span style="color: rgba(0, 0, 0, 1)">
}
}, {
$unwind: { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 拆分子数组</span>
path: "$orders"<span style="color: rgba(0, 0, 0, 1)">,
preserveNullAndEmptyArrays: </span><span style="color: rgba(0, 0, 255, 1)">true</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空的数组也拆分</span>
<span style="color: rgba(0, 0, 0, 1)">}
}, { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分组求和并返回</span>
$group: { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分组查询</span>
_id: "$_id"<span style="color: rgba(0, 0, 0, 1)">,
name: { $first: </span>"$name"<span style="color: rgba(0, 0, 0, 1)"> },
age: { $first: </span>"$age"<span style="color: rgba(0, 0, 0, 1)"> },
money: {$sum: </span>"$orders.money"<span style="color: rgba(0, 0, 0, 1)">}
}
}]);</span></pre>
</div>
<p> </p>
<p><br>2. 查询用户的订单信息<br>2.1 连表查询<br>这个时候的连表是 order 表 跟 user 表关联(上一个是 user 表 和 order 表关联)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
$lookup: {
from: </span>"users"<span style="color: rgba(0, 0, 0, 1)">,
localField: </span>"openid"<span style="color: rgba(0, 0, 0, 1)">,
foreignField: </span>"openid"<span style="color: rgba(0, 0, 0, 1)">,
as: </span>"u"<span style="color: rgba(0, 0, 0, 1)">
}
}</span></pre>
</div>
<p> </p>
<p><br>2.2 拆分子数组</p>
<div class="cnblogs_code">
<pre>{ $unwind: "$u" }</pre>
</div>
<p> </p>
<p><br>2.3 只返回需要的字段<br>将 user 中需要返回的字段,提到子目录来</p>
<div class="cnblogs_code">
<pre>{$addFields: { name: "$u.name" }}</pre>
</div>
<p> </p>
<p><br>2.4 返回最终需要的字段结果</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">{
$project: {
_id: </span>1<span style="color: rgba(0, 0, 0, 1)">,
product: </span>1<span style="color: rgba(0, 0, 0, 1)">,
money: </span>1<span style="color: rgba(0, 0, 0, 1)">,
name: </span>1<span style="color: rgba(0, 0, 0, 1)">
}
}</span></pre>
</div>
<p> </p>
<p><br>最终的代码为:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">db.order.aggregate([{
$lookup: {
from: </span>"users"<span style="color: rgba(0, 0, 0, 1)">,
localField: </span>"openid"<span style="color: rgba(0, 0, 0, 1)">,
foreignField: </span>"openid"<span style="color: rgba(0, 0, 0, 1)">,
as: </span>"u"<span style="color: rgba(0, 0, 0, 1)">
}
}, {
$unwind: </span>"$u"<span style="color: rgba(0, 0, 0, 1)">
}, {
$addFields: {name: </span>"$u.name"<span style="color: rgba(0, 0, 0, 1)"> }
}, {
$project: {
_id: </span>1<span style="color: rgba(0, 0, 0, 1)">,
product: </span>1<span style="color: rgba(0, 0, 0, 1)">,
money: </span>1<span style="color: rgba(0, 0, 0, 1)">,
name: </span>1<span style="color: rgba(0, 0, 0, 1)">
}
}]);</span></pre>
</div>
<p> </p>
<p><br>虽然在 MongoDB 3.2 后我们能够进行连表查询了,方便了很多。但是其实 MongoDB<br>本身是非关系性数据库。如果需要进行频繁的这种连表查询,我们可以考虑优化我们的数据库表。比如在订单表里面,每一条的订单记录都把我们的用户信息放进去。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">[{
_id: ObjectId(</span>"4af2b2c6b138c267e414c071"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>100<span style="color: rgba(0, 0, 0, 1)">,
user: {
_id: ObjectId(</span>"5af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
name: </span>"小红"<span style="color: rgba(0, 0, 0, 1)">,
age: </span>26<span style="color: rgba(0, 0, 0, 1)">
}
}, {
_id: ObjectId(</span>"4af2b2c6b138c267e414c071"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
product: </span>"产品1"<span style="color: rgba(0, 0, 0, 1)">,
money: </span>100<span style="color: rgba(0, 0, 0, 1)">,
user: {
_id: ObjectId(</span>"5af2b2c6b138c267e414c072"<span style="color: rgba(0, 0, 0, 1)">),
uid: </span>"uid000"<span style="color: rgba(0, 0, 0, 1)">,
name: </span>"小红"<span style="color: rgba(0, 0, 0, 1)">,
age: </span>26<span style="color: rgba(0, 0, 0, 1)">
}
}]</span></pre>
</div>
<p> </p>
<p><br>这个时候,在实现两个需求就很简单了:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1. 查询用户信息并且显示该用户的总消费金额(用户名、年龄、总消费金额)</span>
db.order.aggregate([{ <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据 uid 求和</span>
<span style="color: rgba(0, 0, 0, 1)">$group: {
_id: </span>'$user.uid'<span style="color: rgba(0, 0, 0, 1)">,
money: {
$sum: </span>"$money"<span style="color: rgba(0, 0, 0, 1)">
},
name: { $first: </span>"$user.name"<span style="color: rgba(0, 0, 0, 1)"> },
age: { $first: </span>"$user.age"<span style="color: rgba(0, 0, 0, 1)"> }
}
}]);
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2. 查询用户的订单信息(订单id、产品、价格、用户名)</span>
<span style="color: rgba(0, 0, 0, 1)">db.order.aggregate([{
{$addFields: {name: </span>"$user.name"<span style="color: rgba(0, 0, 0, 1)"> }}
}, { </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据 uid 求和</span>
<span style="color: rgba(0, 0, 0, 1)">$project: {
_id: </span>1<span style="color: rgba(0, 0, 0, 1)">,
money: </span>1<span style="color: rgba(0, 0, 0, 1)">,
product: </span>1<span style="color: rgba(0, 0, 0, 1)">,
name: </span>1<span style="color: rgba(0, 0, 0, 1)">
}
}]);</span></pre>
</div>
<p> </p><br><br>
来源:https://www.cnblogs.com/cangqinglang/p/14783971.html
頁:
[1]