Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/Database/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -1442,4 +1442,38 @@ public function enableAlterLocks(bool $enable): self

return $this;
}

/**
* Does the adapter support trigram index?
*
* @return bool
*/
abstract public function getSupportForTrigramIndex(): bool;

/**
* Is PCRE regex supported?
* PCRE (Perl Compatible Regular Expressions) supports \b for word boundaries
*
* @return bool
*/
abstract public function getSupportForPCRERegex(): bool;

/**
* Is POSIX regex supported?
* POSIX regex uses \y for word boundaries instead of \b
*
* @return bool
*/
abstract public function getSupportForPOSIXRegex(): bool;

/**
* Is regex supported at all?
* Returns true if either PCRE or POSIX regex is supported
*
* @return bool
*/
public function getSupportForRegex(): bool
{
return $this->getSupportForPCRERegex() || $this->getSupportForPOSIXRegex();
}
}
15 changes: 15 additions & 0 deletions src/Database/Adapter/MariaDB.php
Original file line number Diff line number Diff line change
Expand Up @@ -2230,4 +2230,19 @@ public function getSupportForAlterLocks(): bool
{
return true;
}

public function getSupportForTrigramIndex(): bool
{
return false;
}

public function getSupportForPCRERegex(): bool
{
return true;
}

public function getSupportForPOSIXRegex(): bool
{
return false;
}
}
28 changes: 27 additions & 1 deletion src/Database/Adapter/Mongo.php
Original file line number Diff line number Diff line change
Expand Up @@ -2469,7 +2469,8 @@ protected function getQueryOperator(string $operator): string
Query::TYPE_STARTS_WITH,
Query::TYPE_NOT_STARTS_WITH,
Query::TYPE_ENDS_WITH,
Query::TYPE_NOT_ENDS_WITH => '$regex',
Query::TYPE_NOT_ENDS_WITH,
Query::TYPE_REGEX => '$regex',
Query::TYPE_OR => '$or',
Query::TYPE_AND => '$and',
default => throw new DatabaseException('Unknown operator:' . $operator . '. Must be one of ' . Query::TYPE_EQUAL . ', ' . Query::TYPE_NOT_EQUAL . ', ' . Query::TYPE_LESSER . ', ' . Query::TYPE_LESSER_EQUAL . ', ' . Query::TYPE_GREATER . ', ' . Query::TYPE_GREATER_EQUAL . ', ' . Query::TYPE_IS_NULL . ', ' . Query::TYPE_IS_NOT_NULL . ', ' . Query::TYPE_BETWEEN . ', ' . Query::TYPE_NOT_BETWEEN . ', ' . Query::TYPE_STARTS_WITH . ', ' . Query::TYPE_NOT_STARTS_WITH . ', ' . Query::TYPE_ENDS_WITH . ', ' . Query::TYPE_NOT_ENDS_WITH . ', ' . Query::TYPE_CONTAINS . ', ' . Query::TYPE_NOT_CONTAINS . ', ' . Query::TYPE_SEARCH . ', ' . Query::TYPE_NOT_SEARCH . ', ' . Query::TYPE_SELECT),
Expand Down Expand Up @@ -2740,6 +2741,26 @@ public function getSupportForGetConnectionId(): bool
return false;
}

/**
* Is PCRE regex supported?
*
* @return bool
*/
public function getSupportForPCRERegex(): bool
{
return true;
}

/**
* Is POSIX regex supported?
*
* @return bool
*/
public function getSupportForPOSIXRegex(): bool
{
return false;
}

