MongoDB MapReduce weird bug - node.js

I'm trying this simple MapReduce operation:
function map() {
var gameDay = Math.floor((this.matchCreation - 1427846400000) / 86400000) + 1; // day of april 2015 when the game was played
this.teams.forEach (function (team){
**team.bans.forEach(function (ban){** // says bans is undefined
var value ={
banned : 1,
firstBanned: ( ((ban.pickTurn == 1) || (ban.pickTurn == 2))? 1 : 0 )
}
emit({championId: ban.championId,
day: Number(gameDay)}, value);
emit({championId: ban.championId,
day: "all"}, value);
});
});
}
function reduce(key, values) {
var a = values[0];
for(var i = 1 ; i<values.length ; i++){
var b = values[i]; // will merge 'b' into 'a'
a.banned += (b.banned? b.banned : 0);
a.firstBanned += (b.firstBanned? b.firstBanned : 0);
for (var attrname in b){
if(attrname != "banned" && attrname != "firstBanned")
a[attrname] = b[attrname];
}
}
return a;
}
matchesCollection.mapReduce(map, reduce, {
out: { reduce: "mapReduceResults" }
}, function (err, data){
if(err)
return callback (err);
callback (null, "OK");
});
It used to work before, but just when I tried to deploy the app after testing for a while, it seems to fail in this line: team.bans.forEach(function (ban){, says team.bans is undefined, although every one of the documents has a "teams" array and a "bans" array inside of each object in it, I even double checked it by querying the database and there is no document in which those fields dont exist.
So weird. The reduce function is a bit more complex but it seems to work alright, yet the map one (unlike Reduce, its supposed to be called just once per original document, right?) throws this unexplainable error. Could anyone give me some insight?
Sample input:
{
"_id": {
"$oid": "5531a63f2a3f135c11ed14a8"
},
"matchId": 1778704162,
"region": "NA",
"platformId": "NA1",
"matchMode": "CLASSIC",
"matchType": "MATCHED_GAME",
"matchCreation": 1427864425511,
"matchDuration": 1431,
"queueType": "URF_5x5",
"mapId": 11,
"season": "SEASON2015",
"matchVersion": "5.6.0.194",
"participants": [
{
"teamId": 100,
"spell1Id": 12,
"spell2Id": 4,
"championId": 81,
"highestAchievedSeasonTier": "SILVER",
"timeline": [],
"masteries": [],
"stats": {
"winner": false,
"champLevel": 19,
"item0": 1037,
"item1": 3078,
"item2": 3117,
"item3": 3035,
"item4": 3072,
"item5": 1038,
"item6": 3340,
"kills": 7,
"doubleKills": 1,
"tripleKills": 0,
"quadraKills": 0,
"pentaKills": 0,
"unrealKills": 0,
"largestKillingSpree": 3,
"deaths": 15,
"assists": 9,
"totalDamageDealt": 103191,
"totalDamageDealtToChampions": 22148,
"totalDamageTaken": 32924,
"largestCriticalStrike": 669,
"totalHeal": 2263,
"minionsKilled": 97,
"neutralMinionsKilled": 1,
"neutralMinionsKilledTeamJungle": 1,
"neutralMinionsKilledEnemyJungle": 0,
"goldEarned": 13923,
"goldSpent": 13273,
"combatPlayerScore": 0,
"objectivePlayerScore": 0,
"totalPlayerScore": 0,
"totalScoreRank": 0,
"magicDamageDealtToChampions": 6082,
"physicalDamageDealtToChampions": 15803,
"trueDamageDealtToChampions": 263,
"visionWardsBoughtInGame": 0,
"sightWardsBoughtInGame": 0,
"magicDamageDealt": 45997,
"physicalDamageDealt": 56651,
"trueDamageDealt": 543,
"magicDamageTaken": 25249,
"physicalDamageTaken": 7490,
"trueDamageTaken": 184,
"firstBloodKill": false,
"firstBloodAssist": false,
"firstTowerKill": false,
"firstTowerAssist": false,
"firstInhibitorKill": false,
"firstInhibitorAssist": false,
"inhibitorKills": 0,
"towerKills": 4,
"wardsPlaced": 2,
"wardsKilled": 0,
"largestMultiKill": 2,
"killingSprees": 1,
"totalUnitsHealed": 1,
"totalTimeCrowdControlDealt": 98
},
"participantId": 1,
"runes": []
},
... (9 more like that)
],
"participantIdentities": [],
"teams": [
{
"teamId": 100,
"winner": false,
"firstBlood": true,
"firstTower": false,
"firstInhibitor": true,
"firstBaron": false,
"firstDragon": true,
"towerKills": 6,
"inhibitorKills": 2,
"baronKills": 0,
"dragonKills": 3,
"vilemawKills": 0,
"dominionVictoryScore": 0,
"bans": [
{
"championId": 120,
"pickTurn": 1
},
{
"championId": 37,
"pickTurn": 3
},
{
"championId": 13,
"pickTurn": 5
}
]
},
{
"teamId": 200,
"winner": true,
"firstBlood": false,
"firstTower": true,
"firstInhibitor": false,
"firstBaron": false,
"firstDragon": false,
"towerKills": 11,
"inhibitorKills": 4,
"baronKills": 0,
"dragonKills": 0,
"vilemawKills": 0,
"dominionVictoryScore": 0,
"bans": [
{
"championId": 28,
"pickTurn": 2
},
{
"championId": 38,
"pickTurn": 4
},
{
"championId": 63,
"pickTurn": 6
}
]
}
]
}
Expected output:
{
_id: { championId: Number, day: Number }
value: { banned: Number, firstBanned: Number }
}
After that, its supposed to merge with the results of a previous MapReduce operation, copying all the fields of documents with the same key (in the reduce function), but thats irrelevant now since the error happens before...

Related

How do I query a set of objects with an array of values in mongoose?

I have a schema like this
const rankSchema = new Schema(
{
rank: { type: Object, default: {} },
lastUpdated: { type: Date, default: Date.now() },
},
{ minimize: false }
);
And my database has an object 'rank' with many other objects inside of it like this.
rank: {
Person1: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 4 },
Person2: { Stat1: 4, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 2 },
Person3: { Stat1: 1, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 1 },
Person4: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 3 }
}
Now I have an array of strings that contains a few of these people
['Person1', 'Person2']
I want to be able to find all the person objects in that array and return their stats.
So essentially the final output after using the array of strings would be
Person1: { Stat1: 2, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 4 },
Person2: { Stat1: 4, Stat2: 0, Stat3: 0, Stat4: 2, Stat5: 2 }
I tried using $in and various different queries but nothing seems to work and I am stumped.
Thanks
You could use a combination of $objectToArray and $arrayToObject to filter your object by dynamic field names but if your parameters are known when you're building your query then it's easier to use regular .find() and apply projection:
db.collection.find({},{ "rank.Person1": 1, "rank.Person2": 1})
let input = ['Person1', 'Person2'];
let entries = input.map(p => ([`rank.${p}`, 1]))
let projection = Object.fromEntries(entries);
console.log(projection);
Mongo Playground

