Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot infer query fields to set error on insert

I'm trying to achieve a "getOrCreate" behavior using "findAndModify". I'm working in nodejs using the native driver.

I have:

var matches = db.collection("matches");
matches.findAndModify({
        //query
        users: {
            $all: [ userA_id, userB_id ]
        }, 
        lang: lang, 
        category_id: category_id
    }, 
    [[ "_id", "asc"]], //order
    {
        $setOnInsert: { 
            users: [userA_id, userB_id], 
            category_id: category_id, 
            lang: lang, 
            status: 0
        }
    }, 
    {
        new:true, 
        upsert:true
    }, function(err, doc){
        //Do something with doc
    });

What i was trying to do is: "Find specific match with specified users, lang and category... if not found, insert a new one with specified data"

Mongo is throwing this error:

Error getting/creating match { [MongoError: exception: cannot infer query fields to set, path 'users' is matched twice]
  name: 'MongoError',
  message: 'exception: cannot infer query fields to set, path \'users\' is matched twice',
  errmsg: 'exception: cannot infer query fields to set, path \'users\' is matched twice',
  code: 54,
  ok: 0 }

Is there a way to make it work? It's impossible?

Thank you :)

like image 791
Pipe Avatar asked Jul 07 '15 00:07

Pipe


1 Answers

It's not the "prettiest" way to handle this, but current restrictions on the selection operators mean you would need to use a JavaScript expression with $where.

Substituting your vars for values for ease of example:

matches.findAndModify(
    {
        "$where": function() {
            var match = [1,2];
            return this.users.filter(function(el) { 
                return match.indexOf(el) != -1;
            }).length >= 2;
        },
        "lang": "en",
        "category_id": 1
    },
    [],
    { 
        "$setOnInsert": {
            "users": [1,2],
            "lang": "en",
            "category_id": 1      
        }
    },
    {
        "new": true,
        "upsert": true
    },
    function(err,doc) {
        // do something useful here
    }
);

As you might suspect, the "culprit" here is the positional $ operator, even though your operation does not make use of it.

And the problem specifically is because of $all which is looking for the possible match at "two" positions in the array. In the event that a "positional" operator was required, the engine cannot work out ( presently ) which position to present. The position should arguably be the "first" match being consistent with other operations, but it is not currently working like that.

Replacing the logic with a JavaScript expression circumvents this as the JavaScript logic cannot return a matched position anyway. That makes the expression valid, and you can then either "create" and array with the two elements in a new document or retrieve the document that contains "both" those elements as well as the other query conditions.


P.S Little bit worried about your "sort" here. You may have added it because it is "mandatory" to the method, however if you do possibly expect this to match "more than one" document and need "sort" to work out which one to get then your logic is slightly flawed.

When doing this to "find or create" then you really need to specifiy "all" of the "unique" key constraints in your query. If you don't then you are likely to run into duplicate key errors down the track.

Sort can in fact be an empty array if you do not actually need to "pick" a result from many.

Just something to keep in mind.

like image 92
Blakes Seven Avatar answered Oct 31 '22 18:10

Blakes Seven