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
955bf7da
Commit
955bf7da
authored
Sep 30, 2013
by
Carsten Brandt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
basic CRUD for elastic search WIP
parent
a94886fa
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
665 additions
and
278 deletions
+665
-278
ActiveQuery.php
framework/yii/elasticsearch/ActiveQuery.php
+66
-43
ActiveRecord.php
framework/yii/elasticsearch/ActiveRecord.php
+60
-65
Connection.php
framework/yii/elasticsearch/Connection.php
+10
-18
Query.php
framework/yii/elasticsearch/Query.php
+19
-0
QueryBuilder.php
framework/yii/elasticsearch/QueryBuilder.php
+379
-0
ActiveRecordTest.php
tests/unit/framework/elasticsearch/ActiveRecordTest.php
+131
-152
No files found.
framework/yii/elasticsearch/ActiveQuery.php
View file @
955bf7da
...
...
@@ -6,6 +6,8 @@
*/
namespace
yii\elasticsearch
;
use
Guzzle\Http\Client
;
use
Guzzle\Http\Exception\MultiTransferException
;
use
yii\base\NotSupportedException
;
use
yii\db\Exception
;
use
yii\helpers\Json
;
...
...
@@ -78,14 +80,20 @@ class ActiveQuery extends \yii\base\Component
*/
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.
* @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
;
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.
...
...
@@ -128,12 +136,10 @@ class ActiveQuery extends \yii\base\Component
// TODO add support for orderBy
$data
=
$this
->
executeScript
(
'All'
);
$rows
=
array
();
print_r
(
$data
);
foreach
(
$data
as
$dataRow
)
{
$row
=
array
();
$c
=
count
(
$dataRow
);
for
(
$i
=
0
;
$i
<
$c
;
)
{
$row
[
$dataRow
[
$i
++
]]
=
$dataRow
[
$i
++
];
}
$row
=
$dataRow
[
'_source'
];
$row
[
'id'
]
=
$dataRow
[
'_id'
];
$rows
[]
=
$row
;
}
if
(
!
empty
(
$rows
))
{
...
...
@@ -157,14 +163,11 @@ class ActiveQuery extends \yii\base\Component
{
// TODO add support for orderBy
$data
=
$this
->
executeScript
(
'One'
);
if
(
$data
===
array
(
))
{
if
(
!
isset
(
$data
[
'_source'
]
))
{
return
null
;
}
$row
=
array
();
$c
=
count
(
$data
);
for
(
$i
=
0
;
$i
<
$c
;
)
{
$row
[
$data
[
$i
++
]]
=
$data
[
$i
++
];
}
$row
=
$data
[
'_source'
];
$row
[
'id'
]
=
$data
[
'_id'
];
if
(
$this
->
asArray
)
{
$model
=
$row
;
}
else
{
...
...
@@ -284,12 +287,13 @@ class ActiveQuery extends \yii\base\Component
{
if
((
$data
=
$this
->
findByPk
(
$type
))
===
false
)
{
$modelClass
=
$this
->
modelClass
;
/** @var Connection $db */
$db
=
$modelClass
::
getDb
();
$http
=
$modelClass
::
getDb
()
->
http
();
$method
=
'build'
.
$type
;
$script
=
$db
->
getLuaScriptBuilder
()
->
$method
(
$this
,
$columnName
);
return
$db
->
executeCommand
(
'EVAL'
,
array
(
$script
,
0
));
$url
=
'/'
.
$modelClass
::
indexName
()
.
'/'
.
$modelClass
::
indexType
()
.
'/_search'
;
$query
=
$modelClass
::
getDb
()
->
getQueryBuilder
()
->
build
(
$this
);
$response
=
$http
->
post
(
$url
,
null
,
Json
::
encode
(
$query
))
->
send
();
$data
=
Json
::
decode
(
$response
->
getBody
(
true
));
return
$data
[
'hits'
][
'hits'
];
}
return
$data
;
}
...
...
@@ -301,46 +305,47 @@ class ActiveQuery extends \yii\base\Component
{
$modelClass
=
$this
->
modelClass
;
if
(
is_array
(
$this
->
where
)
&&
!
isset
(
$this
->
where
[
0
])
&&
$modelClass
::
isPrimaryKey
(
array_keys
(
$this
->
where
)))
{
/** @var C
onnection $db
*/
$
db
=
$modelClass
::
getDb
();
/** @var C
lient $http
*/
$
http
=
$modelClass
::
getDb
()
->
http
();
$pks
=
(
array
)
reset
(
$this
->
where
);
$start
=
$this
->
offset
===
null
?
0
:
$this
->
offset
;
$i
=
0
;
$data
=
array
();
$url
=
'/'
.
$modelClass
::
indexName
()
.
'/'
.
$modelClass
::
indexType
()
.
'/'
;
$query
=
array
(
'docs'
=>
array
());
foreach
(
$pks
as
$pk
)
{
if
(
++
$i
>
$start
&&
(
$this
->
limit
===
null
||
$i
<=
$start
+
$this
->
limit
))
{
$request
=
$db
->
http
()
->
get
(
$url
.
$pk
);
$response
=
$request
->
send
();
if
(
$response
->
getStatusCode
()
==
404
)
{
// ignore?
}
else
{
$data
[]
=
Json
::
decode
(
$response
->
getBody
(
true
));
if
(
$type
===
'One'
&&
$this
->
orderBy
===
null
)
{
break
;
}
}
$doc
=
array
(
'_id'
=>
$pk
);
if
(
!
empty
(
$this
->
select
))
{
$doc
[
'fields'
]
=
$this
->
select
;
}
$query
[
'docs'
][]
=
$doc
;
}
$url
=
'/'
.
$modelClass
::
indexName
()
.
'/'
.
$modelClass
::
indexType
()
.
'/_mget'
;
$response
=
$http
->
post
(
$url
,
null
,
Json
::
encode
(
$query
))
->
send
();
$data
=
Json
::
decode
(
$response
->
getBody
(
true
));
$start
=
$this
->
offset
===
null
?
0
:
$this
->
offset
;
$data
=
array_slice
(
$data
[
'docs'
],
$start
,
$this
->
limit
);
// TODO support orderBy
switch
(
$type
)
{
case
'All'
:
return
$data
;
case
'One'
:
return
reset
(
$data
);
return
empty
(
$data
)
?
null
:
reset
(
$data
);
case
'Column'
:
// TODO support indexBy
$column
=
array
();
foreach
(
$data
as
$dataRow
)
{
$row
=
array
();
$c
=
count
(
$dataRow
);
for
(
$i
=
0
;
$i
<
$c
;
)
{
$row
[
$dataRow
[
$i
++
]]
=
$dataRow
[
$i
++
];
foreach
(
$data
as
$row
)
{
$row
[
'_source'
][
'id'
]
=
$row
[
'_id'
];
if
(
$this
->
indexBy
===
null
)
{
$column
[]
=
$row
[
'_source'
][
$columnName
];
}
else
{
if
(
is_string
(
$this
->
indexBy
))
{
$key
=
$row
[
'_source'
][
$this
->
indexBy
];
}
else
{
$key
=
call_user_func
(
$this
->
indexBy
,
$row
[
'_source'
]);
}
$models
[
$key
]
=
$row
;
}
$column
[]
=
$row
[
$columnName
];
}
return
$column
;
case
'Count'
:
...
...
@@ -414,6 +419,24 @@ class ActiveQuery extends \yii\base\Component
}
/**
* Sets the SELECT part of the query.
* @param string|array $columns the columns to be selected.
* Columns can be specified in either a string (e.g. "id, name") or an array (e.g. array('id', 'name')).
* Columns can contain table prefixes (e.g. "tbl_user.id") and/or column aliases (e.g. "tbl_user.id AS user_id").
* 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
)
{
if
(
!
is_array
(
$columns
))
{
$columns
=
preg_split
(
'/\s*,\s*/'
,
trim
(
$columns
),
-
1
,
PREG_SPLIT_NO_EMPTY
);
}
$this
->
select
=
$columns
;
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
...
...
framework/yii/elasticsearch/ActiveRecord.php
View file @
955bf7da
...
...
@@ -64,39 +64,43 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
*/
public
static
function
updateAll
(
$attributes
,
$condition
=
null
,
$params
=
array
())
{
// TODO add support for further options as described in http://www.elasticsearch.org/guide/reference/api/bulk/
if
(
empty
(
$attributes
))
{
return
0
;
}
$db
=
static
::
getDb
();
$n
=
0
;
foreach
(
static
::
fetchPks
(
$condition
)
as
$pk
)
{
$newPk
=
$pk
;
$pk
=
static
::
buildKey
(
$pk
);
$key
=
static
::
tableName
()
.
':a:'
.
$pk
;
// save attributes
$args
=
array
(
$key
);
foreach
(
$attributes
as
$attribute
=>
$value
)
{
if
(
isset
(
$newPk
[
$attribute
]))
{
$newPk
[
$attribute
]
=
$value
;
}
$args
[]
=
$attribute
;
$args
[]
=
$value
;
}
$newPk
=
static
::
buildKey
(
$newPk
);
$newKey
=
static
::
tableName
()
.
':a:'
.
$newPk
;
// rename index if pk changed
if
(
$newPk
!=
$pk
)
{
$db
->
executeCommand
(
'MULTI'
);
$db
->
executeCommand
(
'HMSET'
,
$args
);
$db
->
executeCommand
(
'LINSERT'
,
array
(
static
::
tableName
(),
'AFTER'
,
$pk
,
$newPk
));
$db
->
executeCommand
(
'LREM'
,
array
(
static
::
tableName
(),
0
,
$pk
));
$db
->
executeCommand
(
'RENAME'
,
array
(
$key
,
$newKey
));
$db
->
executeCommand
(
'EXEC'
);
}
else
{
$db
->
executeCommand
(
'HMSET'
,
$args
);
if
(
count
(
$condition
)
!=
1
||
!
isset
(
$condition
[
reset
(
static
::
primaryKey
())]))
{
throw
new
NotSupportedException
(
'UpdateAll is only supported by primary key in elasticsearch.'
);
}
if
(
isset
(
$attributes
[
reset
(
static
::
primaryKey
())]))
{
throw
new
NotSupportedException
(
'Updating the primary key is currently not supported by elasticsearch.'
);
}
$query
=
''
;
foreach
((
array
)
reset
(
$condition
)
as
$pk
)
{
if
(
is_array
(
$pk
))
{
$pk
=
reset
(
$pk
);
}
$action
=
Json
::
encode
(
array
(
"update"
=>
array
(
"_id"
=>
$pk
,
"_type"
=>
static
::
indexType
(),
"_index"
=>
static
::
indexName
(),
),
));
$data
=
Json
::
encode
(
array
(
"doc"
=>
$attributes
));
$query
.=
$action
.
"
\n
"
.
$data
.
"
\n
"
;
// TODO implement pk change
}
$url
=
'/'
.
static
::
indexName
()
.
'/'
.
static
::
indexType
()
.
'/_bulk'
;
$response
=
static
::
getDb
()
->
http
()
->
post
(
$url
,
array
(),
$query
)
->
send
();
$body
=
Json
::
decode
(
$response
->
getBody
(
true
));
$n
=
0
;
foreach
(
$body
[
'items'
]
as
$item
)
{
if
(
$item
[
'update'
][
'ok'
])
{
$n
++
;
}
}
return
$n
;
}
...
...
@@ -117,19 +121,7 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
*/
public
static
function
updateAllCounters
(
$counters
,
$condition
=
null
,
$params
=
array
())
{
if
(
empty
(
$counters
))
{
return
0
;
}
$db
=
static
::
getDb
();
$n
=
0
;
foreach
(
static
::
fetchPks
(
$condition
)
as
$pk
)
{
$key
=
static
::
tableName
()
.
':a:'
.
static
::
buildKey
(
$pk
);
foreach
(
$counters
as
$attribute
=>
$value
)
{
$db
->
executeCommand
(
'HINCRBY'
,
array
(
$key
,
$attribute
,
$value
));
}
$n
++
;
}
return
$n
;
throw
new
NotSupportedException
(
'Update Counters is not supported by elasticsearch.'
);
}
/**
...
...
@@ -149,23 +141,36 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
*/
public
static
function
deleteAll
(
$condition
=
null
,
$params
=
array
())
{
$db
=
static
::
getDb
();
$attributeKeys
=
array
();
$pks
=
static
::
fetchPks
(
$condition
);
$db
->
executeCommand
(
'MULTI'
);
foreach
(
$pks
as
$pk
)
{
$pk
=
static
::
buildKey
(
$pk
);
$db
->
executeCommand
(
'LREM'
,
array
(
static
::
tableName
(),
0
,
$pk
));
$attributeKeys
[]
=
static
::
tableName
()
.
':a:'
.
$pk
;
}
if
(
empty
(
$attributeKeys
))
{
$db
->
executeCommand
(
'EXEC'
);
return
0
;
// TODO use delete By Query feature
// http://www.elasticsearch.org/guide/reference/api/delete-by-query/
if
(
count
(
$condition
)
!=
1
||
!
isset
(
$condition
[
reset
(
static
::
primaryKey
())]))
{
throw
new
NotSupportedException
(
'DeleteAll is only supported by primary key in elasticsearch.'
);
}
$query
=
''
;
foreach
((
array
)
reset
(
$condition
)
as
$pk
)
{
if
(
is_array
(
$pk
))
{
$pk
=
reset
(
$pk
);
}
$query
.=
Json
::
encode
(
array
(
"delete"
=>
array
(
"_id"
=>
$pk
,
"_type"
=>
static
::
indexType
(),
"_index"
=>
static
::
indexName
(),
),
))
.
"
\n
"
;
}
$url
=
'/'
.
static
::
indexName
()
.
'/'
.
static
::
indexType
()
.
'/_bulk'
;
$response
=
static
::
getDb
()
->
http
()
->
post
(
$url
,
array
(),
$query
)
->
send
();
$body
=
Json
::
decode
(
$response
->
getBody
(
true
));
$n
=
0
;
foreach
(
$body
[
'items'
]
as
$item
)
{
if
(
$item
[
'delete'
][
'ok'
])
{
$n
++
;
}
$db
->
executeCommand
(
'DEL'
,
$attributeKeys
);
$result
=
$db
->
executeCommand
(
'EXEC'
);
return
end
(
$result
);
}
return
$n
;
}
/**
* Creates an [[ActiveQuery]] instance.
* This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query.
...
...
@@ -189,16 +194,6 @@ abstract class ActiveRecord extends \yii\db\ActiveRecord
return
static
::
getTableSchema
()
->
name
;
}
/**
* This method is ment to be overridden in redis ActiveRecord subclasses to return a [[RecordSchema]] instance.
* @return RecordSchema
* @throws \yii\base\InvalidConfigException
*/
public
static
function
getRecordSchema
()
{
throw
new
InvalidConfigException
(
__CLASS__
.
'::getRecordSchema() needs to be overridden in subclasses and return a RecordSchema.'
);
}
public
static
function
primaryKey
()
{
return
array
(
'id'
);
...
...
framework/yii/elasticsearch/Connection.php
View file @
955bf7da
...
...
@@ -160,27 +160,18 @@ class Connection extends Component
// TODO HTTP request to localhost:9200/
}
public
function
http
()
public
function
getQueryBuilder
()
{
return
new
\Guzzle\Http\Client
(
'http://localhost:9200/'
);
}
public
function
get
(
$url
)
{
$c
=
$this
->
initCurl
(
$url
);
$result
=
curl_exec
(
$c
);
curl_close
(
$c
);
return
new
QueryBuilder
(
$this
);
}
private
function
initCurl
(
$url
)
/**
* @return \Guzzle\Http\Client
*/
public
function
http
()
{
$c
=
curl_init
(
'http://localhost:9200/'
.
$url
);
$fp
=
fopen
(
"example_homepage.txt"
,
"w"
);
curl_setopt
(
$c
,
CURLOPT_FOLLOWLOCATION
,
false
);
curl_setopt
(
$c
,
CURLOPT_FILE
,
$fp
);
curl_setopt
(
$c
,
CURLOPT_HEADER
,
0
);
$guzzle
=
new
\Guzzle\Http\Client
(
'http://localhost:9200/'
);
//$guzzle->setDefaultOption()
return
$guzzle
;
}
}
\ No newline at end of file
framework/yii/elasticsearch/Query.php
0 → 100644
View file @
955bf7da
<?php
/**
* Created by JetBrains PhpStorm.
* User: cebe
* Date: 30.09.13
* Time: 11:39
* To change this template use File | Settings | File Templates.
*/
namespace
yii\elasticsearch
;
use
yii\base\Component
;
class
Query
extends
Component
{
}
\ No newline at end of file
framework/yii/elasticsearch/QueryBuilder.php
0 → 100644
View file @
955bf7da
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace
yii\elasticsearch
;
use
yii\base\NotSupportedException
;
/**
* QueryBuilder builds a SELECT SQL statement based on the specification given as a [[Query]] object.
*
* QueryBuilder can also be used to build SQL statements such as INSERT, UPDATE, DELETE, CREATE TABLE,
* from a [[Query]] object.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class
QueryBuilder
extends
\yii\base\Object
{
/**
* @var Connection the database connection.
*/
public
$db
;
/**
* Constructor.
* @param Connection $connection the database connection.
* @param array $config name-value pairs that will be used to initialize the object properties
*/
public
function
__construct
(
$connection
,
$config
=
array
())
{
$this
->
db
=
$connection
;
parent
::
__construct
(
$config
);
}
/**
* Generates a SELECT SQL statement from a [[Query]] object.
* @param Query $query the [[Query]] object from which the SQL statement will be generated
* @return array the generated SQL statement (the first array element) and the corresponding
* parameters to be bound to the SQL statement (the second array element).
*/
public
function
build
(
$query
)
{
$searchQuery
=
array
();
$this
->
buildSelect
(
$searchQuery
,
$query
->
select
);
// $this->buildFrom(&$searchQuery, $query->from);
$this
->
buildCondition
(
$searchQuery
,
$query
->
where
);
$this
->
buildOrderBy
(
$searchQuery
,
$query
->
orderBy
);
$this
->
buildLimit
(
$searchQuery
,
$query
->
limit
,
$query
->
offset
);
return
$searchQuery
;
}
/**
* Converts an abstract column type into a physical column type.
* The conversion is done using the type map specified in [[typeMap]].
* The following abstract column types are supported (using MySQL as an example to explain the corresponding
* physical types):
*
* - `pk`: an auto-incremental primary key type, will be converted into "int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY"
* - `bigpk`: an auto-incremental primary key type, will be converted into "bigint(20) NOT NULL AUTO_INCREMENT PRIMARY KEY"
* - `string`: string type, will be converted into "varchar(255)"
* - `text`: a long string type, will be converted into "text"
* - `smallint`: a small integer type, will be converted into "smallint(6)"
* - `integer`: integer type, will be converted into "int(11)"
* - `bigint`: a big integer type, will be converted into "bigint(20)"
* - `boolean`: boolean type, will be converted into "tinyint(1)"
* - `float``: float number type, will be converted into "float"
* - `decimal`: decimal number type, will be converted into "decimal"
* - `datetime`: datetime type, will be converted into "datetime"
* - `timestamp`: timestamp type, will be converted into "timestamp"
* - `time`: time type, will be converted into "time"
* - `date`: date type, will be converted into "date"
* - `money`: money type, will be converted into "decimal(19,4)"
* - `binary`: binary data type, will be converted into "blob"
*
* If the abstract type contains two or more parts separated by spaces (e.g. "string NOT NULL"), then only
* the first part will be converted, and the rest of the parts will be appended to the converted result.
* For example, 'string NOT NULL' is converted to 'varchar(255) NOT NULL'.
*
* For some of the abstract types you can also specify a length or precision constraint
* by prepending it in round brackets directly to the type.
* For example `string(32)` will be converted into "varchar(32)" on a MySQL database.
* If the underlying DBMS does not support these kind of constraints for a type it will
* be ignored.
*
* If a type cannot be found in [[typeMap]], it will be returned without any change.
* @param string $type abstract column type
* @return string physical column type.
*/
public
function
getColumnType
(
$type
)
{
if
(
isset
(
$this
->
typeMap
[
$type
]))
{
return
$this
->
typeMap
[
$type
];
}
elseif
(
preg_match
(
'/^(\w+)\((.+?)\)(.*)$/'
,
$type
,
$matches
))
{
if
(
isset
(
$this
->
typeMap
[
$matches
[
1
]]))
{
return
preg_replace
(
'/\(.+\)/'
,
'('
.
$matches
[
2
]
.
')'
,
$this
->
typeMap
[
$matches
[
1
]])
.
$matches
[
3
];
}
}
elseif
(
preg_match
(
'/^(\w+)\s+/'
,
$type
,
$matches
))
{
if
(
isset
(
$this
->
typeMap
[
$matches
[
1
]]))
{
return
preg_replace
(
'/^\w+/'
,
$this
->
typeMap
[
$matches
[
1
]],
$type
);
}
}
return
$type
;
}
/**
* @param array $columns
* @param boolean $distinct
* @param string $selectOption
* @return string the SELECT clause built from [[query]].
*/
public
function
buildSelect
(
&
$query
,
$columns
)
{
if
(
empty
(
$columns
))
{
return
;
}
foreach
(
$columns
as
$i
=>
$column
)
{
if
(
is_object
(
$column
))
{
$columns
[
$i
]
=
(
string
)
$column
;
}
}
$query
[
'fields'
]
=
$columns
;
}
/**
* @param array $columns
* @return string the ORDER BY clause built from [[query]].
*/
public
function
buildOrderBy
(
&
$query
,
$columns
)
{
if
(
empty
(
$columns
))
{
return
;
}
$orders
=
array
();
foreach
(
$columns
as
$name
=>
$direction
)
{
// allow elasticsearch extended syntax as described in http://www.elasticsearch.org/guide/reference/api/search/sort/
if
(
is_array
(
$direction
))
{
$orders
[]
=
array
(
$name
=>
$direction
);
}
elseif
(
is_string
(
$direction
))
{
$orders
[]
=
$direction
;
}
else
{
$orders
[]
=
array
(
$name
=>
(
$direction
===
Query
::
SORT_DESC
?
'desc'
:
'asc'
));
}
}
$query
[
'sort'
]
=
$orders
;
}
/**
* @param integer $limit
* @param integer $offset
* @return string the LIMIT and OFFSET clauses built from [[query]].
*/
public
function
buildLimit
(
&
$query
,
$limit
,
$offset
)
{
if
(
$limit
!==
null
&&
$limit
>=
0
)
{
$query
[
'size'
]
=
$limit
;
}
if
(
$offset
>
0
)
{
$query
[
'from'
]
=
(
int
)
$offset
;
}
}
/**
* Parses the condition specification and generates the corresponding SQL expression.
* @param string|array $condition the condition specification. Please refer to [[Query::where()]]
* on how to specify a condition.
* @param array $params the binding parameters to be populated
* @return string the generated SQL expression
* @throws \yii\db\Exception if the condition is in bad format
*/
public
function
buildCondition
(
&
$query
,
$condition
)
{
static
$builders
=
array
(
'AND'
=>
'buildAndCondition'
,
'OR'
=>
'buildAndCondition'
,
'BETWEEN'
=>
'buildBetweenCondition'
,
'NOT BETWEEN'
=>
'buildBetweenCondition'
,
'IN'
=>
'buildInCondition'
,
'NOT IN'
=>
'buildInCondition'
,
'LIKE'
=>
'buildLikeCondition'
,
'NOT LIKE'
=>
'buildLikeCondition'
,
'OR LIKE'
=>
'buildLikeCondition'
,
'OR NOT LIKE'
=>
'buildLikeCondition'
,
);
if
(
empty
(
$condition
))
{
return
;
}
if
(
!
is_array
(
$condition
))
{
throw
new
NotSupportedException
(
'String conditions are not supported by elasticsearch.'
);
}
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
);
$this
->
$method
(
$query
,
$operator
,
$condition
);
}
else
{
throw
new
Exception
(
'Found unknown operator in query: '
.
$operator
);
}
}
else
{
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
$this
->
buildHashCondition
(
$query
,
$condition
);
}
}
private
function
buildHashCondition
(
&
$query
,
$condition
)
{
$query
[
'query'
][
'term'
]
=
$condition
;
return
;
// TODO more
$parts
=
array
();
foreach
(
$condition
as
$column
=>
$value
)
{
if
(
is_array
(
$value
))
{
// IN condition
$parts
[]
=
$this
->
buildInCondition
(
'IN'
,
array
(
$column
,
$value
),
$params
);
}
else
{
if
(
$value
===
null
)
{
$parts
[]
=
"
$column
IS NULL"
;
// TODO null
}
elseif
(
$value
instanceof
Expression
)
{
$parts
[]
=
"
$column
="
.
$value
->
expression
;
foreach
(
$value
->
params
as
$n
=>
$v
)
{
$params
[
$n
]
=
$v
;
}
}
else
{
$phName
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$parts
[]
=
"
$column
=
$phName
"
;
$params
[
$phName
]
=
$value
;
}
}
}
return
count
(
$parts
)
===
1
?
$parts
[
0
]
:
'('
.
implode
(
') AND ('
,
$parts
)
.
')'
;
}
private
function
buildAndCondition
(
$operator
,
$operands
,
&
$params
)
{
$parts
=
array
();
foreach
(
$operands
as
$operand
)
{
if
(
is_array
(
$operand
))
{
$operand
=
$this
->
buildCondition
(
$operand
,
$params
);
}
if
(
$operand
!==
''
)
{
$parts
[]
=
$operand
;
}
}
if
(
!
empty
(
$parts
))
{
return
'('
.
implode
(
")
$operator
("
,
$parts
)
.
')'
;
}
else
{
return
''
;
}
}
private
function
buildBetweenCondition
(
$operator
,
$operands
,
&
$params
)
{
if
(
!
isset
(
$operands
[
0
],
$operands
[
1
],
$operands
[
2
]))
{
throw
new
Exception
(
"Operator '
$operator
' requires three operands."
);
}
list
(
$column
,
$value1
,
$value2
)
=
$operands
;
if
(
strpos
(
$column
,
'('
)
===
false
)
{
$column
=
$this
->
db
->
quoteColumnName
(
$column
);
}
$phName1
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$params
[
$phName1
]
=
$value1
;
$phName2
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$params
[
$phName2
]
=
$value2
;
return
"
$column
$operator
$phName1
AND
$phName2
"
;
}
private
function
buildInCondition
(
$operator
,
$operands
,
&
$params
)
{
if
(
!
isset
(
$operands
[
0
],
$operands
[
1
]))
{
throw
new
Exception
(
"Operator '
$operator
' requires two operands."
);
}
list
(
$column
,
$values
)
=
$operands
;
$values
=
(
array
)
$values
;
if
(
empty
(
$values
)
||
$column
===
array
())
{
return
$operator
===
'IN'
?
'0=1'
:
''
;
}
if
(
count
(
$column
)
>
1
)
{
return
$this
->
buildCompositeInCondition
(
$operator
,
$column
,
$values
,
$params
);
}
elseif
(
is_array
(
$column
))
{
$column
=
reset
(
$column
);
}
foreach
(
$values
as
$i
=>
$value
)
{
if
(
is_array
(
$value
))
{
$value
=
isset
(
$value
[
$column
])
?
$value
[
$column
]
:
null
;
}
if
(
$value
===
null
)
{
$values
[
$i
]
=
'NULL'
;
}
elseif
(
$value
instanceof
Expression
)
{
$values
[
$i
]
=
$value
->
expression
;
foreach
(
$value
->
params
as
$n
=>
$v
)
{
$params
[
$n
]
=
$v
;
}
}
else
{
$phName
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$params
[
$phName
]
=
$value
;
$values
[
$i
]
=
$phName
;
}
}
if
(
strpos
(
$column
,
'('
)
===
false
)
{
$column
=
$this
->
db
->
quoteColumnName
(
$column
);
}
if
(
count
(
$values
)
>
1
)
{
return
"
$column
$operator
("
.
implode
(
', '
,
$values
)
.
')'
;
}
else
{
$operator
=
$operator
===
'IN'
?
'='
:
'<>'
;
return
"
$column$operator
{
$values
[
0
]
}
"
;
}
}
protected
function
buildCompositeInCondition
(
$operator
,
$columns
,
$values
,
&
$params
)
{
$vss
=
array
();
foreach
(
$values
as
$value
)
{
$vs
=
array
();
foreach
(
$columns
as
$column
)
{
if
(
isset
(
$value
[
$column
]))
{
$phName
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$params
[
$phName
]
=
$value
[
$column
];
$vs
[]
=
$phName
;
}
else
{
$vs
[]
=
'NULL'
;
}
}
$vss
[]
=
'('
.
implode
(
', '
,
$vs
)
.
')'
;
}
foreach
(
$columns
as
$i
=>
$column
)
{
if
(
strpos
(
$column
,
'('
)
===
false
)
{
$columns
[
$i
]
=
$this
->
db
->
quoteColumnName
(
$column
);
}
}
return
'('
.
implode
(
', '
,
$columns
)
.
")
$operator
("
.
implode
(
', '
,
$vss
)
.
')'
;
}
private
function
buildLikeCondition
(
$operator
,
$operands
,
&
$params
)
{
if
(
!
isset
(
$operands
[
0
],
$operands
[
1
]))
{
throw
new
Exception
(
"Operator '
$operator
' requires two operands."
);
}
list
(
$column
,
$values
)
=
$operands
;
$values
=
(
array
)
$values
;
if
(
empty
(
$values
))
{
return
$operator
===
'LIKE'
||
$operator
===
'OR LIKE'
?
'0=1'
:
''
;
}
if
(
$operator
===
'LIKE'
||
$operator
===
'NOT LIKE'
)
{
$andor
=
' AND '
;
}
else
{
$andor
=
' OR '
;
$operator
=
$operator
===
'OR LIKE'
?
'LIKE'
:
'NOT LIKE'
;
}
if
(
strpos
(
$column
,
'('
)
===
false
)
{
$column
=
$this
->
db
->
quoteColumnName
(
$column
);
}
$parts
=
array
();
foreach
(
$values
as
$value
)
{
$phName
=
self
::
PARAM_PREFIX
.
count
(
$params
);
$params
[
$phName
]
=
$value
;
$parts
[]
=
"
$column
$operator
$phName
"
;
}
return
implode
(
$andor
,
$parts
);
}
}
tests/unit/framework/elasticsearch/ActiveRecordTest.php
View file @
955bf7da
...
...
@@ -3,6 +3,7 @@
namespace
yiiunit\framework\elasticsearch
;
use
yii\db\Query
;
use
yii\elasticsearch\Connection
;
use
yii\redis\ActiveQuery
;
use
yiiunit\data\ar\elasticsearch\ActiveRecord
;
use
yiiunit\data\ar\elasticsearch\Customer
;
...
...
@@ -15,7 +16,12 @@ class ActiveRecordTest extends ElasticSearchTestCase
public
function
setUp
()
{
parent
::
setUp
();
ActiveRecord
::
$db
=
$this
->
getConnection
();
/** @var Connection $db */
$db
=
ActiveRecord
::
$db
=
$this
->
getConnection
();
// delete all indexes
$db
->
http
()
->
delete
(
'_all'
)
->
send
();
$customer
=
new
Customer
();
$customer
->
setAttributes
(
array
(
'id'
=>
1
,
'email'
=>
'user1@example.com'
,
'name'
=>
'user1'
,
'address'
=>
'address1'
,
'status'
=>
1
),
false
);
...
...
@@ -236,122 +242,122 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this
->
assertFalse
(
Customer
::
find
()
->
where
(
array
(
'id'
=>
5
))
->
exists
());
}
public
function
testFindLazy
()
{
/** @var $customer Customer */
$customer
=
Customer
::
find
(
2
);
$orders
=
$customer
->
orders
;
$this
->
assertEquals
(
2
,
count
(
$orders
));
$orders
=
$customer
->
getOrders
()
->
where
(
array
(
'id'
=>
3
))
->
all
();
$this
->
assertEquals
(
1
,
count
(
$orders
));
$this
->
assertEquals
(
3
,
$orders
[
0
]
->
id
);
}
public
function
testFindEager
()
{
$customers
=
Customer
::
find
()
->
with
(
'orders'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$customers
));
$this
->
assertEquals
(
1
,
count
(
$customers
[
0
]
->
orders
));
$this
->
assertEquals
(
2
,
count
(
$customers
[
1
]
->
orders
));
}
public
function
testFindLazyVia
()
{
/** @var $order Order */
$order
=
Order
::
find
(
1
);
$this
->
assertEquals
(
1
,
$order
->
id
);
$this
->
assertEquals
(
2
,
count
(
$order
->
items
));
$this
->
assertEquals
(
1
,
$order
->
items
[
0
]
->
id
);
$this
->
assertEquals
(
2
,
$order
->
items
[
1
]
->
id
);
$order
=
Order
::
find
(
1
);
$order
->
id
=
100
;
$this
->
assertEquals
(
array
(),
$order
->
items
);
}
public
function
testFindEagerViaRelation
()
{
$orders
=
Order
::
find
()
->
with
(
'items'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$orders
));
$order
=
$orders
[
0
];
$this
->
assertEquals
(
1
,
$order
->
id
);
$this
->
assertEquals
(
2
,
count
(
$order
->
items
));
$this
->
assertEquals
(
1
,
$order
->
items
[
0
]
->
id
);
$this
->
assertEquals
(
2
,
$order
->
items
[
1
]
->
id
);
}
public
function
testFindNestedRelation
()
{
$customers
=
Customer
::
find
()
->
with
(
'orders'
,
'orders.items'
)
->
all
();
$this
->
assertEquals
(
3
,
count
(
$customers
));
$this
->
assertEquals
(
1
,
count
(
$customers
[
0
]
->
orders
));
$this
->
assertEquals
(
2
,
count
(
$customers
[
1
]
->
orders
));
$this
->
assertEquals
(
0
,
count
(
$customers
[
2
]
->
orders
));
$this
->
assertEquals
(
2
,
count
(
$customers
[
0
]
->
orders
[
0
]
->
items
));
$this
->
assertEquals
(
3
,
count
(
$customers
[
1
]
->
orders
[
0
]
->
items
));
$this
->
assertEquals
(
1
,
count
(
$customers
[
1
]
->
orders
[
1
]
->
items
));
}
public
function
testLink
()
{
$customer
=
Customer
::
find
(
2
);
$this
->
assertEquals
(
2
,
count
(
$customer
->
orders
));
// has many
$order
=
new
Order
;
$order
->
total
=
100
;
$this
->
assertTrue
(
$order
->
isNewRecord
);
$customer
->
link
(
'orders'
,
$order
);
$this
->
assertEquals
(
3
,
count
(
$customer
->
orders
));
$this
->
assertFalse
(
$order
->
isNewRecord
);
$this
->
assertEquals
(
3
,
count
(
$customer
->
getOrders
()
->
all
()));
$this
->
assertEquals
(
2
,
$order
->
customer_id
);
// belongs to
$order
=
new
Order
;
$order
->
total
=
100
;
$this
->
assertTrue
(
$order
->
isNewRecord
);
$customer
=
Customer
::
find
(
1
);
$this
->
assertNull
(
$order
->
customer
);
$order
->
link
(
'customer'
,
$customer
);
$this
->
assertFalse
(
$order
->
isNewRecord
);
$this
->
assertEquals
(
1
,
$order
->
customer_id
);
$this
->
assertEquals
(
1
,
$order
->
customer
->
id
);
// via model
$order
=
Order
::
find
(
1
);
$this
->
assertEquals
(
2
,
count
(
$order
->
items
));
$this
->
assertEquals
(
2
,
count
(
$order
->
orderItems
));
$orderItem
=
OrderItem
::
find
(
array
(
'order_id'
=>
1
,
'item_id'
=>
3
));
$this
->
assertNull
(
$orderItem
);
$item
=
Item
::
find
(
3
);
$order
->
link
(
'items'
,
$item
,
array
(
'quantity'
=>
10
,
'subtotal'
=>
100
));
$this
->
assertEquals
(
3
,
count
(
$order
->
items
));
$this
->
assertEquals
(
3
,
count
(
$order
->
orderItems
));
$orderItem
=
OrderItem
::
find
(
array
(
'order_id'
=>
1
,
'item_id'
=>
3
));
$this
->
assertTrue
(
$orderItem
instanceof
OrderItem
);
$this
->
assertEquals
(
10
,
$orderItem
->
quantity
);
$this
->
assertEquals
(
100
,
$orderItem
->
subtotal
);
}
public
function
testUnlink
()
{
// has many
$customer
=
Customer
::
find
(
2
);
$this
->
assertEquals
(
2
,
count
(
$customer
->
orders
));
$customer
->
unlink
(
'orders'
,
$customer
->
orders
[
1
],
true
);
$this
->
assertEquals
(
1
,
count
(
$customer
->
orders
));
$this
->
assertNull
(
Order
::
find
(
3
));
// via model
$order
=
Order
::
find
(
2
);
$this
->
assertEquals
(
3
,
count
(
$order
->
items
));
$this
->
assertEquals
(
3
,
count
(
$order
->
orderItems
));
$order
->
unlink
(
'items'
,
$order
->
items
[
2
],
true
);
$this
->
assertEquals
(
2
,
count
(
$order
->
items
));
$this
->
assertEquals
(
2
,
count
(
$order
->
orderItems
));
}
//
public function testFindLazy()
//
{
//
/** @var $customer Customer */
//
$customer = Customer::find(2);
//
$orders = $customer->orders;
//
$this->assertEquals(2, count($orders));
//
//
$orders = $customer->getOrders()->where(array('id' => 3))->all();
//
$this->assertEquals(1, count($orders));
//
$this->assertEquals(3, $orders[0]->id);
//
}
//
//
public function testFindEager()
//
{
//
$customers = Customer::find()->with('orders')->all();
//
$this->assertEquals(3, count($customers));
//
$this->assertEquals(1, count($customers[0]->orders));
//
$this->assertEquals(2, count($customers[1]->orders));
//
}
//
//
public function testFindLazyVia()
//
{
//
/** @var $order Order */
//
$order = Order::find(1);
//
$this->assertEquals(1, $order->id);
//
$this->assertEquals(2, count($order->items));
//
$this->assertEquals(1, $order->items[0]->id);
//
$this->assertEquals(2, $order->items[1]->id);
//
//
$order = Order::find(1);
//
$order->id = 100;
//
$this->assertEquals(array(), $order->items);
//
}
//
//
public function testFindEagerViaRelation()
//
{
//
$orders = Order::find()->with('items')->all();
//
$this->assertEquals(3, count($orders));
//
$order = $orders[0];
//
$this->assertEquals(1, $order->id);
//
$this->assertEquals(2, count($order->items));
//
$this->assertEquals(1, $order->items[0]->id);
//
$this->assertEquals(2, $order->items[1]->id);
//
}
//
//
public function testFindNestedRelation()
//
{
//
$customers = Customer::find()->with('orders', 'orders.items')->all();
//
$this->assertEquals(3, count($customers));
//
$this->assertEquals(1, count($customers[0]->orders));
//
$this->assertEquals(2, count($customers[1]->orders));
//
$this->assertEquals(0, count($customers[2]->orders));
//
$this->assertEquals(2, count($customers[0]->orders[0]->items));
//
$this->assertEquals(3, count($customers[1]->orders[0]->items));
//
$this->assertEquals(1, count($customers[1]->orders[1]->items));
//
}
//
//
public function testLink()
//
{
//
$customer = Customer::find(2);
//
$this->assertEquals(2, count($customer->orders));
//
//
// has many
//
$order = new Order;
//
$order->total = 100;
//
$this->assertTrue($order->isNewRecord);
//
$customer->link('orders', $order);
//
$this->assertEquals(3, count($customer->orders));
//
$this->assertFalse($order->isNewRecord);
//
$this->assertEquals(3, count($customer->getOrders()->all()));
//
$this->assertEquals(2, $order->customer_id);
//
//
// belongs to
//
$order = new Order;
//
$order->total = 100;
//
$this->assertTrue($order->isNewRecord);
//
$customer = Customer::find(1);
//
$this->assertNull($order->customer);
//
$order->link('customer', $customer);
//
$this->assertFalse($order->isNewRecord);
//
$this->assertEquals(1, $order->customer_id);
//
$this->assertEquals(1, $order->customer->id);
//
//
// via model
//
$order = Order::find(1);
//
$this->assertEquals(2, count($order->items));
//
$this->assertEquals(2, count($order->orderItems));
//
$orderItem = OrderItem::find(array('order_id' => 1, 'item_id' => 3));
//
$this->assertNull($orderItem);
//
$item = Item::find(3);
//
$order->link('items', $item, array('quantity' => 10, 'subtotal' => 100));
//
$this->assertEquals(3, count($order->items));
//
$this->assertEquals(3, count($order->orderItems));
//
$orderItem = OrderItem::find(array('order_id' => 1, 'item_id' => 3));
//
$this->assertTrue($orderItem instanceof OrderItem);
//
$this->assertEquals(10, $orderItem->quantity);
//
$this->assertEquals(100, $orderItem->subtotal);
//
}
//
//
public function testUnlink()
//
{
//
// has many
//
$customer = Customer::find(2);
//
$this->assertEquals(2, count($customer->orders));
//
$customer->unlink('orders', $customer->orders[1], true);
//
$this->assertEquals(1, count($customer->orders));
//
$this->assertNull(Order::find(3));
//
//
// via model
//
$order = Order::find(2);
//
$this->assertEquals(3, count($order->items));
//
$this->assertEquals(3, count($order->orderItems));
//
$order->unlink('items', $order->items[2], true);
//
$this->assertEquals(2, count($order->items));
//
$this->assertEquals(2, count($order->orderItems));
//
}
public
function
testInsertNoPk
()
{
...
...
@@ -410,46 +416,19 @@ class ActiveRecordTest extends ElasticSearchTestCase
$this
->
assertEquals
(
'temp'
,
$customer
->
name
);
}
public
function
testUpdateCounters
()
{
// updateCounters
$pk
=
array
(
'order_id'
=>
2
,
'item_id'
=>
4
);
$orderItem
=
OrderItem
::
find
(
$pk
);
$this
->
assertEquals
(
1
,
$orderItem
->
quantity
);
$ret
=
$orderItem
->
updateCounters
(
array
(
'quantity'
=>
-
1
));
$this
->
assertTrue
(
$ret
);
$this
->
assertEquals
(
0
,
$orderItem
->
quantity
);
$orderItem
=
OrderItem
::
find
(
$pk
);
$this
->
assertEquals
(
0
,
$orderItem
->
quantity
);
// updateAllCounters
$pk
=
array
(
'order_id'
=>
1
,
'item_id'
=>
2
);
$orderItem
=
OrderItem
::
find
(
$pk
);
$this
->
assertEquals
(
2
,
$orderItem
->
quantity
);
$ret
=
OrderItem
::
updateAllCounters
(
array
(
'quantity'
=>
3
,
'subtotal'
=>
-
10
,
),
$pk
);
$this
->
assertEquals
(
1
,
$ret
);
$orderItem
=
OrderItem
::
find
(
$pk
);
$this
->
assertEquals
(
5
,
$orderItem
->
quantity
);
$this
->
assertEquals
(
30
,
$orderItem
->
subtotal
);
}
public
function
testUpdatePk
()
{
// updateCounters
$pk
=
array
(
'order_id'
=>
2
,
'item_id'
=>
4
);
$orderItem
=
OrderItem
::
find
(
$pk
);
$this
->
assertEquals
(
2
,
$orderItem
->
order_id
);
$this
->
assertEquals
(
4
,
$orderItem
->
item_id
);
$orderItem
->
order_id
=
2
;
$orderItem
->
item_id
=
10
;
$this
->
setExpectedException
(
'yii\base\NotSupportedException'
);
$pk
=
array
(
'id'
=>
2
);
$orderItem
=
Order
::
find
(
$pk
);
$this
->
assertEquals
(
2
,
$orderItem
->
id
);
$orderItem
->
id
=
13
;
$orderItem
->
save
();
$this
->
assertNull
(
OrderItem
::
find
(
$pk
));
$this
->
assertNotNull
(
OrderItem
::
find
(
array
(
'
order_id'
=>
2
,
'item_id'
=>
10
)));
$this
->
assertNotNull
(
OrderItem
::
find
(
array
(
'
id'
=>
13
)));
}
public
function
testDelete
()
...
...
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