Is it possible to get a slice of a slice in Mongo? - mongodb

I'm querying a mongo collection that has a field that is an array of arrays. I want to find a record with a projection of one deep value out of the array of arrays. Conceptually, this is a $slice of a $slice. Is there a way to do this in Mongo?
For example - I have a record:
{
name: "foo",
text: [["part 1:1", "part 1:2"],["part 2:1","part 2:2"]]
}
and want to select the record with projection "part 2:2".
db.collection.find({"name":"foo"},{text: {$slice: [1,1]}}
gives me the array with both "part 2:1" and "part 2:2". How do I get just "part 2:2"?

You need to use the aggregation pipeline to achieve a $slice chain, due to the limitations in the project statement being part of the find query.
The below query is invalid because the first $slice would return an array, instead of an index, and the execution of the outer scoped $slice fails.
db.collection.find({"name":"foo"},{text: {$slice:[{$slice: [1,1]}]}})
Moreover there is no way to work on an projected field in the same project statement, if possible, we could have modified the text further by applying a $slice to it.
The way to go would be:
Match the record with the name as foo.
Unwind the text array to get to the first level.
Unwind again to get to the level that we want.
Group the records together by name.
Project the last record in the group which is also the last element
of the last nested array.
The Code:
db.collection.aggregate([
{$match:{"name":"foo"}},
{$unwind:"$text"},
{$unwind:"$text"},
{$group:{"_id":"$name","text":{$last:"$text"}}},
{$project:{"name":"$_id","text":1}}
])
or if you would want to project an element appearing in a particular order, then you could use the $skip and $limit operations to achieve this.
var orderOfElement = 2;
db.collection.aggregate([
{$match:{"name":"foo"}},
{$unwind:"$text"},
{$unwind:"$text"},
{$skip:orderOfElement -1},
{$limit:1}
])
Which projects the second element in order in the nested arrays.

If you want the specific part 2:2 the below query will help.
db.user.find({"name":"foo"},{_id:0,name:0})[0].text[1][1];
part 2:2

Related

How to create a collection from a query in MongoDB?

I have an existing collection where I can do some queries on. For further data processing, it would be handy to create some subset collections via query.
I understood that I can use the aggregate function with $match and $expr to e.g. $group some values and at the end use $out to get a new collection with the results.
The thing I am hanging on is not to $group anything, but just put the objects that $match finds into a new collection only. So not the complete objects with all their values. Just the one I am matching. Like when you db[collection].find({$match: {...}}, {"key1": 1, "key2": 0})
Where I get the new matching objects just containing key1: value1 but not key2: value2, which is also in the original collection.
How do I achieve that using aggregate without grouping anything? I read through the documentation and couldn't find any other stage operation that looks good.
As i mentioned in the comments $project is the right operator to use to achieve this.

Can you $unwind an array within an array when you specify the array element of the preceding array in MongoDB

In my MongoDB aggregation I am trying to determine if I can $unwind an array within an array if I specifically target an element of the initial array.
This is the data structure:
subscription: [
{
agency: []
}
]
What I want to do is unwind agency here, and since I know it will always be found within the first element of the subscription array, I'm doing this:
$unwind : {
path: "$subscription.0.agency"
}
However, this doesn't work. When I plug this in as the last stage in my aggregation (which works up until this step), I end up with zero docs returned. I'm not clear why. Seems like you should be able to do this. Can you simply not $unwind an array within an array, even if you denote a specific array element in the path?

MongoDB Query Nested Array Search

I need to query documents with mongoDb that contain nested arrays. I see a lot of examples using the simple $in operator. The only problem is that I strictly need to check for proper subsets.
Consider the following document.
{data: [[1,2,3], [4,5,6]]}
The query needs to be able to get documents with all of [1,2,3] where 1,2,3 can be in any order, which rules out the following query, because it will only match in the correct order.
{data:{$elemMatch:{$all:[[1,2,3]]}}}
I've also tried nested $elemMatch operators with no success, because the $in operator will return the document even if only one element matches such as the following.
{data:{$elemMatch:{$elemMatch:{$in:[1,4]}}}}
Not sure what your actual query looks like, but this should do what you need:
db.documentDto.find({"some_field":{"$elemMatch":{"$in":[1,2,3]}} })
I haven't got a complete answer (and not much time as its late here) but I would consider
Using aggregation pipeline instead of a query if your not already
Use $unwind operator to deconstruct your nested arrays
Use $sort to sort the contents of the arrays - so you can now compare
Use $match to filter out the arrays which don't fit the array subset values as you can now check based on order.
Use $group to group the result back together based on the _id value
Ref:
http://docs.mongodb.org/manual/reference/operator/aggregation-pipeline/ will give you info on each of the above.
From a quick search I came up with a similar question/example that might be helpful: Mongodb sort inner array

ODM Query Builder: Is there an "inverse in" operator, or equivalent operation?

In my synfony 2 project, I'm filtering search results using a query builder. In my MongoDB i have some values in an array.
Query Bulider has the "in" operator that allows to query for values that equal one of many in an array. I wanted to perform the opposite operation, i.e. given a single value, query for entries in the data base that contain an array, that contains my value.
For instance, say I have this entry in my MongoDB:
{
"_id": 123,
"name": "John",
"countries_visited":
[
"Australia"
"Bulgaria",
"Canada"
]
}
And I want to query my database for persons who have visited "Canada". Right now, I'm using the where attribute as follows, but I'm looking for a better way to do this.
$qb->field('countries_visited')->where("function(){
return this.indexOf(".$countryName.") > -1
}");
edit:
The in and notIn operator receives an array as parameter and compares it against a single value in MongoDB. I need to provide a single parameter and apply it to an array field in MongoDB, hence "inverse in". I guess I need a contains operator if there's such a thing.
Interesting, MongoDB takes care of this automatically. If querying for a single value against an array field, Mongo will assume you want the check the array if it contains the value.
Taken from the docs:
Match an Array Element
Equality matches can specify a single element in the array to match. These specifications match if the array contains at least one element with the specified value.
So you should be able to do
$users = $dm->getRepository('User')->findOneBy([
'countries_visited' => 'Canada'
]);
or
$qb->field('countries_visited')->equals('Canada');

In Mongodb, how to retrieve the subset of an object that matches a condition?

What I'm trying to do:
Filter a field of a collection that matches a given condition. Instead of returning every item in the field (which is an array of items), I only want to see matched items.
Similar to
select items from test where items.histPrices=[10,12]
It is also similar to what's found on the mongodb website here: http://www.mongodb.org/display/DOCS/Retrieving+a+Subset+of+Fields
Here's what I have been trying:
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"}]})
db.test.save({"name":"record", "items":[{"histPrices":[10,12],"name":"stuff"},
{"histPrices":[12,13],"name":"stuff"},{"histPrices":[11,14],"name":"stuff"}]})
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
It will return all the objects that have a match for items.histPrices:[10,12], including ALL of the items in items[]. But I don't want the ones that don't match the condition.
From the comments left on Mongodb two years ago, the solution to get only the items with that histPrices[10,12] is to do it with javascript code, namely, loop through the result set and filter out the other items.
I wonder if there's a way to do that with just the query.
Your find query is wrong
db.test.find({},{"name":1,"items.histPrices":[10, 12]})
Your condition statement should be in the first part of the find statement.In your query {} means fetch all documents similar to this sql
select items from test (no where clause)
you have to change your mongodb find to
db.test.find({"items.histPrices":[10, 12]},{"name":1})
make it work
since your items is an array and if you wanted to return only the matching sub item, you have to use positional operator
db.test.find({"items.histPrices":[10, 12]},{"name":1,'items.$':1})
When working with arrays Embedded to the Document, the best approach is the one suggested by Chien-Wei Huang.
I would just add another aggregation, with the $group (in cases the document is very long, you may not want to retrieve all its content, only the array elements) Operator.
Now the command would look like:
db.test.aggregate({$match:{name:"record"}},
{$unwind:"$items"},
{$match {"items.histPrices":[10, 12]}},
{$group: {_id: "$_id",items: {$push: "$items"}}});)
If you are interested to return only one element from the array in each collection, then you should use projection instead
The same kind of issue solved here:
MongoDB Retrieve a subset of an array in a collection by specifying two fields which should match
db.test.aggregate({$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
But I don't know whether the performance would be OK. You have to verify it with your data.
The usage of $unwind
If you want add some filter condition like name="record", just add another $march at first, ex:
db.test.aggregate({$match:{name:"record"}}, {$unwind:"$items"}, {$match:{"items.histPrices":[10, 12]}})
https://jira.mongodb.org/browse/SERVER-828
Get particular element from mongoDB array
MongoDB query to retrieve one array value by a value in the array