• Yii2 mongodb 扩展的where的条件加入大于小于号浅析(转)


    1. mongodb的where中有比较丰富的 条件,如下:

    static $builders = [
               'NOT' => 'buildNotCondition',
               'AND' => 'buildAndCondition',
               'OR' => 'buildOrCondition',
               'BETWEEN' => 'buildBetweenCondition',
               'NOT BETWEEN' => 'buildBetweenCondition',
               'IN' => 'buildInCondition',
               'NOT IN' => 'buildInCondition',
               'REGEX' => 'buildRegexCondition',
               'LIKE' => 'buildLikeCondition',
                
           ];
    

     

     


    但是没有大于和小于等,因此我们需要加入大于,大于等于, 小于,小于等于

    加入函数部分为:

    在vendor/yiisoft/yii2-mongodb/collection.php

    加入函数:

    public function buildGtCondition($operator, $operands)
    {
        if (!isset($operands[0], $operands[1])) {
            throw new InvalidParamException("Operator '$operator' requires two operands.");
        }
        list($column, $value) = $operands;
        return [$column => ['?gt' => $value]];
    }
     
     
    public function buildGteCondition($operator, $operands)
    {
        if (!isset($operands[0], $operands[1])) {
            throw new InvalidParamException("Operator '$operator' requires two operands.");
        }
        list($column, $value) = $operands;
        return [$column => ['?gte' => $value]];
    }
     
     
    public function buildLtCondition($operator, $operands)
    {
        if (!isset($operands[0], $operands[1])) {
            throw new InvalidParamException("Operator '$operator' requires two operands.");
        }
        list($column, $value) = $operands;
        return [$column => ['?lt' => $value]];
    }
     
    public function buildLteCondition($operator, $operands)
    {
        if (!isset($operands[0], $operands[1])) {
            throw new InvalidParamException("Operator '$operator' requires two operands.");
        }
        list($column, $value) = $operands;
        return [$column => ['?lte' => $value]];
    }
    
     

    buildCondition函数里面加入下面的部分代码

    public function buildCondition($condition)
       {
           static $builders = [
               'NOT' => 'buildNotCondition',
               'AND' => 'buildAndCondition',
               'OR' => 'buildOrCondition',
               'BETWEEN' => 'buildBetweenCondition',
               'NOT BETWEEN' => 'buildBetweenCondition',
               'IN' => 'buildInCondition',
               'NOT IN' => 'buildInCondition',
               'REGEX' => 'buildRegexCondition',
               'LIKE' => 'buildLikeCondition',
               '>'  => 'buildGtCondition',
               '>=' => 'buildGteCondition',
               '<'  => 'buildLtCondition',
               '<='     => 'buildLteCondition',
           ];
    
     
     

    最后collection.php的代码如下:

    mongodb->getCollection('customer');
     * $collection->insert(['name' => 'John Smith', 'status' => 1]);
     * ~~~
     *
     * To perform "find" queries, please use [[Query]] instead.
     *
     * Mongo uses JSON format to specify query conditions with quite specific syntax.
     * However Collection class provides the ability of "translating" common condition format used "yiidb*"
     * into Mongo condition.
     * For example:
     * ~~~
     * $condition = [
     *     [
     *         'OR',
     *         ['AND', ['first_name' => 'John'], ['last_name' => 'Smith']],
     *         ['status' => [1, 2, 3]]
     *     ],
     * ];
     * print_r($collection->buildCondition($condition));
     * // outputs :
     * [
     *     '$or' => [
     *         [
     *             'first_name' => 'John',
     *             'last_name' => 'John',
     *         ],
     *         [
     *             'status' => ['$in' => [1, 2, 3]],
     *         ]
     *     ]
     * ]
     * ~~~
     *
     * Note: condition values for the key '_id' will be automatically cast to [[MongoId]] instance,
     * even if they are plain strings. However, if you have other columns, containing [[MongoId]], you
     * should take care of possible typecast on your own.
     *
     * @property string $fullName Full name of this collection, including database name. This property is
     * read-only.
     * @property array $lastError Last error information. This property is read-only.
     * @property string $name Name of this collection. This property is read-only.
     *
     * @author Paul Klimov <klimov.paul@gmail.com>
     * @since 2.0
     */
    class Collection extends Object
    {
        /**
         * @var MongoCollection Mongo collection instance.
         */
        public $mongoCollection;
     
     
        /**
         * @return string name of this collection.
         */
        public function getName()
        {
            return $this->mongoCollection->getName();
        }
     
        /**
         * @return string full name of this collection, including database name.
         */
        public function getFullName()
        {
            return $this->mongoCollection->__toString();
        }
     
        /**
         * @return array last error information.
         */
        public function getLastError()
        {
            return $this->mongoCollection->db->lastError();
        }
     
        /**
         * Composes log/profile token.
         * @param string $command command name
         * @param array $arguments command arguments.
         * @return string token.
         */
        protected function composeLogToken($command, $arguments = [])
        {
            $parts = [];
            foreach ($arguments as $argument) {
                $parts[] = is_scalar($argument) ? $argument : $this->encodeLogData($argument);
            }
     
            return $this->getFullName() . '.' . $command . '(' . implode(', ', $parts) . ')';
        }
     
        /**
         * Encodes complex log data into JSON format string.
         * @param mixed $data raw data.
         * @return string encoded data string.
         */
        protected function encodeLogData($data)
        {
            return json_encode($this->processLogData($data));
        }
     
        /**
         * Pre-processes the log data before sending it to `json_encode()`.
         * @param mixed $data raw data.
         * @return mixed the processed data.
         */
        protected function processLogData($data)
        {
            if (is_object($data)) {
                if ($data instanceof MongoId ||
                    $data instanceof MongoRegex ||
                    $data instanceof MongoDate ||
                    $data instanceof MongoInt32 ||
                    $data instanceof MongoInt64 ||
                    $data instanceof MongoTimestamp
                ) {
                    $data = get_class($data) . '(' . $data->__toString() . ')';
                } elseif ($data instanceof MongoCode) {
                    $data = 'MongoCode( ' . $data->__toString() . ' )';
                } elseif ($data instanceof MongoBinData) {
                    $data = 'MongoBinData(...)';
                } elseif ($data instanceof MongoDBRef) {
                    $data = 'MongoDBRef(...)';
                } elseif ($data instanceof MongoMinKey || $data instanceof MongoMaxKey) {
                    $data = get_class($data);
                } else {
                    $result = [];
                    foreach ($data as $name => $value) {
                        $result[$name] = $value;
                    }
                    $data = $result;
                }
     
                if ($data === []) {
                    return new stdClass();
                }
            }
     
            if (is_array($data)) {
                foreach ($data as $key => $value) {
                    if (is_array($value) || is_object($value)) {
                        $data[$key] = $this->processLogData($value);
                    }
                }
            }
     
            return $data;
        }
     
        /**
         * Drops this collection.
         * @throws Exception on failure.
         * @return boolean whether the operation successful.
         */
        public function drop()
        {
            $token = $this->composeLogToken('drop');
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $result = $this->mongoCollection->drop();
                $this->tryResultError($result);
                Yii::endProfile($token, __METHOD__);
     
                return true;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Creates an index on the collection and the specified fields.
         * @param array|string $columns column name or list of column names.
         * If array is given, each element in the array has as key the field name, and as
         * value either 1 for ascending sort, or -1 for descending sort.
         * You can specify field using native numeric key with the field name as a value,
         * in this case ascending sort will be used.
         * For example:
         * ~~~
         * [
         *     'name',
         *     'status' => -1,
         * ]
         * ~~~
         * @param array $options list of options in format: optionName => optionValue.
         * @throws Exception on failure.
         * @return boolean whether the operation successful.
         */
        public function createIndex($columns, $options = [])
        {
            $columns = (array)$columns;
            $keys = $this->normalizeIndexKeys($columns);
            $token = $this->composeLogToken('createIndex', [$keys, $options]);
            $options = array_merge(['w' => 1], $options);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                if (method_exists($this->mongoCollection, 'createIndex')) {
                    $result = $this->mongoCollection->createIndex($keys, $options);
                } else {
                    $result = $this->mongoCollection->ensureIndex($keys, $options);
                }
                $this->tryResultError($result);
                Yii::endProfile($token, __METHOD__);
     
                return true;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Drop indexes for specified column(s).
         * @param string|array $columns column name or list of column names.
         * If array is given, each element in the array has as key the field name, and as
         * value either 1 for ascending sort, or -1 for descending sort.
         * Use value 'text' to specify text index.
         * You can specify field using native numeric key with the field name as a value,
         * in this case ascending sort will be used.
         * For example:
         * ~~~
         * [
         *     'name',
         *     'status' => -1,
         *     'description' => 'text',
         * ]
         * ~~~
         * @throws Exception on failure.
         * @return boolean whether the operation successful.
         */
        public function dropIndex($columns)
        {
            $columns = (array)$columns;
            $keys = $this->normalizeIndexKeys($columns);
            $token = $this->composeLogToken('dropIndex', [$keys]);
            Yii::info($token, __METHOD__);
            try {
                $result = $this->mongoCollection->deleteIndex($keys);
                $this->tryResultError($result);
     
                return true;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Compose index keys from given columns/keys list.
         * @param array $columns raw columns/keys list.
         * @return array normalizes index keys array.
         */
        protected function normalizeIndexKeys($columns)
        {
            $keys = [];
            foreach ($columns as $key => $value) {
                if (is_numeric($key)) {
                    $keys[$value] = MongoCollection::ASCENDING;
                } else {
                    $keys[$key] = $value;
                }
            }
     
            return $keys;
        }
     
        /**
         * Drops all indexes for this collection.
         * @throws Exception on failure.
         * @return integer count of dropped indexes.
         */
        public function dropAllIndexes()
        {
            $token = $this->composeLogToken('dropIndexes');
            Yii::info($token, __METHOD__);
            try {
                $result = $this->mongoCollection->deleteIndexes();
                $this->tryResultError($result);
     
                return $result['nIndexesWas'];
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Returns a cursor for the search results.
         * In order to perform "find" queries use [[Query]] class.
         * @param array $condition query condition
         * @param array $fields fields to be selected
         * @return MongoCursor cursor for the search results
         * @see Query
         */
        public function find($condition = [], $fields = [])
        {
            return $this->mongoCollection->find($this->buildCondition($condition), $fields);
        }
     
        /**
         * Returns a single document.
         * @param array $condition query condition
         * @param array $fields fields to be selected
         * @return array|null the single document. Null is returned if the query results in nothing.
         * @see http://www.php.net/manual/en/mongocollection.findone.php
         */
        public function findOne($condition = [], $fields = [])
        {
            return $this->mongoCollection->findOne($this->buildCondition($condition), $fields);
        }
     
        /**
         * Updates a document and returns it.
         * @param array $condition query condition
         * @param array $update update criteria
         * @param array $fields fields to be returned
         * @param array $options list of options in format: optionName => optionValue.
         * @return array|null the original document, or the modified document when $options['new'] is set.
         * @throws Exception on failure.
         * @see http://www.php.net/manual/en/mongocollection.findandmodify.php
         */
        public function findAndModify($condition, $update, $fields = [], $options = [])
        {
            $condition = $this->buildCondition($condition);
            $token = $this->composeLogToken('findAndModify', [$condition, $update, $fields, $options]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $result = $this->mongoCollection->findAndModify($condition, $update, $fields, $options);
                Yii::endProfile($token, __METHOD__);
     
                return $result;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Inserts new data into collection.
         * @param array|object $data data to be inserted.
         * @param array $options list of options in format: optionName => optionValue.
         * @return MongoId new record id instance.
         * @throws Exception on failure.
         */
        public function insert($data, $options = [])
        {
            $token = $this->composeLogToken('insert', [$data]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $options = array_merge(['w' => 1], $options);
                $this->tryResultError($this->mongoCollection->insert($data, $options));
                Yii::endProfile($token, __METHOD__);
     
                return is_array($data) ? $data['_id'] : $data->_id;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Inserts several new rows into collection.
         * @param array $rows array of arrays or objects to be inserted.
         * @param array $options list of options in format: optionName => optionValue.
         * @return array inserted data, each row will have "_id" key assigned to it.
         * @throws Exception on failure.
         */
        public function batchInsert($rows, $options = [])
        {
            $token = $this->composeLogToken('batchInsert', [$rows]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $options = array_merge(['w' => 1], $options);
                $this->tryResultError($this->mongoCollection->batchInsert($rows, $options));
                Yii::endProfile($token, __METHOD__);
     
                return $rows;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Updates the rows, which matches given criteria by given data.
         * Note: for "multiple" mode Mongo requires explicit strategy "$set" or "$inc"
         * to be specified for the "newData". If no strategy is passed "$set" will be used.
         * @param array $condition description of the objects to update.
         * @param array $newData the object with which to update the matching records.
         * @param array $options list of options in format: optionName => optionValue.
         * @return integer|boolean number of updated documents or whether operation was successful.
         * @throws Exception on failure.
         */
        public function update($condition, $newData, $options = [])
        {
            $condition = $this->buildCondition($condition);
            $options = array_merge(['w' => 1, 'multiple' => true], $options);
            if ($options['multiple']) {
                $keys = array_keys($newData);
                if (!empty($keys) && strncmp('$', $keys[0], 1) !== 0) {
                    $newData = ['$set' => $newData];
                }
            }
            $token = $this->composeLogToken('update', [$condition, $newData, $options]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $result = $this->mongoCollection->update($condition, $newData, $options);
                $this->tryResultError($result);
                Yii::endProfile($token, __METHOD__);
                if (is_array($result) && array_key_exists('n', $result)) {
                    return $result['n'];
                } else {
                    return true;
                }
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Update the existing database data, otherwise insert this data
         * @param array|object $data data to be updated/inserted.
         * @param array $options list of options in format: optionName => optionValue.
         * @return MongoId updated/new record id instance.
         * @throws Exception on failure.
         */
        public function save($data, $options = [])
        {
            $token = $this->composeLogToken('save', [$data]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $options = array_merge(['w' => 1], $options);
                $this->tryResultError($this->mongoCollection->save($data, $options));
                Yii::endProfile($token, __METHOD__);
     
                return is_array($data) ? $data['_id'] : $data->_id;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Removes data from the collection.
         * @param array $condition description of records to remove.
         * @param array $options list of options in format: optionName => optionValue.
         * @return integer|boolean number of updated documents or whether operation was successful.
         * @throws Exception on failure.
         * @see http://www.php.net/manual/en/mongocollection.remove.php
         */
        public function remove($condition = [], $options = [])
        {
            $condition = $this->buildCondition($condition);
            $options = array_merge(['w' => 1, 'justOne' => false], $options);
            $token = $this->composeLogToken('remove', [$condition, $options]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $result = $this->mongoCollection->remove($condition, $options);
                $this->tryResultError($result);
                Yii::endProfile($token, __METHOD__);
                if (is_array($result) && array_key_exists('n', $result)) {
                    return $result['n'];
                } else {
                    return true;
                }
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Returns a list of distinct values for the given column across a collection.
         * @param string $column column to use.
         * @param array $condition query parameters.
         * @return array|boolean array of distinct values, or "false" on failure.
         * @throws Exception on failure.
         */
        public function distinct($column, $condition = [])
        {
            $condition = $this->buildCondition($condition);
            $token = $this->composeLogToken('distinct', [$column, $condition]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $result = $this->mongoCollection->distinct($column, $condition);
                Yii::endProfile($token, __METHOD__);
     
                return $result;
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Performs aggregation using Mongo Aggregation Framework.
         * @param array $pipeline list of pipeline operators, or just the first operator
         * @param array $pipelineOperator additional pipeline operator. You can specify additional
         * pipelines via third argument, fourth argument etc.
         * @return array the result of the aggregation.
         * @throws Exception on failure.
         * @see http://docs.mongodb.org/manual/applications/aggregation/
         */
        public function aggregate($pipeline, $pipelineOperator = [])
        {
            $args = func_get_args();
            $token = $this->composeLogToken('aggregate', $args);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $result = call_user_func_array([$this->mongoCollection, 'aggregate'], $args);
                $this->tryResultError($result);
                Yii::endProfile($token, __METHOD__);
     
                return $result['result'];
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Performs aggregation using Mongo "group" command.
         * @param mixed $keys fields to group by. If an array or non-code object is passed,
         * it will be the key used to group results. If instance of [[MongoCode]] passed,
         * it will be treated as a function that returns the key to group by.
         * @param array $initial Initial value of the aggregation counter object.
         * @param MongoCode|string $reduce function that takes two arguments (the current
         * document and the aggregation to this point) and does the aggregation.
         * Argument will be automatically cast to [[MongoCode]].
         * @param array $options optional parameters to the group command. Valid options include:
         *  - condition - criteria for including a document in the aggregation.
         *  - finalize - function called once per unique key that takes the final output of the reduce function.
         * @return array the result of the aggregation.
         * @throws Exception on failure.
         * @see http://docs.mongodb.org/manual/reference/command/group/
         */
        public function group($keys, $initial, $reduce, $options = [])
        {
            if (!($reduce instanceof MongoCode)) {
                $reduce = new MongoCode((string) $reduce);
            }
            if (array_key_exists('condition', $options)) {
                $options['condition'] = $this->buildCondition($options['condition']);
            }
            if (array_key_exists('finalize', $options)) {
                if (!($options['finalize'] instanceof MongoCode)) {
                    $options['finalize'] = new MongoCode((string) $options['finalize']);
                }
            }
            $token = $this->composeLogToken('group', [$keys, $initial, $reduce, $options]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                // Avoid possible E_DEPRECATED for $options:
                if (empty($options)) {
                    $result = $this->mongoCollection->group($keys, $initial, $reduce);
                } else {
                    $result = $this->mongoCollection->group($keys, $initial, $reduce, $options);
                }
                $this->tryResultError($result);
     
                Yii::endProfile($token, __METHOD__);
                if (array_key_exists('retval', $result)) {
                    return $result['retval'];
                } else {
                    return [];
                }
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Performs aggregation using Mongo "map reduce" mechanism.
         * Note: this function will not return the aggregation result, instead it will
         * write it inside the another Mongo collection specified by "out" parameter.
         * For example:
         *
         * ~~~
         * $customerCollection = Yii::$app->mongo->getCollection('customer');
         * $resultCollectionName = $customerCollection->mapReduce(
         *     'function () {emit(this.status, this.amount)}',
         *     'function (key, values) {return Array.sum(values)}',
         *     'mapReduceOut',
         *     ['status' => 3]
         * );
         * $query = new Query();
         * $results = $query->from($resultCollectionName)->all();
         * ~~~
         *
         * @param MongoCode|string $map function, which emits map data from collection.
         * Argument will be automatically cast to [[MongoCode]].
         * @param MongoCode|string $reduce function that takes two arguments (the map key
         * and the map values) and does the aggregation.
         * Argument will be automatically cast to [[MongoCode]].
         * @param string|array $out output collection name. It could be a string for simple output
         * ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection']).
         * You can pass ['inline' => true] to fetch the result at once without temporary collection usage.
         * @param array $condition criteria for including a document in the aggregation.
         * @param array $options additional optional parameters to the mapReduce command. Valid options include:
         *  - sort - array - key to sort the input documents. The sort key must be in an existing index for this collection.
         *  - limit - the maximum number of documents to return in the collection.
         *  - finalize - function, which follows the reduce method and modifies the output.
         *  - scope - array - specifies global variables that are accessible in the map, reduce and finalize functions.
         *  - jsMode - boolean -Specifies whether to convert intermediate data into BSON format between the execution of the map and reduce functions.
         *  - verbose - boolean - specifies whether to include the timing information in the result information.
         * @return string|array the map reduce output collection name or output results.
         * @throws Exception on failure.
         */
        public function mapReduce($map, $reduce, $out, $condition = [], $options = [])
        {
            if (!($map instanceof MongoCode)) {
                $map = new MongoCode((string) $map);
            }
            if (!($reduce instanceof MongoCode)) {
                $reduce = new MongoCode((string) $reduce);
            }
            $command = [
                'mapReduce' => $this->getName(),
                'map' => $map,
                'reduce' => $reduce,
                'out' => $out
            ];
            if (!empty($condition)) {
                $command['query'] = $this->buildCondition($condition);
            }
            if (array_key_exists('finalize', $options)) {
                if (!($options['finalize'] instanceof MongoCode)) {
                    $options['finalize'] = new MongoCode((string) $options['finalize']);
                }
            }
            if (!empty($options)) {
                $command = array_merge($command, $options);
            }
            $token = $this->composeLogToken('mapReduce', [$map, $reduce, $out]);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $command = array_merge(['mapReduce' => $this->getName()], $command);
                $result = $this->mongoCollection->db->command($command);
                $this->tryResultError($result);
                Yii::endProfile($token, __METHOD__);
     
                return array_key_exists('results', $result) ? $result['results'] : $result['result'];
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Performs full text search.
         * @param string $search string of terms that MongoDB parses and uses to query the text index.
         * @param array $condition criteria for filtering a results list.
         * @param array $fields list of fields to be returned in result.
         * @param array $options additional optional parameters to the mapReduce command. Valid options include:
         *  - limit - the maximum number of documents to include in the response (by default 100).
         *  - language - the language that determines the list of stop words for the search
         *    and the rules for the stemmer and tokenizer. If not specified, the search uses the default
         *    language of the index.
         * @return array the highest scoring documents, in descending order by score.
         * @throws Exception on failure.
         */
        public function fullTextSearch($search, $condition = [], $fields = [], $options = [])
        {
            $command = [
                'search' => $search
            ];
            if (!empty($condition)) {
                $command['filter'] = $this->buildCondition($condition);
            }
            if (!empty($fields)) {
                $command['project'] = $fields;
            }
            if (!empty($options)) {
                $command = array_merge($command, $options);
            }
            $token = $this->composeLogToken('text', $command);
            Yii::info($token, __METHOD__);
            try {
                Yii::beginProfile($token, __METHOD__);
                $command = array_merge(['text' => $this->getName()], $command);
                $result = $this->mongoCollection->db->command($command);
                $this->tryResultError($result);
                Yii::endProfile($token, __METHOD__);
     
                return $result['results'];
            } catch (Exception $e) {
                Yii::endProfile($token, __METHOD__);
                throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
            }
        }
     
        /**
         * Checks if command execution result ended with an error.
         * @param mixed $result raw command execution result.
         * @throws Exception if an error occurred.
         */
        protected function tryResultError($result)
        {
            if (is_array($result)) {
                if (!empty($result['errmsg'])) {
                    $errorMessage = $result['errmsg'];
                } elseif (!empty($result['err'])) {
                    $errorMessage = $result['err'];
                }
                if (isset($errorMessage)) {
                    if (array_key_exists('code', $result)) {
                        $errorCode = (int) $result['code'];
                    } elseif (array_key_exists('ok', $result)) {
                        $errorCode = (int) $result['ok'];
                    } else {
                        $errorCode = 0;
                    }
                    throw new Exception($errorMessage, $errorCode);
                }
            } elseif (!$result) {
                throw new Exception('Unknown error, use "w=1" option to enable error tracking');
            }
        }
     
        /**
         * Throws an exception if there was an error on the last operation.
         * @throws Exception if an error occurred.
         */
        protected function tryLastError()
        {
            $this->tryResultError($this->getLastError());
        }
     
        /**
         * Converts "yiidb*" quick condition keyword into actual Mongo condition keyword.
         * @param string $key raw condition key.
         * @return string actual key.
         */
        protected function normalizeConditionKeyword($key)
        {
            static $map = [
                'AND' => '$and',
                'OR' => '$or',
                'IN' => '$in',
                'NOT IN' => '$nin',
            ];
            $matchKey = strtoupper($key);
            if (array_key_exists($matchKey, $map)) {
                return $map[$matchKey];
            } else {
                return $key;
            }
        }
     
        /**
         * Converts given value into [[MongoId]] instance.
         * If array given, each element of it will be processed.
         * @param mixed $rawId raw id(s).
         * @return array|MongoId normalized id(s).
         */
        protected function ensureMongoId($rawId)
        {
            if (is_array($rawId)) {
                $result = [];
                foreach ($rawId as $key => $value) {
                    $result[$key] = $this->ensureMongoId($value);
                }
     
                return $result;
            } elseif (is_object($rawId)) {
                if ($rawId instanceof MongoId) {
                    return $rawId;
                } else {
                    $rawId = (string) $rawId;
                }
            }
            try {
                $mongoId = new MongoId($rawId);
            } catch (MongoException $e) {
                // invalid id format
                $mongoId = $rawId;
            }
     
            return $mongoId;
        }
         
         
        public function buildGtCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
            list($column, $value) = $operands;
            return [$column => ['?gt' => $value]];
        }
         
         
        public function buildGteCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
            list($column, $value) = $operands;
            return [$column => ['?gte' => $value]];
        }
         
         
        public function buildLtCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
            list($column, $value) = $operands;
            return [$column => ['?lt' => $value]];
        }
         
        public function buildLteCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
            list($column, $value) = $operands;
            return [$column => ['?lte' => $value]];
        }
     
        /**
         * Parses the condition specification and generates the corresponding Mongo condition.
         * @param array $condition the condition specification. Please refer to [[Query::where()]]
         * on how to specify a condition.
         * @return array the generated Mongo condition
         * @throws InvalidParamException if the condition is in bad format
         */
        public function buildCondition($condition)
        {
            static $builders = [
                'NOT' => 'buildNotCondition',
                'AND' => 'buildAndCondition',
                'OR' => 'buildOrCondition',
                'BETWEEN' => 'buildBetweenCondition',
                'NOT BETWEEN' => 'buildBetweenCondition',
                'IN' => 'buildInCondition',
                'NOT IN' => 'buildInCondition',
                'REGEX' => 'buildRegexCondition',
                'LIKE' => 'buildLikeCondition',
                '>'  => 'buildGtCondition',
                '>=' => 'buildGteCondition',
                '<'  => 'buildLtCondition',
                '<='     => 'buildLteCondition',
            ];
     
            if (!is_array($condition)) {
                throw new InvalidParamException('Condition should be an array.');
            } elseif (empty($condition)) {
                return [];
            }
            if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
                $operator = strtoupper($condition[0]);
                if (isset($builders[$operator])) {
                    $method = $builders[$operator];
                    array_shift($condition);
     
                    return $this->$method($operator, $condition);
                } else {
                    throw new InvalidParamException('Found unknown operator in query: ' . $operator);
                }
            } else {
                // hash format: 'column1' => 'value1', 'column2' => 'value2', ...
                return $this->buildHashCondition($condition);
            }
        }
     
        /**
         * Creates a condition based on column-value pairs.
         * @param array $condition the condition specification.
         * @return array the generated Mongo condition.
         */
        public function buildHashCondition($condition)
        {
            $result = [];
            foreach ($condition as $name => $value) {
                if (strncmp('$', $name, 1) === 0) {
                    // Native Mongo condition:
                    $result[$name] = $value;
                } else {
                    if (is_array($value)) {
                        if (array_key_exists(0, $value)) {
                            // Quick IN condition:
                            $result = array_merge($result, $this->buildInCondition('IN', [$name, $value]));
                        } else {
                            // Mongo complex condition:
                            $result[$name] = $value;
                        }
                    } else {
                        // Direct match:
                        if ($name == '_id') {
                            $value = $this->ensureMongoId($value);
                        }
                        $result[$name] = $value;
                    }
                }
            }
     
            return $result;
        }
     
        /**
         * Composes `NOT` condition.
         * @param string $operator the operator to use for connecting the given operands
         * @param array $operands the Mongo conditions to connect.
         * @return array the generated Mongo condition.
         * @throws InvalidParamException if wrong number of operands have been given.
         */
        public function buildNotCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
     
            list($name, $value) = $operands;
     
            $result = [];
            if (is_array($value)) {
                $result[$name] = ['$not' => $this->buildCondition($value)];
            } else {
                if ($name == '_id') {
                    $value = $this->ensureMongoId($value);
                }
                $result[$name] = ['$ne' => $value];
            }
     
            return $result;
        }
     
        /**
         * Connects two or more conditions with the `AND` operator.
         * @param string $operator the operator to use for connecting the given operands
         * @param array $operands the Mongo conditions to connect.
         * @return array the generated Mongo condition.
         */
        public function buildAndCondition($operator, $operands)
        {
            $operator = $this->normalizeConditionKeyword($operator);
            $parts = [];
            foreach ($operands as $operand) {
                $parts[] = $this->buildCondition($operand);
            }
     
            return [$operator => $parts];
        }
     
        /**
         * Connects two or more conditions with the `OR` operator.
         * @param string $operator the operator to use for connecting the given operands
         * @param array $operands the Mongo conditions to connect.
         * @return array the generated Mongo condition.
         */
        public function buildOrCondition($operator, $operands)
        {
            $operator = $this->normalizeConditionKeyword($operator);
            $parts = [];
            foreach ($operands as $operand) {
                $parts[] = $this->buildCondition($operand);
            }
     
            return [$operator => $parts];
        }
     
        /**
         * Creates an Mongo condition, which emulates the `BETWEEN` operator.
         * @param string $operator the operator to use
         * @param array $operands the first operand is the column name. The second and third operands
         * describe the interval that column value should be in.
         * @return array the generated Mongo condition.
         * @throws InvalidParamException if wrong number of operands have been given.
         */
        public function buildBetweenCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1], $operands[2])) {
                throw new InvalidParamException("Operator '$operator' requires three operands.");
            }
            list($column, $value1, $value2) = $operands;
            if (strncmp('NOT', $operator, 3) === 0) {
                return [
                    $column => [
                        '$lt' => $value1,
                        '$gt' => $value2,
                    ]
                ];
            } else {
                return [
                    $column => [
                        '$gte' => $value1,
                        '$lte' => $value2,
                    ]
                ];
            }
        }
     
        /**
         * Creates an Mongo condition with the `IN` operator.
         * @param string $operator the operator to use (e.g. `IN` or `NOT IN`)
         * @param array $operands the first operand is the column name. If it is an array
         * a composite IN condition will be generated.
         * The second operand is an array of values that column value should be among.
         * @return array the generated Mongo condition.
         * @throws InvalidParamException if wrong number of operands have been given.
         */
        public function buildInCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
     
            list($column, $values) = $operands;
     
            $values = (array) $values;
     
            if (!is_array($column)) {
                $columns = [$column];
                $values = [$column => $values];
            } elseif (count($column) < 2) {
                $columns = $column;
                $values = [$column[0] => $values];
            } else {
                $columns = $column;
            }
     
            $operator = $this->normalizeConditionKeyword($operator);
            $result = [];
            foreach ($columns as $column) {
                if ($column == '_id') {
                    $inValues = $this->ensureMongoId($values[$column]);
                } else {
                    $inValues = $values[$column];
                }
                $result[$column][$operator] = array_values($inValues);
            }
     
            return $result;
        }
     
        /**
         * Creates a Mongo regular expression condition.
         * @param string $operator the operator to use
         * @param array $operands the first operand is the column name.
         * The second operand is a single value that column value should be compared with.
         * @return array the generated Mongo condition.
         * @throws InvalidParamException if wrong number of operands have been given.
         */
        public function buildRegexCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
            list($column, $value) = $operands;
            if (!($value instanceof MongoRegex)) {
                $value = new MongoRegex($value);
            }
     
            return [$column => $value];
        }
     
        /**
         * Creates a Mongo condition, which emulates the `LIKE` operator.
         * @param string $operator the operator to use
         * @param array $operands the first operand is the column name.
         * The second operand is a single value that column value should be compared with.
         * @return array the generated Mongo condition.
         * @throws InvalidParamException if wrong number of operands have been given.
         */
        public function buildLikeCondition($operator, $operands)
        {
            if (!isset($operands[0], $operands[1])) {
                throw new InvalidParamException("Operator '$operator' requires two operands.");
            }
            list($column, $value) = $operands;
            if (!($value instanceof MongoRegex)) {
                $value = new MongoRegex('/' . preg_quote($value) . '/i');
            }
     
            return [$column => $value];
        }
    }
    

     

     
  • 相关阅读:
    DOS下读取4GB内存
    开始了
    PHP函数补完:var_export()
    php 调试相关
    css选择符
    javascript的urlencode
    用CSS代码绘制三角形 纯CSS绘制三角形的代码
    Jquery 操作Cookie
    提高PHP编程效率的53种方法
    执行phpinfo();时提示:date_default_timezone_set()
  • 原文地址:https://www.cnblogs.com/sandea/p/7058155.html
Copyright © 2020-2023  润新知