在 MongoDB 中使用查詢運算子連線多個條件
今天,我們將瞭解如何在 MongoDB 中使用 $lookup
運算子連線多個條件。此外,我們還將探索一些示例來演示 $group
階段和 $unionWidth
聚合階段的使用。
在 MongoDB 中使用 $lookup
運算子連線多個條件
如果我們有 MongoDB 3.6 或更高版本,我們可以使用 $lookup
聚合 pipeline
運算子來連線多個條件。
為此,我們有兩個名為 users
和 salaries
的集合。你也可以使用以下命令建立它。
建立集合的示例程式碼:
> db.createCollection('users')
> db.createCollection('salaries')
在 users
集合中插入文件的示例程式碼:
> db.users.insertMany(
[
{
username: 'userone',
age: 30,
gender: 'Female',
city: 'Lahore',
country: 'Pakistan'
},
{
username: 'usertwo',
age: 35,
gender: 'Male',
city: 'Florida',
country: 'United States'
}
]
)
在 salaries
集合中插入文件的示例程式碼:
> db.salaries.insertMany(
[
{
username: 'userone',
salary: 3000
},
{
username: 'usertwo',
salary: 5000
}
]
)
users
集合的顯示資料:
> db.users.find().pretty()
輸出:
{
"_id" : ObjectId("628deb40c1e812eeeb311439"),
"username" : "userone",
"age" : 30,
"gender" : "Female",
"city" : "Lahore",
"country" : "Pakistan"
}
{
"_id" : ObjectId("628deb40c1e812eeeb31143a"),
"username" : "usertwo",
"age" : 35,
"gender" : "Male",
"city" : "Florida",
"country" : "United States"
}
salaries
集合的顯示資料:
> db.salaries.find().pretty()
輸出:
{
"_id" : ObjectId("628deb07c1e812eeeb311437"),
"username" : "userone",
"salary" : 3000
}
{
"_id" : ObjectId("628deb07c1e812eeeb311438"),
"username" : "usertwo",
"salary" : 5000
}
建立集合並插入文件後,我們可以探索各種場景來加入多個條件。讓我們從 $lookup
開始。
使用 $lookup
聚合 pipeline
運算子
示例程式碼:
> db.users.aggregate([
{
$lookup: {
from: 'salaries',
let: {
user_name: '$username',
user_salary: 3000
},
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ['$username', '$$user_name'] },
{ $gte: ['$salary','$$user_salary'] }
]
}
}
}],
as: 'usersalary'
}
}
]).pretty()
輸出:
{
"_id" : ObjectId("628deb40c1e812eeeb311439"),
"username" : "userone",
"age" : 30,
"gender" : "Female",
"city" : "Lahore",
"country" : "Pakistan",
"usersalary" : [
{
"_id" : ObjectId("628deb07c1e812eeeb311437"),
"username" : "userone",
"salary" : 3000
}
]
}
{
"_id" : ObjectId("628deb40c1e812eeeb31143a"),
"username" : "usertwo",
"age" : 35,
"gender" : "Male",
"city" : "Florida",
"country" : "United States",
"usersalary" : [
{
"_id" : ObjectId("628deb07c1e812eeeb311438"),
"username" : "usertwo",
"salary" : 5000
}
]
}
在這裡,我們得到滿足兩個條件的文件。
username
欄位在users
和salaries
集合中是相同的。salary
欄位的值大於等於3000
。
我們只得到滿足這兩個條件的檔案。你可能已經注意到 usersalary
是一個元素陣列,其中每個元素都是 salaries
集合的文件。
我們可以使用 $unwind
、$addFields
和 $project
從兩個集合(users
和 salaries
)中獲取特定欄位並形成一個文件,如下例所示。
示例程式碼:
> db.users.aggregate([
{
$lookup: {
from: 'salaries',
let: {
user_name: '$username',
user_salary: 3000
},
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ['$username', '$$user_name'] },
{ $gte: ['$salary','$$user_salary'] }
]
}
}
}],
as: 'usersalary'
}
},
{
$unwind:'$usersalary'
},
{
$addFields: {
salary: '$usersalary.salary'
}
},
{
$project: {
username: 1,
salary: 1
}
}
]).pretty()
輸出:
{
"_id" : ObjectId("628deb40c1e812eeeb311439"),
"username" : "userone",
"salary" : 3000
}
{
"_id" : ObjectId("628deb40c1e812eeeb31143a"),
"username" : "usertwo",
"salary" : 5000
}
使用 $unwind
運算子的目的是為每個具有相同名稱的元素解構一個陣列欄位,從輸入文件到輸出一個文件。
如果陣列中只有一個元素,則 $unwind
階段運算子將物件展平,即元素本身。 $addFields
將物件或陣列中的 salary
欄位連線到文件的根級別。
在瞭解上面給出的示例中的用法之前,讓我們先關注使用 $project
過濾器階段的原因。如果我們不使用 $project
,我們將獲得文件根級別的 salary
欄位和 usersalary
物件,這是不必要的。
這是我們使用 $project
過濾階段並指定輸出中應包含哪些欄位的地方。
如果專案要求限制使用 $unwind
、$addFields
、$project
,我們可以使用下面給出的替代解決方案。
示例程式碼:
> db.users.aggregate([
{
$lookup: {
from: 'salaries',
let: {
user_name: '$username',
user_salary: 3000
},
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: ['$username', '$$user_name'] },
{ $gte: ['$salary','$$user_salary'] }
]
}
}
}],
as: 'usersalary'
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects:[
{
$arrayElemAt: [
"$usersalary", 0
]
},
{
salary: "$$ROOT.salary"
}
]
}
}
}
]
).pretty()
輸出:
{
"_id" : ObjectId("628deb07c1e812eeeb311437"),
"username" : "userone",
"salary" : 3000
}
{
"_id" : ObjectId("628deb07c1e812eeeb311438"),
"username" : "usertwo",
"salary" : 5000
}
我們使用 let
欄位(可選)將欄位的值分配給變數。我們在 pipeline
階段訪問這些變數,我們指定 pipeline
在不同的集合上執行。
請注意,我們還使用 $match
階段來利用名為 $expr
的評估查詢運算子,它比較欄位的值。
此外,$replaceRoot
是 pipeline
中的最後一個聚合 pipeline
階段,我們使用 $mergeObjects
運算子將 $lookup
輸出與 $$ROOT
文件的部分合並。
我們只使用了 $and
運算子來連線條件。你也可以使用 $or
或兩個運算子。
建立新集合並使用 $group
聚合階段加入多個條件
示例程式碼:
> db.users_salaries.insertMany(
db.users.find({}, {"_id": 0})
.toArray()
.concat(db.salaries.find({}, {"_id": 0}).toArray())
)
db.users_salaries.aggregate([
{ "$group": {
"_id": { "username": "$username" },
"salary": { "$push": "$salary" }
}}
])
輸出:
{ "_id" : { "username" : "userone" }, "salary" : [ 3000 ] }
{ "_id" : { "username" : "usertwo" }, "salary" : [ 5000 ] }
對於這個程式碼示例,我們建立一個名為 users_salaries
的新集合,合併兩個名為 users
和 salaries
的集合,然後將這些文件插入到新建立的集合中。然後,按使用者名稱
分組以獲得所需的輸出。
我們也可以在不建立新集合的情況下獲得相同的輸出(如上所示)。為此,我們使用 $unionWith
聚合階段,它為兩個集合執行聯合。
示例程式碼:
> db.users.aggregate([
{ $set: { username: "$username" } },
{ $unionWith: {
coll: "salaries",
pipeline: [{ $set: { salary: "$salary" } }]
}},
{ $group: {
_id: { username: "$username"},
"salary": { "$push": "$salary" }
}}
])
輸出:
{ "_id" : { "username" : "userone" }, "salary" : [ 3000 ] }
{ "_id" : { "username" : "usertwo" }, "salary" : [ 5000 ] }