mongodb / mongo-php-library

The Official MongoDB PHP library
https://mongodb.com/docs/php-library/current/
Apache License 2.0
1.6k stars 263 forks source link

How to access elements in MongoDB\Model\BSONDocument #168

Closed RaeesBhatti closed 8 years ago

RaeesBhatti commented 8 years ago
$Existing = $DB->selectCollection('domains')->findOne(['DomainName' => $BaseDomain]);

$Existing has a several elements inside, for example, DomainName or its _id. How do we access them? HHVM Hack Type Checker reports that the $Existing is an object of type MongoDB\Model\BSONDocument and is not an object of type KeyedContainer, so, we cannot do a $Existing['_id']. Now, how do we access the elements inside?

moderndeveloperllc commented 8 years ago

It's an object, so you can do $Existing->_id. If you have an existing PHP class that implements MongoDB\BSON\Unserializable you can unserialize the return directly to your own class. Check out the documentation here for more: http://mongodb.github.io/mongo-php-library/tutorial/bson/#type-maps

RaeesBhatti commented 8 years ago

I'm using this in HackLang actually and $Existing->_id will not work because you can't apply static typing for each MongoDB\Model\BSONDocument as they can be different all the time. But I just found out that you can do a

$Existing = $Existing->toArray();

Thanks for the time

moderndeveloperllc commented 8 years ago

@raeesiqbal Glad to try to help! I would really look into http://mongodb.github.io/mongo-php-library/tutorial/bson/#persistable-classes if you can. It's very nice to have results just populate into a model class. It's like having a lite ODM.

RaeesBhatti commented 8 years ago

I will look into it. Thanks

jmikola commented 8 years ago

@raeesiqbal: I've not worked much with HackLang's Collection interfaces, but the library's BSONDocument class, which we use by default, extends PHP's ArrayObject class, which implements ArrayAccess. Does HackLang not allow you to use the offset accessors (e.g. []) on ArrayObjects in strict mode?

RaeesBhatti commented 8 years ago

If you disable the type checker $result['_id'] will work just fine (maybe because of PHP compatibility). But the type checker reports that MongoDB\Model\BSONDocument must implement KeyedContainer to be accessible in ['_id'] style. This is tested in strict mode

RaeesBhatti commented 8 years ago

I tried to add KeyedContainer manually to MongoDB\Model\BSONDocument's decl:

class MongoDB\Model\BSONDocument extends ArrayObject implements MongoDB\BSON\Serializable, 
      MongoDB\BSON\Unserializable, MongoDB\BSON\Type, KeyedContainer {
    public static function __set_state(array $properties);
    public function bsonSerialize();
    public function bsonUnserialize(array $data);
}

But it gives an error that KeyedContainer interface expects 2 arguments. Here is the decl for KeyedContainer:

interface KeyedContainer<+Tk, +Tv> extends Container<Tv>, KeyedTraversable<Tk, Tv> {}
jmikola commented 8 years ago

I don't think BSONDocument should be implementing KeyedContainer, as that interface entails declaring a type for all keys and values. The values in your BSON document are going to vary based on the fields in your document schema.

I dug up https://github.com/facebook/hhvm/issues/6745#issuecomment-171022143 from a few months ago, where @simonwelsh reported that "Hack doesn't support ArrayAccess on user land classes." Although BSONDocument is extending ArrayObject and not implementing ArrayAccess itself, it still may be affected by this limitations. It doesn't look like that note has been added to the unsupported features documentation yet.

Since the library is written in PHP, I expect that any HackLang code interacting with it will need to use partial mode. I don't believe you mentioned which mode you were using, but I assumed it was strict. I would hope that partial mode would allow you to inter-operate with BSONDocument, but the unsupported documentation isn't clear on that:

Remember, when we say unsupported, we mean the following:

  • The typechecker will issue an error in strict mode.
  • The typechecker will either issue an error in partial mode, or will ignore (and not typecheck) the construct.
  • These constructs will run unimpeded under HHVM when running PHP (not Hack) code.

I may as well also note that our IndexInfo class does implement ArrayAccess directly (to provide convenient access to the index options).

lindelius commented 8 years ago

If you don't need the returned documents to be objects for some special reason, specify the typeMap-option for the \MongoDB\Client to return the result as an array instead. It's both faster (not by much, but it's faster) and you can access the fields as a normal array.

$driverOptions= [
    'typeMap' => [
        'root' => 'array', 
        'document' => 'array', 
        'array' => 'array'
    ]
];

$client = new \MongoDB\Client($uri, $uriOptions, $driverOptions);
RaeesBhatti commented 8 years ago

@jmikola , I am using strict mode for all of my application except for the libraries written in PHP, like mongo-php-library. And I added nullable array as return type for MongoDB\Collection's functions I'm using, for example:

class MongoDB\Collection implements Stringish {
  public function find($filter = array (), array $options = array ()): Vector<array>;
  public function findOne($filter = array (), array $options = array ()): ?array;
}

And @lindelius HHVM actually does not complain in the runtime when I handle BSONDocument like array. And the type checker wont know if I have specified the typeMap and the result will be in array, because the typeMap is an array and type checker is only smart with shapes in this matter. The problem was with the type checker only which I kind of hacked. PHP and HackLang are pretty different languages, so, unless this library is re-written in HackLang, I think this is the best we can get.