/**
* Is cache fallback supported?
*
Expand Down Expand Up @@ -3221,4 +3242,9 @@ public function getSupportForAlterLocks(): bool
{
return false;
}

public function getSupportForTrigramIndex(): bool
{
return false;
}
}
15 changes: 15 additions & 0 deletions src/Database/Adapter/Pool.php
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,21 @@ public function getSupportForFulltextWildcardIndex(): bool
return $this->delegate(__FUNCTION__, \func_get_args());
}

public function getSupportForPCRERegex(): bool
{
return $this->delegate(__FUNCTION__, \func_get_args());
}

public function getSupportForPOSIXRegex(): bool
{
return $this->delegate(__FUNCTION__, \func_get_args());
}

public function getSupportForTrigramIndex(): bool
{
return $this->delegate(__FUNCTION__, \func_get_args());
}

public function getSupportForCasting(): bool
{
return $this->delegate(__FUNCTION__, \func_get_args());
Expand Down
34 changes: 32 additions & 2 deletions src/Database/Adapter/Postgres.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ public function create(string $name): bool
// Enable extensions
$this->getPDO()->prepare('CREATE EXTENSION IF NOT EXISTS postgis')->execute();
$this->getPDO()->prepare('CREATE EXTENSION IF NOT EXISTS vector')->execute();
$this->getPDO()->prepare('CREATE EXTENSION IF NOT EXISTS pg_trgm')->execute();

$collation = "
CREATE COLLATION IF NOT EXISTS utf8_ci_ai (
Expand Down Expand Up @@ -899,9 +900,10 @@ public function createIndex(string $collection, string $id, string $type, array
Database::INDEX_SPATIAL,
Database::INDEX_HNSW_EUCLIDEAN,
Database::INDEX_HNSW_COSINE,
Database::INDEX_HNSW_DOT => 'INDEX',
Database::INDEX_HNSW_DOT,
Database::INDEX_OBJECT,
Database::INDEX_TRIGRAM => 'INDEX',
Database::INDEX_UNIQUE => 'UNIQUE INDEX',
Database::INDEX_OBJECT => 'INDEX',
default => throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_FULLTEXT . ', ' . Database::INDEX_SPATIAL . ', ' . Database::INDEX_OBJECT . ', ' . Database::INDEX_HNSW_EUCLIDEAN . ', ' . Database::INDEX_HNSW_COSINE . ', ' . Database::INDEX_HNSW_DOT),
};

Expand All @@ -922,6 +924,11 @@ public function createIndex(string $collection, string $id, string $type, array
Database::INDEX_HNSW_COSINE => " USING HNSW ({$attributes} vector_cosine_ops)",
Database::INDEX_HNSW_DOT => " USING HNSW ({$attributes} vector_ip_ops)",
Database::INDEX_OBJECT => " USING GIN ({$attributes})",
Database::INDEX_TRIGRAM =>
" USING GIN (" . implode(', ', array_map(
fn ($a) => "$a gin_trgm_ops",
array_map('trim', explode(',', $attributes))
)) . ")",
default => " ({$attributes})",
};

Expand Down Expand Up @@ -2112,6 +2119,21 @@ public function getSupportForVectors(): bool
return true;
}

public function getSupportForPCRERegex(): bool
{
return false;
}

public function getSupportForPOSIXRegex(): bool
{
return true;
}

public function getSupportForTrigramIndex(): bool
{
return true;
}

/**
* @return string
*/
Expand All @@ -2120,6 +2142,14 @@ public function getLikeOperator(): string
return 'ILIKE';
}

/**
* @return string
*/
public function getRegexOperator(): string
{
return '~';
}

protected function processException(PDOException $e): \Exception
{
// Timeout
Expand Down
10 changes: 10 additions & 0 deletions src/Database/Adapter/SQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -1794,6 +1794,8 @@ protected function getSQLOperator(string $method): string
case Query::TYPE_NOT_ENDS_WITH:
case Query::TYPE_NOT_CONTAINS:
return $this->getLikeOperator();
case Query::TYPE_REGEX:
return $this->getRegexOperator();
case Query::TYPE_VECTOR_DOT:
case Query::TYPE_VECTOR_COSINE:
case Query::TYPE_VECTOR_EUCLIDEAN:
Expand Down Expand Up @@ -2284,6 +2286,14 @@ public function getLikeOperator(): string
return 'LIKE';
}

/**
* @return string
*/
public function getRegexOperator(): string
{
return 'REGEXP';
}

public function getInternalIndexesKeys(): array
{
return [];
Expand Down
22 changes: 22 additions & 0 deletions src/Database/Adapter/SQLite.php
Original file line number Diff line number Diff line change
Expand Up @@ -1876,4 +1876,26 @@ public function getSupportForAlterLocks(): bool
{
return false;
}

/**
* Is PCRE regex supported?
* SQLite does not have native REGEXP support - it requires compile-time option or user-defined function
*
* @return bool
*/
public function getSupportForPCRERegex(): bool
{
return false;
}

/**
* Is POSIX regex supported?
* SQLite does not have native REGEXP support - it requires compile-time option or user-defined function
*
* @return bool
*/
public function getSupportForPOSIXRegex(): bool
{
return false;
}
}
14 changes: 12 additions & 2 deletions src/Database/Database.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class Database
public const INDEX_HNSW_EUCLIDEAN = 'hnsw_euclidean';
public const INDEX_HNSW_COSINE = 'hnsw_cosine';
public const INDEX_HNSW_DOT = 'hnsw_dot';
public const INDEX_TRIGRAM = 'trigram';

