Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
d442f056
Commit
d442f056
authored
Oct 07, 2013
by
Carsten Brandt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
elasticsearch find by simple condition
parent
955bf7da
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
653 additions
and
404 deletions
+653
-404
ActiveQuery.php
framework/yii/elasticsearch/ActiveQuery.php
+77
-394
ActiveRecord.php
framework/yii/elasticsearch/ActiveRecord.php
+1
-1
Command.php
framework/yii/elasticsearch/Command.php
+121
-0
Connection.php
framework/yii/elasticsearch/Connection.php
+17
-0
Query.php
framework/yii/elasticsearch/Query.php
+410
-0
QueryBuilder.php
framework/yii/elasticsearch/QueryBuilder.php
+7
-2
Customer.php
tests/unit/data/ar/elasticsearch/Customer.php
+1
-1
ActiveRecordTest.php
tests/unit/framework/elasticsearch/ActiveRecordTest.php
+19
-6
No files found.
framework/yii/elasticsearch/ActiveQuery.php
View file @
d442f056
...
@@ -47,20 +47,9 @@ use yii\helpers\Json;
...
@@ -47,20 +47,9 @@ use yii\helpers\Json;
* @author Carsten Brandt <mail@cebe.cc>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0
* @since 2.0
*/
*/
class
ActiveQuery
extends
\yii\base\Component
class
ActiveQuery
extends
Query
{
{
/**
/**
* Sort ascending
* @see orderBy
*/
const
SORT_ASC
=
false
;
/**
* Sort descending
* @see orderBy
*/
const
SORT_DESC
=
true
;
/**
* @var string the name of the ActiveRecord class.
* @var string the name of the ActiveRecord class.
*/
*/
public
$modelClass
;
public
$modelClass
;
...
@@ -69,190 +58,44 @@ class ActiveQuery extends \yii\base\Component
...
@@ -69,190 +58,44 @@ class ActiveQuery extends \yii\base\Component
*/
*/
public
$with
;
public
$with
;
/**
/**
* @var string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row or model data. For more details, see [[indexBy()]].
*/
public
$indexBy
;
/**
* @var boolean whether to return each record as an array. If false (default), an object
* @var boolean whether to return each record as an array. If false (default), an object
* of [[modelClass]] will be created to represent each record.
* of [[modelClass]] will be created to represent each record.
*/
*/
public
$asArray
;
public
$asArray
;
/**
* @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()
*/
public
$select
;
/**
* @var array the query condition.
* @see where()
*/
public
$where
;
/**
* @var integer maximum number of records to be returned. If not set or less than 0, it means no limit. TODO infinite possible in ES?
*/
public
$limit
=
10
;
/**
* @var integer zero-based offset from where the records are to be returned.
* If not set, it means starting from the beginning.
* If less than zero it means starting n elements from the end.
*/
public
$offset
;
/**
* @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [[ActiveQuery::SORT_ASC]] or [[ActiveQuery::SORT_DESC]]. The array may also contain [[Expression]] objects.
* If that is the case, the expressions will be converted into strings without any change.
*/
public
$orderBy
;
/**
* PHP magic method.
* This method allows calling static method defined in [[modelClass]] via this query object.
* It is mainly implemented for supporting the feature of scope.
* @param string $name the method name to be called
* @param array $params the parameters passed to the method
* @return mixed the method return result
*/
public
function
__call
(
$name
,
$params
)
{
if
(
method_exists
(
$this
->
modelClass
,
$name
))
{
array_unshift
(
$params
,
$this
);
call_user_func_array
(
array
(
$this
->
modelClass
,
$name
),
$params
);
return
$this
;
}
else
{
return
parent
::
__call
(
$name
,
$params
);
}
}
/**
* Executes query and returns all results as an array.
* @return array the query results. If the query results in nothing, an empty array will be returned.
*/
public
function
all
()
{
// TODO add support for orderBy
$data
=
$this
->
executeScript
(
'All'
);
$rows
=
array
();
print_r
(
$data
);
foreach
(
$data
as
$dataRow
)
{
$row
=
$dataRow
[
'_source'
];
$row
[
'id'
]
=
$dataRow
[
'_id'
];
$rows
[]
=
$row
;
}
if
(
!
empty
(
$rows
))
{
$models
=
$this
->
createModels
(
$rows
);
if
(
!
empty
(
$this
->
with
))
{
$this
->
populateRelations
(
$models
,
$this
->
with
);
}
return
$models
;
}
else
{
return
array
();
}
}
/**
* Executes query and returns a single row of result.
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
* the query result may be either an array or an ActiveRecord object. Null will be returned
* if the query results in nothing.
*/
public
function
one
()
{
// TODO add support for orderBy
$data
=
$this
->
executeScript
(
'One'
);
if
(
!
isset
(
$data
[
'_source'
]))
{
return
null
;
}
$row
=
$data
[
'_source'
];
$row
[
'id'
]
=
$data
[
'_id'
];
if
(
$this
->
asArray
)
{
$model
=
$row
;
}
else
{
/** @var $class ActiveRecord */
$class
=
$this
->
modelClass
;
$model
=
$class
::
create
(
$row
);
}
if
(
!
empty
(
$this
->
with
))
{
$models
=
array
(
$model
);
$this
->
populateRelations
(
$models
,
$this
->
with
);
$model
=
$models
[
0
];
}
return
$model
;
}
/**
* Executes the query and returns the first column of the result.
* @param string $column name of the column to select
* @return array the first column of the query result. An empty array is returned if the query results in nothing.
*/
public
function
column
(
$column
)
{
// TODO add support for indexBy and orderBy
return
$this
->
executeScript
(
'Column'
,
$column
);
}
/**
/**
*
Returns the number of records
.
*
Creates a DB command that can be used to execute this query
.
* @param
string $q the COUNT expression. Defaults to '*'
.
* @param
Connection $db the DB connection used to create the DB command
.
*
Make sure you properly quote column names
.
*
If null, the DB connection returned by [[modelClass]] will be used
.
* @return
integer number of records
* @return
Command the created DB command instance.
*/
*/
public
function
c
ount
(
)
public
function
c
reateCommand
(
$db
=
null
)
{
{
if
(
$this
->
offset
===
null
&&
$this
->
limit
===
null
&&
$this
->
where
===
null
)
{
/** @var $modelClass ActiveRecord */
$modelClass
=
$this
->
modelClass
;
$modelClass
=
$this
->
modelClass
;
/** @var Connection $db */
if
(
$db
===
null
)
{
$db
=
$modelClass
::
getDb
();
$db
=
$modelClass
::
getDb
();
return
$db
->
executeCommand
(
'LLEN'
,
array
(
$modelClass
::
tableName
()));
}
else
{
return
$this
->
executeScript
(
'Count'
);
}
}
/**
* Returns the number of records.
* @param string $column the column to sum up
* @return integer number of records
*/
public
function
sum
(
$column
)
{
return
$this
->
executeScript
(
'Sum'
,
$column
);
}
}
/**
$index
=
$modelClass
::
indexName
();
* Returns the average of the specified column values.
$type
=
$modelClass
::
indexType
();
* @param string $column the column name or expression.
if
(
is_array
(
$this
->
where
)
&&
Activerecord
::
isPrimaryKey
(
array_keys
(
$this
->
where
)))
{
* Make sure you properly quote column names in the expression.
// TODO what about mixed queries?
* @return integer the average of the specified column values.
$query
=
array
();
*/
foreach
((
array
)
reset
(
$this
->
where
)
as
$pk
)
{
public
function
average
(
$column
)
$doc
=
array
(
{
'_id'
=>
$pk
,
return
$this
->
executeScript
(
'Average'
,
$column
);
);
$db
->
getQueryBuilder
()
->
buildSelect
(
$doc
,
$this
->
select
);
$query
[
'docs'
][]
=
$doc
;
}
}
$command
=
$db
->
createCommand
(
$query
,
$index
,
$type
);
/**
$command
->
api
=
'_mget'
;
* Returns the minimum of the specified column values.
return
$command
;
* @param string $column the column name or expression.
}
else
{
* Make sure you properly quote column names in the expression.
$query
=
$db
->
getQueryBuilder
()
->
build
(
$this
);
* @return integer the minimum of the specified column values.
return
$db
->
createCommand
(
$query
,
$index
,
$type
);
*/
public
function
min
(
$column
)
{
return
$this
->
executeScript
(
'Min'
,
$column
);
}
}
/**
* Returns the maximum of the specified column values.
* @param string $column the column name or expression.
* Make sure you properly quote column names in the expression.
* @return integer the maximum of the specified column values.
*/
public
function
max
(
$column
)
{
return
$this
->
executeScript
(
'Max'
,
$column
);
}
}
/**
/**
...
@@ -408,109 +251,84 @@ class ActiveQuery extends \yii\base\Component
...
@@ -408,109 +251,84 @@ class ActiveQuery extends \yii\base\Component
// TODO: refactor. code below here is all duplicated from yii/db/ActiveQuery and yii/db/Query
// TODO: refactor. code below here is all duplicated from yii/db/ActiveQuery and yii/db/Query
/**
/**
* Sets the [[asArray]] property.
* PHP magic method.
* @param boolean $value whether to return the query results in terms of arrays instead of Active Records.
* This method allows calling static method defined in [[modelClass]] via this query object.
* @return ActiveQuery the query object itself
* It is mainly implemented for supporting the feature of scope.
* @param string $name the method name to be called
* @param array $params the parameters passed to the method
* @return mixed the method return result
*/
*/
public
function
asArray
(
$value
=
true
)
public
function
__call
(
$name
,
$params
)
{
{
$this
->
asArray
=
$value
;
if
(
method_exists
(
$this
->
modelClass
,
$name
))
{
array_unshift
(
$params
,
$this
);
call_user_func_array
(
array
(
$this
->
modelClass
,
$name
),
$params
);
return
$this
;
return
$this
;
}
else
{
return
parent
::
__call
(
$name
,
$params
);
}
}
}
/**
/**
* Sets the SELECT part of the query.
* Executes query and returns all results as an array.
* @param string|array $columns the columns to be selected.
* @param Connection $db the DB connection used to create the DB command.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* If null, the DB connection returned by [[modelClass]] will be used.
* Columns can contain table prefixes (e.g. "tbl_user.id") and/or column aliases (e.g. "tbl_user.id AS user_id").
* @return array the query results. If the query results in nothing, an empty array will be returned.
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
*/
*/
public
function
select
(
$columns
)
public
function
all
(
$db
=
null
)
{
{
if
(
!
is_array
(
$columns
))
{
$command
=
$this
->
createCommand
(
$db
);
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
$rows
=
$command
->
queryAll
();
if
(
!
empty
(
$rows
))
{
$models
=
$this
->
createModels
(
$rows
);
if
(
!
empty
(
$this
->
with
))
{
$this
->
populateRelations
(
$models
,
$this
->
with
);
}
}
$this
->
select
=
$columns
;
return
$models
;
return
$this
;
}
else
{
return
array
();
}
}
/**
* Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return ActiveQuery the query object itself
* @see addOrderBy()
*/
public
function
orderBy
(
$columns
)
{
$this
->
orderBy
=
$this
->
normalizeOrderBy
(
$columns
);
return
$this
;
}
}
/**
/**
* Adds additional ORDER BY columns to the query.
* Executes query and returns a single row of result.
* @param string|array $columns the columns (and the directions) to be ordered by.
* @param Connection $db the DB connection used to create the DB command.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* If null, the DB connection returned by [[modelClass]] will be used.
* (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* @return ActiveRecord|array|null a single row of query result. Depending on the setting of [[asArray]],
* The method will automatically quote the column names unless a column contains some parenthesis
* the query result may be either an array or an ActiveRecord object. Null will be returned
* (which means the column contains a DB expression).
* if the query results in nothing.
* @return ActiveQuery the query object itself
* @see orderBy()
*/
*/
public
function
addOrderBy
(
$columns
)
public
function
one
(
$db
=
null
)
{
{
$columns
=
$this
->
normalizeOrderBy
(
$columns
);
$command
=
$this
->
createCommand
(
$db
);
if
(
$this
->
orderBy
===
null
)
{
$row
=
$command
->
queryOne
();
$this
->
orderBy
=
$columns
;
if
(
$row
!==
false
)
{
if
(
$this
->
asArray
)
{
$model
=
$row
;
}
else
{
}
else
{
$this
->
orderBy
=
array_merge
(
$this
->
orderBy
,
$columns
);
/** @var $class ActiveRecord */
$class
=
$this
->
modelClass
;
$model
=
$class
::
create
(
$row
);
}
}
return
$this
;
if
(
!
empty
(
$this
->
with
))
{
$models
=
array
(
$model
);
$this
->
populateRelations
(
$models
,
$this
->
with
);
$model
=
$models
[
0
];
}
}
return
$model
;
protected
function
normalizeOrderBy
(
$columns
)
{
throw
new
NotSupportedException
(
'orderBy is currently not supported'
);
if
(
is_array
(
$columns
))
{
return
$columns
;
}
else
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
$result
=
array
();
foreach
(
$columns
as
$column
)
{
if
(
preg_match
(
'/^(.*?)\s+(asc|desc)$/i'
,
$column
,
$matches
))
{
$result
[
$matches
[
1
]]
=
strcasecmp
(
$matches
[
2
],
'desc'
)
?
self
::
SORT_ASC
:
self
::
SORT_DESC
;
}
else
{
}
else
{
$result
[
$column
]
=
self
::
SORT_ASC
;
return
null
;
}
}
return
$result
;
}
}
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit
* @return ActiveQuery the query object itself
*/
public
function
limit
(
$limit
)
{
$this
->
limit
=
$limit
;
return
$this
;
}
}
/**
/**
* Sets the
OFFSET part of the quer
y.
* Sets the
[[asArray]] propert
y.
* @param
integer $offset the offset
* @param
boolean $value whether to return the query results in terms of arrays instead of Active Records.
* @return ActiveQuery the query object itself
* @return ActiveQuery the query object itself
*/
*/
public
function
offset
(
$offset
)
public
function
asArray
(
$value
=
true
)
{
{
$this
->
offset
=
$offset
;
$this
->
asArray
=
$value
;
return
$this
;
return
$this
;
}
}
...
@@ -546,141 +364,6 @@ class ActiveQuery extends \yii\base\Component
...
@@ -546,141 +364,6 @@ class ActiveQuery extends \yii\base\Component
return
$this
;
return
$this
;
}
}
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row or model data. The signature of the callable should be:
*
* ~~~
* // $model is an AR instance when `asArray` is false,
* // or an array of column values when `asArray` is true.
* function ($model)
* {
* // return the index value corresponding to $model
* }
* ~~~
*
* @return ActiveQuery the query object itself
*/
public
function
indexBy
(
$column
)
{
$this
->
indexBy
=
$column
;
return
$this
;
}
/**
* Sets the WHERE part of the query.
*
* The method requires a $condition parameter, and optionally a $params parameter
* specifying the values to be bound to the query.
*
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
* - hash format: `array('column1' => value1, 'column2' => value2, ...)`
* - operator format: `array(operator, operand1, operand2, ...)`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `array('type' => 1, 'status' => 2)` generates `(type = 1) AND (status = 2)`.
* - `array('id' => array(1, 2, 3), 'status' => 2)` generates `(id IN (1, 2, 3)) AND (status = 2)`.
* - `array('status' => null) generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `array('and', 'id=1', 'id=2')` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `array('and', 'type=1', array('or', 'id=1', 'id=2'))` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `array('between', 'id', 1, 10)` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
* `array('in', 'id', array(1, 2, 3))` will generate `id IN (1, 2, 3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `array('like', 'name', '%tester%')` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `array('like', 'name', array('%test%', '%sample%'))` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @return ActiveQuery the query object itself
* @see andWhere()
* @see orWhere()
*/
public
function
where
(
$condition
)
{
$this
->
where
=
$condition
;
return
$this
;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return ActiveQuery the query object itself
* @see where()
* @see orWhere()
*/
public
function
andWhere
(
$condition
)
{
if
(
$this
->
where
===
null
)
{
$this
->
where
=
$condition
;
}
else
{
$this
->
where
=
array
(
'and'
,
$this
->
where
,
$condition
);
}
return
$this
;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return ActiveQuery the query object itself
* @see where()
* @see andWhere()
*/
public
function
orWhere
(
$condition
)
{
if
(
$this
->
where
===
null
)
{
$this
->
where
=
$condition
;
}
else
{
$this
->
where
=
array
(
'or'
,
$this
->
where
,
$condition
);
}
return
$this
;
}
private
function
createModels
(
$rows
)
private
function
createModels
(
$rows
)
{
{
$models
=
array
();
$models
=
array
();
...
...
framework/yii/elasticsearch/ActiveRecord.php
View file @
d442f056
...
@@ -325,7 +325,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
...
@@ -325,7 +325,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
$values
=
$this
->
getDirtyAttributes
(
$attributes
);
$values
=
$this
->
getDirtyAttributes
(
$attributes
);
$key
=
reset
(
$this
->
primaryKey
());
$key
=
reset
(
$this
->
primaryKey
());
$pk
=
$this
->
getAttribute
(
$key
);
$pk
=
$this
->
getAttribute
(
$key
);
unset
(
$values
[
$key
]);
//
unset($values[$key]);
// save attributes
// save attributes
if
(
$pk
===
null
)
{
if
(
$pk
===
null
)
{
...
...
framework/yii/elasticsearch/Command.php
0 → 100644
View file @
d442f056
<?php
/**
* @author Carsten Brandt <mail@cebe.cc>
*/
namespace
yii\elasticsearch
;
use
yii\base\Component
;
use
yii\helpers\Json
;
class
Command
extends
Component
{
/**
* @var Connection
*/
public
$db
;
public
$api
=
'_search'
;
/**
* @var string|array the indexes to execute the query on. Defaults to null meaning all indexes
* @see http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search.html#search-multi-index
*/
public
$index
;
/**
* @var string|array the types to execute the query on. Defaults to null meaning all types
*/
public
$type
;
/**
* @var array|string array or json
*/
public
$query
;
private
function
createUrl
(
$endPoint
=
null
)
{
if
(
$endPoint
===
null
)
{
$endPoint
=
$this
->
api
;
}
if
(
$this
->
index
===
null
&&
$this
->
type
===
null
)
{
return
'/'
.
$endPoint
;
}
$index
=
$this
->
index
;
if
(
$index
===
null
)
{
$index
=
'_all'
;
}
elseif
(
is_array
(
$index
))
{
$index
=
implode
(
','
,
$index
);
}
$type
=
$this
->
type
;
if
(
is_array
(
$type
))
{
$type
=
implode
(
','
,
$type
);
}
return
'/'
.
$index
.
'/'
.
(
empty
(
$type
)
?
''
:
$type
.
'/'
)
.
$endPoint
;
}
public
function
queryAll
()
{
$query
=
$this
->
query
;
if
(
empty
(
$query
))
{
$query
=
'{}'
;
}
if
(
is_array
(
$query
))
{
$query
=
Json
::
encode
(
$query
);
}
$http
=
$this
->
db
->
http
();
$response
=
$http
->
post
(
$this
->
createUrl
(),
null
,
$query
)
->
send
();
$data
=
Json
::
decode
(
$response
->
getBody
(
true
));
// TODO store query meta data for later use
$docs
=
array
();
switch
(
$this
->
api
)
{
default
:
case
'_search'
:
if
(
isset
(
$data
[
'hits'
][
'hits'
]))
{
$docs
=
$data
[
'hits'
][
'hits'
];
}
break
;
case
'_mget'
:
if
(
isset
(
$data
[
'docs'
]))
{
$docs
=
$data
[
'docs'
];
}
break
;
}
$rows
=
array
();
foreach
(
$docs
as
$doc
)
{
// TODO maybe return type info
if
(
isset
(
$doc
[
'exists'
])
&&
!
$doc
[
'exists'
])
{
continue
;
}
$row
=
$doc
[
'_source'
];
$row
[
'id'
]
=
$doc
[
'_id'
];
$rows
[]
=
$row
;
}
return
$rows
;
}
public
function
queryOne
()
{
// TODO set limit
$rows
=
$this
->
queryAll
();
return
reset
(
$rows
);
}
public
function
queryCount
()
{
//http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-count.html
$query
=
$this
->
query
;
if
(
empty
(
$query
))
{
$query
=
''
;
}
if
(
is_array
(
$query
))
{
$query
=
Json
::
encode
(
$query
);
}
$http
=
$this
->
db
->
http
();
$response
=
$http
->
post
(
$this
->
createUrl
(
'_count'
),
null
,
$query
)
->
send
();
$data
=
Json
::
decode
(
$response
->
getBody
(
true
));
// TODO store query meta data for later use
return
$data
[
'count'
];
}
}
\ No newline at end of file
framework/yii/elasticsearch/Connection.php
View file @
d442f056
...
@@ -54,6 +54,23 @@ class Connection extends Component
...
@@ -54,6 +54,23 @@ class Connection extends Component
}
}
/**
/**
* Creates a command for execution.
* @param string $query the SQL statement to be executed
* @return Command the DB command
*/
public
function
createCommand
(
$query
=
null
,
$index
=
null
,
$type
=
null
)
{
$this
->
open
();
$command
=
new
Command
(
array
(
'db'
=>
$this
,
'query'
=>
$query
,
'index'
=>
$index
,
'type'
=>
$type
,
));
return
$command
;
}
/**
* Closes the connection when this component is being serialized.
* Closes the connection when this component is being serialized.
* @return array
* @return array
*/
*/
...
...
framework/yii/elasticsearch/Query.php
View file @
d442f056
...
@@ -11,8 +11,417 @@ namespace yii\elasticsearch;
...
@@ -11,8 +11,417 @@ namespace yii\elasticsearch;
use
yii\base\Component
;
use
yii\base\Component
;
use
Yii
;
class
Query
extends
Component
class
Query
extends
Component
{
{
/**
* Sort ascending
* @see orderBy
*/
const
SORT_ASC
=
false
;
/**
* Sort descending
* @see orderBy
*/
const
SORT_DESC
=
true
;
/**
* @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()
*/
public
$select
;
/**
* @var string|array query condition. This refers to the WHERE clause in a SQL statement.
* For example, `age > 31 AND team = 1`.
* @see where()
*/
public
$where
;
/**
* @var integer maximum number of records to be returned. If not set or less than 0, it means no limit.
*/
public
$limit
;
/**
* @var integer zero-based offset from where the records are to be returned. If not set or
* less than 0, it means starting from the beginning.
*/
public
$offset
;
/**
* @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
* The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
* can be either [[Query::SORT_ASC]] or [[Query::SORT_DESC]]. The array may also contain [[Expression]] objects.
* If that is the case, the expressions will be converted into strings without any change.
*/
public
$orderBy
;
/**
* @var string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. For more details, see [[indexBy()]]. This property is only used by [[all()]].
*/
public
$indexBy
;
/**
* Creates a DB command that can be used to execute this query.
* @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.
* @return Command the created DB command instance.
*/
public
function
createCommand
(
$db
=
null
)
{
if
(
$db
===
null
)
{
$db
=
Yii
::
$app
->
elasticsearch
;
}
$query
=
$db
->
getQueryBuilder
()
->
build
(
$this
);
return
$db
->
createCommand
(
$query
);
}
/**
* Sets the [[indexBy]] property.
* @param string|callable $column the name of the column by which the query results should be indexed by.
* This can also be a callable (e.g. anonymous function) that returns the index value based on the given
* row data. The signature of the callable should be:
*
* ~~~
* function ($row)
* {
* // return the index value corresponding to $row
* }
* ~~~
*
* @return Query the query object itself
*/
public
function
indexBy
(
$column
)
{
$this
->
indexBy
=
$column
;
return
$this
;
}
/**
* 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.
* @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
)
{
return
$rows
;
}
$result
=
array
();
foreach
(
$rows
as
$row
)
{
if
(
is_string
(
$this
->
indexBy
))
{
$key
=
$row
[
$this
->
indexBy
];
}
else
{
$key
=
call_user_func
(
$this
->
indexBy
,
$row
);
}
$result
[
$key
]
=
$row
;
}
return
$result
;
}
/**
* 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.
* @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
();
}
/**
* Returns the query result as a scalar value.
* The value returned will be the first column in the first row of the query results.
* @param $column
* @return string|boolean the value of the first column in the first row of the query result.
* False is returned if the query result is empty.
*/
public
function
scalar
(
$column
)
{
// TODO implement
return
null
;
}
// /**
// * 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.
// * @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)
// {
// return $this->createCommand($db)->queryColumn();
// }
/**
* Returns the number of records.
* @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.
* @return integer number of records
*/
public
function
count
(
$db
=
null
)
{
return
$this
->
createCommand
(
$db
)
->
queryCount
();
}
// /**
// * 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.
// * @return integer the sum of the specified column values
// */
// public function sum($q, $db = null)
// {
// $this->select = array("SUM($q)");
// return $this->createCommand($db)->queryScalar();
// }
//
// /**
// * 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.
// * @return integer the average of the specified column values.
// */
// public function average($q, $db = null)
// {
// $this->select = array("AVG($q)");
// return $this->createCommand($db)->queryScalar();
// }
//
// /**
// * 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.
// * @return integer the minimum of the specified column values.
// */
// public function min($q, $db = null)
// {
// $this->select = array("MIN($q)");
// return $this->createCommand($db)->queryScalar();
// }
//
// /**
// * 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.
// * @return integer the maximum of the specified column values.
// */
// public function max($q, $db = null)
// {
// $this->select = array("MAX($q)");
// return $this->createCommand($db)->queryScalar();
// }
/**
* Returns a value indicating whether the query result contains any row of data.
* @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.
* @return boolean whether the query result contains any row of data.
*/
public
function
exists
()
{
return
$this
->
one
()
!==
null
;
}
/**
* Sets the WHERE part of the query.
*
* The method requires a $condition parameter, and optionally a $params parameter
* specifying the values to be bound to the query.
*
* The $condition parameter should be either a string (e.g. 'id=1') or an array.
* If the latter, it must be in one of the following two formats:
*
* - hash format: `array('column1' => value1, 'column2' => value2, ...)`
* - operator format: `array(operator, operand1, operand2, ...)`
*
* A condition in hash format represents the following SQL expression in general:
* `column1=value1 AND column2=value2 AND ...`. In case when a value is an array,
* an `IN` expression will be generated. And if a value is null, `IS NULL` will be used
* in the generated expression. Below are some examples:
*
* - `array('type' => 1, 'status' => 2)` generates `(type = 1) AND (status = 2)`.
* - `array('id' => array(1, 2, 3), 'status' => 2)` generates `(id IN (1, 2, 3)) AND (status = 2)`.
* - `array('status' => null) generates `status IS NULL`.
*
* A condition in operator format generates the SQL expression according to the specified operator, which
* can be one of the followings:
*
* - `and`: the operands should be concatenated together using `AND`. For example,
* `array('and', 'id=1', 'id=2')` will generate `id=1 AND id=2`. If an operand is an array,
* it will be converted into a string using the rules described here. For example,
* `array('and', 'type=1', array('or', 'id=1', 'id=2'))` will generate `type=1 AND (id=1 OR id=2)`.
* The method will NOT do any quoting or escaping.
*
* - `or`: similar to the `and` operator except that the operands are concatenated using `OR`.
*
* - `between`: operand 1 should be the column name, and operand 2 and 3 should be the
* starting and ending values of the range that the column is in.
* For example, `array('between', 'id', 1, 10)` will generate `id BETWEEN 1 AND 10`.
*
* - `not between`: similar to `between` except the `BETWEEN` is replaced with `NOT BETWEEN`
* in the generated condition.
*
* - `in`: operand 1 should be a column or DB expression, and operand 2 be an array representing
* the range of the values that the column or DB expression should be in. For example,
* `array('in', 'id', array(1, 2, 3))` will generate `id IN (1, 2, 3)`.
* The method will properly quote the column name and escape values in the range.
*
* - `not in`: similar to the `in` operator except that `IN` is replaced with `NOT IN` in the generated condition.
*
* - `like`: operand 1 should be a column or DB expression, and operand 2 be a string or an array representing
* the values that the column or DB expression should be like.
* For example, `array('like', 'name', '%tester%')` will generate `name LIKE '%tester%'`.
* When the value range is given as an array, multiple `LIKE` predicates will be generated and concatenated
* using `AND`. For example, `array('like', 'name', array('%test%', '%sample%'))` will generate
* `name LIKE '%test%' AND name LIKE '%sample%'`.
* The method will properly quote the column name and escape values in the range.
*
* - `or like`: similar to the `like` operator except that `OR` is used to concatenate the `LIKE`
* predicates when operand 2 is an array.
*
* - `not like`: similar to the `like` operator except that `LIKE` is replaced with `NOT LIKE`
* in the generated condition.
*
* - `or not like`: similar to the `not like` operator except that `OR` is used to concatenate
* the `NOT LIKE` predicates.
*
* @param string|array $condition the conditions that should be put in the WHERE part.
* @return Query the query object itself
* @see andWhere()
* @see orWhere()
*/
public
function
where
(
$condition
)
{
$this
->
where
=
$condition
;
return
$this
;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return Query the query object itself
* @see where()
* @see orWhere()
*/
public
function
andWhere
(
$condition
)
{
if
(
$this
->
where
===
null
)
{
$this
->
where
=
$condition
;
}
else
{
$this
->
where
=
array
(
'and'
,
$this
->
where
,
$condition
);
}
return
$this
;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param string|array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return Query the query object itself
* @see where()
* @see andWhere()
*/
public
function
orWhere
(
$condition
)
{
if
(
$this
->
where
===
null
)
{
$this
->
where
=
$condition
;
}
else
{
$this
->
where
=
array
(
'or'
,
$this
->
where
,
$condition
);
}
return
$this
;
}
/**
* Sets the ORDER BY part of the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see addOrderBy()
*/
public
function
orderBy
(
$columns
)
{
$this
->
orderBy
=
$this
->
normalizeOrderBy
(
$columns
);
return
$this
;
}
/**
* Adds additional ORDER BY columns to the query.
* @param string|array $columns the columns (and the directions) to be ordered by.
* Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
* (e.g. `array('id' => Query::SORT_ASC, 'name' => Query::SORT_DESC)`).
* The method will automatically quote the column names unless a column contains some parenthesis
* (which means the column contains a DB expression).
* @return Query the query object itself
* @see orderBy()
*/
public
function
addOrderBy
(
$columns
)
{
$columns
=
$this
->
normalizeOrderBy
(
$columns
);
if
(
$this
->
orderBy
===
null
)
{
$this
->
orderBy
=
$columns
;
}
else
{
$this
->
orderBy
=
array_merge
(
$this
->
orderBy
,
$columns
);
}
return
$this
;
}
protected
function
normalizeOrderBy
(
$columns
)
{
if
(
is_array
(
$columns
))
{
return
$columns
;
}
else
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
$result
=
array
();
foreach
(
$columns
as
$column
)
{
if
(
preg_match
(
'/^(.*?)\s+(asc|desc)$/i'
,
$column
,
$matches
))
{
$result
[
$matches
[
1
]]
=
strcasecmp
(
$matches
[
2
],
'desc'
)
?
self
::
SORT_ASC
:
self
::
SORT_DESC
;
}
else
{
$result
[
$column
]
=
self
::
SORT_ASC
;
}
}
return
$result
;
}
}
/**
* Sets the LIMIT part of the query.
* @param integer $limit the limit. Use null or negative value to disable limit.
* @return Query the query object itself
*/
public
function
limit
(
$limit
)
{
$this
->
limit
=
$limit
;
return
$this
;
}
/**
* Sets the OFFSET part of the query.
* @param integer $offset the offset. Use null or negative value to disable offset.
* @return Query the query object itself
*/
public
function
offset
(
$offset
)
{
$this
->
offset
=
$offset
;
return
$this
;
}
}
}
\ No newline at end of file
framework/yii/elasticsearch/QueryBuilder.php
View file @
d442f056
...
@@ -46,7 +46,7 @@ class QueryBuilder extends \yii\base\Object
...
@@ -46,7 +46,7 @@ class QueryBuilder extends \yii\base\Object
{
{
$searchQuery
=
array
();
$searchQuery
=
array
();
$this
->
buildSelect
(
$searchQuery
,
$query
->
select
);
$this
->
buildSelect
(
$searchQuery
,
$query
->
select
);
// $this->buildFrom(
&
$searchQuery, $query->from);
// $this->buildFrom($searchQuery, $query->from);
$this
->
buildCondition
(
$searchQuery
,
$query
->
where
);
$this
->
buildCondition
(
$searchQuery
,
$query
->
where
);
$this
->
buildOrderBy
(
$searchQuery
,
$query
->
orderBy
);
$this
->
buildOrderBy
(
$searchQuery
,
$query
->
orderBy
);
$this
->
buildLimit
(
$searchQuery
,
$query
->
limit
,
$query
->
offset
);
$this
->
buildLimit
(
$searchQuery
,
$query
->
limit
,
$query
->
offset
);
...
@@ -209,7 +209,12 @@ class QueryBuilder extends \yii\base\Object
...
@@ -209,7 +209,12 @@ class QueryBuilder extends \yii\base\Object
private
function
buildHashCondition
(
&
$query
,
$condition
)
private
function
buildHashCondition
(
&
$query
,
$condition
)
{
{
$query
[
'query'
][
'term'
]
=
$condition
;
foreach
(
$condition
as
$attribute
=>
$value
)
{
// ['query']['filteredQuery']
$query
[
'filter'
][
'bool'
][
'must'
][]
=
array
(
'term'
=>
array
(
$attribute
=>
$value
),
);
}
return
;
// TODO more
return
;
// TODO more
$parts
=
array
();
$parts
=
array
();
foreach
(
$condition
as
$column
=>
$value
)
{
foreach
(
$condition
as
$column
=>
$value
)
{
...
...
tests/unit/data/ar/elasticsearch/Customer.php
View file @
d442f056
...
@@ -35,6 +35,6 @@ class Customer extends ActiveRecord
...
@@ -35,6 +35,6 @@ class Customer extends ActiveRecord
public
static
function
active
(
$query
)
public
static
function
active
(
$query
)
{
{
$query
->
andWhere
(
'status=1'
);
$query
->
andWhere
(
array
(
'status'
=>
1
)
);
}
}
}
}
tests/unit/framework/elasticsearch/ActiveRecordTest.php
View file @
d442f056
...
@@ -4,7 +4,8 @@ namespace yiiunit\framework\elasticsearch;
...
@@ -4,7 +4,8 @@ namespace yiiunit\framework\elasticsearch;
use
yii\db\Query
;
use
yii\db\Query
;
use
yii\elasticsearch\Connection
;
use
yii\elasticsearch\Connection
;
use
yii\redis\ActiveQuery
;
use
yii\elasticsearch\ActiveQuery
;
use
yii\helpers\Json
;
use
yiiunit\data\ar\elasticsearch\ActiveRecord
;
use
yiiunit\data\ar\elasticsearch\ActiveRecord
;
use
yiiunit\data\ar\elasticsearch\Customer
;
use
yiiunit\data\ar\elasticsearch\Customer
;
use
yiiunit\data\ar\elasticsearch\OrderItem
;
use
yiiunit\data\ar\elasticsearch\OrderItem
;
...
@@ -80,6 +81,17 @@ class ActiveRecordTest extends ElasticSearchTestCase
...
@@ -80,6 +81,17 @@ class ActiveRecordTest extends ElasticSearchTestCase
// $orderItem = new OrderItem();
// $orderItem = new OrderItem();
// $orderItem->setAttributes(array('order_id' => 3, 'item_id' => 2, 'quantity' => 1, 'subtotal' => 40.0), false);
// $orderItem->setAttributes(array('order_id' => 3, 'item_id' => 2, 'quantity' => 1, 'subtotal' => 40.0), false);
// $orderItem->save(false);
// $orderItem->save(false);
for
(
$n
=
0
;
$n
<
20
;
$n
++
)
{
$r
=
$db
->
http
()
->
post
(
'_count'
)
->
send
();
$c
=
Json
::
decode
(
$r
->
getBody
(
true
));
if
(
$c
[
'count'
]
!=
11
)
{
usleep
(
100000
);
}
else
{
return
;
}
}
throw
new
\Exception
(
'Unable to initialize elasticsearch data.'
);
}
}
public
function
testFind
()
public
function
testFind
()
...
@@ -124,13 +136,14 @@ class ActiveRecordTest extends ElasticSearchTestCase
...
@@ -124,13 +136,14 @@ class ActiveRecordTest extends ElasticSearchTestCase
// find count, sum, average, min, max, scalar
// find count, sum, average, min, max, scalar
$this
->
assertEquals
(
3
,
Customer
::
find
()
->
count
());
$this
->
assertEquals
(
3
,
Customer
::
find
()
->
count
());
$this
->
assertEquals
(
6
,
Customer
::
find
()
->
sum
(
'id'
));
//
$this->assertEquals(6, Customer::find()->sum('id'));
$this
->
assertEquals
(
2
,
Customer
::
find
()
->
average
(
'id'
));
//
$this->assertEquals(2, Customer::find()->average('id'));
$this
->
assertEquals
(
1
,
Customer
::
find
()
->
min
(
'id'
));
//
$this->assertEquals(1, Customer::find()->min('id'));
$this
->
assertEquals
(
3
,
Customer
::
find
()
->
max
(
'id'
));
//
$this->assertEquals(3, Customer::find()->max('id'));
// scope
// scope
$this
->
assertEquals
(
2
,
Customer
::
find
()
->
active
()
->
count
());
$this
->
assertEquals
(
2
,
count
(
Customer
::
find
()
->
active
()
->
all
()));
// $this->assertEquals(2, Customer::find()->active()->count());
// asArray
// asArray
$customer
=
Customer
::
find
()
->
where
(
array
(
'id'
=>
2
))
->
asArray
()
->
one
();
$customer
=
Customer
::
find
()
->
where
(
array
(
'id'
=>
2
))
->
asArray
()
->
one
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment