Checking array type before unwind - mongodb

I have a document with the following structure
{testfields:[{"test_id":1,"test_name":"xxxx"}]},
but , my application don't no whether "testfield" is array or plain object, now if I apply "$unwind" in aggregate query and filed is not of array type "mongodb" throws error, that cannot unwind. I wanted to know is their a way I can check if the field is of type array than apply unwind, else treat is as normal object.

I think this could help you.
Before using unwind, try to use a match to filter documents with testfields field is an array:
$match: { $where : "Array.isArray(this.testfields)" }
Then, you could use unwind without errors.

Related

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

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

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

How do I include 'undefined' in the array returned by mongodb's distinct() operator when the field does not exist?

I would like to get an array of distinct values of a certain field in a mongodb collection. However, the field is optional and if any document does not have the field, I want the array to include the value 'undefined' (or null or any indication). The distinct operator seems to ignore any documents that do not have the field, rather than include 'undefined' in the array of distinct values. Does anyone know how I can override this behavior?
Documentation for the distinct operator: http://docs.mongodb.org/manual/reference/method/db.collection.distinct/
I am getting around this now by making a second db call that counts the items with this field equal to undefined, but I would like to do it in one call.
You can do this using aggregation framework:
db.collection.aggregate(
{$group:{_id:"$myField"}}
}
It will include null value if any documents don't have the field, or have it as null value.
I really don't see a way to do what you're describing in one call.
However, to optimize what you're doing now:
I think you're doing something like:
db.myCollection.count({$not: {$exists: myfield}})
If your collection is large, this can get slow. You might consider
db.myCollection.findOne({$not: {$exists: myfield}})
If no documents exist without "myfield", it will return null.

Update not inserting a object in subarray,when upsert=true

I am using mongodb update method with upsert=true.
My data looks like this:
{"my_id":"1",
"test_list":[{"test_id":1,"test_name":"pppp"}]}
now I am using the following command:
db.testcol.update({"my_id":1,"test_list.test_id":2},{"$set":{"test_list.$.test_name":"mmmm"}},true,true)
Now I want a new object inserted into the "test_list" as it does not exist
but I am getting the error:
Cannot apply the positional operator without a corresponding query field containing an array.
I cannot use "insert" for my operation, as I dont know whether the data is their and the field needs to be updated,or its not their and needs to be inserted(for the first time)
Upserts are for when you want the update to work whether the entire document is there or not, not just an array element. For the array element case you can use $addToSet:
db.testcol.update(
{"my_id": "1"},
{"$addToSet": {"test_list": {"test_id": 2, "test_name": "mmmm"}}})
It will only add the new element to the doc's test_list array field if a matching element isn't already present.

Mongodb - proper way to delete all elements in an array field?

I'm looking for the proper way to remove all elements from an array field (across all documents) in Mongodb - these appear to be equivalent, which is recommended: (or perhaps some other way?)
db.collection.update({}, { $pull : { 'myArray': {} }}, {multi:true} )
or
db.collection.update({}, { $set : {'myArray': [] }} , {multi:true} )
The $set variant will be faster as the $pull will have to do calculations on arrays. I am actually not even certain whether it will work, as you're not really removing any elements with your query.
Just to make sure, do you need to keep an empty array? Because otherwise, it's probably better to $unset it.
If you must keep an empty array, I believe your $pull call won't work - I think it will remove all empty elements from your array, not all elements.
According to Mongodb's official document:
When used with $ to match an array element, $unset replaces the
matching element with null rather than removing the matching element
from the array. This behavior keeps consistent the array size and
element positions.
The $set should be the proper way.