【问题标题】:MongoDB projection to return child objects as columnsMongoDB 投影以将子对象作为列返回
【发布时间】:2015-03-17 15:31:17
【问题描述】:

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

【问题讨论】:

  • 我不确定你的问题到底是什么。难道你不能使用Java客户端从Java代码中的数据库中读取你想要的值,然后在Java代码中执行转换,然后将其写回数据库吗?
  • 我只想把子对象转成列
  • @bot,我已经格式化了我的问题。当从 mongodb 返回数据时,我想要上面的结果,而不是在 Java 中进行。
  • 版主注意:我们再次清理了 cmets。如果您想继续讨论与手头问题无关的内容,请带它去聊天。 cmets应该是关于问题的,而不是无关的废话。
  • @NeilLunn - 如果你没有对机器人进行侮辱和屈尊俯就,他们可能不会做出同样的反应。这是你最后的警告。如果我看到您在本网站上侮辱其他用户,我将暂停您的帐户一年。我说清楚了吗?

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


【解决方案1】:

这并不是你使用哪个“客户端”或“驱动程序库”的问题,唯一的方法是通过.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 来“遍历”服务器上的文档才能使用此方法。

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

【讨论】:

  • 这对我来说是不可能的,因为文档的动态特性,如果将来如果向地址添加新字段,那么我必须修改查询,而我不能。
  • @TheIndianProgrammmer 不明白你在说什么。任何 MongoDB 查询表达式中都没有“通配符”值。
  • @NeilLunn 感谢您花时间回答这个问题,我已经尝试过您之前提到的内容,我要求在没有对象链的情况下展平文档,所有子对象都应该是根文档的一部分。我不能硬编码查询中除了父项之外的任何字段,因为子对象可能随时更改。
  • @TheIndianProgrammmer 哎哟!除了使用 mapReduce 的 JavaScript 评估之外,真的没有实际的方法可以做到这一点。我可以通过示例进行更新,但通常最好在客户端代码中进行。等几分钟,我会更新解释。
  • @TheIndianProgrammmer 抱歉,花了几分钟时间才考虑其他回复。编辑应该解释基本点。
【解决方案2】:

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

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;
}

【讨论】:

    猜你喜欢
    • 2019-07-09
    • 1970-01-01
    • 2016-02-28
    • 2015-12-19
    • 2021-09-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-28
    相关资源
    最近更新 更多