MongoDB(php) - 将文档属性作为数组而不是多个属性返回

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.

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.

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

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.

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.