Fetch documents only when field values are specified - mongodb

I need help with writing dynamic queries in mongo shell.
function(a, obj){
db.courseContent.aggregate({$match: {Actor: a, ObjectId: obj}})
}
In the above function, I sometimes do not pass either a or obj.
I want the function to return documents of respective values.
If both a & obj are passed it should return documents matching both a
& obj but not with matching either 'a' or 'obj'
Can someone help me on this?

The $and operator is what you need here, to match on all the submitted values; the query you want will look something like this:
{$match: {$and: [Actor: a, ObjectId: obj]}}
You can dynamically put together a query like that without too much bother, perhaps like this:
function(a, b, c, d, e, f, g, h){
var match = [];
if (a) { match.push( {Alpha: a} ); }
if (b) { match.push( {Bravo: b} ); }
if (c) { match.push( {Charlie: c} ); }
if (d) { match.push( {Delta: d} ); }
if (g) { match.push( {Echo: e} ); }
if (e) { match.push( {Foxtrot: f} ); }
if (f) { match.push( {Gamma: g} ); }
if (h) { match.push( {Hotel: h} ); }
db.courseContent.aggregate([{$match: {$and: match}}])
}

Generic code which might work for you
function(valuearray,keyarray){
var and={};
and.$and=[];
var obj={};
for(var i=0;i<valuearray.length;i++){
if(valuearray[i]!=undefined && valuearray[i]!=''){
obj[keyarray[i]]=valuearray[i];
and.$and.push(obj);
}
}
db.courseContent.aggregate({$match:and});
}

Try This:
function(a, obj){
if(a!='' && a!=undefined && obj!='' && obj!=undefined){
db.courseContent.aggregate({$match:{$and:[{Actor: a, ObjectId: obj}]}});
}
else if(a!='' && a!=undefined){
var obj={};
var $match={};
var finalobj={};
finalobj["Actor"]=a;
obj["$match"]=finalobj;
db.courseContent.aggregate(obj);
}
else if(obj!='' && obj!=undefined){
var obj={};
var $match={};
var finalobj={};
finalobj["ObjectId"]=obj;
obj["$match"]=finalobj;
db.courseContent.aggregate(obj);
}
}

Related

How can i override a map1 with another map2 leaving the key which is not available in the map2 in Dart?

I am new to flutter/dart, i have to replace a map with another map only when there is key match between them else leave the first map as it is.
This should work like array_replace_recursive() function in PHP.
Here is the code sample:
map1 = {
"t":{
"l":{
"-2":{
"n":"rocky",
"e":"rohit#gmail.com",
"r":"23567"
}
},
"o":{
"xyz":{
"p":"hi",
"x":"cdcbcbk"
}
}
},
"lang":{
"eng":"english",
"spn":"spanish"
}
"sc":{
"math"{
"lb":"30",
"pr":"60"
}
}
}
i have to overide it with
map2 = {
"t":{
"l":{
"-2":{
"n":"rohit",
}
},
},
}
expected output should be
finalMap = {
"t":{
"l":{
"-2":{
"n":"rohit",
"e":"rohit#gmail.com",
"r":"23567"
}
},
"o":{
"xyz":{
"p":"hi",
"x":"cdcbcbk"
}
}
},
"lang":{
"eng":"english",
"spn":"spanish"
}
"sc":{
"math"{
"lb":"30",
"pr":"60"
}
}
}
Thanks
Check this once, made changes based on your specification
replaceIfSame(Map a,Map b){
b.keys.forEach((k){
if(a.containsKey(k)){
if(a[k] is Map && b[k] is Map){
replaceIfSame(a[k],b[k]);
}
else{
a[k] = b[k];
}
}
});
}
Here replaceIfSame is a function that I call recursively passing the maps as input.
Do specify if it's not the case.
Cheers!!
I tried below method it also gives the same result but i'm not sure whether it is efficient or not
Map<dynamic, dynamic> mergemap(Map map1, Map map2) {
map2.forEach((k, v) {
if (map1.containsKey(k)) {
if (v.runtimeType != String) {
mergemap(map1[k], v);
} else {
map1[k] = v;
}
}
});
return map1;
}

Conditionally add to Map

I have a Map to which I would like add elements only if they meet a condition.
For example, if for keys a and b, is there something that works like this?
Map myMap = {
if a is not null "a" : a,
if b is not null and be is not empty "b": b,
}
So a and b would not be in the map unless the condition is met
For example for react is goes something like this
const myMap = {
...(a != null && { a: a }),
...(b != null && b.length > 0 && { b: b }),
};
Try this in dartpad:
void main() {
for (var a in [null, 15]) {
for (var b in [null, '', 'hello']) {
var myMap = {
if (a != null) 'a': a,
if (b != null && b.isNotEmpty) 'b': b,
};
print('a = $a, b = $b, myMap = $myMap');
}
}
}
You can do absolutely the same in Dart!
return {
if (true) ...{'key': value},
};
Dartpad is very useful
void main() {
Map<String, String> myMap = new Map();
Map<String,String> array = {"a":"apple","b":"g"};
array.forEach((key,value){
value.length> 0 ? myMap.addAll({key: value}): "";
});
print(myMap);
}

