Mongodb - Query embedded object with variable key - mongodb

I have a Mongo DB with the following object:
"clients" : {
"x" : {
"clientId" : "x1",
"mainInfo" : {
...
},
"events" :
{
"58a176bbc3588410cd5450c6" : {
"clientType" : "5001",
"location" : "60001"
}
"58a176bbc3588410cd5450c8" : {
"clientType" : "5001",
"location" : "60002"
}
....}
I cannot seem to figure out how to query where 'clients.x.events.(variable id).clientType' = 50001. Is there a way to drill down inside the events embedded object to get all records matching "clientType" : "5001"?
Thank you

You need to create the key value pair object like below.
var variable_id = <your variable id>;
db.collection.find({ [ 'clients.x.events.'+variable_id+'.clientType' ]:5001 });
Read more about this here

Maybe you can use $where operator.It works for me.
Use the $where operator to pass either a string containing a
JavaScript expression or a full JavaScript function to the query
system.
db.collection.find({"$where" : function(){
for( var c in this ){
if( c == "x" ){
for(var i in this[c]){
for(var j in this[c][i]){
if(j == 'clientType' && this[c][i][j] == '5001'){
return true;
}
}
}
};
}
return false;
}});
Hope this helps.

Related

Find records with field in a nested document when parent fields are not known

With a collection with documents like below, I need to find the documents where a particular field - eg. lev3_field2 (in document below) is present.
I tried the following, but this doesn't return any results, though the field lev3_field2 is present in some documents.
db.getCollection('some_collection').find({"lev3_field2": { $exists: true, $ne: null } })
{
"_id" : ObjectId("5884de15bebf420cf8bb2857"),
"lev1_field1" : "139521721",
"lev1_field2" : "276183",
"lev1_field3" : {
"lev2_field1" : "4",
"lev2_field2" : {
"lev3_field1" : "1",
"lev3_field2" : {
"lev4_field1" : "1",
"lev4_field2" : "1"
},
"lev3_field3" : "5"
},
"lev2_field3" : {
"lev3_field3" : "0",
"lev3_field4" : "0"
}
}
}
update1: this is an example, however in the real document it is not known what the parent fields are for the field to look for. So instead of lev3_field2 , I would be looking for `levM_fieldN'.
update2: Speed is not a primary concern for me, I can work with relatively a bit slower options as well, as the primary function is to find documents with the criteria discussed and once the document is found and the schema is understood, the query can be re-written for performance by including the parent keys.
To search a key in nested document you need to iterate the documents fields recursively, you can do this in JavaScript by the help of $where method in MongoDB
The below query will search if a key name exists in a documents and its subdocuments.
I have checked this with the example you have given, and it is working perfectly fine.
db.getCollection('test').find({ $where: function () {
var search_key = "lev3_field2";
function check_key(document) {
return Object.keys(document).some(function(key) {
if ( typeof(document[key]) == "object" ) {
if ( key == search_key ) {
return true;
} else {
return check_key(document[key]);
}
} else {
return ( key == search_key );
}
});
}
return check_key(this);
}}
);
There is no built-in function to iterate over document keys in MongoDB, but you can achieve this with MapReduce. The main advantage is that all the code is executed directly in the MongoDB database, and not in the js client, so there is no network overhead, hence it should be faster than client side js
here is the script :
var found;
// save a function in MongoDB to iterate over documents key and check for
// key name. Need to be done only once
db.system.js.save({
_id: 'findObjectByLabel',
value: function(obj, prop) {
Object.keys(obj).forEach(function(key) {
if (key === prop) {
found = true
}
if (!found && typeof obj[key] === 'object') {
findObjectByLabel(obj[key], prop)
}
})
}
})
// run the map reduce fonction
db.ex.mapReduce(
function() {
found = false;
var key = this._id
findObjectByLabel(this, 'lev3_field2')
value = found;
if (found) {
// if the document contains the key we are looking for,
// emit {_id: ..., value: true }
emit(key, value)
}
},
function(key, values) {
return values
}, {
'query': {},
'out': {inline:1}
}
)
this output ( run on 4 sample doc, with only one containing 'lev3_field2' )
{
"results" : [
{
"_id" : ObjectId("5884de15bebf420cf8bb2857"),
"value" : true
}
],
"timeMillis" : 18,
"counts" : {
"input" : 4,
"emit" : 1,
"reduce" : 0,
"output" : 1
},
"ok" : 1
}
to run the script, copy it to a file name "script.js" for example, and then run from your shell
mongo databaseName < script.js
It's because you're trying to see if a nested field exists. This is the query you want:
db.some_collection.find({"lev1_field3.lev2_field2.lev3_field2": { $exists: true, $ne: null } })

How to query MongoDb documents using the indices of embedded arrays

I am trying to learn how to use mongo queries to reach deep into a data tree. Specifically, I'm trying to remove the object below {"object": 'to remove'}
{
"_id" : ObjectId("7840f22736341b09154f7ebf"),
"username" : "nmay",
"fname" : "Nate",
"lname" : "May",
"data" : [
{
"monthNum" : 1,
"year" : 2016,
"days" : [
{
"date" : "2016-01-01T06:00:00.000Z",
"type1" : [],
"type2" : []
},
{
"date" : "2016-01-02T06:00:00.000Z",
"type1" : [
{"object": 'to remove'}
],
"type2" : []
}
]
}
]
}
so far I know how to query for the user _id, but I'm not sure how to remove the desired object using the indices in each array. In this example I want to remove data[0].days[1].type1[0]
Here is the query that I have so far:
app.delete('/user/:id/data/:monthIndex/days/:dayIndex/type1/:type1Index', function (req, res, next) {
var monthIndex = parseInt(req.params.monthIndex); // these console the value properly
var dayIndex = parseInt(req.params.dayIndex); // -1 is applied to the parameter to translate to array position
var type1Index = parseInt(req.params.type1Index);
db.users.update(
{ _id: mongojs.ObjectId(req.params.id) },
{ $pull: data.monthIndex.days.dayIndex.type1.type1Index }
);
}
It gives me the error
ReferenceError: data is not defined
Can someone demonstrate how I can pass this query my index parameters to remove the desired object?
Unfortunately, there is no way to remove an array element by its numerical index with a single operation in MongoDB. In order to do this, you need to unset desired element(s) first, and remove the resulting null-valued fields afterwards.
Your code should look something like this:
db.users.update(
{ _id : mongojs.ObjectId(req.params.id) },
{ $unset : { 'data.0.days.1.type1.0' : 1 } }
);
db.users.update(
{ _id : mongojs.ObjectId(req.params.id) },
{ $pull : { 'data.0.days.1.type1' : null } }
);
Edit by #bob: to pass in the parameters you have to build the query string, which is ugly:
var unset = {};
unset['$unset'] = {};
unset.$unset['data.' + req.params.monthIndex + '.days.' + req.params.dayIndex + '.foods.' + req.params.foodIndex] = 1;
db.users.update( { _id : mongojs.ObjectId(req.params.id) }, unset );
var pull = {};
pull['$pull'] = {};
pull.$pull['data.' + req.params.monthIndex + '.days.' + req.params.dayIndex + '.foods'] = null;
db.users.update( { _id : mongojs.ObjectId(req.params.id) }, pull );

Mongodb update subdocument inside array field of a collection

I have a mongodb collection like
{
"_id" : ObjectId("5375ef2153bb790b20d8a660"),
"association" : [
{
"count" : 3,
"name" : "hayatdediğin"
},
{
"count" : 2,
"name" : "sadecesenolsan"
},
{
"count" : 2,
"name" : "üslupnamustur"
}
],
"tag_count" : 4,
"tag_name" : "vazgeçilmezolan",
"variation" : [
{
"count" : 4,
"name" : "VazgeçilmezOlan"
}
]
}
Each collection consists of tag_name, tag_count, array field association and array field variation. For each name inside association, there exists a different document same as this document. I need to add new field "total_count" inside each association dictionary whose value equals the tag_count of the name by querying the database.
I tried this code but its not working
db.hashtag.find().forEach(function (doc) {
if (doc.association.length != 0 ) {
doc.association.forEach(function (assoc) {
db.hashtag.find({'tag_name': assoc.name}).forEach(function(tag){
assoc.total_count=tag.tag_count;
})
});
}
});
After modifying each doc you need to call save on the collection to commit the change.
Assuming you're doing this in the shell:
db.hashtag.find().forEach(function (doc) {
if (doc.association.length != 0 ) {
doc.association.forEach(function (assoc) {
db.hashtag.find({'tag_name': assoc.name}).forEach(function(tag){
assoc.total_count=tag.tag_count;
});
});
// Save the changed doc back to the collection
db.hashtag.save(doc);
}
});
To update doc in database you have to use db.hashtag.update, not db.hashtag.find. Find only retrieves document from db.
I changed the previous method of looping using forEach and then saved the doc at last and the code worked.
db.hashtag.find().forEach(function (doc) {
var array = doc.association;
if (array != undefined){
for(var i=0;i<array.length;i++)
{
var obj = db.hashtag.findOne({'name':array[i].name});
var count = obj.count;
doc.association[i].total_count = count;
db.hashtag.save(doc);
}
}
});

mongoDB mapReduce: reduce function returning plain object instead of map

Is it possible to have a reduce function that returns a flat object instead of map-like thing?
More details:
db.getCollection('calls').mapReduce(function () {
emit(this.reportDate + '-' + this.reportTime, {
from: this.caller,
to: this.called,
callEnds: this.callEnds,
callBegins: this.callBegins,
location: this.location
});
}, function (k, v) {
var result = {};
v.forEach(function (value) {
result.from = value.from;
result.to = value.to;
result.callBegins = value.callBegins;
result.callEnds = value.callEnds;
if (value.location) {
result.location = value.location;
}
});
return result;
}, {
out: 'mapReducedCalls'
})
Using this, output collection's documents are all
{ "_id" : "k",
"value" :
{ "from" : "b5c06aafa4be00db3d6acadb67b6ceef",
"to" : "0afba72b041e3ccb5a62f0b0b44cceea",
"callEnds" : "01/03/2013 10:45:44",
"callBegins" : "01/03/2013 10:45:40",
"location" : 44763
}
}
While I'd rather have it in a flat object form like
{ "_id" : "k",
"from" : "b5c06aafa4be00db3d6acadb67b6ceef",
"to" : "0afba72b041e3ccb5a62f0b0b44cceea",
"callEnds" : "01/03/2013 10:45:44",
"callBegins" : "01/03/2013 10:45:40",
"location" : 44763
}
No, the 'value' field is currently required (assuming that's what you mean by map-like thing).

Atomically reorder items in nested array of MongoDB document

I have a document with nested array of ordered items and I need to atomically reorder items in that array.
are the server-side code is a right solution for this task?
are the server-side code is an only solution for this task?
at witch scope this code will blocking:
single document
single collection
whole server ?
this is the code that will be executed at server-side:
var reorder = function (
catalog_id,
parent_id,
item_id,
new_pos) {
var old_pos;
var collection = db.catalogs;
var catalog = collection.findOne({catalog_id:catalog_id});
var result = [];
for(i in catalog.list) {
var item = catalog.list[i];
if(item.id == item_id) {
old_pos = item.order;
result.push({old_pos:old_pos});
break;
}
}
if(old_pos == new_pos)
return result;
var up = new_pos < old_pos;
catalog.list.forEach(function(item){
if( item.parent == parent_id &&
(up ?
(item.order <= old_pos && item.order >= new_pos) :
(item.order <= old_pos && item.order >= new_pos))){
if(item.id != item_id) {
item.order++;
result.push({down:item});
}
else {
item.order = new_pos;
result.push({up:item});
}
collection.update(
{catalog_id:catalog_id, 'list.id':item.id},
{$set:{'list.$.order':item.order}});
}
});
return result;
};
reorder('diagnostic_graph', 'n1', 'n5', 1);
this is a sample data:
{
"_id" : ObjectId("4efc939094f4a115d80c8543"),
"catalog_id" : "diagnostic_graph",
"list" : [{
"id" : "n1",
"order" : 0
}, {
"id" : "n2",
"parent" : "n1",
"order" : 0
}, {
"id" : "n3",
"parent" : "n1",
"order" : 1
}, {
"id" : "n4",
"parent" : "n1",
"order" : 2
}, {
"id" : "n5",
"parent" : "n1",
"order" : 3
}]
}
PS. sorry if something is unclear - english is not my best skill
Q 1. are the server-side code is a right solution for this task?
A 1. It depends. If the array needs to be reordered whenever you use it, than you should reorder it whenever you add an item to the array. You can call server side .js code whenever you push an item to array, or do it in your application.
Q 2. are the server-side code is an only solution for this task?
A 2. As I stated in the answer of the first question, it is not the only solution.
Q 3. at witch scope this code will blocking:
A 3. The answer is single document (AFAIK).