MongoDB 專案巢狀欄位

Mehvish Ashiq 2023年1月30日
  1. MongoDB 專案巢狀欄位
  2. 使用 $project 聚合階段在 MongoDB 中投影巢狀欄位
  3. 使用 $unset 聚合階段獲取 MongoDB 中排除指定欄位的巢狀欄位
  4. 使用 forEach() 迴圈在 MongoDB 中獲取巢狀欄位
  5. 使用 mapReduce() 方法在 MongoDB 中投影巢狀欄位
MongoDB 專案巢狀欄位

今天,我們將學習如何使用 $project$unset 聚合階段、forEach() 迴圈和 mapReduce() 方法在 MongoDB 中查詢資料時投影巢狀欄位。

MongoDB 專案巢狀欄位

在 MongoDB 中,我們可以使用 find() 方法檢索所有文件,但如果我們只想訪問特定的巢狀欄位怎麼辦。這就是我們使用投影的地方。

我們可以通過各種方式投影巢狀欄位。在這裡,我們將瞭解以下專案巢狀欄位的解決方案。

  1. 使用 $project 聚合階段
  2. 使用 $unset 聚合階段
  3. 使用 forEach() 迴圈
  4. 使用 mapReduce() 函式

為了學習上述方法,讓我們建立一個名為 nested 的集合,其中包含一個文件。你也可以使用下面給出的查詢與我們聯絡。

示例程式碼:

// MongoDB version 5.0.8

> db.nested.insertOne(
    {
        "name": {
            "first_name": "Mehvish",
            "last_name": "Ashiq",
         },
         "contact": {
            "phone":{"type": "manager", "number": "123456"},
            "email":{ "type": "office", "mail": "delfstack@example.com"}
         },
         "country_name" : "Australien",
         "posting_locations" : [
             {
                 "city_id" : 19398,
                 "city_name" : "Bondi Beach (Sydney)"
             },
             {
                  "city_id" : 31101,
                  "city_name" : "Rushcutters Bay (Sydney)"
             },
             {
                  "city_id" : 31022,
                  "city_name" : "Wolly Creek (Sydney)"
             }
          ],
          "regions" : {
              "region_id" : 796,
              "region_name" : "Australien: New South Wales (Sydney)"
          }
    }
);

使用 db.nested.find().pretty(); 在 mongo shell 上檢視插入的資料。

使用 $project 聚合階段在 MongoDB 中投影巢狀欄位

示例程式碼:

// MongoDB version 5.0.8

> var current_location = "posting_locations";
> var project = {};
> project["id"] = "$"+current_location+".city_id";
> project["name"] = "$"+current_location+".city_name";
> project["regions"] = 1;

> var find = {};
> find[current_location] = {"$exists":true};

> db.nested.aggregate([
    { $match : find },
    { $project : project }
]).pretty()

輸出:

{
        "_id" : ObjectId("62a96d397c7e3688aea26d0d"),
        "regions" : {
                "region_id" : 796,
                "region_name" : "Australien: New South Wales (Sydney)"
        },
        "id" : [
                19398,
                31101,
                31022
        ],
        "name" : [
                "Bondi Beach (Sydney)",
                "Rushcutters Bay (Sydney)",
                "Wolly Creek (Sydney)"
        ]
}

在這裡,我們將名為 posting_locations 的第一級欄位儲存在名為 current_location 的變數中。

然後,我們使用該變數訪問 city_idcity_name 並將它們儲存在 project 物件中,同時使用括號表示法為 project 物件建立屬性。此外,我們將 regions 欄位儲存在 project["regions"] 中。

接下來,我們有另一個名為 find 的物件,我們將在 aggregate() 方法中使用它來匹配文件。在 aggregate() 方法中,我們使用 $match 階段來匹配文件,並使用 $project 來投影欄位,無論是巢狀的還是第一級的。

我們使用 $project 來指定要在輸出中顯示的欄位。如果我們只想在沒有任何過濾查詢的情況下投影指定的巢狀欄位,我們可以使用以下解決方案。

示例程式碼:

// MongoDB version 5.0.8

> var current_location = "posting_locations";
> db.nested.aggregate({
    $project: {
         "_id": 0,
         "city_id": "$" + current_location + ".city_id",
         "city_name": "$" + current_location + ".city_name",
         "regions": 1
    }
}).pretty();

輸出:

{
        "regions" : {
                "region_id" : 796,
                "region_name" : "Australien: New South Wales (Sydney)"
        },
        "city_id" : [
                19398,
                31101,
                31022
        ],
        "city_name" : [
                "Bondi Beach (Sydney)",
                "Rushcutters Bay (Sydney)",
                "Wolly Creek (Sydney)"
        ]
}

使用 $unset 聚合階段獲取 MongoDB 中排除指定欄位的巢狀欄位

示例程式碼:

// MongoDB version 5.0.8

> db.nested.aggregate({
        $unset: ["posting_locations.city_id", "contact", "regions", "name", "_id"]
}).pretty()

輸出:

