![](/img/trans.png)
[英]Is there a way to match 2 specific field values in a sub-document array in mongodb?
[英]MongoDB conditionally $addToSet sub-document in array by specific field
有沒有辦法根據數組子文檔中的特定鍵字段有條件地$addToSet
?
這是我的意思的一個例子 - 鑒於以下示例引導程序生成的集合;
cls
db.so.remove();
db.so.insert({
"Name": "fruitBowl",
"pfms" : [
{
"n" : "apples"
}
]
});
n
定義了唯一的文檔鍵。 在任何時候,我只希望數組中有一個具有相同n
值的條目。 所以我希望能夠使用n
更新pfms
數組,以便我最終得到這個;
{
"Name": "fruitBowl",
"pfms" : [
{
"n" : "apples",
"mState": 1111234
}
]
}
這就是我現在所處的位置;
db.so.update({
"Name": "fruitBowl",
},{
// not allowed to do this of course
// "$pull": {
// "pfms": { n: "apples" },
// },
"$addToSet": {
"pfms": {
"$each": [
{
"n": "apples",
"mState": 1111234
}
]
}
}
}
)
不幸的是,這增加了另一個數組元素;
db.so.find().toArray();
[
{
"Name" : "fruitBowl",
"_id" : ObjectId("53ecfef5baca2b1079b0f97c"),
"pfms" : [
{
"n" : "apples"
},
{
"n" : "apples",
"mState" : 1111234
}
]
}
]
我需要有效地將在n
匹配的apples
文檔更新為唯一標識符,並設置mState
是否已存在條目。 很遺憾我不能在同一個文檔中執行$pull
和$addToSet
(我試過)。
我在這里真正需要的是字典語義,但這不是現在的選擇,也不是打破文檔 - 任何人都可以想出另一種方式嗎?
FWIW - 現有格式是語言/驅動程序序列化的結果,我沒有完全選擇它。
更遠
在我知道數組元素已經存在的情況下,我可以做到這一點;
db.so.update({
"Name": "fruitBowl",
"pfms.n": "apples",
},{
$set: {
"pfms.$.mState": 1111234,
},
}
)
但當然這只是有效;
第一個限制不是災難,但如果我不能有效地將$addToSet
與前一個$set
合並或合並(當然我不能),那么我現在能想到的唯一解決方法意味着兩個 DB 輪- 旅行。
$addToSet
運算符當然要求被“添加到集合中”的“整個”文檔實際上是唯一的,因此您不能更改文檔的“部分”或以其他方式將其視為“部分匹配”。
您偶然發現了使用$pull
刪除任何帶有“key”字段的元素會導致“重復”的最佳方法,但當然您不能像這樣在不同的更新運算符中修改相同的路徑。
因此,您將得到的最接近的事情是發出單獨的操作,但也使用 MongoDB 2.6 引入的“批量操作 API”來執行此操作。 這允許將兩者同時發送到服務器,以獲得與您將獲得的“連續”操作列表最接近的內容:
var bulk = db.so.initializeOrderedBulkOp();
bulk.find({ "Name": "fruitBowl", "pfms.n": "apples": }).updateOne({
"$pull": { "pfms": { "n": "apples" } }
});
bulk.find({ "Name": "fruitBowl" }).updateOne({
"$push": { "pfms": { "n": "apples", "state": 1111234 } }
})
bulk.execute();
如果將元素移動到另一個集合並依賴“upserts”和$set
以具有相同的功能但依賴於集合而不是數組是不可能或不切實際的,那么這幾乎是您最好的方法。
我遇到了完全相同的情況。 我正在從帖子中插入和刪除喜歡。
我所做的是使用 mongoose findOneAndUpdate 函數(類似於 mongodb 中的 update 或 findAndModify 函數)。
關鍵概念是
插入是
findOneAndUpdate({ _id: theId, 'likes.userId': { $ne: theUserId }},
{ $push: { likes: { userId: theUserId, createdAt: new Date() }}},
{ 'new': true }, function(err, post) { // do the needful });
刪除是
findOneAndUpdate({ _id: theId, 'likes.userId': theUserId},
{ $pull: { likes: { userId: theUserId }}},
{ 'new': true }, function(err, post) { // do the needful });
這使得整個操作具有原子性,並且沒有關於userId字段的重復項。
我希望這會有所幫助。 如果您有任何疑問,請隨時提出。
作為一名業務分析師,我遇到了同樣的問題,希望經過數小時的調查后我能找到解決方案。
// The customer document:
{
"id" : "1212",
"customerCodes" : [
{
"code" : "I"
},
{
"code" : "YK"
}
]
}
// 問題:我想將 dateField "01.01.2016" 插入到 customerCodes 子文檔具有代碼“YK”但沒有 dateField 的文檔的客戶文檔中。 最終文件必須如下:
{
"id" : "1212",
"customerCodes" : [
{
"code" : "I"
},
{
"code" : "YK" ,
"dateField" : "01.01.2016"
}
]
}
// 解決方案:解決方案代碼分三步:
// 第 1 部分 - 查找帶有 customerCodes“YK”但沒有 dateField 的客戶
// 第 2 部分 - 在 customerCodes 列表中查找帶有“YK”的子文檔的索引。
// PART 3 - 將值插入到文檔中
// 這里是代碼
// PART 1
var myCursor = db.customers.find({ customerCodes:{$elemMatch:{code:"YK", dateField:{ $exists:false} }}});
// PART 2
myCursor.forEach(function(customer){
if(customer.customerCodes != null )
{
var size = customer.customerCodes.length;
if( size > 0 )
{
var iFoundTheIndexOfSubDocument= -1;
var index = 0;
customer.customerCodes.forEach( function(clazz)
{
if( clazz.code == "YK" && clazz.changeDate == null )
{
iFoundTheIndexOfSubDocument = index;
}
index++;
})
// PART 3
// What happens here is : If i found the indice of the
// "YK" subdocument, I create "updates" document which
// corresponds to the new data to be inserted`
//
if( iFoundTheIndexOfSubDocument != -1 )
{
var toSet = "customerCodes."+ iFoundTheIndexOfSubDocument +".dateField";
var updates = {};
updates[toSet] = "01.01.2016";
db.customers.update({ "id" : customer.id } , { $set: updates });
// This statement is actually interpreted like this :
// db.customers.update({ "id" : "1212" } ,{ $set: customerCodes.0.dateField : "01.01.2016" });
}
}
}
});
祝你今天過得愉快 !
據我所知,MongoDB 現在(從v 4.2
)允許使用聚合管道進行更新。 使其工作的或多或少優雅的方式(根據問題)如下所示:
db.runCommand({
update: "your-collection-name",
updates: [
{
q: {},
u: {
$set: {
"pfms.$[elem]": {
"n":"apples",
"mState": NumberInt(1111234)
}
}
},
arrayFilters: [
{
"elem.n": {
$eq: "apples"
}
}
],
multi: true
}
]
})
在我的場景中,數據不存在時需要初始化,如果存在則更新字段,數據不會被刪除。 如果數據有這些狀態,您可能想嘗試以下方法。
// Mongoose, but mostly same as mongodb
// Update the tag to user, If there existed one.
const user = await UserModel.findOneAndUpdate(
{
user: userId,
'tags.name': tag_name,
},
{
$set: {
'tags.$.description': tag_description,
},
}
)
.lean()
.exec();
// Add a default tag to user
if (user == null) {
await UserModel.findOneAndUpdate(
{
user: userId,
},
{
$push: {
tags: new Tag({
name: tag_name,
description: tag_description,
}),
},
}
);
}
這是場景中最干凈、最快速的方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.