Is it possible to Rename the output key in Mongo's MapReduce Result? - mongodb

I am trying to perform an Inline mapreduce operation using pyMongo.
The code looks like this:
import pymongo
from bson.code import Code
con = pymongo.MongoClient()
map_func = Code("""
function() {
var all = this.members.concat(this.admins)
var group_id = this._id
all.forEach(function(_id) {
emit(_id, [group_id])
})
}
""")
reduce_func = Code("""
function(key, values) {
var ob = {};
ob[key] = [];
for (var i=0; i<values.length; i++) {
ob[key].push(values[i][0])
}
return ob
}
""")
finalize_func = Code("""
function(key, value) {
if (typeof(value.push) == "function") {
return value
} else {
return value[key]
}
}
""")
result = con.test.group.inline_map_reduce(
map_func,
reduce_func,
finalize=finalize_func)
import pprint; pprint.pprint(result)
Output for this operation is:
[{u'_id': u'135348133252952338363702',
u'value': [u'135457069105859781018098',
u'135661481520484615218098',
u'135391961249458761918098',
u'135758863859275369318098',
u'135156779012512657918098',
u'135285081801846289218098',
u'136040996346306049718098',
u'136237587535011048218098',
u'136862399436556328318098']},
{u'_id': u'136068596781820946163702',
u'value': [u'136068597966313224518098',
u'135156779012512657918098',
u'136415311739865096818098']}]
Is there any hook/operator by which I can rename the output field to any custom string example "group_id" instead of "values" ?
I have read Mongo's MapReduce documentation, but did not find any tip on how to accomplish this.

Using the MapReduce finalize hook described here, I reformatted my output to the field names I wanted:
db.collection.mapReduce(
mapFunction(),
reduceFunction(),
{
query: {params},
finalize: function(key, reduced_value) {
return {
my_field: key,
my_value: reduced_value
}
}
}
)

No, the reduced values are always in a field named value.

Related

Not iterable when using find ObjectId

I'm trying to find a certain document in my mongodb then update the int value of it using find query, I'm using $in because I used an array to find each element inside it, but when I used ObjectId it gives me error:
bloodinventoryDocs is not iterable
Here is what I did
var mongoose = require('mongoose');
var id = mongoose.Types.ObjectId('5c014c999cc48c3b0057988b');
var newValue = 1;
var newBloodgroup = "A_positive";
var newGetbloodcomponent = "Whole Blood";
Bloodinventory.find({ blood_component : { $in : newGetbloodcomponent} , blood_group: { $in :newBloodgroup},chapter: { $in :id}}, function(err, bloodinventoryDocs) {
for(let bloodinventory of bloodinventoryDocs) {
bloodinventory.num_stock = bloodinventory.num_stock + newValue ;
bloodinventory.save(function(err) {
if (err) {
console.log(err);
} else {
console.log('success');
}
});
}
});
Just use chapter: { $in: [id] }

Search with multiple criterias in LiteDB : combine queries

Can someone tell me how to search data in liteDb using the following pseudocode ?
Pseudo Code
col = db.GetCollection<Product>("products");
string keyword = "1AS";
Query query;
if (condition1)
{
query += Query.Contains("ProductName", keyword);
}
if (condition2)
{
query += Query.Contains("ProductModel", keyword);
}
if (condition3)
{
query += Query.Contains("Note", keyword);
}
if (query.Any()) //
{
var data = col.Find(query).toList();
}
Thanks in advance
You can use Query.And(params Query[] queries), like this:
var list = new List<Query>();
if (condition1)
{
list.Add(Query.Contains("ProductName", keyword));
}
if (condition2)
{
list.Add(Query.Contains("ProductModel", keyword));
}
...
if (list.Count > 0) //
{
var q = list.Count == 1 ? list.First() : Query.And(list.ToArray());
var data = col.Find(q);
}

in mongo need to join two collections using Identity columns and emit the needed columns from both collections

I have book and author collection.in this name and works_written are the same value column respectively.so i tried the following script but it emit only first map values,second map values not emitted.
book = function() {
emit(this.id, {name: this.name,editions:this.editions});
}
author = function() {
emit(this.id, {name:this.name,works_written: this.works_writtten,});
}
r_b = function(k, values) {
var result = {};
values.forEach(function(value) {
var name;
for (name in value) {
if (value.hasOwnProperty(name)) {
result[name] = value[name];
}
}
});
return result;
};
r_a = function(k, values) {
var result = {};
values.forEach(function(value) {
var works_written;
for (works_written in value) {
if (value.hasOwnProperty(works_written)) {
result[works_written] = value[works_written];
}
}
});
return result;
};
res = db.book.mapReduce(book, r_ja, {out: {reduce: 'joined'}})
res = db.author.mapReduce(author, r_jp, {out: {reduce: 'joined'}})
can someone help me out?
From looking at your code, it seems like you have two collections, "book" and "author". Each book is structured as
{
id: <some id>,
name: <some name>,
editions: <comma-separated string of editions>
}
and each author is structured as
{
id: <some id>,
name: <some name>,
works_written: <comma-separated string of works written>
}
It would be more reasonable to store both works_written and editions as arrays rather than comma-separated lists each packed into an individual string. This would make iterating over the array possible.
Additionally, do you have multiple documents for each author and each book? If not, you do not need a mapreduce to do what you are attempting to do - a simple find() should work.
In case I have misinterpreted, what exactly are you attempting to do?

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;
}

mongodb map emit keys from subdocument

I have a document which includes a subdocument:
{
"_id" : ObjectId("XXXXX"),
"SearchKey" : "1234",
"SearchTerms" : {
"STA" : ["1"],
"STB" : ["asdfasdf"],
"STC" : ["another"]
}
}
The SearchTerm elements are not fixed - sometimes we'll have STA without STC, for example.
I can do this:
var map = function() {
for (key in this.SearchTerms)
{
emit(key, 1);
}
}
but I can't do this:
var map = function() {
for (var i=0; i< this.SearchTerms.length; i++)
{
emit(this.SearchTerms[i], 1)
}
}
because the latter doesn't produce any results after the reduce. Why not?
As an aside - what I need to do is count the cross-product of the search terms over all documents, that is, find the incidence of (STA and STB) and (STA and STC) and (STB and STC) in the case above. If someone knows how to do that right away, that works even better.
As always, thanks for the help
The key that you emit should be a composite of both keys.
var map = function() {
if(!this.SearchTerms) return;
for(var i = 0 ; i < this.SearchTerms.length; i++){
var outerKey = this.SearchTerms[i];
for(var j = i + 1; j < this.SearchTerms.length; j++){
var innerKey = this.SearchTerms[j];
// assuming you don't care about counting (STA and STC) separately from (STC and STA),
// then order doesn't matter, lets make sure both occurences have the same key.
var compositeKey = (outerKey < innerKey) ? outerKey+"_"+innerKey : innerKey+"_"+outerKey;
emit(compositeKey, 1);
}
}
}
This is because this.SearchTerms is a dictionary/subdocument and not an array. this.SearchTerms[0] doesn't exist.
For the second question: something like this should work:
for (key1 in this.SearchTerms)
{
for (key2 in this.SearchTerms)
{
if (key1 < key2)
{
key = [key1, key2];
emit(key, 1);
}
}
}