@ -74,12 +74,19 @@ class Query extends Component implements QueryInterface
*
*
* [ свойство => е г о значение ]
* [ свойство => е г о значение ]
*/
*/
public array $search ;
public array | string $search ;
/**
/**
* Тип поиска
* Фильтр
*
* [ свойство => е г о значение ]
*/
*/
public string $searchType = 'START' ;
public array | string $filter ;
/**
* Отладка
*/
public bool $debug = false ;
public $orderBy ;
public $orderBy ;
@ -145,6 +152,11 @@ class Query extends Component implements QueryInterface
]
]
);
);
if ( $this -> debug ) {
var_dump ( $aql );
die ;
}
return $this -> getStatement ( $options , $db );
return $this -> getStatement ( $options , $db );
}
}
@ -221,22 +233,27 @@ class Query extends Component implements QueryInterface
/**
/**
*/
*/
public function search ( array $text , string $type = 'START' ) : self
public function search ( array | string $expressions ) : self
{
{
$this -> search = $text ;
$this -> search = $expressions ;
$this -> searchType = $type ;
return $this ;
return $this ;
}
}
/**
*/
public function filter ( array | string $expressions ) : self
{
$this -> filter = $expressions ;
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 +270,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 +404,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 " ;
@ -751,8 +785,13 @@ class Query extends Component implements QueryInterface
return '' ;
return '' ;
}
}
$orders = [];
$orders = [];
foreach ( $columns as $name => $direction ) {
foreach ( $columns as $name => $direction ) {
$orders [] = $this -> quoteColumnName ( $name ) . ( $direction === SORT_DESC ? ' DESC' : '' );
$orders [] = $this -> quoteColumnName ( $name ) . match ( $direction ) {
SORT_DESC => ' DESC' ,
SORT_ASC => ' ASC' ,
default => ''
};
}
}
return 'SORT ' . implode ( ', ' , $orders );
return 'SORT ' . implode ( ', ' , $orders );
@ -819,27 +858,33 @@ 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 ),
isset ( $query -> search ) ? $query -> genSearch ( $query -> search ) : null ,
isset ( $this -> search ) ? $this -> genSearch ( $this -> search , $this -> searchType ) : null ,
isset ( $query -> filter ) ? $query -> genFilter ( $query -> filter ) : null ,
$this -> genOrderBy ( $query -> orderBy , $params ),
$query -> genWhere ( $query -> where , $params ),
$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 ));
if ( $query -> debug ) {
var_dump ( $aql );
die ;
}
return self :: prepareBindVars ( $aql , $params );
return self :: prepareBindVars ( $aql , $params );
}
}
@ -874,9 +919,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 +932,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 +942,14 @@ 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 +974,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 +1020,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 -> collect io n, $columns ),
$this -> genUpdate ( $this -> in, $columns ),
$this -> genOptions (),
$this -> genOptions (),
];
];
@ -1020,15 +1072,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 (),
];
];
@ -1085,17 +1137,65 @@ class Query extends Component implements QueryInterface
* @ param $columns
* @ param $columns
* @ return string
* @ return string
*/
*/
protected function genSearch ( array $expression , string $type = 'START' ) : string
protected function genSearch ( array | string $expressions ) : string
{
{
if ( is_string ( $expressions )) return $expressions ;
// Инициализация строки запроса
$query = 'SEARCH ' ;
$query = 'SEARCH ' ;
return match ( strtoupper ( $type )) {
foreach ( $expressions as $expression ) {
'START' , 'STARTS' , 'STARTS_WITH' => $query . $this -> filterStartsWith ( $expression ),
// Перебор выражений
'CONTAINS' , 'LIKE' => $query . $this -> filterContains ( $expression ),
default => $query . Serializer :: encode ( $expression )
// Инициализация оператора
$operator = isset ( $expression [ 'operator' ]) ? ' ' . $expression [ 'operator' ] . ' ' : '' ;
// Генерация строки запроса
$query .= match ( strtoupper ( $expression [ 'type' ])) {
'START' , 'STARTS' , 'STARTS_WITH' => $operator . $this -> filterStartsWith ( $expression [ 'condition' ]),
'START_SENSETIVE' => $operator . $this -> filterStartsWith ( $expression [ 'condition' ], sensetive : true ),
'CONTAINS' , 'LIKE' => $operator . $this -> filterContains ( $expression [ 'condition' ]),
'LEVENSHTEIN' => $operator . $this -> filterLevenshtein ( $expression [ 'condition' ], ... $expression [ 'parameters' ]),
'PHRASE' => $operator . $this -> filterPhrase ( $expression [ 'condition' ], ... $expression [ 'parameters' ]),
default => $operator . Serializer :: encode ( $expression [ 'condition' ])
};
};
}
}
return $query ;
}
/**
* @ param $collection
* @ param $columns
* @ return string
*/
protected function genFilter ( array | string $expressions ) : string
{
if ( isString ( $expressions )) return $expressions ;
// Инициализация строки запроса
$query = 'FILTER ' ;
foreach ( $expressions as $expression ) {
// Перебор выражений
// Инициализация оператора
$operator = isset ( $expression [ 'operator' ]) ? ' ' . $expression [ 'operator' ] . ' ' : '' ;
// Генерация строки запроса
$query .= match ( strtoupper ( $expression [ 'type' ])) {
'START' , 'STARTS' , 'STARTS_WITH' => $operator . $this -> filterStartsWith ( $expression [ 'condition' ]),
'START_SENSETIVE' => $operator . $this -> filterStartsWith ( $expression [ 'condition' ], sensetive : true ),
'CONTAINS' , 'LIKE' => $operator . $this -> filterContains ( $expression [ 'condition' ]),
'LEVENSHTEIN' => $operator . $this -> filterLevenshtein ( $expression [ 'condition' ], ... $expression [ 'parameters' ]),
'PHRASE' => $operator . $this -> filterPhrase ( $expression [ 'condition' ], ... $expression [ 'parameters' ]),
default => $operator . Serializer :: encode ( $expression [ 'condition' ])
};
}
return $query ;
}
/**
/**
* Присвоение переменной значения
* Присвоение переменной значения
*
*
@ -1133,8 +1233,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 Коллекция вершин из которой требуется обход
*/
*/
@ -1298,6 +1396,17 @@ class Query extends Component implements QueryInterface
return $this ;
return $this ;
}
}
/**
* @ param array $expression
*/
public function debug ( bool $status = true )
{
$this -> debug = $status ;
return $this ;
}
/**
/**
*/
*/
public function let ( string $name , mixed $value ) : static
public function let ( string $name , mixed $value ) : static
@ -1375,20 +1484,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 ( $sensetive ) {
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 {
if ( isset ( $return )) {
if ( isset ( $return )) {
$return .= ' OR STARTS_WITH(' . $this -> quoteCollectionName ( $this -> collection ) . " . $key , \" $value\ " ) " ;
$return .= ' OR STARTS_WITH(' . $this -> quoteCollectionName ( $this -> collection ) . " . $key , \" $value\ " ) " ;
} else {
} else {
$return = 'STARTS_WITH(' . $this -> quoteCollectionName ( $this -> collection ) . " . $key , \" $value\ " ) " ;
$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
{
{
// Инициализация
// Инициализация
@ -1406,6 +1533,44 @@ class Query extends Component implements QueryInterface
return $return ;
return $return ;
}
}
public function filterPhrase ( array $expression , string $analyzer = 'text_en' ) : string
{
// Инициализация
$return = [];
foreach ( $expression as $key => $value ) {
// Перебор выражений
if ( $return ) {
$return .= ' OR PHRASE(' . $this -> quoteCollectionName ( $this -> collection ) . " . $key , \" $value\ " , \ " $analyzer\ " ) " ;
} else {
$return = 'PHRASE(' . $this -> quoteCollectionName ( $this -> collection ) . " . $key , \" $value\ " , \ " $analyzer\ " ) " ;
}
}
return $return ;
}
public function filterLevenshtein ( array $expression , string $analyzer = 'text_en' , int $distance = 3 , bool $transpositions = true ) : string
{
// Инициализация
$return = [];
$transpositions = $transpositions ? 'true' : 'false' ;
foreach ( $expression as $key => $value ) {
// Перебор выражений
if ( $return ) {
$return .= ' OR ANALYZER(LEVENSHTEIN_MATCH(' . $this -> quoteCollectionName ( $this -> collection ) . " . $key , \" $value\ " , $distance , $transpositions ), \ " $analyzer\ " ) " ;
} else {
$return = 'ANALYZER(LEVENSHTEIN_MATCH(' . $this -> quoteCollectionName ( $this -> collection ) . " . $key , \" $value\ " , $distance , $transpositions ), \ " $analyzer\ " ) " ;
}
}
return $return ;
}
/**
/**
* Adds an additional WHERE condition to the existing one but ignores [[ isEmpty () | empty operands ]] .
* Adds an additional WHERE condition to the existing one but ignores [[ isEmpty () | empty operands ]] .
* The new condition and the existing one will be joined using the 'AND' operator .
* The new condition and the existing one will be joined using the 'AND' operator .
@ -1603,6 +1768,7 @@ class Query extends Component implements QueryInterface
$result [ $column ] = SORT_ASC ;
$result [ $column ] = SORT_ASC ;
}
}
}
}
return $result ;
return $result ;
}
}
}
}