ozomer / node-red-contrib-mongodb2

MongoDB driver node for Node-RED
Apache License 2.0
15 stars 19 forks source link

mongodb2 trouble with group operation #2

Closed mfeblowitz closed 9 years ago

mfeblowitz commented 9 years ago

When I send this command directly to mongodb or via robomongo, all is well and I see the expected results:

db.btnii.group(
   {
    keyf: function(doc) {
               return {time:"at "+doc._id.getTimestamp().getHours()+" on the "+doc._id.getTimestamp().getDate()+"th"}
           },
    cond: {"id":"sls-18446744073519651090"},
    reduce: function( curr, result ) {
               result.count++;
            },
    initial: { count: 0 },
    finalize: function(result) {

              }
   }
);

However, when I send this content to the mongodb2 group operation:

{ 
     keyf: function(doc) { 
               return {time:"at "+doc._id.getTimestamp().getHours()+" on the "+doc._id.getTimestamp().getDate()+"th"} 
           },
    cond: {"id":"sls-18446744073519651090"}, 
    reduce: function( curr, result ) { 
               result.count++; 
            }, 
    initial: { count: 0 }, 
    finalize: function(result) { 
              } 
   }

I see this error:

[error] [mongodb2 in:BTN-II mongodb2 group] TypeError: undefined is not a function

I've tried quite a few things and can't get this to work. Any suggestons? Is this an issue or an error on my part?

ozomer commented 9 years ago

Can you provide a flow example? I never tested this function in node-red. The documentation of the group command in api2.0 expects multiple arguments, not a single hash. Try passing the following array via the msg.payload:

[
  function(doc) { // keys
    return {
      time:"at "+doc._id.getTimestamp().getHours()+" on the" +doc._id.getTimestamp().getDate()+"th"
    };
  },
  {"id":"sls-18446744073519651090"}, // condition
  { count: 0 }, // initial
  function(curr, result) { // reduce
    result.count++;
  },
  function(result) { // finailize
  }
]

Or this one:

[
  function(doc) { // keys
    return {
      time:"at "+doc._id.getTimestamp().getHours()+" on the" +doc._id.getTimestamp().getDate()+"th"}
    };
  },
  {"id":"sls-18446744073519651090"}, // condition
  { count: 0 }, // initial
  function(curr, result) { // reduce
    result.count++;
  },
  function(result) { // finailize
  },
  true, // command
  null, // options
]

P.S. I now notice that the group command returns an array (via the callback), similarly to the aggregate, find and listIndexes commands. A cursor may sometimes make find.forEach, which fetches the results in bulks, more efficient than find.toArray, which fetches all the results. However, the api of the group operation does not return a cursor, so there is no point in separating the group operation to group.forEach and group.toArray.

mfeblowitz commented 9 years ago

After removing the extra brace (one was hidden at the end of the long return element), both of your suggested array forms worked fine and the results look indistinguishable. That makes sense, as "true" and "null" are defaults.

The flow is quite simple: a http get trigger node, the function node with the above contents, and a "mongodb2 in" node to a remote mongo server.

Out of curiosity, is the presence of the keys for each array element (as in my example above) a newer mongodb syntax for sending through a group operation? Would I need to transform other mongodb2 operations as you have suggested above? That is, would I need to transform the keyed operation invocation structure to an array without keys?

Thanks so much for you fast response. We really needed it!

ozomer commented 9 years ago

I don't know what the changes from api1 to api2 exactly are. If you look at my code you'll see that the node simply takes the array of arguments from msg.payload, adds a callback and calls the api2 operation. There are special cases with find, aggregate and listIndexes which return cursors, so I splitted them to find.toArray, find.forEach, aggregate.toArray, aggregate.forEach, listIndexes.toArray, listIndexes.forEach.

mfeblowitz commented 9 years ago

Ok. Understood.

My question was more about the difference between the calling forms. It seems that mongodb now accepts forms like the one I presented in my initial note, where the argument set is sent in a single structure and the argument names are reflected in keys. The mongodb2 node expects an array with arguments in the same order as those in the group function. That's easy enough to map in a function node, if the queries are to be sent in using mongo syntax.