How can I use a $elemMatch on first level array? - mongodb

Consider the following document:
{
"_id" : "ID_01",
"code" : ["001", "002", "003"],
"Others" : "544554"
}
I went through this MongoDB doc for elemmatch-query & elemmatch-projection, but not able to figure it out how to use the same for the above document.
Could anyone tell me how can I use $elemMatch for the field code?

You'll want to use the $in operator rather than $elemMatch in this case as $in can be used to search for a value (or values) inside a specific field. $in requires a list of values to be passed as an array. Additionally, and for your case, it will find either a single value, or by searching in an array of values. The entire matching document is returned.
For example, you might use it like this:
db.mycodes.find( { code: { $in: ["001"] } } )
Which could be simplified to just be:
db.mycodes.find({ code: "001" })
As MongoDB will look in an array for a single match like above ("001").
Or if you want to search for "001" or "002":
db.mycodes.find( { code: { $in: ["001", "002"] } } )
$in documentation

If you're simply looking to match all documents with an array containing a given value, you can just specify the value on the reference to that array, e.g.
db.mycodes.find( { code: '001' } )
Which thus would return you all documents that contained '001' in their code array

Related

How to change attribute name of the embed type?

How I can change the name of the embed type by query $rename?
{ list_name: [{ name_1: String }] } => { list_name: [{ name_2: String }] }
I tried db.getCollection('test').updateMany({}, { $rename: { 'list_name.name_1': 'name_2' }})
But it's throwing an error: WriteError: cannot use the part (list_name of list_name.name_1) to traverse the element ({list_name: [ { name_1: "test" } ]})
$rename does not work if these fields are in array elements
Refer
To achieve, you need $unset and $set document by document. You can use bulkwrite also.
list_name is an array. Hence $rename didn't work.
When I search, I get this. You can refer the second answer if you have less number of docs.
Else, you need to use either bulk write or mongo dump options.

MongoDB $pop element which is in 3 time nested array

Here is the data structure for each document in the collection. The datastructure is fixed.
{
'_id': 'some-timestamp',
'RESULT': [
{
'NUMERATION': [ // numeration of divisions
{
// numeration of producttypes
'DIVISIONX': [{'PRODUCTTYPE': 'product xy', COUNT: 100}]
}
]
}
]
}
The query result should be in the same structure but only contain producttypes matching a regular expression.
I tried using an nested $elemMatchoperator but this doesn't get me any closer. I don't know how I can iterate each value in the producttypes array for each division.
How can I do that? Then I could apply $pop, $in and $each.
I looked at:
Querying an array of arrays in MongoDB
https://docs.mongodb.com/manual/reference/operator/update/each/
https://docs.mongodb.com/manual/reference/operator/update/pop/
... and more
The solution I want to avoid is writing something like this:
collection.find().forEach(function(x) { /* more for eaches */ })
Edit:
Here is an example document to copy:
{"_id":"5ab550d7e85d5930b0879cbe","RESULT":[{"NUMERATION":[{"DIVISION":[{"PRODUCTTYPE":"Book","COUNT":10},{"PRODUCTTYPE":"Giftcard","COUNT":"300"}]}]}]}
E.g. the query result should only return the entry with the giftcard:
{"_id":"5ab550d7e85d5930b0879cbe","RESULT":[{"NUMERATION":[{"DIVISION":[{"PRODUCTTYPE":"Giftcard","COUNT":"300"}]}]}]}
Using the forEach approach the result is in the correct format. I'm still looking for a better way which does not involve the use of that function - therefore I will not mark this as an answer.
But for now this works fine:
db.collection.find().forEach(
function(wholeDocument) {
wholeDocument['RESULT'].forEach(function (resultEntry) {
resultEntry['NUMERATION'].forEach(function (numerationEntry) {
numerationEntry['DIVISION'].forEach(function(divisionEntry, index) {
// example condition (will be replaced by regular expression evaluation)
if(divisionEntry['PRODUCTTYPE'] != 'Giftcard'){
numerationEntry['DIVISION'].splice(index, 1);
}
})
})
})
print(wholeDocument);
}
)
UPDATE
Thanks to Rahul Raj's comments I have read up the aggregation with the $redact operator. A prototype of the solution to the issue is this query:
db.getCollection('DeepStructure').aggregate( [
{ $redact: {
$cond: {
if: { $ne: [ "$PRODUCTTYPE", "Giftcard" ] },
then: "$$DESCEND",
else: "$$PRUNE"
}
}
}
]
)
I hope you're trying to update nested array.
You need to use positional operators $[] or $ for that.
If you use $[], you will be able to remove all matching nested array elements.
And if you use $, only the first matching array element will get removed.
Use $regex operator to pass on your regular expression.
Also, you need to use $pull to remove array elements based on matching condition. In your case, its regular expression. Note that $elemMatch is not the correct one to use with $pull as arguments to $pull are direct queries to the array.
db.collection.update(
{/*additional matching conditions*/},
{$pull: {"RESULT.$[].NUMERATION.$[].DIVISIONX":{PRODUCTTYPE: {$regex: "xy"}}}},
{multi: true}
)
Just replace xy with your regular expression and add your own matching conditions as required. I'm not quite sure about your data set, but I came up with the above answer based on my assumptions from the given info. Feel free to change according to your requirements.

