<?php
/*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* This software consists of voluntary contributions made by many individuals
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ODM\MongoDB;
use Doctrine\ODM\MongoDB\DocumentManager,
Doctrine\ODM\MongoDB\Hydrator;
/**
* Query object that represents a query using a documents MongoCollection::find()
* method. Offers a fluent chainable interface similar to the Doctrine ORM.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @since 1.0
* @author Jonathan H. Wage <hide@address.com>
*/
class Query
{
const TYPE_FIND = 1;
const TYPE_INSERT = 2;
const TYPE_UPDATE = 3;
const TYPE_REMOVE = 4;
const TYPE_GROUP = 5;
/** The DocumentManager instance for this query */
private $dm;
/** The Document class name being queried */
private $className;
/** The ClassMetadata instance for the class being queried */
private $class;
/** Array of fields to select */
private $select = array();
/** Array that stores the built up query to execute. */
private $query = array();
/** Array to pass to MongoCollection::update() 2nd argument */
private $newObj = array();
/** Array of sort options */
private $sort = array();
/** Limit number of records */
private $limit = null;
/** Skip a specified number of records (offset) */
private $skip = null;
/** Group information. */
private $group = array();
/** Pass hints to the MongoCursor */
private $hints = array();
/** Pass immortal to cursor */
private $immortal = false;
/** Pass snapshot to cursor */
private $snapshot = false;
/** Pass slaveOkaye to cursor */
private $slaveOkay = false;
/** Whether or not to try and hydrate the returned data */
private $hydrate = true;
/** Map reduce information */
private $mapReduce = array();
/** Field to select distinct values of */
private $distinctField;
/** Data to use with $near operator for geospatial indexes */
private $near;
/** The type of query */
private $type = self::TYPE_FIND;
/**
* Mongo command prefix
* @var string
*/
private $cmd;
/** The current field adding conditions to */
private $currentField;
/** Whether or not the query is a findAndModify query. Stores an array of options if not false. */
private $findAndModify = false;
/** Refresh hint */
const HINT_REFRESH = 1;
/**
* Create a new MongoDB Query.
*
* @param DocumentManager $dm
* @param string $className
*/
public function __construct(DocumentManager $dm, $className = null)
{
$this->dm = $dm;
$this->hydrator = $dm->getHydrator();
$this->cmd = $dm->getConfiguration()->getMongoCmd();
if ($className !== null) {
$this->find($className);
}
}
/**
* Returns the DocumentManager instance for this query.
*
* @return Doctrine\ODM\MongoDB\DocumentManager $dm
*/
public function getDocumentManager()
{
return $this->dm;
}
/**
* Get the type of this query.
*
* @return string $type
*/
public function getType()
{
return $this->type;
}
public function setQuery($query)
{
$this->query = $query;
}
/**
* Whether or not to hydrate the data into objects or to return the raw results
* from mongo.
*
* @param boolean $bool
*/
public function hydrate($bool)
{
$this->hydrate = $bool;
return $this;
}
/**
* Set slave okaye.
*
* @param bool $bool
* @return Query
*/
public function slaveOkay($bool = true)
{
$this->slaveOkay = $bool;
return $this;
}
/**
* Set snapshot.
*
* @param bool $bool
* @return Query
*/
public function snapshot($bool = true)
{
$this->snapshot = $bool;
return $this;
}
/**
* Set immortal.
*
* @param bool $bool
* @return Query
*/
public function immortal($bool = true)
{
$this->immortal = $bool;
return $this;
}
/**
* Pass a hint to the MongoCursor
*
* @param string $keyPattern
* @return Query
*/
public function hint($keyPattern)
{
$this->hints[] = $keyPattern;
return $this;
}
/**
* Change the query type to find and optionally set and change the class being queried.
*
* @param string $className The Document class being queried.
* @return Query
*/
public function find($className = null)
{
$this->setClassName($className);
$this->type = self::TYPE_FIND;
return $this;
}
/**
* Sets a flag for the query to be executed as a findAndModify query query.
*
* @param array $findAndModify Boolean true value or array containing key "upsert" set to true to
* create object if it doesn't exist and a second key "new"
* set to true if you want to return the modified object
* rather than the original. "new" is ignored for remove.
* @return Query
*/
public function findAndModify($findAndModify = true)
{
$this->findAndModify = $findAndModify;
return $this;
}
/**
* Change the query type to update and optionally set and change the class being queried.
*
* @param string $className The Document class being queried.
* @return Query
*/
public function update($className = null)
{
$this->setClassName($className);
$this->type = self::TYPE_UPDATE;
return $this;
}
/**
* Change the query type to insert and optionally set and change the class being queried.
*
* @param string $className The Document class being queried.
* @return Query
*/
public function insert($className = null)
{
$this->setClassName($className);
$this->type = self::TYPE_INSERT;
return $this;
}
/**
* Change the query type to remove and optionally set and change the class being queried.
*
* @param string $className The Document class being queried.
* @return Query
*/
public function remove($className = null)
{
$this->setClassName($className);
$this->type = self::TYPE_REMOVE;
return $this;
}
/**
* Perform an operation similar to SQL's GROUP BY command
*
* @param array $keys
* @param array $initial
* @param string $reduce
* @param array $condition
* @return Query
*/
public function group($keys, array $initial)
{
$this->group = array(
'keys' => $keys,
'initial' => $initial
);
$this->type = self::TYPE_GROUP;
return $this;
}
/**
* The distinct method queries for a list of distinct values for the given
* field for the document being queried for.
*
* @param string $field
* @return Query
*/
public function distinct($field)
{
$this->distinctField = $field;
return $this;
}
/**
* The fields to select.
*
* @param string $fieldName
* @return Query
*/
public function select($fieldName = null)
{
$select = func_get_args();
foreach ($select as $fieldName) {
$this->select[] = $fieldName;
}
return $this;
}
/**
* Select a slice of an embedded document.
*
* @param string $fieldName
* @param integer $skip
* @param integer $limit
* @return Query
*/
public function selectSlice($fieldName, $skip, $limit = null)
{
$slice = array($skip);
if ($limit !== null) {
$slice[] = $limit;
}
$this->select[$fieldName][$this->cmd . 'slice'] = $slice;
return $this;
}
/**
* Set the current field to operate on.
*
* @param string $field
* @return Query
*/
public function field($field)
{
$this->currentField = $field;
return $this;
}
/**
* Add a new where criteria erasing all old criteria.
*
* @param string $value
* @return Query
*/
public function equals($value, array $options = array())
{
$value = $this->prepareWhereValue($this->currentField, $value);
if (isset($options['elemMatch'])) {
return $this->elemMatch($value, $options);
}
if (isset($options['not'])) {
return $this->not($value, $options);
}
$this->query[$this->currentField] = $value;
return $this;
}
/**
* Add $where javascript function to reduce result sets.
*
* @param string $javascript
* @return Query
*/
public function where($javascript)
{
return $this->field($this->cmd . 'where')->equals($javascript);
}
/**
* Add element match to query.
*
* @param string $value
* @return Query
*/
public function elemMatch($value)
{
$e = explode('.', $this->currentField);
$fieldName = array_pop($e);
$embeddedPath = implode('.', $e);
$this->query[$embeddedPath][$this->cmd . 'elemMatch'][$fieldName] = $value;
return $this;
}
/**
* Add element match operator to the query.
*
* @param string $operator
* @param string $value
* @return Query
*/
public function elemMatchOperator($operator, $value)
{
$e = explode('.', $this->currentField);
$fieldName = array_pop($e);
$embeddedPath = implode('.', $e);
$this->query[$embeddedPath][$this->cmd . 'elemMatch'][$fieldName][$operator] = $value;
return $this;
}
/**
* Add MongoDB operator to the query.
*
* @param string $operator
* @param string $value
* @param array $options
* @return Query
*/
public function operator($operator, $value, array $options = array())
{
if (isset($options['elemMatch'])) {
return $this->elemMatchOperator($operator, $value);
}
if (isset($options['not'])) {
$this->query[$this->currentField][$this->cmd . 'not'][$operator] = $value;
return $this;
}
$this->query[$this->currentField][$operator] = $value;
return $this;
}
/**
* Add a new where not criteria
*
* @param string $value
* @param array $options
* @return Query
*/
public function not($value, array $options = array())
{
return $this->operator($this->cmd . 'not', $value);
}
/**
* Add a new where in criteria.
*
* @param mixed $values
* @param array $options
* @return Query
*/
public function in($values, array $options = array())
{
return $this->operator($this->cmd . 'in', $values, $options);
}
/**
* Add where not in criteria.
*
* @param mixed $values
* @param array $options
* @return Query
*/
public function notIn($values, array $options = array())
{
return $this->operator($this->cmd . 'nin', (array) $values, $options);
}
/**
* Add where not equal criteria.
*
* @param string $value
* @param array $options
* @return Query
*/
public function notEqual($value, array $options = array())
{
return $this->operator($this->cmd . 'ne', $value, $options);
}
/**
* Add where greater than criteria.
*
* @param string $value
* @param array $options
* @return Query
*/
public function greaterThan($value, array $options = array())
{
return $this->operator($this->cmd . 'gt', $value, $options);
}
/**
* Add where greater than or equal to criteria.
*
* @param string $value
* @param array $options
* @return Query
*/
public function greaterThanOrEq($value, array $options = array())
{
return $this->operator($this->cmd . 'gte', $value, $options);
}
/**
* Add where less than criteria.
*
* @param string $value
* @param array $options
* @return Query
*/
public function lessThan($value, array $options = array())
{
return $this->operator($this->cmd . 'lt', $value, $options);
}
/**
* Add where less than or equal to criteria.
*
* @param string $value
* @param array $options
* @return Query
*/
public function lessThanOrEq($value, array $options = array())
{
return $this->operator($this->cmd . 'lte', $value, $options);
}
/**
* Add where range criteria.
*
* @param string $start
* @param string $end
* @param array $options
* @return Query
*/
public function range($start, $end, array $options = array())
{
return $this->operator($this->cmd . 'gte', $start, $options)
->operator($this->cmd . 'lt', $end, $options);
}
/**
* Add where size criteria.
*
* @param string $size
* @param array $options
* @return Query
*/
public function size($size, array $options = array())
{
return $this->operator($this->cmd . 'size', $size, $options);
}
/**
* Add where exists criteria.
*
* @param string $bool
* @param array $options
* @return Query
*/
public function exists($bool, array $options = array())
{
return $this->operator($this->cmd . 'exists', $bool, $options);
}
/**
* Add where type criteria.
*
* @param string $type
* @param array $options
* @return Query
*/
public function type($type, array $options = array())
{
$map = array(
'double' => 1,
'string' => 2,
'object' => 3,
'array' => 4,
'binary' => 5,
'undefined' => 6,
'objectid' => 7,
'boolean' => 8,
'date' => 9,
'null' => 10,
'regex' => 11,
'jscode' => 13,
'symbol' => 14,
'jscodewithscope' => 15,
'integer32' => 16,
'timestamp' => 17,
'integer64' => 18,
'minkey' => 255,
'maxkey' => 127
);
if (is_string($type) && isset($map[$type])) {
$type = $map[$type];
}
return $this->operator($this->cmd . 'type', $type, $options);
}
/**
* Add where all criteria.
*
* @param mixed $values
* @param array $options
* @return Query
*/
public function all($values, array $options = array())
{
return $this->operator($this->cmd . 'all', (array) $values, $options);
}
/**
* Add where mod criteria.
*
* @param string $mod
* @param array $options
* @return Query
*/
public function mod($mod, array $options = array())
{
return $this->operator($this->cmd . 'mod', $mod, $options);
}
/**
* Add where near criteria.
*
* @param string $x
* @param string $y
* @return Query
*/
public function near($x, $y)
{
list($xMapping, $yMapping) = array_values($this->dm->getClassMetadata($this->class->fieldMappings[$this->currentField]['targetDocument'])->fieldMappings);
$this->near = array($xMapping['name'] => $x, $yMapping['name'] => $y);
return $this;
}
/**
* Add where $within $box query.
*
* @param string $x1
* @param string $y1
* @param string $x2
* @param string $y2
* @return Query
*/
public function withinBox($x1, $y1, $x2, $y2)
{
$this->query[$this->currentField][$this->cmd . 'within'][$this->cmd . 'box'] = array(array($x1, $y1), array($x2, $y2));
return $this;
}
/**
* Add where $within $center query.
*
* @param string $x
* @param string $y
* @param string $radius
* @return Query
*/
public function withinCenter($x, $y, $radius)
{
$this->query[$this->currentField][$this->cmd . 'within'][$this->cmd . 'center'] = array(array($x, $y), $radius);
return $this;
}
/**
* Set sort and erase all old sorts.
*
* @param string $order
* @return Query
*/
public function sort($fieldName, $order)
{
$this->sort[$fieldName] = strtolower($order) === 'asc' ? 1 : -1;
return $this;
}
/**
* Set the Document limit for the MongoCursor
*
* @param string $limit
* @return Query
*/
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Set the number of Documents to skip for the MongoCursor
*
* @param string $skip
* @return Query
*/
public function skip($skip)
{
$this->skip = $skip;
return $this;
}
/**
* Specify a map reduce operation for this query.
*
* @param mixed $map
* @param mixed $reduce
* @param array $options
* @return Query
*/
public function mapReduce($map, $reduce, array $options = array())
{
$this->mapReduce = array(
'map' => $map,
'reduce' => $reduce,
'options' => $options
);
return $this;
}
/**
* Specify a map operation for this query.
*
* @param string $map
* @return Query
*/
public function map($map)
{
$this->mapReduce['map'] = $map;
return $this;
}
/**
* Specify a reduce operation for this query.
*
* @param string $reduce
* @return Query
*/
public function reduce($reduce)
{
$this->mapReduce['reduce'] = $reduce;
return $this;
}
/**
* Specify the map reduce array of options for this query.
*
* @param array $options
* @return Query
*/
public function mapReduceOptions(array $options)
{
$this->mapReduce['options'] = $options;
return $this;
}
/**
* Set field to value.
*
* @param mixed $value
* @param boolean $atomic
* @return Query
*/
public function set($value, $atomic = true)
{
if ($this->type == self::TYPE_INSERT) {
$atomic = false;
}
if ($atomic === true) {
$this->newObj[$this->cmd . 'set'][$this->currentField] = $value;
} else {
if (strpos($this->currentField, '.') !== false) {
$e = explode('.', $this->currentField);
$current = &$this->newObj;
foreach ($e as $v) {
$current[$v] = null;
$current = &$current[$v];
}
$current = $value;
} else {
$this->newObj[$this->currentField] = $value;
}
}
return $this;
}
/**
* Set the $newObj array
*
* @param array $newObj
*/
public function setNewObj($newObj)
{
$this->newObj = $newObj;
return $this;
}
/**
* Increment field by the number value if field is present in the document,
* otherwise sets field to the number value.
*
* @param integer $value
* @return Query
*/
public function inc($value)
{
$this->newObj[$this->cmd . 'inc'][$this->currentField] = $value;
return $this;
}
/**
* Deletes a given field.
*
* @return Query
*/
public function unsetField()
{
$this->newObj[$this->cmd . 'unset'][$this->currentField] = 1;
return $this;
}
/**
* Appends value to field, if field is an existing array, otherwise sets
* field to the array [value] if field is not present. If field is present
* but is not an array, an error condition is raised.
*
* @param mixed $value
* @return Query
*/
public function push($value)
{
$this->newObj[$this->cmd . 'push'][$this->currentField] = $value;
return $this;
}
/**
* Appends each value in valueArray to field, if field is an existing
* array, otherwise sets field to the array valueArray if field is not
* present. If field is present but is not an array, an error condition is
* raised.
*
* @param array $valueArray
* @return Query
*/
public function pushAll(array $valueArray)
{
$this->newObj[$this->cmd . 'pushAll'][$this->currentField] = $valueArray;
return $this;
}
/**
* Adds value to the array only if its not in the array already.
*
* @param mixed $value
* @return Query
*/
public function addToSet($value)
{
$this->newObj[$this->cmd . 'addToSet'][$this->currentField] = $value;
return $this;
}
/**
* Adds values to the array only they are not in the array already.
*
* @param array $values
* @return Query
*/
public function addManyToSet(array $values)
{
if ( ! isset($this->newObj[$this->cmd . 'addToSet'][$this->currentField])) {
$this->newObj[$this->cmd . 'addToSet'][$this->currentField][$this->cmd . 'each'] = array();
}
if ( ! is_array($this->newObj[$this->cmd . 'addToSet'][$this->currentField])) {
$this->newObj[$this->cmd . 'addToSet'][$this->currentField] = array($this->cmd . 'each' => array($this->newObj[$this->cmd . 'addToSet'][$this->currentField]));
}
$this->newObj[$this->cmd . 'addToSet'][$this->currentField][$this->cmd . 'each'] = array_merge_recursive($this->newObj[$this->cmd . 'addToSet'][$this->currentField][$this->cmd . 'each'], $values);
}
/**
* Removes first element in an array
*
* @return Query
*/
public function popFirst()
{
$this->newObj[$this->cmd . 'pop'][$this->currentField] = 1;
return $this;
}
/**
* Removes last element in an array
*
* @return Query
*/
public function popLast()
{
$this->newObj[$this->cmd . 'pop'][$this->currentField] = -1;
return $this;
}
/**
* Removes all occurrences of value from field, if field is an array.
* If field is present but is not an array, an error condition is raised.
*
* @param mixed $value
* @return Query
*/
public function pull($value)
{
$this->newObj[$this->cmd . 'pull'][$this->currentField] = $value;
return $this;
}
/**
* Removes all occurrences of each value in value_array from field, if
* field is an array. If field is present but is not an array, an error
* condition is raised.
*
* @param array $valueArray
* @return Query
*/
public function pullAll(array $valueArray)
{
$this->newObj[$this->cmd . 'pullAll'][$this->currentField] = $valueArray;
return $this;
}
/**
* Proxy to execute() method
*
* @param array $options
* @return Query
*/
public function getResult(array $options = array())
{
return $this->execute($options);
}
/**
* Execute the query and return an array of results
*
* @param array $options
* @return mixed $result The result of the query.
*/
public function execute(array $options = array())
{
switch ($this->type) {
case self::TYPE_FIND;
if ($this->distinctField !== null) {
return $this->executeDistinctFieldQuery($options);
} elseif ($this->near !== null) {
return $this->executeGeoLocationQuery($options);
} else {
return $this->getCursor();
}
break;
case self::TYPE_REMOVE;
if ($this->findAndModify !== false) {
return $this->executeFindAndModify($options);
} else {
return $this->dm->getDocumentCollection($this->className)
->remove($this->query, $options);
}
break;
case self::TYPE_UPDATE;
if ($this->findAndModify !== false) {
return $this->executeFindAndModify($options);
} else {
return $this->dm->getDocumentCollection($this->className)
->update($this->query, $this->newObj, $options);
}
break;
case self::TYPE_INSERT;
return $this->dm->getDocumentCollection($this->className)
->insert($this->newObj);
break;
case self::TYPE_GROUP;
return $this->dm->getDocumentCollection($this->className)
->group(
$this->group['keys'], $this->group['initial'],
$this->mapReduce['reduce'], $this->query
);
break;
}
}
/**
* Count the number of results for this query.
*
* @param bool $all
* @return integer $count
*/
public function count($all = false)
{
return $this->getCursor()->count($all);
}
/**
* Execute the query and get a single result
*
* @return object $document The single document.
*/
public function getSingleResult(array $options = array())
{
if ($results = $this->execute($options)) {
if ($results instanceof MongoCursor) {
return $results->getSingleResult();
}
return array_shift($results);
}
return null;
}
/**
* Iterator over the query using the MongoCursor.
*
* @return MongoCursor $cursor
*/
public function iterate()
{
return $this->getCursor();
}
/**
* Gets an array of information about this query for debugging.
*
* @param string $name
* @return array $debug
*/
public function debug($name = null)
{
$debug = get_object_vars($this);
unset($debug['dm'], $debug['hydrator'], $debug['class']);
if ($name !== null) {
return $debug[$name];
}
foreach ($debug as $key => $value) {
if ( ! $value) {
unset($debug[$key]);
}
}
return $debug;
}
/**
* Get the MongoCursor for this query instance.
*
* @return MongoCursor $cursor
*/
private function getCursor()
{
if ($this->type !== self::TYPE_FIND) {
throw new \InvalidArgumentException(
'Cannot get cursor for an update or remove query. Use execute() method.'
);
}
if (isset($this->mapReduce['map']) && $this->mapReduce['reduce']) {
$cursor = $this->dm->mapReduce($this->className, $this->mapReduce['map'], $this->mapReduce['reduce'], $this->query, isset($this->mapReduce['options']) ? $this->mapReduce['options'] : array());
$cursor->hydrate(false);
} else {
if (isset($this->mapReduce['reduce'])) {
$this->query[$this->cmd . 'where'] = $this->mapReduce['reduce'];
}
$cursor = $this->dm->find($this->className, $this->query, $this->select);
$cursor->hydrate($this->hydrate);
}
$cursor->limit($this->limit);
$cursor->skip($this->skip);
$cursor->sort($this->sort);
$cursor->immortal($this->immortal);
$cursor->slaveOkay($this->slaveOkay);
if ($this->snapshot) {
$cursor->snapshot();
}
foreach ($this->hints as $keyPattern) {
$cursor->hint($keyPattern);
}
return $cursor;
}
/**
* Execute find and modify query.
*
* @return mixed Returns either a managed document or an array with information if something went wrong.
*/
private function executeFindAndModify()
{
$command = array();
$command['findandmodify'] = $this->dm->getDocumentCollection($this->className)->getName();
if ($this->query) {
$command['query'] = $this->query;
}
if ($this->sort) {
$command['sort'] = $this->sort;
}
if ($this->select) {
$command['fields'] = $this->select;
}
if ($this->type == self::TYPE_REMOVE) {
$command['remove'] = true;
} elseif ($this->type === self::TYPE_UPDATE) {
$command['update'] = $this->newObj;
}
if (isset($this->findAndModify['upsert']) && $this->findAndModify['upsert']) {
$command['upsert'] = true;
}
if (isset($this->findAndModify['new']) && $this->findAndModify['new']) {
$command['new'] = true;
}
if ($this->limit) {
$command['num'] = $this->limit;
}
$result = $this->dm->getDocumentDB($this->className)
->command($command);
if (isset($result['value'])) {
return $this->dm->getUnitOfWork()->getOrCreateDocument(
$this->class->name, $result['value']
);
}
return $result;
}
/**
* Execute distinct field query.
*
* @return array $values Array of distinct values.
*/
private function executeDistinctFieldQuery()
{
$result = $this->dm->getDocumentDB($this->className)
->command(array(
'distinct' => $this->dm->getDocumentCollection($this->className)->getName(),
'key' => $this->distinctField,
'query' => $this->query
));
return $result['values'];
}
/**
* Execute geo location query.
*
* @return array $documents Array of documents.
*/
private function executeGeoLocationQuery()
{
$command = array(
'geoNear' => $this->dm->getDocumentCollection($this->className)->getName(),
'near' => $this->near,
'query' => $this->query
);
if ($this->limit) {
$command['num'] = $this->limit;
}
$result = $this->dm->getDocumentDB($this->className)
->command($command);
if ( ! isset($result['results'])) {
return array();
}
if ($this->hydrate) {
$uow = $this->dm->getUnitOfWork();
$documents = array();
foreach ($result['results'] as $result) {
$document = $result['obj'];
if ($this->class->distance) {
$document[$this->class->distance] = $result['dis'];
}
$documents[] = $uow->getOrCreateDocument($this->class->name, $document);
}
return $documents;
} else {
return $result['results'];
}
}
/**
* Prepare where values converting document object field names to the document collection
* field name.
*
* @param string $fieldName
* @param string $value
* @return string $value
*/
private function prepareWhereValue(&$fieldName, $value)
{
if (strpos($fieldName, '.') !== false) {
$e = explode('.', $fieldName);
if ($e[1] === '$id') {
$fieldName = $e[0] . '.$id';
$value = $this->class->getDatabaseIdentifierValue($value);
} elseif ($this->class->hasField($e[0])) {
$mapping = $this->class->getFieldMapping($e[0]);
if (isset($mapping['targetDocument'])) {
$targetClass = $this->dm->getClassMetadata($mapping['targetDocument']);
if ($targetClass->hasField($e[1]) && $targetClass->identifier === $e[1]) {
$fieldName = $e[0] . '.$id';
$value = $targetClass->getDatabaseIdentifierValue($value);
}
}
}
} else {
if ($fieldName === $this->class->identifier) {
$fieldName = '_id';
if (is_array($value)) {
foreach ($value as $k => $v) {
$value[$k] = $this->class->getDatabaseIdentifierValue($v);
}
} else {
$value = $this->class->getDatabaseIdentifierValue($value);
}
}
}
return $value;
}
private function setClassName($className)
{
if (is_array($className)) {
$classNames = $className;
$className = $classNames[0];
$discriminatorField = $this->dm->getClassMetadata($className)->discriminatorField['name'];
$discriminatorValues = $this->dm->getDiscriminatorValues($classNames);
$this->field($discriminatorField)->in($discriminatorValues);
}
if ($className !== null) {
$this->className = $className;
$this->class = $this->dm->getClassMetadata($className);
}
}
}