使用$ addToSet使用另一个数组字段更新数组字段

时间:2015-05-06 20:42:52

标签: mongodb

我应该从:我对MongoDB以及文档样式的数据库一般都知道。

我有一个看起来像这样的集合:

{
    "_id" : ObjectId("554a5e72b16f31ff0894310e"),
    "title" : "ABC",
    "admins" : [
        "personA",
        "personB",
    ],
    "email_address" : "ABC@mysite.com"
}
{
    "_id" : ObjectId("554a5e72b16f31ff0894310f"),
    "title" : "Junk Site",
    "admins" : [
        "personA",
        "personB"
    ],
    "email_address" : "garbage@mysite.com"
}
{
    "_id" : ObjectId("554a5e72b16f31ff08943110"),
    "title" : "Company Three Site",
    "admins" : [
        "personC"
        "personD",
    ],
    "email_address" : "company2plus1@mysite.com"
}

我需要做的是将Company One的管理员列表附加到Company Three,以便Company Three现在有四位管理员(A,B,C,D)。

我尝试了以下内容,因为它对我来说非常简单 - 从原点获取数据并直接附加到目的地:

db.runCommand({ 
    findAndModify : 'sites', 
    query : {'title' : 'Company Three Site'}, 
    update : { '$addToSet' : 
              {'admins' : 
              db.projects.find({'title' : 'ABC'}, {'_id' : 0, 'admins' : 1}
              }
             }
})

但是,这不能正常工作。

我仍在试图找出直接做到这一点的方法,但问题是......

1)使用单个命令甚至可以实现这一点,还是需要将其拆分? 2)我的逻辑思路是否有意义,或者我是否应该采用其他更简单的方式对MongoDB风格的数据库更常规?

2 个答案:

答案 0 :(得分:2)

原子更新无法做到这一点。但是,解决方法是使用find()方法查询源集合,并使用游标的forEach()方法迭代结果,获取数组并使用$addToSet运算符更新目标集合和$each修饰符。

让我们通过插入测试集合的上述示例文档来证明这一点:

#include <iostream>
#include <string>

class LibraryInterface {
public:
    std::string name;
    std::string name1;
    std::string name2;
    std::string name3;
    std::string name4;
    std::string name5;
    std::string name6;
};

class Library : private LibraryInterface
{
public:
    Library() {name="BOB";}
private:
    LibraryInterface* getLibraryInterface() {return this;} //only LibraryCustomer can aquire the interface pointer
    friend class LibraryCustomer;
};

class LibraryCustomer
{
   protected:
       LibraryInterface* getLibraryInterface(Library& lib) {return lib.getLibraryInterface();} //only things deriving from LibraryCustomer can aquire the interface pointer
};

class Kid : public LibraryCustomer
{
public:
    void function(Library& lib) {
        LibraryInterface* interface = getLibraryInterface(lib);
        std::cout << interface->name;
    }
};

int main() {
    Library lib;
    Kid k;
    k.function(lib);
}

以下操作会将公司“ABC”中的admins数组元素添加到公司“Company Three Site”管理数组中:

db.test.insert([
{
    "title" : "ABC",
    "admins" : [ 
        "personA", 
        "personB"
    ],
    "email_address" : "ABC@mysite.com"
},
{
    "title" : "Junk Site",
    "admins" : [ 
        "personA", 
        "personB"
    ],
    "email_address" : "garbage@mysite.com"
},
{
    "title" : "Company Three Site",
    "admins" : [ 
        "personC", 
        "personD"
    ],
    "email_address" : "company2plus1@mysite.com"
}
])

使用公司“公司三站点”db.test.find({"title" : "ABC"}).forEach(function (doc){ var admins = doc.admins; db.test.update( {"title" : "Company Three Site"}, { "$addToSet": { "admins": { "$each": admins } } }, { "multi": true } ); });

查询文档的集合

将产生:

db.collection.find({"title" : "Company Three Site"});

答案 1 :(得分:2)

db.projects.find实际上会返回一个游标,您绝对不希望将其添加到您的集合中。由于您事先知道您将只找到一个值,因此您可以使用.next().admin专门从光标中获取属性 - 但请记住,可以使用从.find返回的第一个值。否则,我认为你将不得不使用循环。

$addToSet也会将数组添加为一个整体,因此您必须append multiple values using $each

所有在一起:

db.runCommand({
    findAndModify: 'sites',
    query: {'title': 'Company Three Site'},
    update: {
        $addToSet: {
            "admins": {
                $each: db.projects.find(
                    {"title": "ABC"},
                    {"_id": 0, "admins": 1}
                ).next().admins
            }
        }
    }
})