Search code examples
phpmongodbfindnestedmongodate

PHP + MongoClient + MongoDate , operand not working on nested elements


I've got an headache that I would like to share with you :)

This is my code (portion of a small project I'm doing in php+mongo):

<?php
$db = new MongoClient('mongodb://localhost');
$db->connect();
$collection = $db->{'someDb'}->{'someCollection'};
print '********************************************' . PHP_EOL;
print '********          SAVE           ***********' . PHP_EOL;
print '********************************************' . PHP_EOL;
$obj = array(
    "_id" =>new MongoId(),
    "dob" => new MongoDate(strtotime("1983-08-31T04:00:00Z")),
    "name" =>"Author [541a88934d1ed8.64628062]",
    "password" => "229fe88b25ae8307601bf6c9c050bf02755b7e26",
    "timezone" => "Australia/Sydney",
    "expiry" => new MongoDate(strtotime("2015-09-18T07:24:03Z")),
    "token" => array(
        0 => array(
            "title" => "API Test",
            "hash" => "ce9808c5063f114f21cbf2d7e194caeccd17d0ae",
            "expiry" => new MongoDate(strtotime("2015-09-18T07:24:03Z"))
        )
    )
);
print_r($obj);
$collection->save($obj);
print '********************************************' . PHP_EOL;
print '********         SEARCH          ***********' . PHP_EOL;
print '********************************************' . PHP_EOL;
$where = array(
    'token.hash' => 'ce9808c5063f114f21cbf2d7e194caeccd17d0ae',
    'expiry' => array('$lt' => new \MongoDate())
);
print '>>> CRITERIA ' . PHP_EOL;
print_r($where);
print '>>> RESULT ' . PHP_EOL;
$cursor = $collection->find()->limit(1);
$result = iterator_to_array($cursor);
$result = array_shift($result);
print_r($result);
print '********************************************' . PHP_EOL;
print '********     NESTED SEARCH       ***********' . PHP_EOL;
print '********************************************' . PHP_EOL;
$where = array(
    'token.hash' => 'ce9808c5063f114f21cbf2d7e194caeccd17d0ae',
    'token.expiry' => array('$lt' => new \MongoDate())
);
print '>>> CRITERIA ' . PHP_EOL;
print_r($where);
$cursor = $collection->find($where)->limit(1);
$result = iterator_to_array($cursor);
$result = array_shift($result);
print '>>> RESULT ' . PHP_EOL;
var_dump($result);

The first search work perfectly, but the second search does not work as expected, check out the result:

********************************************
********          SAVE           ***********
********************************************
Array
(
    [_id] => MongoId Object
        (
            [$id] => 541a9111c48e5859108b4567
        )

    [dob] => MongoDate Object
        (
            [sec] => 431150400
            [usec] => 0
        )

    [name] => Author [541a88934d1ed8.64628062]
    [password] => 229fe88b25ae8307601bf6c9c050bf02755b7e26
    [timezone] => Australia/Sydney
    [expiry] => MongoDate Object
        (
            [sec] => 1442561043
            [usec] => 0
        )

    [token] => Array
        (
            [0] => Array
                (
                    [title] => API Test
                    [hash] => ce9808c5063f114f21cbf2d7e194caeccd17d0ae
                    [expiry] => MongoDate Object
                        (
                            [sec] => 1442561043
                            [usec] => 0
                        )

                )

        )

)
********************************************
********         SEARCH          ***********
********************************************
>>> CRITERIA 
Array
(
    [token.hash] => ce9808c5063f114f21cbf2d7e194caeccd17d0ae
    [expiry] => Array
        (
            [$lt] => MongoDate Object
                (
                    [sec] => 1411027217
                    [usec] => 868000
                )

        )

)
>>> RESULT 
Array
(
    [_id] => MongoId Object
        (
            [$id] => 541a8e23c48e58700f8b4567
        )

    [dob] => MongoDate Object
        (
            [sec] => 431150400
            [usec] => 0
        )

    [name] => Author [541a88934d1ed8.64628062]
    [password] => 229fe88b25ae8307601bf6c9c050bf02755b7e26
    [timezone] => Australia/Sydney
    [token] => Array
        (
            [0] => Array
                (
                    [title] => API Test
                    [hash] => ce9808c5063f114f21cbf2d7e194caeccd17d0ae
                    [expiry] => MongoDate Object
                        (
                            [sec] => 1442561043
                            [usec] => 0
                        )

                )

        )

)
********************************************
********     NESTED SEARCH       ***********
********************************************
>>> CRITERIA 
Array
(
    [token.hash] => ce9808c5063f114f21cbf2d7e194caeccd17d0ae
    [token.expiry] => Array
        (
            [$lt] => MongoDate Object
                (
                    [sec] => 1411027217
                    [usec] => 870000
                )

        )

)
>>> RESULT 
NULL

I'm not a MongoDb guru, just a poor programmer trying to save his day, any one got any idea why this is happening? What Am I doing wrong?

PHP Version:

valerio@beatrice:~$ php -v
PHP 5.5.9-1ubuntu4.4 (cli) (built: Sep  4 2014 06:56:34) 
Copyright (c) 1997-2014 The PHP Group
Zend Engine v2.5.0, Copyright (c) 1998-2014 Zend Technologies
with Zend OPcache v7.0.3, Copyright (c) 1999-2014, by Zend Technologies
valerio@beatrice:~$ 

MongoDb driver info (from php -info)

MongoDB Support => enabled
Version => 1.4.5
SSL Support => enabled
Streams Support => enabled

Directive => Local Value => Master Value
mongo.allow_empty_keys => 0 => 0
mongo.chunk_size => 262144 => 262144
mongo.cmd => $ => $
mongo.default_host => localhost => localhost
mongo.default_port => 27017 => 27017
mongo.is_master_interval => 15 => 15
mongo.long_as_object => 0 => 0
mongo.native_long => 0 => 0
mongo.ping_interval => 5 => 5

Solution

  • Assuming the document you provided is the only thing in the collection, it does not match either of the criteria patterns that you've listed. The only reason that the first "SEARCH" example returns the document is because you're not passing $where to the find() method:

    $collection->find()->limit(1);
    

    On a side note, since your second criteria pattern (i.e. "NESTED SEARCH") is querying two nested fields in the same array, I assume you might actually want to ensure that they match nested fields within the same array element. The $elemMatch query operator can assist with this:

    $where = [
      'token' => [
        '$elemMatch' => [
          'hash' => 'ce9808c5063f114f21cbf2d7e194caeccd17d0ae',
          'expiry' => ['$lt' => new \MongoDate()],
        ],
      ],
    ];
    

    Without $elemMatch, your original criteria could match a document with multiple token objects, where one of those happened to only match the hash clause, and another matched expiry.