Get position in an array in mongodb

Is it possible to get the position of an array element that matches a query?

For example, I have a collection with documents like this:

{"_id":ObjectId("560122469431950bf55cb095"), "otherIds": [100, 103, 108, 104]}

      

And I would like to get a query result similar to this for otherId-> 108:

{"_id":ObjectId("560122469431950bf55cb095"),"position":3}

      

Can you get something like this? Thanks in advance!

+3


source to share


1 answer


Ok, you can always run mapReduce for this and map the array index with .indexOf()

:

db.collection.mapReduce(
    function() {
        emit(this._id,{ "position": this.otherIds.indexOf(108) });
    },
    function() {},
    {
        "out": { "inline": 1 },
        "query": { "otherIds": 108 }
    }
)

      

Or for possible "multiple" matches, use .map()

with the optional "index" parameter:

db.collection.mapReduce(
    function() {
        emit(this._id,{ 
            "positions": this.otherIds.map(function(el,idx) {
                return (el == 108) ? idx : -1;
            }).filter(function(el) {
                return el != -1;
            })
       });
    },
    function() {},
    {
        "out": { "inline": 1 },
        "query": { "otherIds": 108 }
    }
)

      

But, of course, array indices start at 0

, so if you expect the result to be 3

, you can always add 1

the index position to match.



Of course, you just simply return the array in response to the request and just agree with the position (s) of the matched element in the client code, and it is probably best to handle this way unless you have particularly large arrays.

There is a statement on the MongoDB development branch $arrayElemAt

, but this unfortunately works the other way around, and instead of returning the position of the matched element, it returns the element at the given position. And since there is no way to determine the current position of the array or to cycle through all possible positions, it cannot be inversely converted to provide a positive match at a given position.

It could also be argued that other statements such as the $map

upcoming one $filter

(also in the dev branch) should have a similar parameter, such as a system variable, available inside those commands, such as their JavaScript and other language equivalents. Then something like $$IDX

(trend from other system variables like $$ROOT

) then you can do it in .aggregate()

:

db.collection.aggregate([
    { "$match": { "otherIds": 108 } },
    { "$project": {
        "position": {
            "$filter": {
                "input": {
                    "$map": {
                        "input": "$otherIds",
                        "as": "el",
                        "in": {
                            "$cond": [
                                { "$eq": [ "$$el", 108 ] },
                                "$$IDX",
                                -1
                            ]
                        }
                    }
                },
                "as": "el",
                "cond": { "$ne": ["$$el", -1] }
            }
        }
    }}
])

      

But that still has to happen, although it would be nice to see and seemingly not hard to ask, since both have inner workings that keep the current index position anyway, so you just have to expose this for use as an accessible variable.

+1


source







All Articles