// Max limits
public const MAX_INT = 2147483647;
Expand Down Expand Up @@ -1641,6 +1642,7 @@ public function createCollection(string $id, array $attributes = [], array $inde
$this->adapter->getSupportForMultipleFulltextIndexes(),
$this->adapter->getSupportForIdenticalIndexes(),
$this->adapter->getSupportForObject(),
$this->adapter->getSupportForTrigramIndex(),
);
foreach ($indexes as $index) {
if (!$validator->isValid($index)) {
Expand Down Expand Up @@ -2785,7 +2787,8 @@ public function updateAttribute(string $collection, string $id, ?string $type =
$this->adapter->getSupportForAttributes(),
$this->adapter->getSupportForMultipleFulltextIndexes(),
$this->adapter->getSupportForIdenticalIndexes(),
$this->adapter->getSupportForObject()
$this->adapter->getSupportForObject(),
$this->adapter->getSupportForTrigramIndex()
);

foreach ($indexes as $index) {
Expand Down Expand Up @@ -3665,8 +3668,14 @@ public function createIndex(string $collection, string $id, string $type, array
}
break;

case self::INDEX_TRIGRAM:
if (!$this->adapter->getSupportForTrigramIndex()) {
throw new DatabaseException('Trigram indexes are not supported');
}
break;

default:
throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_FULLTEXT . ', ' . Database::INDEX_SPATIAL . ', ' . Database::INDEX_OBJECT . ', ' . Database::INDEX_HNSW_EUCLIDEAN . ', ' . Database::INDEX_HNSW_COSINE . ', ' . Database::INDEX_HNSW_DOT);
throw new DatabaseException('Unknown index type: ' . $type . '. Must be one of ' . Database::INDEX_KEY . ', ' . Database::INDEX_UNIQUE . ', ' . Database::INDEX_FULLTEXT . ', ' . Database::INDEX_SPATIAL . ', ' . Database::INDEX_OBJECT . ', ' . Database::INDEX_HNSW_EUCLIDEAN . ', ' . Database::INDEX_HNSW_COSINE . ', ' . Database::INDEX_HNSW_DOT . ', '.Database::INDEX_TRIGRAM);
}

/** @var array<Document> $collectionAttributes */
Expand Down Expand Up @@ -3722,6 +3731,7 @@ public function createIndex(string $collection, string $id, string $type, array
$this->adapter->getSupportForMultipleFulltextIndexes(),
$this->adapter->getSupportForIdenticalIndexes(),
$this->adapter->getSupportForObject(),
$this->adapter->getSupportForTrigramIndex(),
);
if (!$validator->isValid($index)) {
throw new IndexException($validator->getDescription());
Expand Down
14 changes: 14 additions & 0 deletions src/Database/Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class Query
public const TYPE_NOT_STARTS_WITH = 'notStartsWith';
public const TYPE_ENDS_WITH = 'endsWith';
public const TYPE_NOT_ENDS_WITH = 'notEndsWith';
public const TYPE_REGEX = 'regex';

// Spatial methods
public const TYPE_CROSSES = 'crosses';
Expand Down Expand Up @@ -109,6 +110,7 @@ class Query
self::TYPE_CURSOR_BEFORE,
self::TYPE_AND,
self::TYPE_OR,
self::TYPE_REGEX
];

public const VECTOR_TYPES = [
Expand Down Expand Up @@ -1178,4 +1180,16 @@ public static function vectorEuclidean(string $attribute, array $vector): self
{
return new self(self::TYPE_VECTOR_EUCLIDEAN, $attribute, [$vector]);
}

/**
* Helper method to create Query with regex method
*
* @param string $attribute
* @param string $pattern
* @return Query
*/
public static function regex(string $attribute, string $pattern): self
{
return new self(self::TYPE_REGEX, $attribute, [$pattern]);
}
}
Loading