|
|
@ -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
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Инициализация
|
|
|
|
// Инициализация
|
|
|
|