MongoDB 投影到 return 个子对象作为列

MongoDB projection to return child objects as columns

我是 MongoDB 的新手,我有一个 mongodb 这样的文档

{
    "_id": 1,
    "title": "abc123",
    "author": {
        "last": "zzz",
        "first": "aaa",
        "address": {
            "street": "stvalue",
            "city": "NewYork"
        }
    }
}

我正在使用 MongoDB Java 客户端并尝试将上述文档转换为

{
    "_id": 1,
    "title": "abc123",
    "author_last": "zzz",
    "author_first": "aaa",
    "author_address_street": "stvalue",
    "author_address_city": "New York"
}

是否可以使用 MongoDB Java 客户端使用 MongoDB 查询而不修改 Java 中的结果。

更新

由于无法在 MongoQuery 中转换为所需的格式,我不得不改变主意并添加一些 Java 代码以获得所需的格式。

使用 "client" 或 "driver library" 并不是真正的问题,因为唯一的方法是通过 .aggregate() and $project 到 "alter the fields"回复。这是您最实用的方法:

db.collection.aggregate([
    { "$project": {
        "title": 1,
        "author_last": "$author.last",
        "author_first": "$author.first",
        "author_address_street": "$author.address.street",
        "author_address_city": "$author.address.city"
    }}
])

很容易翻译成任何语言,但向您展示了基本原理。

非常Java(esque) 然后语法变为(在基本驱动程序中):

BasicDBObject project = new BasicDBObject("$project",
    new BasicDBObject( "title", 1 )
        .append( "author_last", "$author.last" )
        .append( "author_first", "$author.first" )
        .append( "author_address_street", "$author.address.street" ),
        .append( "author_address_city", "$author.address.city" )
);

collection.aggregate(project);

如果您不能仅以这种方式指定键,那么您在服务器上唯一真正的选择是 mapReduce。与上面的方式大致相同,最好在客户端响应中执行此操作:

db.collection.mapReduce(
    function() {
        var item = {};
        var id = this._id;
        delete this._id;

        var obj = this;

        Object.keys(obj).forEach(function(key) { 
            if ( typeof(obj[key]) == "object" ) {
                Object.keys(obj[key]).forEach(function(subKey) {
                     if ( typeof(obj[key][subkey] == "object" ) {
                         Object.keys(obj[key][subKey]).forEach(function(subSub) {
                             item[key + "_" + subKey + "_" + subSub] = obj[key][subKey][subSub];
                         });
                     } else {
                          item[key + "_" + subKey] = obj[key][subKey];
                     }
                });
            } else {
                item[key] = obj[key];
            }
        });

        emit( id, item );
    },
    function() {}, // no reduction required
    { "out": { "inline": 1 } }
)

我认为这是有道理的,但我只是输入它所以可能存在语法问题,但这个想法应该是相关的。根据您的实际数据,您可能还需要更复杂的东西。

要点是,您需要Java脚本执行到"traverse"您在服务器上的文档才能使用此方法。

否则,只需在代码中执行相同操作,因为无论如何您都在迭代游标。

这是一个 Java 实现,它将所有子对象转换为 HashMap

private List<Map<String,String>> mapReduce(AggregationOutput output){
    List<Map<String,String>> out = new ArrayList<Map<String, String>>();
    for (DBObject result : output.results()) {
        Map<String,String> map = ConvertDBObjectToMap(result, "");
        out.add(map);
    }
    return out;
}


private Map<String,String> ConvertDBObjectToMap(DBObject result, String parent)
{
    String prefix = parent == "" ? "" : parent + "_";

    Map<String,String> out = new HashMap<String, String>();
    String[] keys = result.keySet().toArray(new String[result.keySet().size()]);
    for(String key : keys)
    {
        Object obj = result.get(key);
        if(obj instanceof BasicDBObject)
        {
            out.putAll(ConvertDBObjectToMap((BasicDBObject) obj, prefix + key));
        }
        else if(obj instanceof String)
        {
            out.put(prefix + key,(String)obj);
        }
        else if(obj instanceof  Date){
            out.put(prefix + key,obj.toString());
        }
        else{
            out.put(prefix + key,obj.toString());
        }
    }
    return out;
}