Commit 983b2286 by Carsten Brandt

elasticsearch AR relations + null values

parent 58b1538b
...@@ -129,4 +129,38 @@ class ActiveQuery extends Query implements ActiveQueryInterface ...@@ -129,4 +129,38 @@ class ActiveQuery extends Query implements ActiveQueryInterface
} }
return $model; return $model;
} }
/**
* @inheritDocs
*/
public function scalar($field, $db = null)
{
$record = parent::one($db);
if ($record !== false) {
if ($field == 'primaryKey') {
return $record['_id'];
} elseif (isset($record['_source'][$field])) {
return $record['_source'][$field];
}
}
return null;
}
/**
* @inheritDocs
*/
public function column($field, $db = null)
{
if ($field == 'primaryKey') {
$command = $this->createCommand($db);
$command->queryParts['fields'] = [];
$rows = $command->queryAll()['hits'];
$result = [];
foreach ($rows as $row) {
$result[] = $row['_id'];
}
return $result;
}
return parent::column($field, $db);
}
} }
...@@ -312,16 +312,21 @@ class ActiveRecord extends \yii\db\ActiveRecord ...@@ -312,16 +312,21 @@ class ActiveRecord extends \yii\db\ActiveRecord
* @param array $attributes attribute values (name-value pairs) to be saved into the table * @param array $attributes attribute values (name-value pairs) to be saved into the table
* @param array $condition the conditions that will be put in the WHERE part of the UPDATE SQL. * @param array $condition the conditions that will be put in the WHERE part of the UPDATE SQL.
* Please refer to [[ActiveQuery::where()]] on how to specify this parameter. * Please refer to [[ActiveQuery::where()]] on how to specify this parameter.
* @param array $params this parameter is ignored in redis implementation. * @param array $params this parameter is ignored in elasticsearch implementation.
* @return integer the number of rows updated * @return integer the number of rows updated
*/ */
public static function updateAll($attributes, $condition = [], $params = []) public static function updateAll($attributes, $condition = [], $params = [])
{ {
if (empty($condition)) { if (count($condition) == 1 && isset($condition['primaryKey'])) {
$primaryKeys = (array) $condition['primaryKey'];
} else {
$primaryKeys = static::find()->where($condition)->column('primaryKey');
}
if (empty($primaryKeys)) {
return 0; return 0;
} }
$bulk = ''; $bulk = '';
foreach((array) $condition as $pk) { foreach((array) $primaryKeys as $pk) {
$action = Json::encode([ $action = Json::encode([
"update" => [ "update" => [
"_id" => $pk, "_id" => $pk,
...@@ -362,16 +367,21 @@ class ActiveRecord extends \yii\db\ActiveRecord ...@@ -362,16 +367,21 @@ class ActiveRecord extends \yii\db\ActiveRecord
* *
* @param array $condition the conditions that will be put in the WHERE part of the DELETE SQL. * @param array $condition the conditions that will be put in the WHERE part of the DELETE SQL.
* Please refer to [[ActiveQuery::where()]] on how to specify this parameter. * Please refer to [[ActiveQuery::where()]] on how to specify this parameter.
* @param array $params this parameter is ignored in redis implementation. * @param array $params this parameter is ignored in elasticsearch implementation.
* @return integer the number of rows deleted * @return integer the number of rows deleted
*/ */
public static function deleteAll($condition = [], $params = []) public static function deleteAll($condition = [], $params = [])
{ {
if (empty($condition)) { if (count($condition) == 1 && isset($condition['primaryKey'])) {
$primaryKeys = (array) $condition['primaryKey'];
} else {
$primaryKeys = static::find()->where($condition)->column('primaryKey');
}
if (empty($primaryKeys)) {
return 0; return 0;
} }
$bulk = ''; $bulk = '';
foreach((array) $condition as $pk) { foreach((array) $primaryKeys as $pk) {
$bulk .= Json::encode([ $bulk .= Json::encode([
"delete" => [ "delete" => [
"_id" => $pk, "_id" => $pk,
......
...@@ -152,6 +152,13 @@ class QueryBuilder extends \yii\base\Object ...@@ -152,6 +152,13 @@ class QueryBuilder extends \yii\base\Object
{ {
$parts = []; $parts = [];
foreach($condition as $attribute => $value) { foreach($condition as $attribute => $value) {
if ($attribute == 'primaryKey') {
if ($value == null) { // there is no null pk
$parts[] = ['script' => ['script' => '0==1']];
} else {
$parts[] = ['ids' => ['values' => is_array($value) ? $value : [$value]]];
}
} else {
if (is_array($value)) { // IN condition if (is_array($value)) { // IN condition
$parts[] = ['in' => [$attribute => $value]]; $parts[] = ['in' => [$attribute => $value]];
} else { } else {
...@@ -162,6 +169,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -162,6 +169,7 @@ class QueryBuilder extends \yii\base\Object
} }
} }
} }
}
return count($parts) === 1 ? $parts[0] : ['and' => $parts]; return count($parts) === 1 ? $parts[0] : ['and' => $parts];
} }
...@@ -190,6 +198,9 @@ class QueryBuilder extends \yii\base\Object ...@@ -190,6 +198,9 @@ class QueryBuilder extends \yii\base\Object
} }
list($column, $value1, $value2) = $operands; list($column, $value1, $value2) = $operands;
if ($column == 'primaryKey') {
throw new NotSupportedException('Between condition is not supported for primaryKey.');
}
$filter = ['range' => [$column => ['gte' => $value1, 'lte' => $value2]]]; $filter = ['range' => [$column => ['gte' => $value1, 'lte' => $value2]]];
if ($operator == 'not between') { if ($operator == 'not between') {
$filter = ['not' => $filter]; $filter = ['not' => $filter];
...@@ -197,7 +208,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -197,7 +208,7 @@ class QueryBuilder extends \yii\base\Object
return $filter; return $filter;
} }
private function buildInCondition($operator, $operands, &$params) private function buildInCondition($operator, $operands)
{ {
if (!isset($operands[0], $operands[1])) { if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator '$operator' requires two operands."); throw new InvalidParamException("Operator '$operator' requires two operands.");
...@@ -208,7 +219,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -208,7 +219,7 @@ class QueryBuilder extends \yii\base\Object
$values = (array)$values; $values = (array)$values;
if (empty($values) || $column === []) { if (empty($values) || $column === []) {
return $operator === 'in' ? ['script' => ['script' => '0=1']] : []; return $operator === 'in' ? ['script' => ['script' => '0==1']] : [];
} }
if (count($column) > 1) { if (count($column) > 1) {
...@@ -226,21 +237,32 @@ class QueryBuilder extends \yii\base\Object ...@@ -226,21 +237,32 @@ class QueryBuilder extends \yii\base\Object
unset($values[$i]); unset($values[$i]);
} }
} }
if ($column == 'primaryKey') {
if (empty($values) && $canBeNull) { // there is no null pk
$filter = ['script' => ['script' => '0==1']];
} else {
$filter = ['ids' => ['values' => array_values($values)]];
if ($canBeNull) {
$filter = ['or' => [$filter, ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]]]];
}
}
} else {
if (empty($values) && $canBeNull) { if (empty($values) && $canBeNull) {
return ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]]; $filter = ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]];
} else { } else {
$filter = ['in' => [$column => $values]]; $filter = ['in' => [$column => array_values($values)]];
if ($canBeNull) { if ($canBeNull) {
$filter = ['or' => [$filter, ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]]]]; $filter = ['or' => [$filter, ['missing' => ['field' => $column, 'existence' => true, 'null_value' => true]]]];
} }
}
}
if ($operator == 'not in') { if ($operator == 'not in') {
$filter = ['not' => $filter]; $filter = ['not' => $filter];
} }
return $filter; return $filter;
} }
}
protected function buildCompositeInCondition($operator, $columns, $values, &$params) protected function buildCompositeInCondition($operator, $columns, $values)
{ {
throw new NotSupportedException('composite in is not supported by elasticsearch.'); throw new NotSupportedException('composite in is not supported by elasticsearch.');
$vss = array(); $vss = array();
...@@ -265,8 +287,9 @@ class QueryBuilder extends \yii\base\Object ...@@ -265,8 +287,9 @@ class QueryBuilder extends \yii\base\Object
return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')'; return '(' . implode(', ', $columns) . ") $operator (" . implode(', ', $vss) . ')';
} }
private function buildLikeCondition($operator, $operands, &$params) private function buildLikeCondition($operator, $operands)
{ {
throw new NotSupportedException('like conditions is not supported by elasticsearch.');
if (!isset($operands[0], $operands[1])) { if (!isset($operands[0], $operands[1])) {
throw new Exception("Operator '$operator' requires two operands."); throw new Exception("Operator '$operator' requires two operands.");
} }
...@@ -276,7 +299,7 @@ class QueryBuilder extends \yii\base\Object ...@@ -276,7 +299,7 @@ class QueryBuilder extends \yii\base\Object
$values = (array)$values; $values = (array)$values;
if (empty($values)) { if (empty($values)) {
return $operator === 'LIKE' || $operator === 'OR LIKE' ? '0=1' : ''; return $operator === 'LIKE' || $operator === 'OR LIKE' ? '0==1' : '';
} }
if ($operator === 'LIKE' || $operator === 'NOT LIKE') { if ($operator === 'LIKE' || $operator === 'NOT LIKE') {
......
...@@ -24,7 +24,7 @@ class Customer extends ActiveRecord ...@@ -24,7 +24,7 @@ class Customer extends ActiveRecord
public function getOrders() public function getOrders()
{ {
return $this->hasMany('Order', array('customer_id' => 'id'))->orderBy('id'); return $this->hasMany(Order::className(), array('customer_id' => 'primaryKey'))->orderBy('create_time');
} }
public static function active($query) public static function active($query)
......
...@@ -19,33 +19,31 @@ class Order extends ActiveRecord ...@@ -19,33 +19,31 @@ class Order extends ActiveRecord
public function getCustomer() public function getCustomer()
{ {
return $this->hasOne('Customer', ['id' => 'customer_id']); return $this->hasOne(Customer::className(), ['primaryKey' => 'customer_id']);
} }
public function getOrderItems() public function getOrderItems()
{ {
return $this->hasMany('OrderItem', ['order_id' => 'id']); return $this->hasMany(OrderItem::className(), ['order_id' => 'primaryKey']);
} }
public function getItems() public function getItems()
{ {
return $this->hasMany('Item', ['id' => 'item_id']) return $this->hasMany(Item::className(), ['primaryKey' => 'item_id'])
->via('orderItems', function ($q) { ->via('orderItems')->orderBy('name');
// additional query configuration
})->orderBy('id');
} }
public function getBooks() // public function getBooks()
{ // {
return $this->hasMany('Item', ['id' => 'item_id']) // return $this->hasMany('Item', ['primaryKey' => 'item_id'])
->viaTable('tbl_order_item', ['order_id' => 'id']) // ->viaTable('tbl_order_item', ['order_id' => 'primaryKey'])
->where(['category_id' => 1]); // ->where(['category_id' => 1]);
} // }
public function beforeSave($insert) public function beforeSave($insert)
{ {
if (parent::beforeSave($insert)) { if (parent::beforeSave($insert)) {
$this->create_time = time(); // $this->create_time = time();
return true; return true;
} else { } else {
return false; return false;
......
...@@ -19,11 +19,11 @@ class OrderItem extends ActiveRecord ...@@ -19,11 +19,11 @@ class OrderItem extends ActiveRecord
public function getOrder() public function getOrder()
{ {
return $this->hasOne('Order', ['id' => 'order_id']); return $this->hasOne(Order::className(), ['primaryKey' => 'order_id']);
} }
public function getItem() public function getItem()
{ {
return $this->hasOne('Item', ['id' => 'item_id']); return $this->hasOne(Item::className(), ['primaryKey' => 'item_id']);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment