How to make nested group in MongoDB? - mongodb

Assume there's a data in Mongo! There are a few projects, and there're managers, to who the problems are assigned. Each problem is assigned to different people as the project has different parts.
How can one compute the number, each assignee has been assigned within a project.
For example:
{
"Project A": {
"Ted":10,
"Matt":5
},
"Project B": {
"Mary":3,
"Hector":30
}
}
The following writes the sum but doesn't group it:
db.test.issues.group({
"key": {
"project": true
"assignee":true
},
"initial": {
"countassignee": 0
},
"reduce": function(obj, prev) {
prev.countassignee++;
}
});
It will return as:
{
{
"project": "Project A",
"countassignee":10,
"assignee":"Ted"
}
....
}

The data sample provide by you is pretty confusing. its Project A and project B supposed to be different document.
I believe the best use of group is to aggegrate within a collections, if you want to count within a doucment you may use javascript.

OK! It doesn't seem like the best answer, but it'll do!
first define a cursor sorting by projects
var cursor = db.test.find().sort({projects:1});
a list for the counter:
var asgn_nums = {};
and a variable holding project name;:
var project = '0';
Here counts for each project:
while (cursor.hasNext()) {
var issue = cursor.next();
var p = issue.project;
if(p != project)
{
if(project != '0'){
print(project+":");
printjson(asgn_nums);
}
project = p;
asgn_nums = {};
}
if(issue.assignee != null)
{
if (asgn_nums[issue.assignee] == null)
asgn_nums[issue.assignee] = 1;
else
asgn_nums[issue.assignee]+= 1;
}
if(!cursor.hasNext())
{
print(project+":");
printjson(asgn_nums);
}
}

Related

Conditional Query based on Variable value in MongoDB

