簡體   English   中英

貓鼬:在嵌套數組中查找和更新

[英]Mongoose: Find and Update in nested array

所以,我有這個模式

購買模式:

const purchasesSchema = new Schema({
  date: String,
  status: String,
  product: { type: Schema.Types.ObjectId, ref: 'Product' }
})

產品架構:

const productSchema = new Schema({
  product_name: String,
  transaction: [{
    price: Number,
    purchase: { type: Schema.Types.ObjectId, ref: 'Purchases' }
  }]

該算法在進行購買之前必須先創建一個產品名稱,然后再通過bond transaction.purchase在購買中創建產品交易。

采購文件樣本:

[{
  "_id": "5ac0b7cab7924a1710398c9e",
  "date": "01/04/2018",
  "status": "Not Paid",
  "product": {
    "_id": "5ac0b7b1b7924a1710398c9a",
    "product_name": "Milk",
    "transactions": [
      {
      "_id": "5ac0b7c9b7924a1710398c9b",
      "price": 5000,
      }
    ],
  }
}]

交易中具有購買ID綁定的預期憑證

[{
  "_id": "5ac0b7cab7924a1710398c9e",
  "date": "01/04/2018",
  "status": "Not Paid",
  "product": {
    "_id": "5ac0b7b1b7924a1710398c9a",
    "product_name": "Milk",
    "transactions": [
      {
      "_id": "5ac0b7c9b7924a1710398c9b",
      "price": 5000,
      "purchase": "the id"
      }
    ],
  }
}]

到目前為止,我已經嘗試過了,以防萬一我想將交易推送到已經存在的產品名稱中:

    const newTransaction = {
        product_price: req.body.product_price
      }
      Product.findOneAndUpdate({ _id: req.body.product_id }, { $push: { transaction: newTransaction } }, { new: true }, (err, data) => {
        const TRANSACTION_ID = data.transaction[data.transaction.length -1]._id

        const newPurchase = new Purchase({
          date: moment(req.body.date).format('DD/MM/YYYY'),
          status: 'Not Paid',
          product: req.body.product_id,
        })
        newPurchase.save((err, data) => {
          if (err) throw err
          const PURCHASES_ID = data._id
          Product.findOneAndUpdate({ 'transactions._id': TRANSCATION_ID }, { $set: { transactions: { purchase: PURCHASES_ID } } }, (err, data) => { if (err) return handleError(err) })

在嵌套數組產品架構中如何將“購買ID”作為“ transactions.purchase”推送的問題。 謝謝。

從這個答案中解決了https://stackoverflow.com/a/23577266/5834822

一般范圍和說明

您在這里所做的事情有些錯誤。 首先,您的查詢條件。 您所指的是幾個_id值,這些值您不需要,並且其中至少一個不在頂層。

為了獲得“嵌套”值,並還假定_id值是唯一的並且不會出現在任何其他文檔中,您查詢的表單應如下所示:

Model.update(
    { "array1.array2._id": "123" },
    { "$push": { "array1.0.array2.$.answeredBy": "success" } },
    function(err,numAffected) {
       // something with the result in here
    }
);

現在可以實際使用,但實際上只是really幸,因為有很好的理由說明它不適合您。

重要的閱讀內容是“ $嵌套數組”主題下的位置$運算符的官方文檔。 這是什么意思:

位置$運算符不能用於遍歷一個以上數組的查詢,例如遍歷嵌套在其他數組中的數組的查詢,因為$占位符的替換是單個值

具體而言,這意味着將被匹配並在位置占位符中返回的元素是來自第一個匹配數組的索引值。 在您的情況下,這意味着“頂級”數組上的匹配索引。

因此,如果您查看所示的查詢符號,我們已經對頂層數組中的第一個 (或0索引)位置進行了“硬編碼”,並且碰巧“ array2”中的匹配元素也是零索引條目。

為了說明這一點,您可以將匹配的_id值更改為“ 124”,結果將把新條目$push到具有_id “ 123”的元素上,因為它們都在“ array1”的零索引條目中,並且這是返回的值到占位符。

這就是嵌套數組的普遍問題。 您可以刪除其中一個級別,仍然可以將$push到“頂部”數組中的正確元素,但是仍然會有多個級別。

嘗試避免嵌套數組,因為您將遇到如圖所示的更新問題。

通常的情況是“平化”您“認為”的事物是“層次”,並實際上將這些歸因於最終細節項目。 例如,問題中結構的“扁平化”形式應類似於:

 {
   "answers": [
     { "by": "success", "type2": "123", "type1": "12" }
   ]
 }

甚至當接受內部數組時,它只是$push ,並且從未更新:

 {
   "array": [
     { "type1": "12", "type2": "123", "answeredBy": ["success"] },
     { "type1": "12", "type2": "124", "answeredBy": [] }
   ]
 }

兩者都適合在$運算符范圍內進行原子更新


MongoDB 3.6及更高版本

從MongoDB 3.6開始,有一些新功能可用於嵌套數組。 為了匹配特定元素並通過update語句中的arrayFilters應用不同的條件,這使用位置過濾的$[<identifier>]語法:

Model.update(
  {
    "_id": 1,
    "array1": {
      "$elemMatch": {
        "_id": "12","array2._id": "123"
      }
    }
  },
  {
    "$push": { "array1.$[outer].array2.$[inner].answeredBy": "success" }
  },
  {
    "arrayFilters": [{ "outer._id": "12" },{ "inner._id": "123" }] 
  }
)

傳遞給.update()甚至.updateOne() .updateMany() .findOneAndUpdate().bulkWrite()方法的選項的"arrayFilters"指定與update語句中給出的標識符匹配的條件。 符合給定條件的任何元素都將被更新。

因為結構是“嵌套的”,所以我們實際上使用“多個過濾器”,如通過過濾器定義的“數組”指定的那樣,如圖所示。 標記的“標識符”用於與語句的更新塊中實際使用的位置過濾的$[<identifier>]語法匹配。 在這種情況下, innerouter是嵌套鏈所指定的每個條件所使用的標識符。

這個新的擴展使嵌套數組內容的更新成為可能,但是它對“查詢”此類數據的實用性並沒有真正的幫助,因此如前所述適用相同的警告。

即使您的大腦最初認為是“嵌套”,您通常也確實會“平均”地表示為“屬性”,這通常只是對您認為“先前的關系部分”融合在一起的一種反應。 實際上,您確實需要更多的非規范化。

另請參閱如何在mongodb中更新多個數組元素 ,因為這些新的更新運算符實際上匹配並更新“多個數組元素”,而不僅僅是第一個 (位置更新的先前動作)。

注意有點諷刺意味的是,由於這是在.update()和類似方法的“ options”參數中指定的,因此該語法通常與所有最新的發行版驅動程序兼容。

但是,對於mongo shell並非如此,因為在那里實現該方法的方式(“具有諷刺意味的是向后兼容”), arrayFilters解析選項的內部方法來識別和刪除arrayFilters參數,以實現“向后兼容性”以及以前的MongoDB服務器版本和“舊版” .update() API調用語法。

因此,如果要在mongo shell或其他“基於shell的”產品(尤其是Robo 3T)中使用該命令,則需要開發分支或生產版本中的3.6或更高版本。

另請參見positional all $[] ,它還會更新“多個數組元素”,但不適用於指定條件,並且適用於所需動作的數組中的所有元素。

暫無
暫無

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

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