0
votes

I'm building a persistent html fields framework using Node, Mongoose, and Socket.io for the backend. I've run into what seems like a bug in Mongoose's update method.

The documents in mongo look like this:

{
    "_id" : "pickable_qty",
    "currently_connected" : [ ],
    "locks" : [
        {
            socket_id: 'eQYVyz1z28rJZRPpAAAB',
            unique_values:{
                merchant_warehouse_id: 11.1,
                product_item_id: 5555
            }
        },
        {
            socket_id: 'eQYVyz1z28rJZRPpAAAB',
            unique_values:{
                merchant_warehouse_id: 11.2,
                product_item_id: 5555
            }
        },
        {
            socket_id: 'eQYVyz1z28rJZRPpAAAB',
            unique_values:{
                merchant_warehouse_id: 11.1,
                product_item_id: 1234
            }
        }
    ],
    "definition" : {
        "ajax_url" : "/persistent-fields/pickable_qty",
        "unique_keys" : [
            "product_item_id",
            "merchant_warehouse_id"
        ],
        "max_idle_seconds" : 30,
        "field_type" : "text"
    },
    "__v" : 0
}

The problem arises when I start inserting subdocuments into the locks array.

I insert them using Mongoose's update method like so:

FieldSchema.update(
    {
        _id: 'pickable_qty',
        'locks.unique_values': { 
            '$ne': { 
                merchant_warehouse_id: 11.1, 
                product_item_id: 5334 
            } 
        },
        'definition.unique_keys': { 
            '$all': [ 
                'merchant_warehouse_id', 
                'product_item_id' 
            ] 
        } 
    },
    {
        '$push': {
            locks: { 
                socket_id: 'eQYVyz1z28rJZRPpAAAB', 
                unique_values: {
                    merchant_warehouse_id: 11.1, 
                    product_item_id: 5334 
                } 
            } 
        }
    },
    function(err, count, res){
        console.log('err:', err, 'count:', count, 'res:', res);
        //err: null count: 1 res: { ok: true, n: 1, updatedExisting: true }
    }
);

The first insert works exactly as expected, there were no locks in the array containing the unique values {merchant_warehouse_id: 11.1,product_item_id: 5334} so the document was found and the lock subdocument was inserted.

However running this same update the second time should not insert a new locks subdocument because one already exists with the same unique_values and the $ne part of the query should cause it to return no matches to update.

I have confirmed that doing a find with the same query returns no documents both on the MongoDB command line and using Mongoose's own Schema.find method, but the Schema.update method still finds a document and inserts a duplicate lock subdoc.

Am I just going crazy or is there a reason why Mongoose's find method won't retrieve the document while update will?

2

2 Answers

0
votes

The problem is that you are querying whether or not an object matches a subdocument, and this can be tricky. What you want to do use a combination of $elemMatch and $ne in your query.

var query = {
    _id: 'pickable_qty',
    'locks': { 
        $elemMatch: {
            merchant_warehouse_id: {
                $ne: 11.1
            },
            product_item_id: {
                $ne: 5334
            }
        }
    },
    'definition.unique_keys': { 
        '$all': [ 
            'merchant_warehouse_id', 
            'product_item_id' 
        ] 
    } 
}

It's basically a query for a document inside of an array.

0
votes

After using MongoDB's query profiler, I figured out that Mongoose was switching the order of the keys in the lock's unique_values object while doing the update query. Then it was inserting the duplicate subdocuments with the keys in the correct order. So apparently MongoDB's $ne operator will match nested subdocument arrays only if the keys in the subdocument are in the same order as your query.