Is there a way to project the type of a field

删除回忆录丶 提交于 2019-12-12 06:46:47

问题


Suppose we had something like the following document, but we wanted to return only the fields that had numeric information:

{
    "_id" : ObjectId("52fac254f40ff600c10e56d4"),
    "name" : "Mikey",
    "list" : [ 1, 2, 3, 4, 5 ],
    "people" : [ "Fred", "Barney", "Wilma", "Betty" ],
    "status" : false,
    "created" : ISODate("2014-02-12T00:37:40.534Z"),
    "views" : 5
}

Now I know that we can query for fields that match a certain type by use of the $type operator. But I'm yet to stumble upon a way to $project this as a field value. So if we looked at the document in the "unwound" form you would see this:

{
    "_id" : ObjectId("52fac254f40ff600c10e56d4"),
    "name" : 2,
    "list" : 16,
    "people" : 2
    "status" : 8,
    "created" : 9,
    "views" : 16
}

The final objective would be list only the fields that matched a certain type, let's say compare to get the numeric types and filter out the fields, after much document mangling, to produce a result as follows:

{
    "_id" : ObjectId("52fac254f40ff600c10e56d4"),
    "list" : [ 1, 2, 3, 4, 5 ],
    "views" : 5
}

Does anyone have an approach to handle this.


回答1:


There are a few issues that make this not practical:

  1. Since the query is a distinctive parameter from the ability to do a projection, this isn't possible from a single query alone, as the projection cannot be influenced by the results of the query
  2. As there's no way with the aggregation framework to iterate fields and check type, that's also not an option

That being said, there's a slightly whacky way of using a Map-Reduce that does get similar answers, albeit in a Map-Reduce style output that's not awesome:

map = function() {
    function isNumber(n) {
      return !isNaN(parseFloat(n)) && isFinite(n);
    }

    var numerics = [];
    for(var fn in this) {
        if (isNumber(this[fn])) {
            numerics.push({f: fn, v: this[fn]});
        }
        if (Array.isArray(this[fn])) {
            // example ... more complex logic needed
            if(isNumber(this[fn][0])) {
                numerics.push({f: fn, v: this[fn]});
            }
        }
    }
    emit(this._id, { n: numerics });
};

reduce = function(key, values) {
  return values;  
};

It's not complete, but the results are similar to what you wanted:

"_id" : ObjectId("52fac254f40ff600c10e56d4"),
 "value" : {
         "n" : [
                 {
                         "f" : "list",
                         "v" : [
                                 1,
                                 2,
                                 3,
                                 4,
                                 5
                         ]
                 },
                 {
                         "f" : "views",
                         "v" : 5
                 }
         ]
 }

The map is just looking at each property and deciding whether it looks like a number ... and if so, adding to an array that will be stored as an object so that the map-reduce engine won't choke on array output. I've kept it simple in the example code -- you could improve the logic of numeric and array checking for sure. :)

Of course, it's not live like a find or aggregation, but as MongoDB wasn't designed with this in mind, this may have to do if you really wanted this functionality.



来源:https://stackoverflow.com/questions/21717154/is-there-a-way-to-project-the-type-of-a-field

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!