Reputation: 365
I’m new to MongoDB and I’m trying to get my head around whether I can perform this query conveniently and with decent performance using MongoDB. I’d like to pass numeric and array parameters to a query and use them to perform element by element operations on array values in each document in the collection. Is this possible?
e.g. A collection contains documents like the following:
{
"name" : "item1",
"m" : 5.2,
"v" : 1.1,
"data1" : [ 0, 0, 0.3, 0.7, 0.95, 0.9, 0.75, 0.4, 0.1, 0 ],
"data2" : [ 0, -1, 0, 1, 1, 0 ]
}
And I have another "search" document that might look something like this:
{
"x" : 8,
"K" : 1,
"dataA" : [ 0, 0, 0, 0, 0, 0, 0, 0.5, 1, 0.5],
"dataB" : [ 0, -2, 0, 1, 1, 0 ]
}
I want to run a query, or map-reduce, using the above search document against the collection above that returns a collection containing:
{
"name",
"y" = fn(m, v, x, K) = Kvx^(1/m) (not the real formula but just an example)
"dataF" = Max(i=0..9) {data1[i] * dataA[i] }
"dataS" = Sum(j=0..5) {data2[j] * dataB[j] }
}
where y>0
So for the above example, the result returned would be
{
"name" : "item1",
"y" : 1 * 1.1 * 8^5.2 = 1.641
"dataF" : Max(..., 0.4*0.5, 0.1*1, 0 * 0.5 ) = 0.2
"dataS" : 0*0 + (-1)*(-2) + 0*0 + 1*1 + 1*1 + 0*0 = 4
}
Is this going to be possible/convenient using MongoDB?
Note: In my application there will be more standard criteria included in the search using standard MongoDB operations so I was hoping to include the above processing in the query and avoid doing it on the client.
Upvotes: 3
Views: 1186
Reputation: 33175
Here's a map/reduce version:
db.data.save({
"name" : "item1",
"m" : 5.2,
"v" : 1.1,
"data1" : [ 0, 0, 0.3, 0.7, 0.95, 0.9, 0.75, 0.4, 0.1, 0 ],
"data2" : [ 0, -1, 0, 1, 1, 0 ]
});
db.data.mapReduce( function() {
var searchdoc = {
"x" : 8,
"K" : 1,
"dataA" : [ 0, 0, 0, 0, 0, 0, 0, 0.5, 1, 0.5],
"dataB" : [ 0, -2, 0, 1, 1, 0 ]
};
var result = {name: this.name};
result.y = searchdoc.K * this.v * Math.pow(searchdoc.x, 1 / this.m);
if(result.y > 0) {
result.dataF = 0;
for(i=0;i<this.data1.length;i++) {
var f = this.data1[i] * searchdoc.dataA[i];
if(f > result.dataF) {
result.dataF = f;
}
}
result.dataS = 0;
for(i=0;i<this.data2.length;i++) {
var s = this.data2[i] * searchdoc.dataB[i];
result.dataS += s;
}
emit(this.name, result);
}
}, function(key, values){}, {out: {inline: 1}});
result:
{
"results" : [
{
"_id" : "item1",
"value" : {
"name" : "item1",
"y" : 1.640830939540542,
"dataF" : 0.2,
"dataS" : 4
}
}
],
"timeMillis" : 0,
"counts" : {
"input" : 1,
"emit" : 1,
"reduce" : 0,
"output" : 1
},
"ok" : 1,
}
This is the shell version:
db.data.save({
"name" : "item1",
"m" : 5.2,
"v" : 1.1,
"data1" : [ 0, 0, 0.3, 0.7, 0.95, 0.9, 0.75, 0.4, 0.1, 0 ],
"data2" : [ 0, -1, 0, 1, 1, 0 ]
});
var searchdoc = {
"x" : 8,
"K" : 1,
"dataA" : [ 0, 0, 0, 0, 0, 0, 0, 0.5, 1, 0.5],
"dataB" : [ 0, -2, 0, 1, 1, 0 ]
};
var search = function(searchdoc) {
db.data.find().forEach(function(obj) {
var result = {name:obj.name};
result.y = searchdoc.K * obj.v * Math.pow(searchdoc.x, 1 / obj.m);
if( result.y > 0 ) {
result.dataF = 0;
for(i=0;i<obj.data1.length;i++) {
var f = obj.data1[i] * searchdoc.dataA[i];
if(f > result.dataF) {
result.dataF = f;
}
}
result.dataS = 0;
for(i=0;i<obj.data2.length;i++) {
var s = obj.data2[i] * searchdoc.dataB[i];
result.dataS += s;
}
db.results.save(result);
}
});
}
search(searchdoc);
db.results.find();
{ "_id" : ObjectId("4f08ffe4264d23670eeaaadf"), "name" : "item1", "y" : 1.640830939540542, "dataF" : 0.2, "dataS" : 4 }
Upvotes: 2