在更新使用 Mongoose 模型检索并作为承诺处理的文档时遇到问题

Posted

技术标签:

【中文标题】在更新使用 Mongoose 模型检索并作为承诺处理的文档时遇到问题【英文标题】:Having issues updating a document thats retrieved using a Mongoose model and processed as a promise 【发布时间】:2016-05-16 15:19:30 【问题描述】:

跳转到更新 #2 以获得更详细的信息

我对通过 Mongoose 模型查询容器检索到的文档进行简单更新时遇到问题。 find 查询确实有两个群体,但除此之外,我不确定问题可能是什么

我遇到的问题是,当我检索文档,更新属性,然后尝试通过 Mongooses Doc.save() 方法将更新保存到文档时,似乎什么也没发生。奇怪的是,save() 甚至没有触发传递给它的回调..(或者如果我将其作为 Promise 处理,则触发 then()catch()

模型:资产

module.exports = Mongoose => 
    const Schema = Mongoose.Schema

    const assetSchema = new Schema(
        attributes: [
            _field: 
                type: Schema.Types.ObjectId,
                ref: 'Field',
                required: true
            ,
            value: 
                type: Schema.Types.Mixed,
                required: true
            
        ],
        _partition: 
            type: Schema.Types.ObjectId,
            ref: 'Partition'
        
    )

    return Mongoose.model( 'Asset', assetSchema )

只是为了一些细节,这里有一个来自相同 find 查询的示例结果,具有相同的两个群体

[
    
        "_id" : ObjectId("56b626dc4040b5383696d16f"),
        "_partition" :  
            _fields: [Object],
            name: 'Server stuff',
            _id: ObjectId("56ae4707f3d4645b2e5b0537")
        ,
        "attributes" : [
            
                _field: 
                    _id: ObjectId("56ae4703f3d4645b2e5b0534"),
                    name: 'Hostname'
                ,
                value: 'server-04.foobar.ad',
                _id: ObjectId("56b626dc4040b5383696d172")
            
        ]
    
]

文档查询

这是我通过Foo.find 方法(有效)检索一些文档的代码示例,并更新第一个属性的值(我有效),但是当我尝试a.save() 文档时..什么都没有发生:

Asset
    .find(  _id: '56b6568cb5195902381f6c65'  )
    .populate(  path: 'attributes._field'  )
    .populate(  path: '_partition'  )
    .then( docs => 
        if( ! docs )
            throw new Error(`No docs were found`)

        console.log('# Docs Found:', docs.length) // 1
        console.log('# Instance?:', docs[0] instanceof Asset) // true
        console.log('# Docs:', assets) // Shows single document inside array

        let a = docs[0]

        a.attributes[0].value = 'blah'

        // This is where the problem is, nothing below here is displayed in the console
        a.save(function (err) 
            if (err)
                throw new Error('Failed to update:' + err)

            console.log('Updated!')
        )

     )
    .catch( err => console.error( 'ERROR:',err ) )
    .finally( () => Mongoose.connection.close() )

在控制台中,一切都按预期显示,直到a.save().. 既没有显示错误也没有显示Updated!

这绝对是我正在与之交互的 Mongoose 文档(a instanceof Foo 显示为真),所以我完全不确定为什么 save() 没有做任何事情..

我尝试将 a.save() 作为 Promise 处理,而不是对其进行回调,同样,什么也没发生,thencatch 都没有被执行。

这快把我逼疯了!!我确定它是我忽略的一些愚蠢的东西,但我似乎找不到它。任何帮助将不胜感激

PS 我没有包含 PartitionField 模型/模式,因为我非常怀疑它们是否相关......但如果有人这么想就告诉我吧

P.S.S.仅供参考,MongoDB 用户确实具有写入权限

更新 #1

根据@JohnnyHK 的建议,我尝试执行Doc.markModified()

Asset
    .find(  _id: '56b6568cb5195902381f6c65'  )
    .populate(  path: 'attributes._field'  )
    .populate(  path: '_partition'  )
    .then( docs => 
        if( ! docs )
            throw new Error(`No docs were found`)

        console.log('# Docs Found:', docs.length) // 1
        console.log('# Instance?:', docs[0] instanceof Asset) // true
        console.log('# Docs:', assets) // Shows single document inside array

        let a = docs[0]

        a.attributes[0].value = 'blah'

        a.markModified('attributes')

        // This is where the problem is, nothing below here is displayed in the console
        a.save(function (err) 
            if (err)
                throw new Error('Failed to update:' + err)

            console.log('Updated!')
        )

     )
    .catch( err => console.error( 'ERROR:',err ) )
    .finally( () => Mongoose.connection.close() )

无变化...a.save() 中的任何内容均未显示在控制台中,并且文档未更新

更新 #2

经过一番修修补补..它似乎与它是一个承诺有关..

这是成功

// AS A CALLBACK
Asset.find(  _id: '56b6568cb5195902381f6c65'  )
    .populate(  path: 'attributes._field'  )
    .populate(  path: '_partition'  )
    .exec(function (err, doc) 
    if (err) throw new Error(err)

    doc[0].attributes[0].value = 'FOO'

    doc[0].save(function (err) 
        if (err) throw new Error(err)

        console.log('Updated to:',doc[0])

        Mongoose.connection.close()
    )
)

这是不成功

// AS A PROMISE

import Promise from 'bluebird'
Mongoose.Promise = Promise
// ..
Asset.find(  _id: '56b6568cb5195902381f6c65'  )
    .populate(  path: 'attributes._field'  )
    .populate(  path: '_partition'  )
    .then( doc => 
        doc[0].attributes[0].value = 'FOO'

        doc[0].save(function (err) 
            if (err) throw new Error(err)

            console.log('Updated to:',doc[0])
        )
    )
    .catch( err => 
        throw new Error(err)
    )
    .finally( () => Mongoose.connection.close() )

我能想到的唯一区别是承诺,而不是回调

什么超级奇怪...是.then()执行的一些代码......只是不是save()

更新 #3

我创建了一个github issue,并且根据我上次的更新..

我找到了this issue,他的“解决方案”是将整个主要版本从 4.X 降级到 3.X...

我当前的版本是^4.3.7,我尝试将其更改为3.8.35,降级顺利,但脚本本身抛出了一个一堆错误......老实说,我真的不使用这么旧的版本。

【问题讨论】:

没有forEach循环有什么问题吗? 不,因为它输出# Foo instance? true 好的,但没有 forEach 是否有任何“更新!”在控制台中? 不,没有。我更新了原版,删除了_.forEach @johnnyHK 不确定,在他的代码中,显示了来自user.save 的控制台输出,我的不是......就像我的情况一样,save() 甚至没有被执行 【参考方案1】:

我使用您提供的架构复制了您遇到的问题并找到了解决方案。以下对我有用并更新了 .value 属性。尝试在 doc[0].save() 前添加 return 以返回它,如 this article 中所述

var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
// .....


Asset.find(  _id: '56b6568cb5195902381f6c65'  )
    .populate(  path: 'attributes._field'  )
    .populate(  path: '_partition'  )
    .exec()       // Queries aren't full-fledged promises!
    .then( doc => 
        console.log('Before:',doc[0]);
        doc[0].attributes[0].value = 'FOO'

        // Return a promise!
        return doc[0].save(function(err)
            console.log('Updated to:', doc[0]);
        );
    )
    .catch( err => 
        throw new Error(err)
    )
    .finally( () => Mongoose.connection.close() )

我也使用.exec(),因为根据文档queries aren't full-fledged promises(以防万一)。由于.save() 返回一个promise,你应该返回它以便promise 顺序解决;等到之前的承诺完成后再执行。贴出代码here

【讨论】:

不确定有什么区别,但其中一位 Mongo 开发人员能够验证这是一个问题,我发现另一个人也遇到了完全相同的问题。他们将其添加到Mongoose 4.4.3 milestones。现在,我刚刚开始使用 Bluebirds asCallback,这让我暂时了解它 我的意思是,我能够复制您的问题并且确实看到没有触发对.save() 的回调。但是将return 添加到它可以修复它。 .save() 返回的值是一个 promise,所以返回它会导致顺序执行 @Justin 有时这是一件小事。希望它有所帮助 确实做到了......我一直在想“这不可能,有很多模型正在更新/保存而我没有返回文档查询”,然后我回去查看代码,并注意到它们都是new 实例,仅表示new Something().save(),显然不需要返回。我想知道为什么在这种情况下会这样做 @Justin 当then() 的回调返回一个值时,我只看到它返回一个值,该值作为参数传递给下一个then() 回调,在promise 链完成之前它也会做类似的事情,在这种情况下为finally()。然后我看到了我在帖子中提到的文章,并看到了它如何返回一个承诺而不是一个值。做了一些实验,我注意到了

以上是关于在更新使用 Mongoose 模型检索并作为承诺处理的文档时遇到问题的主要内容,如果未能解决你的问题,请参考以下文章

无法从 Mongoose 模型中获取文档“find”承诺

未来所有模型实例的 Mongoose Save 方法的Sinon Mock(带有承诺)

以角度(mongoose/mongodb)更新模型,特别是作为模型属性的对象数组

如何承诺 Mongo/Mongoose 找到

在递归节点函数中从 Mongoose 检索树数据

无法操纵 mongoose 查询结果 - 承诺未定义