How to remove empty objects recursively?

If all the keys of an object is empty, I want to replace them with undefined. For example
{
a: 1,
b: {
c: 2,
d: {
e: undefined,
f: undefined,
}
},
g: {
h: undefined,
i: undefined,
}
}
Should be:
{
a: 1,
b: {
c: 2,
d: undefined
},
g: undefined
}
It is important to keep the undefined value because it is to remove de key from mongodb
First of all, we can define one function to remove all undefined value to meet your requirement
function removeUndefinedObject(obj) {
var isAllUndefined = true;
for (var k in obj) {
if (typeof obj[k] === 'object') {
obj[k] = removeUndefinedObject(obj[k]);
}
if (typeof obj[k] !== 'undefined')
isAllUndefined = false;
}
if (isAllUndefined)
obj = undefined;
return obj;
}
Then to use this function in mongodb collection
var cursor = db.collection.find({}); // get all documents from collection
cursor.forEach(function(doc) {
// remove undefined value for each document
removeUndefinedObject(doc);
// save this changed doc into this collection
db.collection.update({_id: doc._id}, doc);
});

How to count objects inside object (without count var index)

I have the following model stored in mongodb:
{
"_id": (MongoId)
"user": "X",
"field-name-1":{
"obj-1": 12345,
"obj-2": 54321,
"obj-3": 67890,
(...)
},
"field-name-2:{
"obj-1": {"foo":"bar", "bar":"foo"},
"obj-2": {"foo":"bar2", "bar":"foo2"},
(...)
}
}
How do I find the ocurrences that contains more than 5 objects inside the "field-name-1"? Is it the same for "field-name-2"?
I can't use a variable to count the amount of objects because I upsert values inside the "field-name-1"/"field-name-2" constantly.
Thanks.
Already tried (without success):
db.summoners.find({$where: "this.['field-name-1'].length > 5"})
db.summoners.find({$where: "['field-name-1'].length > 5"})
$where should be qualified in this case:
function findMyDocument(key, value) {
var f = this[key];
if (typeof f != "object" || typeof value != "number") {
return false;
}
var count = 0;
for (var x in f) {
if (++count > value) {
return true;
}
}
return false;
}
// store above function to database for invoking
db.system.js.insert({_id:"findMyDocument", value: findMyDocument});
// apply here
db.summoners.find({$where: "findMyDocument.call(this, 'field-name-1', 5);"});
db.summoners.find({$where: "findMyDocument.call(this, 'field-name-2', 5);"});

MongoDB/Mongoose: Can't put simplest MapReduce to work

Hello all I'm trying to do is to get the count of each distinct departmentType:
fnMap = function() {
emit(this.departments.departmentType, {typeCount:1} );
}
fnReduce = function(key, values) {
var result = {typeCount: 0};
values.forEach(function(value) {
result.typeCount += value.brandCount;
});
return result;
};
var command = {
mapreduce : "clients",
query : {"departments.departmentType": {$exists: true}},
map : fnMap.toString(),
reduce : fnReduce.toString(),
//sort: {"departments.departmentType":1},
out: {inline: 1}
};
mongoose.connection.db.executeDbCommand(command, function(err, dbres) {
});
When executing the command, dbres.documents[0].results only contains 1 item with the total number of departmentTypes, instead of several items one for each departmentType with its count.
Any ideas what am I doing wrong?
Also, when I uncomment the SORT line, I get error "db assertion failure: could not create cursor over...", I believe the field name is written correctly.
Mongoose v3 has now a Model.mapreduce() function (see doc).
The full example shown is:
var o = {};
o.map = function () { emit(this.name, 1) }
o.reduce = function (k, vals) { return vals.length }
o.out = { replace: 'createdCollectionNameForResults' }
o.verbose = true;
User.mapReduce(o, function (err, model, stats) {
console.log('map reduce took %d ms', stats.processtime)
model.find().where('value').gt(10).exec(function (err, docs) {
console.log(docs);
});
})
The problem with count i believe is because in your fnReduce() function you are summit the results instead of displaying them in an array.
You can use:
db.clients.distinct("departments.departmentType")
That will give an array with all the distinct departmentType values.
There were two problems in your map/reduce. One is brandCount in reduce rather than typeCount. But more importantly, you are trying to emit once per document, when you need to emit once per department array element. Corrected (and slightly simplified) code:
> fnMap = function () {
this.departments.forEach(
function (d) {
emit(d.departmentType, 1);
}
);
}
> fnReduce = function (key, values) {
var result = 0;
values.forEach(
function (value) {result += value;});
return result;
}