mongodb在查询查询中将_id作为字符串

时间:2015-10-23 16:49:54

标签: php mongodb mongodb-query aggregation-framework

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

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”)vs“562a6300bbc948a4315f3abc”

    { "id" : "562a6300bbc948a4315f3abc" }

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

3 个答案:

答案 0 :(得分:3)

MongoDB 4.0添加了How to fill a form with post request and get response聚合运算符和$convert别名,允许您完全执行此操作:

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 }),{ })

非常简单的东西,没有在聚合框架中需要处理的复杂性。并且很可能在任何语言中使用相同的方法。

答案 1 :(得分:2)

您需要使用.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
        )

)

答案 2 :(得分:1)

在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处结识更多。

所以,现在你可以使用/重用它(或类似的实现)来提出任何"投射"您在PHP端的数据与任何数量的映射(就像aggregate中的管道一样)但迭代次数较少。在重用map函数的情况下,此解决方案对于OOP非常方便。

UPD: 仅为您的案例示例:

$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进行简单的数组检索。