How do I get the sum of all the properties?

Given:
{
"property1": 10,
"property2": 20,
"property3": 30
}
I'd like to add a total property of all the properties, like this:
{
"property1": 10,
"property2": 20,
"property3": 30,
"Total": 60
}
You can use array#reduce and Object.values().
let o = { "property1": 10, "property2": 20, "property3": 30 };
o.Total = Object.values(o).reduce((s,v) => s + +v, 0);
console.log(o);

Node/MongoDb JSON empty array bug

I recently upgraded to Node 5.4 and MongoDb 2.1.4 Driver for Node and I have the following problem :
I have a file called buildings.json containing something like this :
{
"military_base": {
"type": "military",
"level": 0,
"maxLevel": 25,
"upgrade": true,
"targetEnvironment": [],
"timeUntilBuilt": 0,
"costCoef": 0.4,
"requiredResearches": [
"foo#4",
"bar#1"
],
"requiredResources": [
"cash",
"metal",
"palladium"
],
"inProgress": 0,
"queue": []
},
"aqua_center": {
"type": "industrial",
"level": 0,
"maxLevel": 25,
"upgrade": true,
"targetEnvironment": [
"ocean",
"snowy"
],
"timeUntilBuilt": 0,
"costCoef": 0.7,
"requiredResearches": [
"lorem#10",
"ipsum#3"
],
"requiredResources": [
"cash",
"cristal"
],
"inProgress": 0,
"queue": []
}
}
Now, I want to insert this document into a mongo database:
let buildings = require( "buildings" );
...
collection.insertOne( { foo: buildings }, callback );
If the first "targetEnvironment" array is empty, nothing is inserted, the callback is not called and there is no error. But if it's filled, all is fine, the callback is called and the document is correctly inserted...
I must admit that I don't know if the problem is related to Node or the MongoDb Driver...
Can someone help me, please ?
#!/usr/bin/env node
const mongodb = require( "mongodb" );
const MongoClient = mongodb.MongoClient;
const ObjectID = mongodb.ObjectID;
MongoClient.connect( "mongodb://127.0.0.1:27017/test", function( err, db ) {
if ( err ) {
throw err;
}
var collection = db.collection( "test-insert" );
collection.insertOne( test, ( err, obj ) => {
if ( err ) {
throw err;
}
console.log( obj.insertedId );
db.close();
} );
} );
var test = {
"military_base": {
"type": "military",
"level": 0,
"maxLevel": 25,
"upgrade": true,
"targetEnvironment": [],
"timeUntilBuilt": 0,
"costCoef": 0.4,
"requiredResearches": ["foo#4", "bar#1"],
"requiredResources" : ["cash", "metal", "palladium"],
"inProgress": 0,
"queue": []
},
"aqua_center": {
"type": "industrial",
"level": 0,
"maxLevel": 25,
"upgrade": true,
"targetEnvironment": ["ocean", "snowy"],
"timeUntilBuilt": 0,
"costCoef": 7,
"requiredResearches": ["lorem#10", "ipsum#3"],
"requiredResources" : ["cash", "cristal"],
"inProgress": 0,
"queue": []
}
};
If I try to insert this document, it will not be inserted because of the first costCoef property is a floating number. It will also fail if it's a large number like timestamp.
Oh... As I have a 64bit Windows, I installed 64bit nodejs version and the problem came from here... with 32bit, there is no error ! :o

mongodb explain always returns millis as 0

I am using the node.js mongodb native driver and I have a collection with a bunch of docs inside like:
{
name: "cat",
}
I have a query which I am trying to test the speed of:
collection.find({name: { $gt: 'ca', $lt: 'cb' } }).explain(function(err, docs){
console.log( docs );
});
But the milliseconds is always 0
{ cursor: 'BasicCursor',
isMultiKey: false,
n: 2,
nscannedObjects: 8,
nscanned: 8,
nscannedObjectsAllPlans: 8,
nscannedAllPlans: 8,
scanAndOrder: false,
indexOnly: false,
nYields: 0,
nChunkSkips: 0,
millis: 0,
allPlans:
[ { cursor: 'BasicCursor',
isMultiKey: false,
n: 2,
nscannedObjects: 8,
nscanned: 8,
scanAndOrder: false,
indexOnly: false,
nChunkSkips: 0 } ],
server: 'h003723.mongolab.com:33453',
filterSet: false,
stats:
{ type: 'COLLSCAN',
works: 10,
yields: 0,
unyields: 0,
invalidates: 0,
advanced: 2,
needTime: 7,
needFetch: 0,
isEOF: 1,
docsTested: 8,
children: [] } }
I cant seem to figure this out, and obviously I cant test the query like this, am I missing something?

Confirm $addToSet adds an element

I have a function adding a bunch of data into db through $addToSet, and I require a confirmation if the data has been added or not. Since $addToSet does not add duplicates, I want to know if a duplicate has not been added (show an error to the user) or if a db entry has been added (show confirmation to user).
I basically need a callback for the $addToSet operation. Couldnt find it in the docs. New to mongodb. Would appreciate help.
_notifications.update(
{'username': data.username},
{ $addToSet: pushNotification }, function(err, docs){
console.log(docs);
if (docs == 0){
co_notifications.insert(
{'username': data.username, 'notifications': insertNotification}, function(er, doc){
});
}
},
{ upsert: true }
);
I may be missing something but the only thing I can really see is in the result of the new Batch operations API.
var mongodb = require('mongodb'),
MongoClient = mongodb.MongoClient;
MongoClient.connect("mongodb://localhost/test",function(err,db) {
var col = db.collection("mytest");
var batch = col.initializeOrderedBulkOp();
batch.find({ "user": "me" }).upsert().updateOne({ "$addToSet": { "list": 5 } });
batch.execute(function(err,result) {
console.log( JSON.stringify( result, undefined, 4 ) );
});
});
On that sample listing the first time you execute the contents of "result" will dump like this:
{
"ok": 1,
"writeErrors": [],
"writeConcernErrors": [],
"nInserted": 0,
"nUpserted": 1,
"nMatched": 0,
"nModified": 0,
"nRemoved": 0,
"upserted": [
{
"index": 0,
"_id": "5397ae995f04804cbeb7c663"
}
]
}
The notable keys there being "nUpserted" and the "upserted" array for each document that was created. It is a batch operation afterall even though we are doing it once.
On the second execution where it should find the existing document and also match the set member you would get this:
{
"ok": 1,
"writeErrors": [],
"writeConcernErrors": [],
"nInserted": 0,
"nUpserted": 0,
"nMatched": 1,
"nModified": 0,
"nRemoved": 0,
"upserted": []
}
That shows that while the document was matched nothing was actually modified as the set member was the same. But if you change the value of what is applied in the $addToSet operation to something that doesn't exist then the response would be this:
{
"ok": 1,
"writeErrors": [],
"writeConcernErrors": [],
"nInserted": 0,
"nUpserted": 0,
"nMatched": 1,
"nModified": 1,
"nRemoved": 0,
"upserted": []
}
This shows both the "nMatched" and "nModified" values indicating the document was actually updated by the $addToSet operation.
So that may be a possible approach

Resources