1

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!

2
  • What is your expected output? Commented Nov 10, 2015 at 10:45
  • 1
    Sorry, I should have mentioned that, for my incorrect query: db.test.findOne({}, {"l0.0.l1" : {$slice: 2}}) it should be: { "_id" : "1:1419552000", "l0" : [ "2", "1" ] } or similar Commented Nov 10, 2015 at 11:23

3 Answers 3

2

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" ] }
Sign up to request clarification or add additional context in comments.

Comments

2

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.

1 Comment

Thanks for replying! Sorry about not adding the expected output in the question. Added it as a comment now. Accepting another answer as it, IMO, explains things better. Thanks again.
-1

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

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

1 Comment

Thanks for replying, this is not what I had in mind. I just updated the question with the expected output in comments.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.