在 MongoDB 中使用查詢運算子連線多個條件

Mehvish Ashiq 2023年1月30日
  1. 在 MongoDB 中使用 $lookup 運算子連線多個條件
  2. 建立新集合並使用 $group 聚合階段加入多個條件
在 MongoDB 中使用查詢運算子連線多個條件

今天,我們將瞭解如何在 MongoDB 中使用 $lookup 運算子連線多個條件。此外,我們還將探索一些示例來演示 $group 階段和 $unionWidth 聚合階段的使用。

在 MongoDB 中使用 $lookup 運算子連線多個條件

如果我們有 MongoDB 3.6 或更高版本,我們可以使用 $lookup 聚合 pipeline 運算子來連線多個條件。

為此,我們有兩個名為 userssalaries 的集合。你也可以使用以下命令建立它。

建立集合的示例程式碼:

> 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
                }
        ]
}

在這裡,我們得到滿足兩個條件的文件。

  1. username 欄位在 userssalaries 集合中是相同的。
  2. salary 欄位的值大於等於 3000

我們只得到滿足這兩個條件的檔案。你可能已經注意到 usersalary 是一個元素陣列,其中每個元素都是 salaries 集合的文件。

我們可以使用 $unwind$addFields$project 從兩個集合(userssalaries)中獲取特定欄位並形成一個文件,如下例所示。

示例程式碼:

> 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 的評估查詢運算子,它比較欄位的值。

此外,$replaceRootpipeline 中的最後一個聚合 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 的新集合,合併兩個名為 userssalaries 的集合,然後將這些文件插入到新建立的集合中。然後,按使用者名稱分組以獲得所需的輸出。

我們也可以在不建立新集合的情況下獲得相同的輸出(如上所示)。為此,我們使用 $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 ] }
作者: Mehvish Ashiq
Mehvish Ashiq avatar Mehvish Ashiq avatar

Mehvish Ashiq is a former Java Programmer and a Data Science enthusiast who leverages her expertise to help others to learn and grow by creating interesting, useful, and reader-friendly content in Computer Programming, Data Science, and Technology.

LinkedIn GitHub Facebook

相關文章 - MongoDB Operator