更改 MongoDB 自然顺序,手动生成 _id 并按 _id 排序,在两行之间插入行实现(React Data Grid)

Change MongoDB natural order, generate _id manually and sort by _id, insert rows between two rows implementation (React Data Grid )

我正在开发一个像 webApp 这样的 Excel,我想实现一个让用户自由插入行的功能。我可以很容易地修改前端以可视化通过拼接和 selected 行 ID 插入的位置,但我们的后端使用 mongoDB 默认生成 _id 和顺序。为了解决这个问题,我们生成了之前和之后的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初始未定义,单元格selected时setState,行删除后重置为undefined

不过这个算法好像有很多边缘情况没有考虑,有没有人有经验让它更健壮?或者对边缘情况有什么建议吗?

已知问题:

其他想法

有什么想法吗? 非常感谢

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

这是我们自己生成的代码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 生成器,我们可以不受限制地插入。据我研究,从来没有人想出这样的事情,但我花了几天时间才弄明白。我希望这一发现能为您节省几天的研究时间。