Vaibhaw
Vaibhaw

Reputation: 646

Use MongoDB $slice operator on an array within an array

I have a schema that looks like this:

> db.test.findOne()
{
    "_id" : "1:1419552000",
    "l0" : [
        {
            "l1" : [
                "1",
                "2",
                "3",
                "4"
            ]
        },
        {
            "l1" : [
                "11",
                "12",
                "13",
                "14"
            ]
        },
        {
            "l1" : [
                "21",
                "22",
                "23",
                "24"
            ]
        },
        {
            "l1" : [
                "31",
                "32",
                "33",
                "34"
            ]
        },
        {
            "l1" : [
                "41",
                "42",
                "43",
                "44"
            ]
        }
    ]
}

I wish to be able to fetch a range of elements from within any of the arrays at level 1 (l1). But the $slice operator doesn't to be able to do that. Is this not possible at all?

> db.test.findOne({}, {"l0" : {$slice: 1}})
{
    "_id" : "1:1419552000",
    "l0" : [
        {
            "l1" : [
                "1",
                "2",
                "3",
                "4"
            ]
        }
    ]
}

> db.test.findOne({}, {"l0.0.l1" : {$slice: 2}})
{
    "_id" : "1:1419552000",
    "l0" : [
...
returns the entire object
...
}

Any pointers or help is welcome. Thanks!

Upvotes: 1

Views: 966

Answers (3)

Dmytro Shevchenko
Dmytro Shevchenko

Reputation: 34591

You can use the aggregation framework with $skip, $limit and $unwind to get to the subarray range you need:

db.test.aggregate(
    {
        $match: { _id: "1:1419552000" }
    },
    {
        $unwind: "$l0"
    },
    {
        $limit: 1 // keeps the first element in l0
    },
    {
        $unwind: "$l0.l1"
    },
    {
        $limit: 2 // keeps the first two elements in the first l1
    },
    {
        $group: // groups the unwound elements back into an array
        {
            _id: "$_id",
            l0: { $addToSet: "$l0.l1" }
        }
    }
)

This will be returned:

{ "_id" : "1:1419552000", "l0" : [ "2", "1" ] }

Upvotes: 2

Alex
Alex

Reputation: 21766

You don't specify the required output in your question. Assuming you only want to return elements of l1 that meet a certain range criteria (e.g >41) you could use the $unwind parameter and the aggregate function:

 db.test.aggregate({
    $unwind: "$l0"
}, {
    $unwind: "$l0.l1"
}, {
    $match: {
        "l0.l1": {
            $gt: "41"
        }
    }
}, {
    "$group": {
        "_id": "$_id",
        "l1": {
            "$addToSet": "$l0.l1"
        }
    }
})

This will return:

{
   "_id": "1:1419552000",
    "l1": ["43", "44", "42"]
}

If you add the required output to your question, I can modify the query to return the correct output.

Upvotes: 2

code-kobold
code-kobold

Reputation: 824

For the sample document you provided, db.test.findOne({}, {"l0.l1" : {$slice: 2}}) returns

{
    "_id" : "1:1419552000",
    "l0" : [
        {
            "l1" : [
                "1",
                "2"
            ]
        },
        {
            "l1" : [
                "11",
                "12"
            ]
        }, ...

Upvotes: -1

Related Questions