Compare commits

...

9 Commits

Author SHA1 Message Date
Arsen Mirzaev Tatyano-Muradovich 8c8d34f5b2 Исправление версии пакета (без изменений) 2021-07-26 05:29:30 +10:00
Arsen Mirzaev Tatyano-Muradovich 96c80c1a77 Возможность создавать бесконечное количество FOR (foreach) 2021-07-26 05:27:33 +10:00
Arsen Mirzaev Tatyano-Muradovich 03bff66b62 Тест доступа к репозиторию 2021-06-30 14:44:54 +10:00
Arsen Mirzaev Tatyano-Muradovich b92e8792f0 Добавление фильтра STARTS_WIDTH для регистронезависимого поиска 2021-06-29 09:07:43 +10:00
Arsen Mirzaev Tatyano-Muradovich cbc26916ea Исправления 2021-04-12 05:27:43 +10:00
Arsen Mirzaev Tatyano-Muradovich b3a5b7b51f Исправление ошибок 2021-04-11 11:51:59 +10:00
Arsen Mirzaev Tatyano-Muradovich 778513b3bb Исправление ошибок и обработка for 2021-04-11 11:48:55 +10:00
Arsen Mirzaev Tatyano-Muradovich eb5e18dfd6 Исправление ошибки в genQuery() 2021-04-11 03:30:56 +10:00
Arsen Mirzaev Tatyano-Muradovich bb7b1b8f8e Улучшение логики запросов 2021-04-11 02:39:51 +10:00
4 changed files with 127 additions and 48 deletions

View File

