MongoDB投影将子对象作为列返回

时间:2015-03-17 15:31:18

标签: javascript java mongodb mongodb-query aggregation-framework

我是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代码以获得所需的格式。

2 个答案:

答案 0 :(得分:2)

使用哪个“客户端”或“驱动程序库”并不是真正的问题,唯一的方法是通过.aggregate()$project来“改变字段”作为回应。这是你最实际的方式:

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"
    }}
])

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

Very 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 } }
)

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

重点是,您需要JavaScript执行来“遍历”服务器上的文档才能使用此方法。

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

答案 1 :(得分:0)

这是一个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;
}