【问题标题】:How to resort array objects by position when one of their positions changes?当其中一个位置发生变化时,如何按位置排列数组对象?
【发布时间】:2018-09-11 11:34:39
【问题描述】:

期望的行为

当其中一个位置发生变化时,我正在尝试更新对象数组中每个对象的 position 属性。例如,位置为5 的对象移动到位置0,因此其他对象的位置也必须改变。

我无法为 MongoDB 找到“数组迭代器”,因此一直在尝试从其他角度解决问题,但我认为我过于复杂了。

架构

statements: [
{
    "position": 0,
    "id": "a"
},
{
    "position": 1,
    "id": "t"
},
{
    "position": 2,
    "id": "z"
},
{
    "position": 3,
    "id": "q"
},
{
    "position": 4,
    "id": "l"
},
{
    "position": 5,
    "id": "b"
}
]

前端场景

Figure A: Starting state of ordered divs

Figure B: Changed state of ordered divs (statement5has been moved to position0)

我的尝试

伪代码

我认为逻辑可以表示为:

对象位置将从 old_position 更改为 new_position 任意数量的位置。

如果 reposition_direction 是向后的,所有有位置的对象 大于或等于 new_position 应该增加(除了 被移动的对象应该被分配 new_position)

如果reposition_direction是forward,所有有位置的物体 小于或等于 new_position 应该递减(除了 被移动的对象应该被分配 new_position)

更新:此逻辑不正确,已添加工作逻辑以回答)

这是我目前所拥有的:

if (reposition_direction === "backwards") {

    var new_filter = { _id: o_id, "statements.position": { $gte: new_position } };

    var new_update = { $inc: { "statements.$[elem].position": 1 } };

    var array_filters = { "arrayFilters": [{ "elem.position": { $gte: new_position } }], "multi": true };

} else if (reposition_direction === "forwards") {

    var new_filter = { _id: o_id, "statements.position": { $lte: new_position } };

    var new_update = { $inc: { "statements.$[elem].position": -1 } };

    var array_filters = { "arrayFilters": [{ "elem.position": { $lte: new_position } }], "multi": true };
}

collection.updateOne(new_filter, new_update, array_filters, function(err, doc) {
    if (err) {
        res.send(err);
    } else {
        res.json({ response: "hurrah" });
    }
});

【问题讨论】:

  • 为什么要保留位置作为对象本身的单独“位置”属性。它们的位置是已知的,因为它们在一个数组中。任何时候你都需要遍历数组的位置,这样你就可以从数组的索引中知道它。
  • 一年后,我意识到更简单的解决方案是 $pull 来自数组的对象,然后 $push 使用 $each$position 将其设置为所需的索引:stackoverflow.com/a/36084944

标签: javascript node.js mongodb


【解决方案1】:

每次更改数组时,设置每个成员的位置:

for (var i = 0; i < statements.length; i++) {
    statements[i].position = i;
}

【讨论】:

  • 是的,前端就是这样实现的,mongodb不行。
  • 要更改mongodb中的数据首先将整个对象从mongodb中取出,用上面的代码更改对象,然后将其放回mongodb中。
  • 或者你是说每个语句都作为单独的mongodb对象存储在mongodb中?如果是这种情况,您可以从 mongo 中检索所有关联的语句,使用上述代码分配位置,然后将它们全部存储回 mongo。但是,最好将数组存储在 mongo 中。
  • 不管怎样,我肯定不会用 collection.updateOne 和一组难以理解的过滤器来做你正在尝试的事情。
  • 多大?如果数组中的对象少于 1000 个,我不会再考虑了。只需执行 get modify set。
【解决方案2】:

有人建议更好的主意是获取数组,迭代结果,然后设置整个数组的值,但我很想看看我是否可以正确地获得排序逻辑,我认为以下作品。只是粘贴在下面以供参考。

var new_position = Number(request_body.new_position);
var old_position = Number(request_body.old_position);
var reposition_direction = request_body.reposition_direction;
var statement_id = request_body.statement_id;
var filter = { _id: o_id };

if (reposition_direction === "backwards") {

    // if backwards, position filter is $gte new_position AND $lt old_position
    var new_filter = { _id: o_id, "statements.position": { $gte: new_position, $lt: old_position } };

    // increment
    var new_update = { $inc: { "statements.$[elem].position": 1 } };

    var array_filters = { "arrayFilters": [{ "elem.position": { $gte: new_position, $lt: old_position } }], "multi": true };

} else if (reposition_direction === "forwards") {

    // if forwards, position filter is $lte new_position AND $gt old_position
    var new_filter = { _id: o_id, "statements.position": { $lte: new_position, $gt: old_position } };

    // decrement
    var new_update = { $inc: { "statements.$[elem].position": -1 } };

    var array_filters = { "arrayFilters": [{ "elem.position": { $lte: new_position, $gt: old_position } }], "multi": true };

}

collection.updateOne(new_filter, new_update, array_filters, function(err, doc) {
    if (err) {
        res.send(err);
    } else {

        // set the moved objects property to the new value
        var new_filter = { _id: o_id, "statements.id": statement_id };
        var new_update = { $set: { "statements.$.position": new_position } };

        collection.findOneAndUpdate(new_filter, new_update, function(err, doc) {
            if (err) {
                res.send(err);
            } else {
                res.json({ response: "hurrah" });
            }
        });
    }
});

findOneAndUpdate() 的成功处理程序调用受到此答案的启发:

https://stackoverflow.com/a/30387038

我测试了以下组合,它们似乎有效:

1 移至4
4 移动到1

0 移至5
5 移动到0

1 移至2
2 移动到1

0 移至1
1 移动到0

4 移至5
5 移动到4

【讨论】:

    猜你喜欢
    • 2020-06-26
    • 1970-01-01
    • 1970-01-01
    • 2017-10-22
    • 2018-08-14
    • 1970-01-01
    • 2023-03-03
    • 2011-09-15
    • 1970-01-01
    相关资源
    最近更新 更多