{
        "country_name" : "Australien",
        "posting_locations" : [
                {
                        "city_name" : "Bondi Beach (Sydney)"
                },
                {
                        "city_name": "Rushcutters Bay (Sydney)"
                },
                {
                        "city_name": "Wolly Creek (Sydney)"
                }
        ]
}

在這裡,我們使用 $unset 運算子,用於刪除指定的欄位或欄位陣列。

請記住,我們使用點符號來指定嵌入的文件或文件陣列。如果給定欄位不存在,$unset 運算子不執行任何操作。

當我們使用 $ 匹配陣列的元素時,$unset 運算子將匹配的元素替換為 null 而不是從陣列中刪除它們。此行為有助於保持元素位置和陣列大小一致。

使用 forEach() 迴圈在 MongoDB 中獲取巢狀欄位

示例程式碼:

// MongoDB version 5.0.8

> var bulk = db.newcollection.initializeUnorderedBulkOp(),
   counter = 0;

> db.nested.find().forEach(function(doc) {
    var document = {};
    document["name"] = doc.name.first_name + " " + doc.name.last_name;
    document["phone"] = doc.contact.phone.number;
    document["mail"] = doc.contact.email.mail;
    bulk.insert(document);
    counter++;
    if (counter % 1000 == 0) {
        bulk.execute();
        bulk = db.newcollection.initializeUnorderedBulkOp();
    }
});

> if (counter % 1000 != 0) { bulk.execute(); }

你將看到類似於以下內容的內容。

BulkWriteResult({
        "writeErrors" : [ ],
        "writeConcernErrors" : [ ],
        "nInserted" : 1,
        "nUpserted" : 0,
        "nMatched" : 0,
        "nModified" : 0,
        "nRemoved" : 0,
        "upserted" : [ ]
})

接下來,在你的 mongo shell 上執行以下命令以檢視投影欄位。

// MongoDB version 5.0.8

> db.newcollection.find().pretty();

輸出:

{
        "_id" : ObjectId("62a96f2d7c7e3688aea26d0e"),
        "name" : "Mehvish Ashiq",
        "phone" : "123456",
        "mail" : "delfstack@example.com"
}

為了學習這個示例程式碼,假設我們想要獲取某些巢狀欄位並將它們插入到一個新集合中。在這裡,將轉換後的欄位作為文件插入到新集合中可能會根據 nested 集合的大小影響我們的操作。

我們可以通過使用新的無序 bulk insert API 來避免這種緩慢的插入效能。它將通過批量傳送來簡化插入操作,並實時向我們反饋操作是成功還是失敗。

因此,我們使用 bulk insert API 將所需的資料結構插入 newcollection 集合中,其中全新的文件將使用 nested 集合遊標的 forEach() 迴圈建立。要建立新屬性,我們使用括號表示法

對於此程式碼,我們假設有大量資料。因此,我們將把操作以 1000 的批次傳送到伺服器以執行批量插入操作。

結果,它為我們提供了良好的效能,因為我們不是向伺服器傳送每個請求,而是每 1000 個請求傳送一次。

使用 mapReduce() 方法在 MongoDB 中投影巢狀欄位

示例程式碼:

// MongoDB version 5.0.8

> function map() {
    for(var i in this.posting_locations) {
         emit({
             "country_id" : this.country_id,
             "city_id" : this.posting_locations[i].city_id,
             "region_id" : this.regions.region_id
         },1);
    }
}

> function reduce(id,docs) {
      return Array.sum(docs);
}

> db.nested.mapReduce(map,reduce,{ out : "map_reduce_output" } )

現在,執行以下查詢以檢視輸出。

// MongoDB version 5.0.8
> db.map_reduce_output.find().pretty();

輸出:

{
        "_id" : {
                "country_id" : undefined,
                "city_id" : 19398,
                "region_id" : 796
        },
        "value" : 1
}
{
        "_id" : {
                "country_id" : undefined,
                "city_id" : 31022,
                "region_id" : 796
        },
        "value" : 1
}
{
        "_id" : {
                "country_id" : undefined,
                "city_id" : 31101,
                "region_id" : 796
        },
        "value" : 1
}

對於這個示例程式碼,我們使用 mapReduce() 函式對 nested 集合的所有文件執行 map-reduce。為此,我們必須遵循下面簡要說明的三步過程。

  • 定義 map() 函式來處理每個輸入文件。在這個函式中,this 關鍵字是指正在由 map-reduce 操作處理的當前文件,emit() 函式將給定的值對映到鍵並返回它們。
  • 在這裡,我們定義了相應的 reduce() 函式,這是發生資料聚合的實際位置。它需要兩個引數(keysvalues);我們的程式碼示例採用 iddocs

    請記住,docs 的元素是由 map() 方法中的 emit() 函式返回的。在這一步,reduce() 函式將 docs 陣列縮減為其值(元素)的總和。

  • 最後,我們使用 map()reduce() 函式對 nested 集合中的所有文件執行 map-reduce。我們使用 out 將輸出儲存在指定的集合中,在本例中為 map_reduce_output
作者: 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