簡體   English   中英

Mongodb 檢查數組的子文檔中是否存在字段

[英]Mongodb check If field exists in an sub-document of an array

我正在嘗試檢查數組的子文檔中是否存在字段,如果存在,它將僅在回調中提供這些文檔。 但是每次我記錄回調文檔時,它都會給我數組中的所有值,而不是基於查詢的值。

我正在關注本教程,唯一的區別是我使用的是findOne function 而不是find function 但它仍然給了我所有的值。 我嘗試使用 find ,它做同樣的事情。

我也使用與上面鏈接中的示例相同的集合樣式。

例子在此處輸入圖像描述 在上圖中,您可以在上圖中看到我有一個帶有uid字段和聯系人數組的文檔。 我要做的是首先 select 一個基於輸入uid的文檔。 然后在選擇該文檔后,我想顯示contacts.uid字段存在的contacts數組中的值。 因此,從上圖中,僅顯示的值是contacts[0]contacts[3] ,因為contacts 1沒有 uid 字段。

Contact.contactModel.findOne({$and: [

        {uid: self.uid},

        {contacts: {

          $elemMatch: {

            uid: {

              $exists: true,

              $ne: undefined,

            }

          }

        }}

      ]}

在您上面提到的教程中,他們將 2 個參數傳遞給該方法,一個用於過濾器,一個用於投影,但您只傳遞了一個,這就是區別。 您可以將查詢更改為:

Contact.contactModel.findOne( 
    {uid: self.uid},
    {contacts: {
      $elemMatch: {
        uid: {
          $exists: true,
          $ne: undefined,
        }
      }
    }}
 )

agg 框架使過濾字段的存在變得有點棘手。 我相信 OP 想要一個字段存在於子文檔數組中的所有文檔,然后只返回那些存在該字段的子文檔。 以下應該可以解決問題:

var inputtedUID = "0";  // doesn't matter
db.foo.aggregate(
[
// This $match finds the docs with our input UID:
{$match: {"uid": inputtedUID }}

// ... and the $addFields/$filter will strip out those entries in contacts where contacts.uid does NOT exist.  We wish we could use {cond: {$zz.name: {$exists:true} }} but
// we cannot use $exists here so we need the convoluted $ifNull treatment.  Note we
// overwrite the original contacts with the filtered contacts:
,{$addFields: {contacts: {$filter: {
                input: "$contacts",
                as: "zz",
                cond: {$ne: [ {$ifNull:["$$zz.uid",null]}, null]}
            }}
    }}
,{$limit:1}  // just get 1 like findOne()
 ]);

show(c);

{
    "_id" : 0,
    "uid" : 0,
    "contacts" : [
        {
            "uid" : "buzz",
            "n" : 1
        },
        {
            "uid" : "dave",
            "n" : 2
        }
    ]
}

您的問題來自對 MongoDB 中的數據建模的誤解,這對於來自其他 DBMS 的開發人員來說並不少見。 讓我通過數據建模如何與 RDBMS 與 MongoDB(以及許多其他 NoSQL 數據庫)一起工作的示例來說明這一點。

使用 RDBMS,您可以識別您的實體及其屬性。 接下來,您識別關系,規范化數據 model 並將您的數據撞到牆上,以獲得UPPER LEFT ABOVE AND BEYOND JOIN ™,這將回答用例 A 產生的問題。然后,您幾乎做同樣的事情對於用例 B。

使用 MongoDB,您可以顛倒過來。 查看您的用例,您將嘗試找出回答用例產生的問題所需的信息,然后 model 您的數據,以便以最有效的方式回答這些問題。

讓我們堅持您的聯系人數據庫示例。 這里需要做一些假設:

  1. 每個用戶可以有任意數量的聯系人。
  2. 每個聯系人和每個用戶都需要用名字以外的東西唯一標識,因為名字可以改變等等。
  3. 冗余並不是一件壞事。

對於第一個假設,將聯系人嵌入到用戶文檔中是沒有問題的,因為存在文檔大小限制。 關於我們的第二個假設: uid字段變得不是多余的,而只是無用的,因為已經_id字段唯一地標識了所討論的數據集。

用例

讓我們看一些用例,這些用例是為了示例而簡化的,但它會給你圖片。

  1. 給定一個用戶,我想找到一個聯系人。
  2. 給定一個用戶,我想找到他的所有聯系人。
  3. 給定一個用戶,我想找到他的聯系人“John Doe”的詳細信息
  4. 給定一個聯系人,我想對其進行編輯。
  5. 給定一個聯系人,我想刪除它。

數據模型

用戶

{
  "_id": new ObjectId(),
  "name": new String(),
  "whatever": {}
}

接觸

{
  "_id": new ObjectId(),
  "contactOf": ObjectId(),
  "name": new String(),
  "phone": new String()
}

顯然, contactOf指的是一個 ObjectId,它必須存在於 User 集合中。

實施

給定一個用戶,我想找到一個聯系人。

如果我有用戶 object,我有它的_id ,單個聯系人的查詢變得像

db.contacts.findOne({"contactOf":self._id})

給定一個用戶,我想找到他的所有聯系人。

同樣容易:

db.contacts.find({"contactOf":self._id})

給定一個用戶,我想找到他的聯系人“John Doe”的詳細信息

db.contacts.find({"contactOf":self._id,"name":"John Doe"})

現在我們有了一種或另一種方式的聯系人,包括他/她/未決定/選擇不說_id ,我們可以輕松編輯/刪除它:

給定一個聯系人,我想對其進行編輯。

db.contacts.update({"_id":contact._id},{$set:{"name":"John F Doe"}})

我相信您現在已經了解如何從我們用戶的聯系人中刪除 John。

筆記

指數

使用您的數據 model,您將需要為uid字段添加額外的索引 - 正如我們發現的那樣,這沒有任何作用。 此外, _id是默認索引的,所以我們很好地利用了這個索引。 但是,應該對contact集合進行附加索引:

db.contact.ensureIndex({"contactOf":1,"name":1})

正常化

這里根本沒有完成。 造成這種情況的原因是多方面的,但最重要的是,雖然 John Doe 可能只有“Mallory H Ousefriend”的手機號碼,但他的妻子 Jane Doe 也可能擁有 email 地址“janes_naughty_boy@censored.com” -至少馬洛里肯定不想出現在約翰的聯系人列表中。 因此,即使我們有聯系人的身份,您也很可能不想反映這一點。

結論

通過一些數據重構,我們將需要的附加索引數量減少到 1,使查詢更加簡單,並規避了 BSON 文檔大小限制。 至於性能,我想我們說的是至少一個數量級。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM