【问题标题】:How do I keep mongo's array index from changing during an update?如何在更新期间保持 mongo 的数组索引不变?
【发布时间】:2019-07-02 01:29:51
【问题描述】:

我正在尝试更新我的对象中的一个数组。但是,每次我发送 post 调用时,数组中的索引都会发生变化。

我尝试过使用 $set 并手动更新数组...但是数组上的索引一直在变化。

这是模型:

const MappedImageSchema = new Schema({
    imageUrl: {type: String, required: true},
    name: {type: String, required: true},
    areas:[
        {
            name: {type: String},
            shape: {type: String},
            coords:[{type: Number}],
        }
    ]
});
module.exports = MappedImage = mongoose.model('mappedImages', MappedImageSchema)

这是执行更新的代码:

// @route   POST api/maps/:id/areas
// @desc    add an area to a map (by map id)
// @access  Private
router.post('/:id/areas/:id_area', passport.authenticate('jwt', { session: false }),
    (req, res) => {

        MappedImage.findById(req.params.id)
        .then(map => {

            // get all of the areas from the map...
            var allAreas = map.areas;

            // now get the index of the area we are going to update
            const areaIndex = map.areas.map(item => item._id.toString()).indexOf(req.params.id_area);

            // update the information
            var coords = req.body.coords.split(',');
            const updatedArea = {
                name: req.body.name,
                shape: req.body.shape,

                coords: coords,
            };

            // set the updated information in the correct map area
            allAreas[areaIndex] = updatedArea;

            var query = {_id: req.params.id}; // this is the MAP id
            var update = {$set: {areas:allAreas}};  // update the areas 
            var options = {new: true};

            MappedImage.findOneAndUpdate(query, update, options)
            .then(map => res.json(map))
            .catch(err => res.status(404).json({ mapnotfound: err }));

        })
        .catch(err => res.status(404).json({ mapnotfound: 'Map not found while updating area' }));
    }
  );

这是调用前的数据

{
    "_id": "5c5c69dda40e872258b4531d",
    "imageUrl": "url test",
    "name": "name test",
    "areas": [
        {
        "coords": [1,2,3,4,5,6],
        "_id": "5c5c8db2f904932dd8d4c560",  <---- _id changes every time !
        "name": "area name",
        "shape": "poly"
        }
    ],
    "__v": 3
}

这是我打的邮递员电话:

调用的结果是名称被更改...但索引也是如此...使下一次调用失败并显示“找不到具有该索引的区域”。

这个问题令人困惑的是,当我运行这段代码时,地图的 _id 没有得到更新:

router.post('/:id', passport.authenticate('jwt', { session: false }),
(req, res) => {

    var query = {_id: req.params.id};
    var update = {imageUrl: req.body.imageUrl, name: req.body.name};
    var options = {new: true};

    MappedImage.findOneAndUpdate(query, update, options)
    .then(map => res.json(map))
    .catch(err => res.status(404).json({ mapnotfound: err }));
});

更新 1

我尝试使用区域索引并仅更新该区域...但 _id 也会随此代码更改:

        ... same code all the way down to here
        allAreas[areaIndex] = updatedArea;

        // but instead of calling 'findOneAndUpdate'... call map save
        map.save().then(map => res.json(map));

更新 2

我无法让此代码工作,因为 area._id 和 area.$ 未定义?

var query = {_id: req.params.id, areas._id: id_area}; // this is the MAP id
var update = {$set: {areas.$: updatedArea}};  // update the area

更新 3

因此,将 _id 放在 updatedArea 中可以解决此问题...但这样做“感觉”是错误的:(根据 eol 答案)

        const updatedArea = {
            _id: req.params.id_area,
            name: req.body.name,
            shape: req.body.shape,

            coords: coords,
        };

更新 4

eol - 感谢 mongoDB 方面的验证...如果这解决了 DB id 问题...我只需要知道为什么我的查询失败。我试过这个,我在终端输出中看到的只是“创建查询”.​​.....我从来没有看到“查询”和它的定义......所以出了点问题,我不知道如何弄清楚是什么。这是我现在拥有的:

 console.log('creating query');
 var query = {"_id": req.params.id, "areas._id": id_area};
 console.log('query');
 console.log(query);

更新 5 弄清楚为什么没有输出查询,id_area 没有定义......但是 req.params.id_area 是!

 console.log('creating query');
 var query = {"_id": req.params.id, "areas._id": req.params.id_area};
 console.log('query');

更新 6

代码在...中,但仍然无法正常工作。一张图片值一千字……所以这里有两个:

这个显示区域 ID 仍在变化:

这是我现在拥有的代码:

    console.log('Update area');
    console.log('changing area ' + req.params.id_area);
    //console.log(req.body);

    const { errors, isValid } = mapValidators.validateAreaInput(req.body);

    // Check Validation
    if(!isValid){
        return res.status(400).json(errors);
    }

    MappedImage.findById(req.params.id)
    .then(map => {

        // Check to see if area exists
        if (
            map.areas.filter(
            area => area._id.toString() === req.params.id_area
            ).length === 0
        ) {
            return res.status(404).json({ areanotfound: 'Area does not exist' });
        }

        console.log('area exists');

        // get all of the areas from the map...
        var allAreas = map.areas;

        console.log('all areas');
        console.log(allAreas);

        // now get the index of the area we are going to update
        const areaIndex = map.areas.map(item => item._id.toString()).indexOf(req.params.id_area);

        console.log('area index');
        console.log(areaIndex);

        // update the information
        var coords = req.body.coords.split(',');
        const updatedArea = {
            name: req.body.name,
            shape: req.body.shape,
            preFillColor: req.body.preFillColor,
            fillColor: req.body.fillColor,

            coords: coords,
        };

        console.log('updated area');
        console.log(updatedArea);


        // set the updated information in the maps areas
        allAreas[areaIndex] = updatedArea;

        console.log('creating query');
        var query = {"_id": req.params.id, "areas._id": req.params.id_area};
        console.log('query');
        console.log(query);


        var update = {$set: {"areas.$": updatedArea}};
        console.log('update');
        console.log(update);

        var options = {new: true};

        MappedImage.findOneAndUpdate(query, update, options)
        .then(map => res.json(map))
        .catch(err => res.status(404).json({ mapnotfound: err }));

    })
    .catch(err => res.status(404).json({ mapnotfound: 'Map not found while updating area' }));

这是终端输出:

【问题讨论】:

    标签: node.js mongodb indexing


    【解决方案1】:

    您可以尝试将updatedArea 对象中的_id 属性设置为您要更新的区域的值。这将防止在使用 $set 运算符时创建新 id。像这样的:

    // now get the index of the area we are going to update
    const areaIndex = map.areas.map(item => item._id.toString()).indexOf(req.params.id_area);
    
    // update the information
    var coords = req.body.coords.split(',');
    const updatedArea = {
            _id: id_area,
            name: req.body.name,
            shape: req.body.shape,
            coords: coords,
    };
    ...
    

    请注意,使用上述解决方案,您总是设置一个新数组,这就是生成新 id 的原因。

    您也可以尝试使用 $ operator 更新数组中的特定元素:

    var query = {"_id": req.params.id, "areas._id": id_area}; // this is the MAP id
    var update = {$set: {"areas.$": updatedArea}};  // update the area
    

    请参阅下面的屏幕截图以获取示例(在 mongodb-shell 中执行命令),其中我尝试仅更新第二个数组元素(即使用 _id 5c5c8db2f904932dd8d4c561

    【讨论】:

    • 我想我想知道为什么当我进行类似调用时地图的 ID 没有更新? (我将编辑问题以包含代码,以便您了解我的意思)
    • 已编辑问题以显示主地图更新调用并声明主 id 不改变如何令人困惑
    • 这行得通...但它看起来有点像“黑客”...如果你明白我的意思...必须有一个充分的理由让它发生并且有一种方法让它不会发生。
    • stackoverflow.com/users/3761628/eol : 我写的代码有意义吗?可以做得更优雅吗?还有其他提示或建议吗?
    • 问题是您实际上并没有“更新”值,而是设置了一个新数组。这就是为什么它得到一个新的id。在上面带有var update = {imageUrl: req.body.imageUrl, name: req.body.name}; 的示例中,您实际上只是在更新值,因此对象ID 保持不变。但我认为还有另一种方法->您也可以使用 id 访问数组索引并执行对象的实际更新,我会更新我的答案。我现在无法测试它,如果该解决方案也有效,请告诉我:)
    猜你喜欢
    • 1970-01-01
    • 2021-04-03
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多