我正在使用类似webApp的Excel,并且我想实现一个函数,让用户自由地插入行。我可以轻松地修改前端以通过拼接和选定的行ID可视化插入位置,但是我们的后端使用的是mongoDB,默认情况下会生成_id和顺序。为了解决此问题,我们在Ids之前和之后生成,并在之前和之后之间生成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初始为undefined,在选定单元格时为setState,在行删除后重置为undefined
但是,此算法似乎有许多未考虑的边缘情况,有人有经验使其更加健壮吗?或对边缘案例有何建议?
已知问题:
其他想法
有什么想法吗? 非常感谢
答案 0 :(得分:0)
几天后,我讨论了有关解决方案的这项工作。我的方法没有维护_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 generate方法的缘故,边缘情况很多,我们限制了两个时间戳的间隔至少应大于1000,否则会引起问题。
在获取时,我们需要检查是否生成了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生成器,我们也许可以无限制地插入它。据我研究,没有人想到过这样的事情,但是花了我几天的时间才弄清楚。希望这项发现可以为您节省研究时间。