Mongoose删除文档中的数组元素并保存。

126

我在我的模型文档中有一个数组。我想根据我提供的键删除该数组中的元素,然后更新 MongoDB。这可能吗?

这是我的尝试:

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var favorite = new Schema({
    cn: String,
    favorites: Array
});

module.exports = mongoose.model('Favorite', favorite, 'favorite');

exports.deleteFavorite = function (req, res, next) {
    if (req.params.callback !== null) {
        res.contentType = 'application/javascript';
    }
    Favorite.find({cn: req.params.name}, function (error, docs) {
        var records = {'records': docs};
        if (error) {
            process.stderr.write(error);
        }
        docs[0]._doc.favorites.remove({uid: req.params.deleteUid});

        Favorite.save(function (error, docs) {
            var records = {'records': docs};
            if (error) {
                process.stderr.write(error);
            }
            res.send(records);

            return next();
        });
    });
};

到目前为止,它找到了文档,但是删除和保存都无法工作。

9个回答

190

您也可以直接在MongoDB中进行更新,而无需加载文档并使用代码进行修改。使用$pull$pullAll操作符从数组中删除该项:

您也可以直接在MongoDB中进行更新,而无需加载文档并使用代码进行修改。使用$pull$pullAll操作符从数组中删除该项:

Favorite.updateOne({ cn: req.params.name }, {
    $pullAll: {
        favorites: req.params.deleteUid,
    },
});

从数组中删除对象,则

Favorite.updateOne({ cn: req.params.name }, {
    $pullAll: {
        favorites: [{_id: req.params.deleteUid}],
    },
});

(您还可以使用updateMany处理多个文档)

http://docs.mongodb.org/manual/reference/operator/update/pullAll/


13
我不理解这如何从收藏夹数组中(子数组而非文档)移除一个项目,而没有提及收藏夹名称。 - Dominic
1
此操作也是原子性的。这意味着文档在单独的查找和更新操作之间不会有更改的可能性。 - James
1
谢谢!非常优雅的解决方案。 - Miguel Lara
2
@Dominic,“收藏夹”现在已经添加了。我简直不敢相信这个问题被忽视了6年以上。 - Parzh from Ukraine

99

@LondonRob +1 做了一个好的编辑,我们应该小心不要在一般情况下使用不安全的链接。 - King Friday
1
这正是我所需要的!它干净易用,其中的ID在另一个模型中是引用。 - tarafenton
当文档再次更新时,这个不起作用。 - Ozan Kurt

16

以上回答展示了如何移除一个数组,这里是如何从数组中取出一个对象。

参考:https://docs.mongodb.com/manual/reference/operator/update/pull/

db.survey.update( // select your doc in moongo
    { }, // your query, usually match by _id
    { $pull: { results: { $elemMatch: { score: 8 , item: "B" } } } }, // item(s) to match from array you want to pull/remove
    { multi: true } // set this to true if you want to remove multiple elements.
)

请注意使用此示例时要小心,因为如上文档中所述,“实际上,以下操作不会从原始集合中提取任何元素。” 请阅读上面的文档以获取更多详细信息。 - cjmling

8

由于收藏夹是一个数组,您只需要将其切割并保存文档即可。

var mongoose = require('mongoose'),
    Schema = mongoose.Schema;

var favorite = new Schema({
    cn: String,
    favorites: Array
});

module.exports = mongoose.model('Favorite', favorite);

exports.deleteFavorite = function (req, res, next) {
    if (req.params.callback !== null) {
        res.contentType = 'application/javascript';
    }
    // Changed to findOne instead of find to get a single document with the favorites.
    Favorite.findOne({cn: req.params.name}, function (error, doc) {
        if (error) {
            res.send(null, 500);
        } else if (doc) {
            var records = {'records': doc};
            // find the delete uid in the favorites array
            var idx = doc.favorites ? doc.favorites.indexOf(req.params.deleteUid) : -1;
            // is it valid?
            if (idx !== -1) {
                // remove it from the array.
                doc.favorites.splice(idx, 1);
                // save the doc
                doc.save(function(error) {
                    if (error) {
                        console.log(error);
                        res.send(null, 500);
                    } else {
                        // send the records
                        res.send(records);
                    }
                });
                // stop here, otherwise 404
                return;
            }
        }
        // send 404 not found
        res.send(null, 404);
    });
};

非常感谢您提供如此详尽的回复。 - occasl
4
如果您尝试同时移除两个东西,它们可能都会被取出数组,然后更改该数组。而第一个被保存的更改将被第二个更改覆盖。我建议使用类似于这个链接中提到的方法来进行操作:https://dev59.com/oWQn5IYBdhLWcg3wRlYM。 - Maximosaic

4

这对我很有效,非常有帮助。

SubCategory.update({ _id: { $in:
        arrOfSubCategory.map(function (obj) {
            return mongoose.Types.ObjectId(obj);
        })
    } },
    {
        $pull: {
            coupon: couponId,
        }
    }, { multi: true }, function (err, numberAffected) {
        if(err) {
            return callback({
                error:err
            })
        }
    })
});

我有一个名为SubCategory的模型,我想从这个类别数组中移除Coupon。我有一个类别数组arrOfSubCategory,所以我使用$in运算符的map函数来获取该数组中每个对象的数组。


3

我在我的项目中使用了这种格式,它起到了作用。

router.delete('/dashboard/participant/:id', async (req, res, next) => {
    try {
        const participant = await Participant.findByIdAndDelete({ _id: req.params.id });
        // { $pull: { templates: { _id: templateid } } },
        const event = await Event.findOneAndUpdate({ participants: participant._id }, { $pull: { participants: participant._id } }, { new: true });
        res.status(200).json({ request: 'Deleted', participant, event });
    } catch (error) {
        res.json(error)
    }
});

2
keywords = [1,2,3,4];
doc.array.pull(1) //this remove one item from a array
doc.array.pull(...keywords) // this remove multiple items in a array

如果您想使用...,请在您的js文件顶部调用'use strict'; :)

0
Favorite.update({ cn: req.params.name }, { "$pull": { "favorites": { "_id": favoriteId } }}, { safe: true, multi:true }, function(err, obj) {
    //do something smart
});

3
您的答案可以通过添加更多关于代码的功能及其如何帮助提问者的信息来改进。 - Tyler2P
如果你先阅读问题,然后再检查我的代码,那么它就很容易阅读。 - Cristian Zumelzu
1
仍需要解释。 - asciidude
多个:一次更新多个文档。 安全:验证更新(写入)是否已执行然后您有更新查询,“cn”是字段,因此您将仅更新名称等于“req.params.name”值的那些名称(在节点中,您可以像req.params一样获取查询参数)。然后在更新的另一部分中,您必须指定要更新的内容。因此,基本上拉动意味着您将在数组上操作并删除一个值,该值是具有属性“收藏夹”的子对象 - Cristian Zumelzu
这个“favorites”是来自其他集合的对象数组。 Favorite { ... favorites:[{ _id: ObjectId("<id>") }] .. - Cristian Zumelzu

0
这是一个使用Mongoose的TypeScript示例。
// Each company has COI
// COI looks like { buildingId: string, name: string, file: string }
// Company is an Entity Company looks like { companyId: string, cois: COI[] }

function deleteCOI(companyId: string, buildingId: string) {
  const company = await Companies.findOneAndUpdate(
    { companyId },
    { $pull: { cois: { buildingId: buildingId } } }
  );
  return company;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接