Обновление двойного вложенного массива MongoDB

Рассмотрим эту схему:

let userSchema = new mongoose.Schema({
id: String,
displayName: String,
displayImage: String,
posts: [
    {
        url: String,
        description: String,
        likes: [String],
        comments: [
            { content: String, date: String, author: { id: String, displayName: String, displayImage: String } }
        ]
    }
]
});

Я могу удалить определенный элемент из массива комментариев, используя этот запрос

controller.deleteComment = (req, res, next) => {
User.findOneAndUpdate(
    { id: req.query.userid, 'posts._id': req.params.postid, },
    {
        $pull: {
            'posts.$.comments': { _id: req.body.commentID },
        }
    }
)
    .exec()
    .then(() => {
        res.send('deleted');
    })
    .catch(next);
};

В любом случае, я могу ОБНОВИТЬ элемент внутри массива комментариев, используя $set оператор? Мне нужно изменить содержимое комментария на основе его идентификатора комментария.. что-то вроде этого:

controller.editComment = (req, res, next) => {
    User.findOneAndUpdate(
        { id: req.query.userid, 'posts._id': req.params.postid, 'comments._id':req.body.commentID },
        {
            $set: {
                'posts.$.comments': {  content: req.body.edited },
            }
        }
    )
        .exec()
        .then(() => {
            res.send('deleted');
        })
        .catch(next);
};

Это... очевидно не работает, но мне интересно, есть ли способ, которым я могу это сделать?

ОБНОВЛЕНИЕ Согласно предложениям ниже, я делаю следующее, чтобы управлять только одной Схемой. Это работает, однако обновляются только комментарии первого поста, независимо от того, какие комментарии я редактирую. Я проверил, и обратные документы всегда верны. Там должно быть проблемы с doc.save() метод.

controller.editComment = (req, res, next) => {
User.findOne(
    { id: req.query.userid, 'posts._id': req.params.postid },
    { 'posts.$.comments._id': req.body.commentID }
)
    .exec()
    .then((doc) => {
        let thisComment = doc.posts[0].comments.filter((comment) => { return comment._id == req.body.commentID; });
        thisComment[0].content = req.body.edited;
        doc.save((err) => { if (err) throw err; });
        res.send('edited');
    })
    .catch(next);
};

2 ответа

Решение

Я не знаю простой (или даже жесткий:P) способ добиться того, что пытаются сделать. В монго манипулирование сравнительно сложно в двухуровневых массивах и, следовательно, его лучше избегать.

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

Таким образом, ваша схема комментариев будет выглядеть так:

let commentSchema = new mongoose.Schema({
     content: String, 
     date: String,
     author: { 
          id: String,
          displayName: String,
          displayImage: String
     }
});

И ваша пользовательская схема должна выглядеть так:

let userSchema = new mongoose.Schema({
     id: String,
     displayName: String,
     displayImage: String,
     posts: [{
        url: String,
        description: String,
        likes: [String],
        comments: [{
            type: Schema.Types.ObjectId,
            ref: 'comment'   //reference to comment schema
        }]
    }]
});

Таким образом, ваши манипуляции с данными будут намного проще. Вы можете заполнить комментарии при получении пользовательского документа. И обратите внимание, насколько просты операции обновления / удаления, учитывая, что вы уже знаете _id комментариев, которые хотите обновить.

Надеюсь, вы найдете этот ответ полезным!

controller.editComment = (req, res, next) => {
    User.findOneAndUpdate(
        { id: req.query.userid, 'posts._id': req.params.postid, 'comments._id':req.body.commentID },
        {
            $push: {
                'posts.$.comments': {  content: req.body.edited },
            }
        }
    )
        .exec()
        .then(() => {
            res.send('deleted');
        })
        .catch(next);
};
Другие вопросы по тегам