在 Mongoose 模型中将对象添加到 Population

Add to an object to Population in a Mongoose Model

我是 Mongoose 的新手,正在了解人群。我的架构看起来像...

var ProjectSchema = new Schema({
    name: {
        type: String,
        default: '',
        required: 'Please fill Project name',
        trim: true
    },
    created: {
        type: Date,
        default: Date.now
    },
    topics: [{
        type: Schema.ObjectId,
        ref: 'Topic'
    }]
});

当我查询项目时,我使用 .populate('topics') 并且它工作正常。当项目被填充时,主题 属性 包含实际对象而不是引用。

P.S。该主题没有项目参考(我发现在MongoDB中很难维持相互关系)。

我的问题是:当我想将主题对象添加到项目时。我是添加 ObjectId 还是对象本身?

如果主题模式有项目引用,您只需要保存 ObjectId,因为将引用保存到其他文档的方式与您通常保存属性的方式相同,只需分配 _id 值:

var topicSchema = Schema({
    _author : { type: Schema.ObjectId, ref: 'Project' },
    title    : String    
});

var Topic  = mongoose.model('Topic', topicSchema);
var Project = mongoose.model('Project', projectSchema);

var my_project = new Project({ name: 'Test Project' });

my_project.save(function (err) {
    if (err) return handleError(err);

    var topic1 = new Topic({
        title: "My Topic",
        _author: my_project._id    // assign the _id from the project
    });

    topic1.save(function (err) {
        if (err) return handleError(err);
        // thats it!
    });
});

参考docs了解更多详情。


-- 编辑 --

如果主题架构没有项目引用,那么您需要推送对象本身:

var Topic  = mongoose.model('Topic', topicSchema);
var Project = mongoose.model('Project', projectSchema);

var my_project = new Project({ name: 'Test Project' });
var topic1 = new Topic({
    title: "My Topic"    
});

my_project.stories.push(topic1);
my_project.save(callback);

我看到了这个问题和给出的答案,但我认为它没有真正解释你的问题,所以我认为这里有些东西是值得的。

如所提供的模式所示,"topics" 属性 是一个 "referenced array",这基本上意味着它将容纳一个 "reference"(或者本质上是一个 ObjectId value ) 到位于另一个集合中的文档。 mongoose "schema" 定义将其与该对象所在的 "associated" 模型联系起来,正如您应该知道的那样。

提出的问题是"do I push the object, or do I just push the _id value",这本身就提出了一些关于"what do you really want to do here?"的问题。

对于最后一个,采用以下代码示例(假设模型和模式都已定义):

var project = new Project({ "name": "something" });
var topic = new Topic({ "name": "something" });

project.topics.push(topic);  // this actually just adds _id since it's a ref

// More code to save topic and project in their colllections

所以根据代码中的注释,猫鼬实际上只将“_id”值添加到父文档中,即使您要求它 "push" 整个 "object"。它只是通过提供给模型的模式接口计算 "that is what you meant to do"。在代码中确实不难做到,但只是为了让您了解底层机制。

您可以交替 "just use the _id value" 从创建的对象(为了安全起见保存它之后)并通过类似的方式将其添加到数组中。结果大致相同:

var project = new Project({ "name": "something" });

// Yes we saved the Project object, but later...

var topic = new Topic({ "name": "something" });

topic.save(function(err,topic) {
    if (err) throw (err):            // or better handling
    project.topics.push(topic._id);  // explicit _id
});

该方法很好,当然,前提是在处理 ProjectTopic 时,通过某种形式或其他方式您实际上拥有 "Objects in memory"适当的数据。

另一方面,让我们假设 Project 表示集合中的一个对象,尽管您知道它是 _id 值或其他 "unique" 表示 属性,对象数据实际上并没有通过.findOne()类型的操作或类似的操作从数据库中加载。

然后让我们假设您有 none 个 Project 模型数据驻留在内存中。那么如何添加新主题呢?

这是 MongoDB 的本地运算符发挥作用的地方。特别是 $push,它当然类似于 JavaScript 中的 .push() 数组运算符,但具有特定的 "Server Side" 操作。

如前所述,您没有加载 Project 模型数据,但您希望通过 "pushing" 所需的 Topic 修改存储中的特定 Project 项目] 反对在你的 Project 对象集合中定义的东西的标识符:

var topic = new Topic({ "name": "something" });

topic.save(function(err,topic) {
    if (err) throw err;     // or much better handling
    Project.update(
        { "_id": projectId },
        { "$push": {
            "topics": topic._id
        }},
        function(err,numAffected) {
            // and handle responses here
        }
    );
})

"update" 机制可以是 .findOneAndUpdate() 甚至是 .findByIdAndUpdate(),只要你觉得合适(这些方法都是 return 默认情况下修改的对象,其中 .update()不 ) 到你在这里的操作想要达到的结果。

与之前方法的主要区别在于,由于要修改的 Project 的对象不驻留在应用程序代码的内存中,因此您可以使用这些方法在服务器上修改它.这可以是 "good thing",因为您 不需要 到 "load" 数据只是为了进行修改。 MongoDB 运算符允许您 $push 数组内容而无需先加载。

这种方法实际上是 "concurrent updates" 高交易系统的最佳方法。原因是 "no guarantee" 在您的应用程序中的 .findOne() 或类似操作与服务器存储上 "no data has been changed" 最终 .save() 操作之间的修改之间正在发生的动作。

$push 运算符 "ensures" 即在服务器上修改的数据在执行时仍然保持 "as it was" 当然还有你添加到数组中的新数据。

这里另一个明显的事情是,由于该操作使用本机 MongoDB 运算符来实现这种效率,因此 mongoose Schema 规则被绕过了。因此,您当然不能将整个 "topic" 对象 "push" 放入数组中,当然不能在存储中以 "entire" 主题对象结束:

    Project.update(
        { "_id": projectId },
        { "$push": {
            "topics": topic   // Oops, now that would store the whole object!
        }},
        function(err,numAffected) {
            // and handle responses here
        }
    );

这本质上是 "adding an object reference" 数组和 "adding an 'Object' to an array" 之间的区别。这取决于使用的方法和您实际选择的 "efficiency" 方法。

希望这对您自己以及可能偶然发现与您提出的相同主题的其他人有用。