diff --git a/framework/yii/db/Query.php b/framework/yii/db/Query.php index 50ed105..20d13a8 100644 --- a/framework/yii/db/Query.php +++ b/framework/yii/db/Query.php @@ -42,7 +42,7 @@ class Query extends Component implements QueryInterface /** * @var array the columns being selected. For example, `['id', 'name']`. - * This is used to construct the SELECT clause in a SQL statement. If not set, if means selecting all columns. + * This is used to construct the SELECT clause in a SQL statement. If not set, it means selecting all columns. * @see select() */ public $select; diff --git a/framework/yii/elasticsearch/ActiveQuery.php b/framework/yii/elasticsearch/ActiveQuery.php index 4e1ecd2..fa74ba8 100644 --- a/framework/yii/elasticsearch/ActiveQuery.php +++ b/framework/yii/elasticsearch/ActiveQuery.php @@ -85,7 +85,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface { $command = $this->createCommand($db); $result = $command->queryAll(); - if ($result['total'] == 0) { + if (empty($result['hits'])) { return []; } $models = $this->createModels($result['hits']); @@ -111,19 +111,16 @@ class ActiveQuery extends Query implements ActiveQueryInterface */ public function one($db = null) { - $command = $this->createCommand($db); - $result = $command->queryOne(); - if ($result['total'] == 0 || empty($result['hits'])) { + if (($result = parent::one($db)) === false) { return null; } if ($this->asArray) { - $first = reset($result['hits']); - $model = $first['_source']; - $model['primaryKey'] = $first['_id']; + $model = $result['_source']; + $model['primaryKey'] = $result['_id']; } else { /** @var ActiveRecord $class */ $class = $this->modelClass; - $model = $class::create(reset($result['hits'])); + $model = $class::create($result); } if (!empty($this->with)) { $models = [$model]; @@ -132,24 +129,4 @@ class ActiveQuery extends Query implements ActiveQueryInterface } return $model; } - - /** - * Returns the query result as a scalar value. - * The value returned will be the specified attribute in the first record of the query results. - * @param string $attribute name of the attribute to select - * @param Connection $db the database connection used to execute the query. - * If this parameter is not given, the `db` application component will be used. - * @return string the value of the specified attribute in the first record of the query result. - * Null is returned if the query result is empty. - */ - public function scalar($attribute, $db = null) - { - $record = $this->one($db); - if ($record !== null) { - return $record->$attribute; - } else { - return null; - } - } - } diff --git a/framework/yii/elasticsearch/ActiveRecord.php b/framework/yii/elasticsearch/ActiveRecord.php index 3ea130f..8cd0e34 100644 --- a/framework/yii/elasticsearch/ActiveRecord.php +++ b/framework/yii/elasticsearch/ActiveRecord.php @@ -64,7 +64,6 @@ class ActiveRecord extends \yii\db\ActiveRecord * @param mixed $primaryKey the primaryKey value * @param array $options options given in this parameter are passed to elasticsearch * as request URI parameters. - * * Please refer to the [elasticsearch documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-get.html) * for more details on these options. * @return static|null The record instance or null if it was not found. diff --git a/framework/yii/elasticsearch/Command.php b/framework/yii/elasticsearch/Command.php index b19d763..2712583 100644 --- a/framework/yii/elasticsearch/Command.php +++ b/framework/yii/elasticsearch/Command.php @@ -59,12 +59,6 @@ class Command extends Component return Json::decode($response->getBody(true))['hits']; } - public function queryOne($options = []) - { - $options['size'] = 1; - return $this->queryAll($options); - } - public function queryCount($options = []) { $options['search_type'] = 'count'; diff --git a/framework/yii/elasticsearch/Query.php b/framework/yii/elasticsearch/Query.php index c430608..4b56721 100644 --- a/framework/yii/elasticsearch/Query.php +++ b/framework/yii/elasticsearch/Query.php @@ -23,19 +23,36 @@ class Query extends Component implements QueryInterface use QueryTrait; /** - * @var array the columns being selected. For example, `array('id', 'name')`. - * This is used to construct the SELECT clause in a SQL statement. If not set, if means selecting all columns. - * @see select() + * @var array the fields being retrieved from the documents. For example, `['id', 'name']`. + * If not set, it means retrieving all fields. An empty array will result in no fields being + * retrieved. This means that only the primaryKey of a record will be available in the result. + * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-fields.html#search-request-fields + * @see fields() + */ + public $fields; + /** + * @var string|array The index to retrieve data from. This can be a string representing a single index + * or a an array of multiple indexes. If this is not set, indexes are being queried. + * @see from() */ - public $select; // TODO fields - public $index; - + /** + * @var string|array The type to retrieve data from. This can be a string representing a single type + * or a an array of multiple types. If this is not set, all types are being queried. + * @see from() + */ public $type; + /** + * @var integer A search timeout, bounding the search request to be executed within the specified time value + * and bail with the hits accumulated up to that point when expired. Defaults to no timeout. + * @see timeout() + * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-body.html#_parameters_3 + */ + public $timeout; /** * Creates a DB command that can be used to execute this query. - * @param Connection $db the database connection used to generate the SQL statement. + * @param Connection $db the database connection used to execute the query. * If this parameter is not given, the `elasticsearch` application component will be used. * @return Command the created DB command instance. */ @@ -51,22 +68,28 @@ class Query extends Component implements QueryInterface /** * Executes the query and returns all results as an array. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return array the query results. If the query results in nothing, an empty array will be returned. */ public function all($db = null) { - $rows = $this->createCommand($db)->queryAll(); - if ($this->indexBy === null) { + $rows = $this->createCommand($db)->queryAll()['hits']; + if ($this->indexBy === null && $this->fields === null) { return $rows; } $result = []; - foreach ($rows as $row) { - if (is_string($this->indexBy)) { - $key = $row[$this->indexBy]; - } else { - $key = call_user_func($this->indexBy, $row); + foreach ($rows as $key => $row) { + if ($this->fields !== null) { + $row['_source'] = isset($row['fields']) ? $row['fields'] : []; + unset($row['fields']); + } + if ($this->indexBy !== null) { + if (is_string($this->indexBy)) { + $key = $row['_source'][$this->indexBy]; + } else { + $key = call_user_func($this->indexBy, $row); + } } $result[$key] = $row; } @@ -75,30 +98,40 @@ class Query extends Component implements QueryInterface /** * Executes the query and returns a single row of result. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query * results in nothing. */ public function one($db = null) { - return $this->createCommand($db)->queryOne(); + $options['size'] = 1; + $result = $this->createCommand($db)->queryAll($options); + if (empty($result['hits'])) { + return false; + } + $record = reset($result['hits']); + if ($this->fields !== null) { + $record['_source'] = isset($record['fields']) ? $record['fields'] : []; + unset($record['fields']); + } + return $record; } /** * Returns the query result as a scalar value. - * The value returned will be the specified attribute in the first record of the query results. - * @param string $attribute name of the attribute to select + * The value returned will be the specified field in the first document of the query results. + * @param string $field name of the attribute to select * @param Connection $db the database connection used to execute the query. - * If this parameter is not given, the `db` application component will be used. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return string the value of the specified attribute in the first record of the query result. - * Null is returned if the query result is empty. + * Null is returned if the query result is empty or the field does not exist. */ - public function scalar($attribute, $db = null) + public function scalar($field, $db = null) { - $record = $this->one($db); - if ($record !== null) { - return $record->$attribute; + $record = self::one($db); + if ($record !== false && isset($record['_source'][$field])) { + return $record['_source'][$field]; } else { return null; } @@ -106,20 +139,27 @@ class Query extends Component implements QueryInterface /** * Executes the query and returns the first column of the result. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. + * @param string $field the field to query over + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return array the first column of the query result. An empty array is returned if the query results in nothing. */ - public function column($db = null) + public function column($field, $db = null) { - return $this->createCommand($db)->queryColumn(); + $query = clone $this; + $rows = $query->fields([$field])->createCommand($db)->queryAll()['hits']; + $result = []; + foreach ($rows as $row) { + $result[] = $row['fields'][$field]; + } + return $result; } /** * Returns the number of records. * @param string $q the COUNT expression. This parameter is ignored by this implementation. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given (or null), the `db` application component will be used. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return integer number of records */ public function count($q = '*', $db = null) @@ -138,8 +178,8 @@ class Query extends Component implements QueryInterface * Returns the sum of the specified column values. * @param string $q the column name or expression. * Make sure you properly quote column names in the expression. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return integer the sum of the specified column values */ public function sum($q, $db = null) @@ -152,8 +192,8 @@ class Query extends Component implements QueryInterface * Returns the average of the specified column values. * @param string $q the column name or expression. * Make sure you properly quote column names in the expression. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return integer the average of the specified column values. */ public function average($q, $db = null) @@ -166,8 +206,8 @@ class Query extends Component implements QueryInterface * Returns the minimum of the specified column values. * @param string $q the column name or expression. * Make sure you properly quote column names in the expression. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return integer the minimum of the specified column values. */ public function min($q, $db = null) @@ -180,8 +220,8 @@ class Query extends Component implements QueryInterface * Returns the maximum of the specified column values. * @param string $q the column name or expression. * Make sure you properly quote column names in the expression. - * @param Connection $db the database connection used to generate the SQL statement. - * If this parameter is not given, the `db` application component will be used. + * @param Connection $db the database connection used to execute the query. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return integer the maximum of the specified column values. */ public function max($q, $db = null) @@ -193,7 +233,7 @@ class Query extends Component implements QueryInterface /** * Returns a value indicating whether the query result contains any row of data. * @param Connection $db the database connection used to execute the query. - * If this parameter is not given, the `db` application component will be used. + * If this parameter is not given, the `elasticsearch` application component will be used. * @return boolean whether the query result contains any row of data. */ public function exists($db = null) @@ -213,9 +253,43 @@ class Query extends Component implements QueryInterface // TODO http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-delete-by-query.html } + /** + * Sets the index and type to retrieve documents from. + * @param string|array $index The index to retrieve data from. This can be a string representing a single index + * or a an array of multiple indexes. If this is `null` it means that all indexes are being queried. + * @param string|array $type The type to retrieve data from. This can be a string representing a single type + * or a an array of multiple types. If this is `null` it means that all types are being queried. + * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-search.html#search-multi-index-type + */ public function from($index, $type = null) { $this->index = $index; $this->type = $type; } + + /** + * Sets the fields to retrieve from the documents. + * @param array $fields the fields to be selected. + * @return static the query object itself + * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-fields.html + */ + public function fields($fields) + { + $this->fields = $fields; + return $this; + } + + /** + * Sets the search timeout. + * @param integer $timeout A search timeout, bounding the search request to be executed within the specified time value + * and bail with the hits accumulated up to that point when expired. Defaults to no timeout. + * @return static the query object itself + * @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-request-body.html#_parameters_3 + */ + public function timeout($timeout) + { + $this->timeout = $timeout; + return $this; + } + } \ No newline at end of file diff --git a/framework/yii/elasticsearch/QueryBuilder.php b/framework/yii/elasticsearch/QueryBuilder.php index 4280fe6..005d053 100644 --- a/framework/yii/elasticsearch/QueryBuilder.php +++ b/framework/yii/elasticsearch/QueryBuilder.php @@ -45,7 +45,7 @@ class QueryBuilder extends \yii\base\Object public function build($query) { $searchQuery = array(); - $this->buildSelect($searchQuery, $query->select); + $this->buildFields($searchQuery, $query->fields); // $this->buildFrom($searchQuery, $query->from); $this->buildCondition($searchQuery, $query->where); $this->buildOrderBy($searchQuery, $query->orderBy); @@ -113,9 +113,9 @@ class QueryBuilder extends \yii\base\Object * @param string $selectOption * @return string the SELECT clause built from [[query]]. */ - public function buildSelect(&$query, $columns) + public function buildFields(&$query, $columns) { - if (empty($columns)) { + if ($columns === null) { return; } foreach ($columns as $i => $column) {