简体   繁体   中英

Having issues updating a document thats retrieved using a Mongoose model and processed as a promise

Jump to update #2 for some closer detail

Im having issues with a simple update to a document retrieved from querying a container via a Mongoose model. The find query does have two populations, but other than that, im not sure what the issue could be

The problem I'm running into, is when I retrieve a document, update a property, then attempt to save the updates to the document via Mongooses Doc.save() method, nothing seems to happen. Whats weird, is it the save() doesn't even fire off the callback passed to it.. (Or fire off the then() or catch() if I handle it as a Promise)

Model: Asset

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 )
}

And just for some detail, heres an example result from the same find query with the same two populations

[
    {
        "_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")
            }
        ]
    }
]

Document Query

Heres the code example of where I retrieve some documents via the Foo.find method (which works), and update the value of the first attribute (which I works), but when I try to a.save() the doc.. nothing happens:

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() )

In the console, everything displays as expected, right up until the a.save() .. Neither an error or Updated! is displayed.

It's definitely a Mongoose document that I'm interacting with (the a instanceof Foo shows true), so I'm not at all sure why the save() isn't doing anything..

I tried to handle a.save() as a Promise, instead of handing a callback to it, and again, nothing happened at all, neither the then or catch was executed.

This is driving me crazy!! Im sure its something stupid that I've overlooked, but I cant seem to find it. Any help would be appreceated

PS I didn't include the Partition or Field models/schemas because I highly doubt they are related... But if someone thinks so, just let me know

PSS Just an FYI, the MongoDB user does have write access

Update #1

Per a suggestion from @JohnnyHK, I tried to execute 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() )

No changes... Nothing in the a.save() is displayed in the console, and the doc isn't updated

Update #2

After some tinkering.. it seems to be related to it being a promise..

This is Successful :

// 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()
    })
})

This is UNSUCCESSFUL :

// 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() )

The only difference I can think of is the promise, as opposed to a callback

Whats super weird ... is some of the code inside the .then() executes.. Just not the save()

UPDATE #3

I created a github issue , and per my last update..

I found this issue , where his "solution" was to downgrade an entire major version, from 4.X to 3.X...

The version im currently at is ^4.3.7 , I tried to change it to 3.8.35 , which the downgrade went fine but then the script itself threw a bunch of errors... Which honestly, id rather really not use such an old version anyways.

I replicated the issue you are having using the schema you have provided and found a solution. The following worked for me and updated the .value property. Try prepending return to doc[0].save() to return it as mentioned in 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() )

I also use .exec() because according to the docs queries aren't full-fledged promises (just in case). Since .save() returns a promise, you should return it so that the promises resolve sequentially; waiting until previous promises complete before executing. Posted the code here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM