最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - MongoDB (php) - return document property as an array instead of multiple properties - Stack Overflow

programmeradmin1浏览0评论

I am reading a document from a mongodb database and passing it with php to client side.

The document contains an array property. The problem is that the client side receives it as an object with properties with the names 0,1 and so on, instead of a standard array.

This is the original data:

{ 
    "_id" : ObjectId("573b47a1f99a8a1f9a6278a5"), 
    "persons" : [
        {
            "name" : "Moshe",
        }, 
        {
            "name" : "E",
        }, ...
    ]
}

As requested, I am attaching the var_export:

array (
  0 => 
  MongoDB\Model\BSONDocument::__set_state(array(
     '_id' => 
    MongoDB\BSON\ObjectID::__set_state(array(
    )),
     'persons' => 
    MongoDB\Model\BSONArray::__set_state(array(
       0 => 
      MongoDB\Model\BSONDocument::__set_state(array(
         'name' => 'Moshe',
      )),
       1 => 
      MongoDB\Model\BSONDocument::__set_state(array(
         'name' => 'E',
      )),
    )),
  )),
)

And var_dump:

array(1) {
  [0]=>
  object(MongoDB\Model\BSONDocument)#40 (1) {
    ["storage":"ArrayObject":private]=>
    array(2) {
      ["_id"]=>
      object(MongoDB\BSON\ObjectID)#11 (1) {
        ["oid"]=>
        string(24) "573b47a1f99a8d1f986278a5"
      }
      ["persons"]=>
      object(MongoDB\Model\BSONArray)#34 (1) {
        ["storage":"ArrayObject":private]=>
        array(2) {
          [0]=>
          object(MongoDB\Model\BSONDocument)#10 (1) {
            ["storage":"ArrayObject":private]=>
            array(1) {
              ["name"]=>
              string(5) "Moshe"
            }
          }
          [1]=>
          object(MongoDB\Model\BSONDocument)#12 (1) {
            ["storage":"ArrayObject":private]=>
            array(1) {
              ["name"]=>
              string(1) "E"
            }
          }
        }
      }
    }
  }
}

This is the PHP code(all of it):

function select(){
    $conn = new MongoDB\Client("mongodb://localhost:27017");
    $db = $conn->mydb;
    $cursor = $db->entries_meta_data->find();
    return current($cursor->toArray());
}

Then i pass the object to the client with a json_encode like this:

echo json_encode(select());

And the result as it appears in the client side is:

{ 
    "_id" : ObjectId("573b47a1f99a8a1f9a6278a5"), 
    "persons" : {
        "0" : {
            "name" : "Moshe",
        }, 
        "1" : {
            "name" : "E",
        }, ...
    }
}

EDIT: LordNeo actually solved it. After reading his answer i have changed the last line in my "select" function to the following:

return json_decode(json_encode(current($cursor->toArray()),true);

It looks horrible, but it works.

I will be more than happy to hear a better solution.

I am reading a document from a mongodb database and passing it with php to client side.

The document contains an array property. The problem is that the client side receives it as an object with properties with the names 0,1 and so on, instead of a standard array.

This is the original data:

{ 
    "_id" : ObjectId("573b47a1f99a8a1f9a6278a5"), 
    "persons" : [
        {
            "name" : "Moshe",
        }, 
        {
            "name" : "E",
        }, ...
    ]
}

As requested, I am attaching the var_export:

array (
  0 => 
  MongoDB\Model\BSONDocument::__set_state(array(
     '_id' => 
    MongoDB\BSON\ObjectID::__set_state(array(
    )),
     'persons' => 
    MongoDB\Model\BSONArray::__set_state(array(
       0 => 
      MongoDB\Model\BSONDocument::__set_state(array(
         'name' => 'Moshe',
      )),
       1 => 
      MongoDB\Model\BSONDocument::__set_state(array(
         'name' => 'E',
      )),
    )),
  )),
)

And var_dump:

array(1) {
  [0]=>
  object(MongoDB\Model\BSONDocument)#40 (1) {
    ["storage":"ArrayObject":private]=>
    array(2) {
      ["_id"]=>
      object(MongoDB\BSON\ObjectID)#11 (1) {
        ["oid"]=>
        string(24) "573b47a1f99a8d1f986278a5"
      }
      ["persons"]=>
      object(MongoDB\Model\BSONArray)#34 (1) {
        ["storage":"ArrayObject":private]=>
        array(2) {
          [0]=>
          object(MongoDB\Model\BSONDocument)#10 (1) {
            ["storage":"ArrayObject":private]=>
            array(1) {
              ["name"]=>
              string(5) "Moshe"
            }
          }
          [1]=>
          object(MongoDB\Model\BSONDocument)#12 (1) {
            ["storage":"ArrayObject":private]=>
            array(1) {
              ["name"]=>
              string(1) "E"
            }
          }
        }
      }
    }
  }
}

This is the PHP code(all of it):

function select(){
    $conn = new MongoDB\Client("mongodb://localhost:27017");
    $db = $conn->mydb;
    $cursor = $db->entries_meta_data->find();
    return current($cursor->toArray());
}

Then i pass the object to the client with a json_encode like this:

echo json_encode(select());

And the result as it appears in the client side is:

{ 
    "_id" : ObjectId("573b47a1f99a8a1f9a6278a5"), 
    "persons" : {
        "0" : {
            "name" : "Moshe",
        }, 
        "1" : {
            "name" : "E",
        }, ...
    }
}

EDIT: LordNeo actually solved it. After reading his answer i have changed the last line in my "select" function to the following:

return json_decode(json_encode(current($cursor->toArray()),true);

It looks horrible, but it works.

I will be more than happy to hear a better solution.

Share Improve this question edited May 27, 2016 at 13:40 Dorad asked May 17, 2016 at 18:05 DoradDorad 3,7135 gold badges52 silver badges77 bronze badges 6
  • Not enough code provided – Richard Commented May 18, 2016 at 6:37
  • Just added the only line i didn't provide before. – Dorad Commented May 18, 2016 at 6:52
  • Can you add a var_dump of $cursor->toArray()? – guessimtoolate Commented May 21, 2016 at 19:42
  • Have you tried using iterator_to_array($cursor) instead of $cursor->toArray() ? – runz0rd Commented May 22, 2016 at 11:15
  • 1 @runz0rd - I have tried. Returns the same. Thx. – Dorad Commented May 22, 2016 at 11:36
 |  Show 1 more comment

6 Answers 6

Reset to default 14

The reason is that the new MongoDB driver handles the conversion of MongoDB documents to PHP types different than the old driver.

The good news is that you can get the old behaviour by specifying a so called "Type Map".

The documentation is a bit hidden, but you can read about it in the github repository of the driver or in the online docs.

The TL;DR is that you pass an array like

array(
  'array' => 'array',
  'document' => 'array',
  'root' => 'array'
)

either as the 3rd argument ("driverOptions") to the constructor of MongoDB\Client or individually call MongoDB\Driver\Cursor::setTypeMap() for each query.

In your example the call

$cursor->setTypeMap(array(
  'array' => 'array',
  'document' => 'array',
  'root' => 'array'
));

after $db->entries_meta_data->find() should do the trick.

The MongoDB driver provides a MongoDB\BSON\toJSON() function, which correctly converts the data to JSON. Because it requires a string as input, you first need to call MongoDB\BSON\fromPHP() on the document. As it looks like you just want to get the first found element, you can use the findOne() method instead of find(), which makes it quite easy to get the output:

function select() {
  $conn = new MongoDB\Client("mongodb://localhost:27017");
  $db = $conn->mydb;
  $doc = $db->entries_meta_data->findOne();
  return MongoDB\BSON\toJSON(MongoDB\BSON\fromPHP($doc));
}

If you want to output multiple entries, it gets a bit more complicated. One (admittedly hacky) way to output the array is shown here:

function select() {
  $conn = new MongoDB\Client("mongodb://localhost:27017");
  $db = $conn->mydb;
  $cursor = $db->entries_meta_data->find();
  $result = $cursor->toArray();
  foreach($result as $i => $doc) {
    $result[$i] = MongoDB\BSON\toJSON(MongoDB\BSON\fromPHP($doc));
  }
  return '[' . implode($res) . ']';
}

An alternative is to adjust the output of the BSONArray class when called by json_encode(). To do so, you need to adjust the BSONArray.php file you got when setting up the MongoDB driver located in the folder 'vendor/mongodb/mongodb/src/Model' and add the JsonSerializable interface and a jsonSerialize() method, so that it looks like this:

<?php

...

class BSONArray extends ArrayObject implements \JsonSerializable,
    Serializable, Unserializable
{
    ...

    /**
     * Serialize the ArrayObject as normal array
     *
     * @return array
     */  
    public function jsonSerialize() {
        return $this->getArrayCopy();
    }
}

When using json_decode you can use the optional "true" parameter and it will associate to an array

$obj = json_decode($json, true);
//$obj will be an associative array

http://php.net/manual/en/function.json-decode.php

Then you can strip off the index using array_shift:

$obj = array_shift($obj);

http://php.net/manual/en/function.array-shift.php

JSON adds the numeric index when it's not explicitly set, so you should probably send an array to the client instead of decoding -> removing index -> enconding -> removing index again.

Or just remove the index after being received by the client.

are there all the indices from 0 to n or is any of them missing? That would probably be the reason. If you want to convert it back to an array, you may probably use

$obj = select(); // changed by the new line added

and then

$obj['persons'] = array_values($obj['persons']); 

to get rid of the indices.

I'm still pretty sure that there's some value missing. But it's not visible from your example.

mthierer almost had the correct solution.

You need to put the typeMap in the driver options (3rd parameter) when instantiating the client.

var driver_options = {'typeMap'=>['root':'array', 'document':'array', 'array':'array']}; var client = new MongoDB\Client(conn, options, driver_options);

This is better than having to do this after every cursor instantiation.

For anyone in need of it using Laravel eloquent and aggregation, you can pass a second parameter $option to the aggregation method

Collection::raw(function ($collection) {
  $aggregationPipeLine = [];
  return $collection->aggregate($aggregationPipeLine, [
    'typeMap' => [
      'array' => 'array',
      'document' => 'array',
      'root' => 'array'
    ]
  ]);
});

Check https://www.mongodb.com/docs/php-library/current/reference/method/MongoDBCollection-aggregate/#definition

发布评论

评论列表(0)

  1. 暂无评论