【问题标题】:mongodb get _id as string in find querymongodb在查找查询中获取_id作为字符串
【发布时间】:2016-01-23 07:13:39
【问题描述】:

在这里我创建了一个包含单个文档的集合

db.getCollection('example').insert({"example":1});

我尝试过使用 Projection,但我找回了 _id。

db.getCollection('example').find({"example":1},{"_id":1});

{
    "_id" : ObjectId("562a6300bbc948a4315f3abc")
}

但是,我需要如下所示的输出。

  1. id 而不是 _id
  2. ObjectId("562a6300bbc948a4315f3abc") 与 "562a6300bbc948a4315f3abc"

    { "id" : "562a6300bbc948a4315f3abc" }

虽然我可以在我的应用服务器(基于 PHP)上处理 #1 和 #2 以获得所需的输出,但我正在寻找是否有一种方法可以在从 mongo 本身查询时获得预期结果

【问题讨论】:

    标签: php mongodb mongodb-query aggregation-framework


    【解决方案1】:

    您需要使用.aggregate() 方法。

    db.getCollection('example').aggregate([ { "$project": { "_id": 0, "id": "$_id" } } ]);
    

    产量:

    { "id" : ObjectId("562a67745488a8d831ce2e35") }
    

    或使用.str 属性。

    db.getCollection('example').find({"example":1},{"_id":1}).map(function(doc) { 
        return {'id': doc._id.str }
    })
    

    返回:

    [ { "id" : "562a67745488a8d831ce2e35" } ]
    

    如果您使用的是 PHP 驱动程序,您可以执行以下操作:

    $connection = new MongoClient();
    $db = $connection->test;
    $col = $db->example;
    $cursor = $col->find([], ["_id" => 1]);
    foreach($cursor as $doc) { print_r(array("id" => $doc["_id"])); } 
    

    产量:

    Array
    (
        [id] => MongoId Object
            (
                [$id] => 562a6c60f850734c0c8b4567
            )
    
    )
    

    或者再次使用MongoCollection::aggregate 方法。

    $result = $col->aggregate(array(["$project" => ["id" => "$_id", "_id" => 0]]))
    

    然后使用foreach循环:

    Array
    (
        [_id] => MongoId Object
            (
                [$id] => 562a6c60f850734c0c8b4567
            )
    
    )
    

    【讨论】:

    • 谢谢。我不确定 PHP mongo 驱动程序是否支持 cursor.map。有什么想法吗?
    • 在我的问题中提到,在获得光标句柄后,我能够在 PHP 上处理 1&2。但是我的问题是从 mongo 本身检索 [ { "id" : "562a67745488a8d831ce2e35" } ] :)
    【解决方案2】:

    在 PHP 端遍历 MongoCursor 的一个简单解决方案是使用生成器以及 foreacharray_map($function, iterator_to_array($cursor))。 示例:

    function map_traversable(callable $mapper, \Traversable $iterator) {
        foreach($iterator as $val) {
            yield $mapper($val);
        }
    }
    

    您可以在PHP documentation about generators syntax 认识更多。

    因此,现在您可以使用/重用它(或类似的实现)以任何数量的映射(就像管道在 aggregate 中所做的那样)在 PHP 端“投影”您的数据的任何建议,但迭代量更少。在重用 map 函数的情况下,这个解决方案对于 OOP 来说非常方便。

    更新: 仅针对您的案例示例:

    $cursor = $db->getCollection('example')->find(["example":1],["_id":1]);
    $mapper = function($record) {
        return array('id' => (string) $record['_id']); //see \MongoId::__toString()
    }
    $traversableWithIdAsStringApplied = map_traversable($mapper, $cursor);
    //...
    

    现在您可以继续对 $traversableWithIdAsStringApplied 应用更多映射,或者仅使用 iterator_to_array 进行简单的数组检索。

    【讨论】:

      【解决方案3】:

      MongoDB 4.0 添加了 $convert 聚合运算符和 $toString 别名,这使您可以做到这一点:

      db.getCollection('example').aggregate([
        { "$match": { "example":1 } },
        { "$project": { "_id": { "$toString": "$_id" } } }
      ])
      

      一个主要的用法很可能是使用_id 值作为文档中的“键”。

      db.getCollection('example').insertOne({ "a": 1, "b": 2 })
      
      db.getCollection('example').aggregate([
        { "$replaceRoot": {
          "newRoot": {
            "$arrayToObject": [
              [{ 
                "k": { "$toString": "$_id" },
                "v": {
                  "$arrayToObject": {
                    "$filter": {
                      "input": { "$objectToArray": "$$ROOT" },
                      "cond": { "$ne": ["$$this.k", "_id"] }
                    }
                  }
                }
              }] 
            ]
          }
        }}
      ])
      

      哪个会返回:

      { 
        "5b06973e7f859c325db150fd" : { "a" : 1, "b" : 2 }
      }
      

      与其他示例一样,清楚地显示了字符串。

      虽然通常有一种方法可以在从服务器返回文档时对光标进行“转换”。这通常是一件好事,因为 ObjectId 是 12 字节的二进制表示,而不是占用更多空间的 24 个字符的十六进制“字符串”。

      shell 有一个.map() 方法

      db.getCollection('example').find().map(d => Object.assign(d, { _id: d._id.valueOf() }) )
      

      NodeJS 有一个 Cursor.map() 可以做很多相同的事情:

      let cursor = db.collection('example').find()
          .map(( _id, ...d }) => ({ _id: _id.toString(), ...d }));
      
      while ( await cursor.hasNext() ) {
        let doc = cursor.next();
        // do something
      })
      

      同样的方法也存在于其他驱动程序中(只是不是 PHP),或者您可以迭代光标并转换内容,这可能是最好的做法。


      事实上,当在 shell 中工作时,通过简单地添加到任何游标返回语句中,整个游标结果可以很容易地简化为单个对象

      .toArray().reduce((o,e) => { 
        var _id = e._id;
        delete e._id;
        return Object.assign(o, { [_id]: e })
      },{ })
      

      或者对于完整的 ES6 JavaScript 支持环境,例如 nodejs:

      .toArray().reduce((o,({ _id, ...e })) =>  ({ ...o, [_id]: e }),{ })
      

      真正简单的东西,没有聚合框架中需要处理的复杂性。并且非常可能通过几乎相同的方式使用任何语言。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-03-12
        • 2014-10-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-04
        • 2021-03-14
        相关资源
        最近更新 更多