Добавить объект в Population в модели Mongoose

Я новичок в 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 или сам объект?


person Mike M    schedule 24.06.2015    source источник


Ответы (2)


Я видел этот вопрос и данный ответ, но я не думал, что он действительно объясняет то, что вы спросили, поэтому я подумал, что здесь что-то стоит.

Как показано в предоставленной схеме, свойство «topics» представляет собой «ссылочный массив», что в основном означает, что оно будет содержать «ссылку» (или, по сути, значение ObjectId) на документ, который находится в другой коллекции. Как вы должны знать, определение «схемы» мангуста связывает это с «ассоциированной» моделью, в которой находится этот объект.

Поставленный вопрос: «Нажимаю ли я объект или просто нажимаю значение _id», что само по себе вызывает некоторые вопросы о том, «что вы действительно хотите здесь делать?».

Для последнего примера возьмите следующий пример кода (предполагая, что модель и схема определены):

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» в родительский документ, даже если вы попросили его «подтолкнуть» весь «объект». Просто получается, что «это то, что вы хотели сделать» через представленный интерфейс схемы к модели. На самом деле это не сложно сделать в коде, просто чтобы вы понимали основную механику.

В качестве альтернативы вы можете «просто использовать значение _id» из созданного объекта (после того, как оно будет лучше всего сохранено в целях безопасности) и добавить его в массив аналогичным образом. Это почти тот же результат:

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
});

Этот метод хорош, если, конечно, в той или иной форме у вас действительно есть «Объекты в памяти» во время обработки как для Project, так и для Topic с соответствующими данными.

С другой стороны, давайте предположим, что Project представляет собой объект, который находится в коллекции, и хотя вы знаете, что это _id значение или другое «уникальное» представляющее свойство, данные этого объекта фактически не были загружены из базы данных с помощью .findOne() типа операции. или похожие.

Давайте тогда предположим, что у вас нет резидентных данных модели Project в памяти. Итак, как добавить новую тему?

Здесь в игру вступают нативные операторы для MongoDB. В частности, есть $push, который Конечно, аналогично оператору массива .push() в JavaScript, но с особым действием «на стороне сервера».

Как указывалось ранее, у вас нет загруженных данных модели Project, но вы хотите изменить конкретный элемент Project в хранилище, «подтолкнув» желаемый объект Topic к чему-то, определенному в вашей коллекции объектов 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
        }
    );
})

Механика «обновления» может быть .findOneAndUpdate() или даже .findByIdAndUpdate() по вашему усмотрению (оба этих метода возвращают измененный объект по умолчанию, где .update() нет) к тому, чего вы хотите достичь в результате вашей операции здесь.

Основное отличие от предыдущих подходов заключается в том, что, поскольку объект для Project, который необходимо изменить, не находится в памяти кода вашего приложения, вы используете эти методы, чтобы просто изменить его на сервере. Это может быть "хорошо", так как вам не нужно "загружать" эти данные только для внесения изменений. Операторы MongoDB позволяют вам $push загружать содержимое массива без предварительной загрузки.

Этот подход на самом деле является лучшим подходом для «одновременных обновлений» с системами с высоким уровнем транзакций. Причина в том, что «нет гарантии», что между .findOne() или подобной операцией в вашем приложении и модификациями с возможным действием .save() «данные не были изменены» в хранилище сервера между этими действиями.

Оператор $push «обеспечивает», чтобы данные, измененные на сервере, оставались «такими, какими они были» во время выполнения с, конечно, добавлением новых данных, которые вы добавили в массив.

Другая очевидная вещь здесь заключается в том, что, поскольку операция использует собственный оператор MongoDB для достижения этой эффективности, правила схемы мангуста игнорируются. Таким образом, вы, конечно, не можете просто «втолкнуть» весь объект «темы» в массив, не получая, конечно, «целого» объекта темы в хранилище:

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

По сути, в этом разница между «добавлением ссылки на объект» в массив и «добавлением объекта в массив». Это зависит от используемых методов и методов «эффективности», которые вы на самом деле выбираете.

Надеюсь, это будет полезно вам и тем, кто может наткнуться на ту же тему, которую вы подняли.

person Blakes Seven    schedule 24.06.2015
comment
Отличный ответ. Как раз то, что мне было нужно. Спасибо за разъяснения! - person Mike M; 24.06.2015
comment
Привет Кира. Не могли бы вы присоединиться к этому чату? chat.stackoverflow.com/rooms/95345 - person Dmytro Shevchenko; 17.11.2015
comment
@BlakesSeven, не могли бы вы прислать мне свой адрес электронной почты? Либо сюда, либо на мою электронную почту: [email protected] — я хочу пригласить вас в созданную мной команду MongoDB Slack. - person Dmytro Shevchenko; 09.12.2015

Вам нужно сохранить ObjectId только в том случае, если в схеме Topics есть ссылка на проект, поскольку сохранение ссылок на другие документы работает так же, как вы обычно сохраняете свойства, просто присвойте значение _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!
    });
});

Дополнительные сведения см. в документах.


-- ИЗМЕНИТЬ --

Если в схеме темы нет ссылки на проект, вам нужно отправить сам объект:

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);
person chridam    schedule 24.06.2015
comment
Здесь вы сделали почти наоборот. Вы сохраняете ссылку на проект в темах, а я храню список тем в проекте. Проблема в том, что во внешнем интерфейсе, с «заполнением», проект поставляется с полными встроенными экземплярами темы. Вы предлагаете мне просто добавить идентификаторы в список, содержащий объекты? - person Mike M; 24.06.2015
comment
@MikeM Ответил, исходя из предположения, что схема Topic также имеет ссылку Project. Можете ли вы обновить свой вопрос с помощью фактической схемы темы? - person chridam; 24.06.2015