I am building a list view page with multiple filters using .net core as backend and mongodb as database. I want to filter data based on some condition such that if the variable is passed as blank, it should retrieve all data, otherwise only the matching data.
In mssql this can be achieved easily by
where (#SymbolCode = '' or SymbolCode = #SymbolCode)
What is the option in mongodb filter
var filter = new BsonDocument
{
{
"symbolCode", SymbolCodeSearchString // If string SymbolCodeSearchString is blank, it should retrieve all data else the search data
}
};
Sample Data
{
"_id": {
"$oid": "60ed91bc65675f966c0eec46"
},
"symbolCode": "F",
"timestamp": {
"$date": "2021-07-13T13:14:35.909Z"
}
}
you should check symboleCode before query on mongodb
first check if symbolCode is empty or ""
define filter query as
var filter = new BsonDocument
{
};
else if it was not empty
var filter = new BsonDocument
{
{
"symbolCode", SymbolCodeSearchString // If string SymbolCodeSearchString is blank, it should retrieve all data else the search data
}
};
I was able to figure out. Thanks to #Naimi and #Takis. Building a dynamic filter is the key. Posting here the answer so that it could help anyone
var filterQuery = new BsonDocument
{
{
"symbolCode", new BsonDocument
{
{ "$regex", SymbolCodeString},
{ "$options", "i"}
}
}
};
if (fromDate != "" && toDate != "")
{
BsonElement dateFilter = new("timestamp", new BsonDocument
{
{ "$gt", fromDate},
{ "$lt", toDate}
});
filterQuery.Add(dateFilter);
}
By this way, i am able to add any number of filters dynamically

Imitate a COUNT... GROUP BY in Cloudant (CouchDB)

In CloudAnt, I want to get counts of features, grouped by two attributes: topics and councils. After a lot of fiddling, I was able to construct this:
// map
function (doc) {
if (doc.properties && doc.properties.openCouncilDataTopic) {
var ret = {};
ret [doc.properties.openCouncilDataTopic] = {};
ret [doc.properties.openCouncilDataTopic][doc.properties.sourceCouncilId] = 1;
emit(null, ret);
}
}
// reduce
function (keys, values, rereduce) {
var ret = values[0];
function zero(x) { return x ? x : 0 }
function add(i, topic, id) {
ret[topic][id] = zero(ret[topic][id]) + values[i][topic][id];
}
function countTopic(topic) {
if (!ret[topic])
ret[topic] = {};
Object.keys(values[i][topic]).forEach(add.bind(undefined, i, topic));
}
for (var i = 1; i < values.length; i++) {
Object.keys(values[i]).forEach(countTopic);
}
return ret;
}
Is there a better way?
Yes, there is a better way! Cloudant supports "aggregation over complex keys"
Instead of manually computing the counts, the map function can simply emit keys which are the attributes to group on:
function (doc) {
if (doc.properties && doc.properties.openCouncilDataTopic) {
emit([doc.properties.openCouncilDataTopic, doc.properties.sourceCouncilId], 1);
}
}
Then we can use the built-in _count reduce function.
To call the view, add group_by=2, where 2 is the number of attributes to group by. (The default, 0, aggregates everything down to a single number).
This is likely to be much faster than implementing the count in Javascript, too.
One downside is we have less control over the output, which comes out like this:
{
"rows": [
{
"key": [
"childcare-centres",
"https://data.gov.au/organization/cardinia-shire-council"
],
"value": 22
},
...
}

Query key with value anywhere in object hierarchy in Mongo

In Mongo how can I find all documents that have a given key and value, regardless of where that key appears in the document's key/value hierarchy?
For example the input key roID and value 5 would match both:
{
roID: '5'
}
and
{
other: {
roID: '5'
}
}
There is no built in way to do this. You might have to scan each matched document recursively to try and locate that attribute. Not recommended. You might want to think about restructuring your data or perhaps manipulating it into a more unified format so that it will be easier (and faster) to query.
If your desired key appears in a fixed number of different locations, you could use the $or operator to scan all the possibilities.
Taking your sample documents as an example, your query would look something like this:
db.data.find( { "$or": [
{ "roID": 5 },
{ "other.roID": 5 },
{ "foo.bar.roID": 5 },
{ any other possbile locations of roID },
...
] } )
If the number of documents in collection is not so large, then it can be done by this:
db.system.js.save({_id:"keyValueExisted", value: function (key, value) {
function findme(obj) {
for (var x in obj) {
var v = obj[x];
if (x == key && v == value) {
return true;
} else if (v instanceof Object) {
if (findme(v)) return true;
}
}
return false;
}
return findme(this);
}});
var param = ['roID', '5'];
db.c.find({$where: "keyValueExisted.apply(this, " + tojsononeline(param) + ");"});

Remove nested document with condition in MongoDB

For the following JSON how do I remove the dog whose height is the least
{
_id:0
"name":"Andy",
"pets":[
{
"type":"dog","name":"max","height":120
},
{
"type":"dog","name":"rover","height":44
},
{
"type":"dog","name":"katie","height":100
},
{
"type":"cat","name":"minni"
}
]
}
The problem is the array of subdocuments is not a collection, you can't sort or do something else on it. But if you have an access to any language interface like JavaScript or else it's possible. You just need to extract list of subdocuments, sort them by height, remember the first one and then run the command to pull it from the array based on its name and height.
It can be done for example using this JavaScript code right in the MongoDB shell:
var min = 0; var name = "";
db.animals.find({ query:{"_id" : 0} }).forEach(
function(record){
var sets = record.pets;
min = sets[0].height;
sets.forEach(function(set){
if(set.height <= min)
{min=set.height;
name=set.name;}
});
print(min);
print(name);
query = {"_id": 0}
update = { "$pull" : { "pets" : { "name" : name } } };
db.animals.update(query, update);
})
I suspect the solution is not the most elegant but anyway it works.

Detect empty pick() from hash in KRL

I have a bunch of data in a hash and I am picking from it. Sometimes there will be data there to pick and sometimes there won't. What is the best way to know when there was something found by the pick operator and when there wasn't so I can react to that in my code?
The pick operator will take a second optional parameter that will make it so that it always returns the results in an array. This means that if something is picked, the length of the array will be greater than 0, otherwise it will be 0. You can then use that to do what you are wanting to do.
Example code/app taken from http://kynetxappaday.wordpress.com/2011/01/04/day-30-detecting-empty-pick/
ruleset a60x526 {
meta {
name "hash-pick-detect"
description <<
hash-pick-detect
>>
author "Mike Grace"
logging on
}
global {
dataHash = {
"one": {
"name": "Mike"
}, // number
"two": {
"random": 8
}, // number
"three": {
"name": "Alex"
} // number
}; // dataHash
} // global
rule detect_the_pick {
select when pageview ".*"
foreach dataHash setting (key, value)
pre {
userName = value.pick("$.name", true);
length = userName.length();
}
if (length > 0) then {
notify("Key: #{key}","Name: #{userName}<br/>Length: #{length}") with sticky = true;
}
notfired {
raise explicit event empty_pick
with pickedKey = key;
}
}
rule empty_pick_found {
select when explicit empty_pick
pre {
pickedKey = event:param("pickedKey");
results =<<
Key: #{pickedKey}<br/>
doesn't have a name associated with it to pick from
>>; //' fixing syntax highlighting
}
{
notify("An empty pick was detected",results) with sticky = true;
}
}
}