How to query nested arrays in mongodb from command line

I have some data structured like this in a mongo collection:
{ "list" : [ { "update_time" : NumberLong(1426690563), "provider" : NumberLong(4) } ] }
What I would like to do is query for all of the entries where the list includes a provider value of 4.
If it helps, all of the list arrays contain only one element.
What I am trying right now is this:
db.collection.find(
{
list: {
provider: 4
}
}
)
This does not work though, and always returns an empty set
Use the dot notation i.e. concatenate the name of the field that contains the array, with a dot (.) and the name of the field in the embedded document and use that as your query:
db.collection.find({"list.provider": 4})
You can also use the $elemMatch operator to specify multiple criteria on an array of embedded documents such that at least one embedded document satisfies all the specified criteria:
db.collection.find({
list: {
$elemMatch: {
provider: 4
}
}
})

Replacing type in MongoDB

I have a MongoDB database where I'm trying to replace all arrays of a particular element with a concatenated string. Currently, most of the documents have an array of strings. I need a script that will find each of the documents that have an array (some have been updated by hand already) and replace that array with a concatenation of what's currently there.
So far, I have:
var cursor = db.recipe.find({ directions: { $type : 4 } });
while ( cursor.hasNext() ) {
var doc = cursor.next();
db.recipe.update(
{ _id : doc._id },
{ $set : { directions : { $concat : { doc.directions } } } }
);
};
Unfortunately, it keeps complaining about an unexpected '.'. I'm assuming that I'm using $concat incorrectly.
$concat is an aggregation operator. To simply find and update, use plain javascript.
Currently, most of the documents have an array of strings. I need a script that will find each of the documents that have an array
When you apply $type:4 to an array, it in turn checks if any of the elements inside the directions field is an array, only if it finds one it returns true.
eg: for input ["x","y"] it would return false, and for [["x"],"y"] it would return true.
Since your array contains only strings, you need to use the $where operator to find the type.
db.recipe.find({$where:"Array.isArray(this.directions)"}).forEach(function(doc){
var value = doc.directions.join();
db.recipe.update({"_id":doc._id},{$set:{"directions":value}});
})

how to remove all documents from a collection except one in MongoDB

Is there any way to remove all the documents except one from a collection based on condition.
I am using MongoDB version 2.4.9
You can do this in below way,
db.inventory.remove( { type : "food" } )
Above query will remove documents with type equals to "food"
To remove document that not matches condition you can do,
db.inventory.remove( { type : { $ne: "food" } } )
or
db.inventory.remove( { type : { $nin: ["Apple", "Mango"] } } )
Check here for more info.
To remove all documents except one, we can use the query operator $nin (not in) over a specified array containing the values related to the documents that we want to keep.
db.collections.remove({"field_name":{$nin:["valueX"]}})
The advantage of $nin array is that we can use it to delete all documents except one or two or even many other documents.
To delete all documents except two:
db.collections.remove({"field_name":{$nin:["valueX", "valueY"]}})
To delete all documents except three:
db.collections.remove({"field_name":{$nin:["valueX", "valueY", "valueZ"]}})
Query
db.collection.remove({ "fieldName" : { $ne : "value"}})
As stated above by Taha EL BOUFFI, the following worked for me.
db.collection.remove({"fieldName" : { $nin: ["value"]}});