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

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.

Related

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

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

Checking array type before unwind

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.

Persist data as an (unordered) set in MongoDB

I am using MongoDB with node.js. Is it possible to persist data as an (unordered) set in MongoDB directly? Or do I have to either
persist them as an array and transform it into a set in JavaScript
or
persist them as an object in the form of {entry1: true, entry2: true, ...}
If I have to do it in either of the two ways above, which one is more efficient? Considering I need to add/remove items frequently, but rarely need to re-assign the whole set/array.
You can manipulate an array as a set in MongoDB... relevant operators include:
$addToSet - The $addToSet operator adds a value to an array only if the value is not in the array already.
$push - The $push operator appends a specified value to an array.
$pop - The $pop operator removes the first or last element of an array.
$pull - The $pull operator removes all instances of a value from an existing array.
$elemMatch - The $elemMatch operator matches more than one component within an array element.