@ -103,9 +103,11 @@ class ActiveQuery extends Query implements ActiveQueryInterface
public function all($db = null) public function all($db = null)
{ {
$statement = $this->createCommand($db); $statement = $this->createCommand($db);
$token = $this->getRawAql($statement); $token = $this->getRawAql($statement);
Yii::info($token, 'mirzaev\yii2\arangodb\Query::query'); Yii::info($token, 'mirzaev\yii2\arangodb\Query::query');
try { try {
Yii::beginProfile($token, 'mirzaev\yii2\arangodb\Query::query'); Yii::beginProfile($token, 'mirzaev\yii2\arangodb\Query::query');
$cursor = $statement->execute(); $cursor = $statement->execute();

View File

@ -23,7 +23,7 @@ abstract class Migration extends Component implements MigrationInterface
{ {
parent::init(); parent::init();
$this->db = Instance::ensure($this->db, Connection::class; $this->db = Instance::ensure($this->db, Connection::class);
} }
public function execute($aql, $bindValues = [], $params = []) public function execute($aql, $bindValues = [], $params = [])

View File

@ -81,6 +81,18 @@ class Query extends Component implements QueryInterface
*/ */
public string $searchType = 'START'; public string $searchType = 'START';
/**
* Поиск
*
* [свойство => его значение]
*/
public array $filter;
/**
* Тип поиска
*/
public string $filterType = 'START';
public $orderBy; public $orderBy;
public $indexBy; public $indexBy;
@ -229,14 +241,21 @@ class Query extends Component implements QueryInterface
return $this; return $this;
} }
/**
*/
public function filter(array $text, string $type = 'START'): self
{
$this->filter = $text;
$this->filterType = $type;
return $this;
}
/** /**
* Обойти коллекцию вершин по направлению * Обойти коллекцию вершин по направлению
* *
* Генерация AQL выражения * Генерация AQL выражения
* *
* @see https://www.arangodb.com/docs/3.7/aql/operations-let.html
*
* @param mixed $vertex Коллекция вершин из которой требуется обход * @param mixed $vertex Коллекция вершин из которой требуется обход
* @param string $direction Направление ('INBOUND', 'OUTBOUND', 'ANY') * @param string $direction Направление ('INBOUND', 'OUTBOUND', 'ANY')
*/ */
@ -253,24 +272,43 @@ class Query extends Component implements QueryInterface
return $this; return $this;
} }
/**
* Проверка типа и конвертация
*/
protected static function checkArrayAndConvert(string|array $text): string
{
if (is_array($text)) {
return self::convertArrayToString($text);
}
return $text;
}
/**
* Конвертация в строку
*/
protected static function convertArrayToString(array $text): string
{
// Очистка
array_walk($text, 'trim');
// Конвертация
return implode(", ", $text);
}
/** /**
* Генерация AQL конструкции "FOR" * Генерация AQL конструкции "FOR"
* *
* Примеры: * Примеры:
* 1. "FOR account" * 1. "FOR account"
* 2. "FOR account, account_edge_supply" * 2. "FOR account, account_edge_supply"
*
*/ */
protected static function genFor(string|array $for): string protected static function genFor(string|array $for): string
{ {
if (is_array($for)) { if (is_array($for)) {
// Если передан массив, то конвертировать в строку // Если передан массив, то конвертировать в строку
// Очистка элементов через trim() $for = self::convertArrayToString($for);
array_walk($for, 'trim');
// Конвертация
$for = implode(", ", $for);
} }
// Генерация // Генерация
@ -368,19 +406,17 @@ class Query extends Component implements QueryInterface
foreach ($conditions as $condition) { foreach ($conditions as $condition) {
// Перебор выражений // Перебор выражений
genForeach_recursion:
foreach ($condition as $FOR => $IN) { foreach ($condition as $FOR => $IN) {
// Инициализация операндов // Инициализация операндов
if (is_int($FOR) && is_array($IN)) { if (is_int($FOR) && is_array($IN)) {
// Вложенный массив (неожиданные входные данные) // Вложенный массив (неожиданные входные данные)
// Реинициализация // !!! Вход в рекурсию !!!
$condition = $IN; // Обработка вложенного массива
$aql .= ' ' . $this->genForeach([$IN]);
// Перебор вложенного массива continue;
goto genForeach_recursion;
} }
$aql .= "FOR $FOR IN $IN "; $aql .= "FOR $FOR IN $IN ";
@ -819,23 +855,24 @@ class Query extends Component implements QueryInterface
protected function genQuery($query = null, array $params = []) protected function genQuery($query = null, array $params = [])
{ {
// Инициализация // Инициализация
isset($query) ? $query : $query = $this; $query ?? $query = $this;
$this->in ?? (isset($this->collection) ? $this->in = $this->collection : throw new Exception('Не найдена коллекция')); $query->in ?? $query->in = $query->collection ?? throw new Exception('Не найдена коллекция');
$this->for ?? $this->for = $this->in; $query->for ?? $query->for = $query->in;
$this->collection ?? $this->collection = $this->in; $query->collection ?? $query->collection = self::checkArrayAndConvert($query->for);
$params = array_merge($params, $query->params); $params = array_merge($params, $query->params);
$clauses = [ $clauses = [
static::genFor($query->for ?? $query->collection), $query::genFor($query->for),
static::genIn($query->in ?? $query->collection, $query->traversals), $query::genIn($query->in, $query->traversals),
static::genLet($query->lets), $query::genLet($query->lets),
$this->genForeach($query->foreach), $query->genForeach($query->foreach),
$this->genWhere($query->where, $params), $query->genWhere($query->where, $params),
isset($this->search) ? $this->genSearch($this->search, $this->searchType) : null, isset($query->search) ? $query->genSearch($query->search, $query->searchType) : null,
$this->genOrderBy($query->orderBy, $params), isset($query->filter) ? $query->genFilter($query->filter, $query->filterType) : null,
$this->genLimit($query->limit, $query->offset, $params), $query->genOrderBy($query->orderBy, $params),
$this->genSelect($query->select, $params), $query->genLimit($query->limit, $query->offset, $params),
$query->genSelect($query->select, $params),
]; ];
$aql = implode($query->separator, array_filter($clauses)); $aql = implode($query->separator, array_filter($clauses));
@ -874,9 +911,11 @@ class Query extends Component implements QueryInterface
public function all($db = null) public function all($db = null)
{ {
$statement = $this->createCommand($db); $statement = $this->createCommand($db);
$token = $this->getRawAql($statement); $token = $this->getRawAql($statement);
Yii::info($token, 'mirzaev\yii2\arangodb\Query::query'); Yii::info($token, 'mirzaev\yii2\arangodb\Query::query');
try { try {
Yii::beginProfile($token, 'mirzaev\yii2\arangodb\Query::query'); Yii::beginProfile($token, 'mirzaev\yii2\arangodb\Query::query');
$cursor = $statement->execute(); $cursor = $statement->execute();
@ -885,6 +924,7 @@ class Query extends Component implements QueryInterface
Yii::endProfile($token, 'mirzaev\yii2\arangodb\Query::query'); Yii::endProfile($token, 'mirzaev\yii2\arangodb\Query::query');
throw new Exception($ex->getMessage(), (int) $ex->getCode(), $ex); throw new Exception($ex->getMessage(), (int) $ex->getCode(), $ex);
} }
return $this->prepareResult($cursor->getAll()); return $this->prepareResult($cursor->getAll());
} }
@ -894,10 +934,13 @@ class Query extends Component implements QueryInterface
public function one($db = null) public function one($db = null)
{ {
$this->limit(1); $this->limit(1);
$statement = $this->createCommand($db); $statement = $this->createCommand($db);
$token = $this->getRawAql($statement); $token = $this->getRawAql($statement);
Yii::info($token, 'mirzaev\yii2\arangodb\Query::query'); Yii::info($token, 'mirzaev\yii2\arangodb\Query::query');
try { try {
Yii::beginProfile($token, 'mirzaev\yii2\arangodb\Query::query'); Yii::beginProfile($token, 'mirzaev\yii2\arangodb\Query::query');
$cursor = $statement->execute(); $cursor = $statement->execute();
@ -922,13 +965,13 @@ class Query extends Component implements QueryInterface
public function insert($columns, $params = [], $db = null) public function insert($columns, $params = [], $db = null)
{ {
// Инициализация // Инициализация
$this->in ?? (isset($this->collection) ? $this->in = $this->collection : throw new Exception('Не найдена коллекция')); $this->in ?? $this->in = $this->collection ?? throw new Exception('Не найдена коллекция');
$this->collection ?? $this->collection = $this->in; $this->collection ?? $this->collection = $this->in;
$data = Serializer::encode($columns); $data = Serializer::encode($columns);
$clauses = [ $clauses = [
"INSERT $data IN {$this->quoteCollectionName($this->in ?? $this->collection)}", "INSERT $data IN {$this->quoteCollectionName($this->collection)}",
$this->genOptions(), $this->genOptions(),
]; ];
@ -968,15 +1011,15 @@ class Query extends Component implements QueryInterface
public function update($columns, $params = [], $db = null) public function update($columns, $params = [], $db = null)
{ {
// Инициализация // Инициализация
$this->in ?? (isset($this->collection) ? $this->in = $this->collection : throw new Exception('Не найдена коллекция')); $this->in ?? $this->in = $this->collection ?? throw new Exception('Не найдена коллекция');
$this->for ?? $this->for = $this->in; $this->for ?? $this->for = $this->in;
$this->collection ?? $this->collection = $this->in; $this->collection ?? $this->collection = self::checkArrayAndConvert($this->for);
$clauses = [ $clauses = [
static::genFor($this->for ?? $this->collection), static::genFor($this->for),
static::genIn($this->in ?? $this->collection, $this->traversals), static::genIn($this->in, $this->traversals),
$this->genWhere($this->where, $params), $this->genWhere($this->where, $params),
$this->genUpdate($this->collection, $columns), $this->genUpdate($this->in, $columns),
$this->genOptions(), $this->genOptions(),
]; ];
@ -1020,15 +1063,15 @@ class Query extends Component implements QueryInterface
public function remove($params = [], $db = null) public function remove($params = [], $db = null)
{ {
// Инициализация // Инициализация
$this->in ?? (isset($this->collection) ? $this->in = $this->collection : throw new Exception('Не найдена коллекция')); $this->in ?? $this->in = $this->collection ?? throw new Exception('Не найдена коллекция');
$this->for ?? $this->for = $this->in; $this->for ?? $this->for = $this->in;
$this->collection ?? $this->collection = $this->in; $this->collection ?? $this->collection = self::checkArrayAndConvert($this->for);
$clauses = [ $clauses = [
static::genFor($this->for ?? $this->collection), static::genFor($this->for),
static::genIn($this->in ?? $this->collection, $this->traversals), static::genIn($this->in, $this->traversals),
$this->genWhere($this->where, $params), $this->genWhere($this->where, $params),
$this->genRemove($this->in ?? $this->collection), $this->genRemove($this->in),
$this->genOptions(), $this->genOptions(),
]; ];
@ -1091,6 +1134,24 @@ class Query extends Component implements QueryInterface
return match (strtoupper($type)) { return match (strtoupper($type)) {
'START', 'STARTS', 'STARTS_WITH' => $query . $this->filterStartsWith($expression), 'START', 'STARTS', 'STARTS_WITH' => $query . $this->filterStartsWith($expression),
'START_SENSETIVE' => $query . $this->filterStartsWith($expression, sensetive: true),
'CONTAINS', 'LIKE' => $query . $this->filterContains($expression),
default => $query . Serializer::encode($expression)
};
}
/**
* @param $collection
* @param $columns
* @return string
*/
protected function genFilter(array $expression, string $type = 'START'): string
{
$query = 'FILTER ';
return match (strtoupper($type)) {
'START', 'STARTS', 'STARTS_WITH' => $query . $this->filterStartsWith($expression),
'START_SENSETIVE' => $query . $this->filterStartsWith($expression, sensetive: true),
'CONTAINS', 'LIKE' => $query . $this->filterContains($expression), 'CONTAINS', 'LIKE' => $query . $this->filterContains($expression),
default => $query . Serializer::encode($expression) default => $query . Serializer::encode($expression)
}; };
@ -1133,8 +1194,6 @@ class Query extends Component implements QueryInterface
* *
* Генерация AQL выражения * Генерация AQL выражения
* *
* @see https://www.arangodb.com/docs/3.7/aql/operations-let.html
*
* @param string $direction Направление * @param string $direction Направление
* @param mixed $vertex Коллекция вершин из которой требуется обход * @param mixed $vertex Коллекция вершин из которой требуется обход
*/ */
@ -1279,7 +1338,7 @@ class Query extends Component implements QueryInterface
{ {
$this->foreach = match (true) { $this->foreach = match (true) {
empty($this->foreach) => [$expression], empty($this->foreach) => [$expression],
default => $this->foreach []= [$expression] default => $this->foreach[] = [$expression]
}; };
return $this; return $this;
@ -1375,20 +1434,38 @@ class Query extends Component implements QueryInterface
return $this; return $this;
} }
public function filterStartsWith(array $expression): string public function filterStartsWith(array $expression, bool $sensetive = false): string
{ {
// Генерация // Генерация
foreach ($expression as $key => $value) { foreach ($expression as $key => $value) {
if (isset($return)) { if ($sensetive) {
$return .= ' OR STARTS_WITH(' . $this->quoteCollectionName($this->collection) . ".$key, \"$value\")"; if (isset($return)) {
$return .= ' OR STARTS_WITH(' . $this->filterLower($this->quoteCollectionName($this->collection) . ".$key") . ", " . $this->filterLower("\"$value\"") . ")";
} else {
$return = 'STARTS_WITH(' . $this->filterLower($this->quoteCollectionName($this->collection) . ".$key") . ", " . $this->filterLower("\"$value\"") . ")";
}
} else { } else {
$return = 'STARTS_WITH(' . $this->quoteCollectionName($this->collection) . ".$key, \"$value\")"; if (isset($return)) {
$return .= ' OR STARTS_WITH(' . $this->quoteCollectionName($this->collection) . ".$key, \"$value\")";
} else {
$return = 'STARTS_WITH(' . $this->quoteCollectionName($this->collection) . ".$key, \"$value\")";
}
} }
} }
return $return; return $return;
} }
public function filterUpper(string $target): string
{
return "UPPER($target)";
}
public function filterLower(string $target): string
{
return "LOWER($target)";
}
public function filterContains(array $expression): string public function filterContains(array $expression): string
{ {
// Инициализация // Инициализация