2

Say I have an object like this:

{default: 'x',
 types: {
  x:1,
  y:2,
  z:3
 }
}

Is it possible to select just types.x (ie a projection of {"types.x":1}) without knowing that x is the default beforehand? Making two queries is clearly possible and not what I'm looking for.

B T
  • 57,525
  • 34
  • 189
  • 207
  • Who are these idiots voting to close questions like this? Totally uncalled for. Hey idiot: when you vote to close something, write a comment about why. If you're *right*, then maybe the OP will avoid questions like that in the future. And if you're wrong, we all get to see how dumb your reasoning is. I think its a fair trade. – B T Jul 25 '14 at 17:29
  • does each document have its own default? and based on that you want that field projected? and are all possible values of default known in advance? – Asya Kamsky Jul 25 '14 at 20:57
  • come to think of it, you would be able to do this with aggregation framework if your schema was different (if you stored the types as array with {type:"x", value:1} type of elements. – Asya Kamsky Jul 25 '14 at 21:10
  • Hmm, interesting idea. I'm surprised the aggregation framework can't handle keys tho. Each document has its own default, yes. And the possible values of default are *not* known in advance. – B T Jul 25 '14 at 21:18

1 Answers1

1

Unfortunately this is not available yet as part of the aggregation framework. However, according to this JIRA ticket, it is currently "planned by not scheduled". The only way of doing this currently is by using the map/reduce functionality. If you want to go ahead and use that, it would mean doing something as follows:

  1. Map each document by _id and emit the appropriate key.
  2. Since there will be only one value per key, the reduce function will not get called, but you still need to initialise the variable you use for the reduce function. You can use an empty function or an empty string.
  3. Run map/reduce, saving the results in a collection of your choice.

In the mongo shell, it looks something as follows:

var mapper = function() {
    var typeValue = this.types[this.default];
    emit(this._id, typeValue);
};
var reducer = "";

db.types.mapReduce(mapper, reducer, { out : "results" } );

If you then query the results collection, you will get something as follows:

> db.results.find();
{ "_id" : ObjectId("53d21a270dcfb83c7dba8da9"), "value" : 1 }

If you want to know what the default value was, you can modify the mapper function in order to return the key as a value as well. It would look something like this:

var mapper = function() {
    var typeValue = this.types[this.default],
        typeKey = "types." + this.default;

    emit(this._id, { key : typeKey, val : typeValue } );
};   

When run, it would produce results that look as follows:

> db.results.find().pretty();
{
    "_id" : ObjectId("53d21a270dcfb83c7dba8da9"),
    "value" : {
        "key" : "types.x",
        "val" : 1
    }
}

Note that this is probably a much more convoluted solution than you might want, but it's the only way to do this using MongoDB without adding more logic to your application.

Juan Carlos Farah
  • 3,829
  • 30
  • 42
  • Thanks for the answer. Do you have any idea what the performance is of this in comparison to say the two regular queries i'd have to run to have the same effect? Or the performance of running one query that selects all the types? – B T Jul 25 '14 at 17:27
  • @BT, I would not recommend you use Map/Reduce for real time aggregation. Some of the reasons why are explained [here](http://stackoverflow.com/questions/12678631/map-reduce-performance-in-mongodb-2-2-2-4-and-2-6). If you cannot change your schema, I think the best option would be to run the two regular queries. Also, you should definitely push for the implementation of the feature in the JIRA ticket I copied above. With some pressure from the community, it might be implemented in future versions of MongoDB. Hopefully sooner rather than later. – Juan Carlos Farah Jul 25 '14 at 18:14
  • Thanks for the great info. I'll add my comment to the list – B T Jul 25 '14 at 20:38