【问题标题】:Mongoose inserting same data three times instead of iterating to next data猫鼬插入相同的数据三次而不是迭代到下一个数据
【发布时间】:2019-01-22 18:06:43
【问题描述】:

我正在尝试将以下数据播种到我的 MongoDB 服务器:

const userRole = {
    role: 'user',
    permissions: ['readPost', 'commentPost', 'votePost']
}
const authorRole = {
    role: 'author',
    permissions: ['readPost', 'createPost', 'editPostSelf', 'commentPost',
'votePost']
}
const adminRole = {
    role: 'admin',
    permissions: ['readPost', 'createPost', 'editPost', 'commentPost',
    'votePost', 'approvePost', 'approveAccount']
}
const data = [
    {
        model: 'roles',
        documents: [
            userRole, authorRole, adminRole
        ]
    }
]

当我尝试遍历此对象/数组并将此数据插入数据库时​​,我最终得到了三个“adminRole”副本,而不是三个单独的角色。无法弄清楚为什么会发生这种情况,我感到非常愚蠢。

我实际遍历对象并为其播种的代码如下,我知道它实际上获取了每个值,因为我已经完成了 console.log 测试并且可以正确获取所有数据:

for (i in data) {
        m = data[i]
        const Model = mongoose.model(m.model)
        for (j in m.documents) {
            var obj = m.documents[j]

            Model.findOne({'role':obj.role}, (error, result) => {
                if (error) console.error('An error occurred.')
                else if (!result) {
                    Model.create(obj, (error) => {
                        if (error) console.error('Error seeding. ' + error)
                        console.log('Data has been seeded: ' + obj)
                    })
                }
            })
        }
    }

更新:

这是我在阅读了大家的回复后想出的解决方案。两个私有函数生成 Promise 对象,用于检查数据是否存在以及插入数据,然后所有的 Promise 都通过 Promise.all 实现。

// Stores all promises to be resolved
var deletionPromises = []
var insertionPromises = []
// Fetch the model via its name string from mongoose
const Model = mongoose.model(data.model)
// For each object in the 'documents' field of the main object
data.documents.forEach((item) => {
    deletionPromises.push(promiseDeletion(Model, item))
    insertionPromises.push(promiseInsertion(Model, item))
})

console.log('Promises have been pushed.')
// We need to fulfil the deletion promises before the insertion promises.
Promise.all(deletionPromises).then(()=> {
    return Promise.all(insertionPromises).catch(()=>{})
}).catch(()=>{})

我不会同时包含 promiseDeletionpromiseInsertion,因为它们在功能上是相同的。

const promiseDeletion = function (model, item) {
    console.log('Promise Deletion ' + item.role)
    return new Promise((resolve, reject) => {
        model.findOneAndDelete(item, (error) => {
            if (error) reject()
            else resolve()
        })
    })
}

更新 2:您应该忽略我最近的更新。我已经修改了我发布的结果,但即便如此,有一半的时间角色被删除而不是插入。何时将角色实际插入服务器是非常随机的。在这一点上,我感到非常困惑和沮丧。

【问题讨论】:

    标签: javascript node.js mongodb mongoose


    【解决方案1】:

    您在使用 Javascript 时遇到了一个非常常见的问题:您不应该在常规 for (-in) 循环中定义 (async) 函数。发生的情况是,当您遍历三个值时,第一个异步 find 被调用。由于您的代码是异步的,因此 nodejs 在继续下一个循环迭代并计数到第三个值之前不会等待它完成,这里是管理规则。 现在,由于您在循环中定义了函数,当第一次异步调用结束时,for-loop 已经循环到最后一个值,这就是为什么要插入 admin 三次。

    为避免这种情况,您可以将异步函数移出循环以强制按值而不是引用进行调用。尽管如此,这可能会带来很多其他问题,所以我建议你宁愿看看承诺以及如何链接它们(例如,将所有猫鼬承诺放在一个数组中并使用 Promise.all 等待它们)或使用更现代的 async/await 语法以及 for-of 循​​环,既易于阅读,又可以顺序异步命令指令。

    检查这个非常相似的问题:Calling an asynchronous function within a for loop in JavaScript

    注意:for-of 正在讨论性能问题,因此请检查这是否适用于您的用例。

    【讨论】:

    • 是的,经过大量研究后,我得出结论是由于异步函数调用,我只是不知道如何实际修复它。该链接很有帮助,并且通过其他响应,我知道如何解决它。非常感谢!
    【解决方案2】:

    在循环中使用异步函数可能会导致一些问题。

    你应该改变你使用 findOne 的方式,使它成为同步函数

    首先你需要将你的函数设置为异步,然后像这样使用 findOne:

    async function myFucntion() {
      let res = await Model.findOne({'role':obj.role}).exec();//Exec will fire the function and give back a promise which the await can handle.
      //do what you need to do here with the result..
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-02
      • 2012-05-11
      • 2022-12-19
      相关资源
      最近更新 更多