1

我正在开发一个类似 webApp 的 Excel,我想实现一个让用户自由插入行的功能。我可以轻松地修改前端以通过拼接和选定的行 id 来可视化插入的位置,但我们的后端使用的是默认生成 _id 和 order 的 mongoDB。为了解决这个问题,我们在之前和之后生成 Id,并在之前和之后之间生成 newId。

if (this.state.insertIndex !== undefined && this.state.insertIndex !== 0 && this.state.insertIndex !== rows.length - 1) {
  const after = parseInt(rows[Math.max(this.state.insertIndex - 1, 0)]._id.toString().substring(0, 8), 16) * 1000;
  const before = parseInt(rows[Math.min(this.state.insertIndex + 1, rows.length - 1)]._id.toString().substring(0, 8), 16) * 1000;
  const timestampAsInteger = Math.floor(Math.random() * (after - before) + before);
  newId = Math.floor(timestampAsInteger / 1000).toString(16) + "0000000000000000";
}

注意:insertIndex 初始未定义,选中单元格时设置状态,删除行后重置为未定义

但是,这个算法似乎有很多边缘情况没有考虑,有没有人有经验让它更健壮?或有关边缘情况的任何建议?

已知问题:

  • 选择行 [0] 时的错误行为
  • 生成 dup _id(不知道为什么)

其他想法

  • 如用户所见,通过 localStorage 渲染保存行状态而不是生成 _id 是否更容易。
  • 我在这里找到了这个输入链接描述但不明白,任何人都可以解释它是否可能的解决方案?

有什么想法吗?非常感谢

4

1 回答 1

0

几天后,我找到了这个解决方案。我的方法不是维护 _ids 列表,而是有其局限性。

这是我们根据前后时间戳生成自己的mongo_id的代码

    let newId;
    let canInsert = true;
    if (this.state.insertIndex !== undefined) {
      if (this.state.insertIndex == 0 && rows.length >=2) {
        const before = parseInt(rows[Math.max(this.state.insertIndex, 0)]._id.toString().substring(0, 8), 16) * 1000
        const after = parseInt(rows[Math.min(this.state.insertIndex + 1, rows.length - 1)]._id.toString().substring(0, 8), 16) * 1000;
        if (after - before > 1000) {
          const timestampAsInteger = Math.floor(Math.random() * (after - before + 1) + before);
          if (timestampAsInteger - before > 1000) {
            newId = Math.floor(timestampAsInteger / 1000).toString(16) + "0000000000000000";
          } else {
            canInsert = false;
          }
        } else {
          canInsert = false;
        }
      } else if (this.state.insertIndex < rows.length - 1) {
        const before = parseInt(rows[Math.max(this.state.insertIndex, 0)]._id.toString().substring(0, 8), 16) * 1000
        const after = parseInt(rows[Math.min(this.state.insertIndex + 1, rows.length - 1)]._id.toString().substring(0, 8), 16) * 1000;
        if (after - before > 1000) {
          const timestampAsInteger = Math.floor(Math.random() * (after - before + 1) + before);
          if (timestampAsInteger - before > 1000) {
            newId = Math.floor(timestampAsInteger / 1000).toString(16) + "0000000000000000";
          } else {
            canInsert = false;
          }
        } else {
          canInsert = false;
        }
      }
    }

我知道上面的代码很难阅读,但是有很多边缘情况,特别是由于 newId 生成方法,我们有两个时间戳至少应该有一个间隔 > 1000 的限制,否则会导致问题。

在 fetch 中,我们需要检查是否生成了 newId,否则我们将在最后一行添加

      let index = this.state.insertIndex == undefined? Math.max(rows.length, 0): Math.max(this.state.insertIndex + 1, 0);
      json["value"] = index;
      rows = canInsert ? update(rows, {$splice: [[index, 0, json]]}): update(rows, { $push: [json] });
      if (canInsert) {
        this.showMessage("The project name cannot be empty");
      } else {
        this.showMessage("Impossible to insert, add last instead");
      }

在我们的后端,将 _id 设置为我们的 newId

if (req.body.newId) {
    initialData['_id'] = req.body.newId;
}

这个特性最困难的部分是捕捉这么多可能的边缘情况。并且存在插入行的限制,两个已存在的行可能不会太紧密地生成。如果应用程序随着更多用户不断扩大规模,就会出现问题。但它适用于较小的用户组,并在需要时作为可选功能。

将来如果我们能想出一个更独特的 id 生成器,我们可以不受限制地插入。据我研究,从来没有人想出过这样的事情,但我花了几天时间才弄清楚这些事情。我希望这一发现可以为您节省几天的研究时间。

于 2019-03-21T20:26:39.017 回答