Add PostgreSQL Support (#3)
Reviewed-on: #3
This commit was merged in pull request #3.
This commit is contained in:
@@ -41,11 +41,12 @@ enum AutoId
|
||||
/**
|
||||
* Generate a random string ID
|
||||
*
|
||||
* @param int|null $length The length of string to generate (optional; defaults to configured ID string length)
|
||||
* @return string A string filled with the hexadecimal representation of random bytes
|
||||
* @throws RandomException If an appropriate source of randomness cannot be found
|
||||
*/
|
||||
public static function generateRandom(): string
|
||||
public static function generateRandom(?int $length = null): string
|
||||
{
|
||||
return bin2hex(random_bytes(Configuration::$idStringLength / 2));
|
||||
return bin2hex(random_bytes(($length ?? Configuration::$idStringLength) / 2));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,4 +36,31 @@ class Count
|
||||
return Custom::scalar(Query\Count::byFields($tableName, $namedFields, $match),
|
||||
Parameters::addFields($namedFields, []), new CountMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Count matching documents using a JSON containment query (`@>`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be counted
|
||||
* @param array|object $criteria The criteria for the JSON containment query
|
||||
* @return int The number of documents matching the JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byContains(string $tableName, array|object $criteria): int
|
||||
{
|
||||
return Custom::scalar(Query\Count::byContains($tableName), Parameters::json(':criteria', $criteria),
|
||||
new CountMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Count matching documents using a JSON Path match query (`@?`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be counted
|
||||
* @param string $path The JSON Path match string
|
||||
* @return int The number of documents matching the given JSON Path criteria
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byJsonPath(string $tableName, string $path): int
|
||||
{
|
||||
return Custom::scalar(Query\Count::byJsonPath($tableName), [':path' => $path], new CountMapper());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,4 +31,16 @@ class Definition
|
||||
{
|
||||
Custom::nonQuery(Query\Definition::ensureIndexOn($tableName, $indexName, $fields), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a full-document index on a table (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table on which the document index should be created
|
||||
* @param DocumentIndex $indexType The type of document index to create
|
||||
* @throws DocumentException If the database mode is not PostgreSQL or if an error occurs creating the index
|
||||
*/
|
||||
public static function ensureDocumentIndex(string $tableName, DocumentIndex $indexType): void
|
||||
{
|
||||
Custom::nonQuery(Query\Definition::ensureDocumentIndexOn($tableName, $indexType), []);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class Delete
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId): void
|
||||
{
|
||||
Custom::nonQuery(Query\Delete::byId($tableName), Parameters::id($docId));
|
||||
Custom::nonQuery(Query\Delete::byId($tableName, $docId), Parameters::id($docId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,4 +33,28 @@ class Delete
|
||||
Custom::nonQuery(Query\Delete::byFields($tableName, $namedFields, $match),
|
||||
Parameters::addFields($namedFields, []));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete documents matching a JSON containment query (`@>`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The table from which documents should be deleted
|
||||
* @param array|object $criteria The JSON containment query values
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byContains(string $tableName, array|object $criteria): void
|
||||
{
|
||||
Custom::nonQuery(Query\Delete::byContains($tableName), Parameters::json(':criteria', $criteria));
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete documents matching a JSON Path match query (`@?`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The table from which documents should be deleted
|
||||
* @param string $path The JSON Path match string
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byJsonPath(string $tableName, string $path): void
|
||||
{
|
||||
Custom::nonQuery(Query\Delete::byJsonPath($tableName), [':path' => $path]);
|
||||
}
|
||||
}
|
||||
|
||||
15
src/DocumentIndex.php
Normal file
15
src/DocumentIndex.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace BitBadger\PDODocument;
|
||||
|
||||
/**
|
||||
* The type of index to generate for the document
|
||||
*/
|
||||
enum DocumentIndex
|
||||
{
|
||||
/** A GIN index with standard operations (all operators supported) */
|
||||
case Full;
|
||||
|
||||
/** A GIN index with JSONPath operations (optimized for @>, @?, @@ operators) */
|
||||
case Optimized;
|
||||
}
|
||||
@@ -19,7 +19,7 @@ class Exists
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId): bool
|
||||
{
|
||||
return Custom::scalar(Query\Exists::byId($tableName), Parameters::id($docId), new ExistsMapper());
|
||||
return Custom::scalar(Query\Exists::byId($tableName, $docId), Parameters::id($docId), new ExistsMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -37,4 +37,31 @@ class Exists
|
||||
return Custom::scalar(Query\Exists::byFields($tableName, $namedFields, $match),
|
||||
Parameters::addFields($namedFields, []), new ExistsMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if documents exist by a JSON containment query (`@>`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which document existence should be determined
|
||||
* @param array|object $criteria The criteria for the JSON containment query
|
||||
* @return bool True if any documents match the JSON containment query, false if not
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byContains(string $tableName, array|object $criteria): bool
|
||||
{
|
||||
return Custom::scalar(Query\Exists::byContains($tableName), Parameters::json(':criteria', $criteria),
|
||||
new ExistsMapper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if documents exist by a JSON Path match query (`@?`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which document existence should be determined
|
||||
* @param string $path The JSON Path match string
|
||||
* @return bool True if any documents match the JSON Path string, false if not
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byJsonPath(string $tableName, string $path): bool
|
||||
{
|
||||
return Custom::scalar(Query\Exists::byJsonPath($tableName), [':path' => $path], new ExistsMapper());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,23 +52,25 @@ class Field
|
||||
*/
|
||||
public function toWhere(): string
|
||||
{
|
||||
$fieldName = ($this->qualifier == '' ? '' : "$this->qualifier.") . 'data' . match (true) {
|
||||
!str_contains($this->fieldName, '.') => "->>'$this->fieldName'",
|
||||
Configuration::$mode == Mode::PgSQL => "#>>'{" . implode(',', explode('.', $this->fieldName)) . "}'",
|
||||
Configuration::$mode == Mode::SQLite => "->>'" . implode("'->>'", explode('.', $this->fieldName)) . "'",
|
||||
default => throw new DocumentException('Database mode not set; cannot make field WHERE clause')
|
||||
};
|
||||
$fieldPath = match (Configuration::$mode) {
|
||||
Mode::PgSQL => match (true) {
|
||||
$this->op == Op::BT => is_numeric($this->value[0]) ? "($fieldName)::numeric" : $fieldName,
|
||||
is_numeric($this->value) => "($fieldName)::numeric",
|
||||
default => $fieldName
|
||||
},
|
||||
default => $fieldName
|
||||
};
|
||||
$criteria = match ($this->op) {
|
||||
Op::EX, Op::NEX => '',
|
||||
Op::BT => " {$this->paramName}min AND {$this->paramName}max",
|
||||
default => " $this->paramName"
|
||||
};
|
||||
$prefix = $this->qualifier == '' ? '' : "$this->qualifier.";
|
||||
$fieldPath = match (Configuration::$mode) {
|
||||
Mode::SQLite => "{$prefix}data->>'"
|
||||
. (str_contains($this->fieldName, '.')
|
||||
? implode("'->>'", explode('.', $this->fieldName))
|
||||
: $this->fieldName)
|
||||
. "'",
|
||||
Mode::PgSQL => $this->op == Op::BT && is_numeric($this->value[0])
|
||||
? "({$prefix}data->>'$this->fieldName')::numeric"
|
||||
: "{$prefix}data->>'$this->fieldName'",
|
||||
default => throw new DocumentException('Database mode not set; cannot make field WHERE clause')
|
||||
};
|
||||
return $fieldPath . ' ' . $this->op->toString() . $criteria;
|
||||
}
|
||||
|
||||
|
||||
65
src/Find.php
65
src/Find.php
@@ -35,7 +35,8 @@ class Find
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId, string $className): mixed
|
||||
{
|
||||
return Custom::single(Query\Find::byId($tableName), Parameters::id($docId), new DocumentMapper($className));
|
||||
return Custom::single(Query\Find::byId($tableName, $docId), Parameters::id($docId),
|
||||
new DocumentMapper($className));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -57,6 +58,37 @@ class Find
|
||||
Parameters::addFields($namedFields, []), new DocumentMapper($className));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve documents via a JSON containment query (`@>`; PostgreSQL only)
|
||||
*
|
||||
* @template TDoc The type of document to be retrieved
|
||||
* @param string $tableName The name of the table from which documents should be retrieved
|
||||
* @param array|object $criteria The criteria for the JSON containment query
|
||||
* @param class-string<TDoc> $className The name of the class to be retrieved
|
||||
* @return DocumentList<TDoc> A list of documents matching the JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byContains(string $tableName, array|object $criteria, string $className): DocumentList
|
||||
{
|
||||
return Custom::list(Query\Find::byContains($tableName), Parameters::json(':criteria', $criteria),
|
||||
new DocumentMapper($className));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve documents via a JSON Path match query (`@?`; PostgreSQL only)
|
||||
*
|
||||
* @template TDoc The type of document to be retrieved
|
||||
* @param string $tableName The name of the table from which documents should be retrieved
|
||||
* @param string $path The JSON Path match string
|
||||
* @param class-string<TDoc> $className The name of the class to be retrieved
|
||||
* @return DocumentList<TDoc> A list of documents matching the JSON Path
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byJsonPath(string $tableName, string $path, string $className): DocumentList
|
||||
{
|
||||
return Custom::list(Query\Find::byJsonPath($tableName), [':path' => $path], new DocumentMapper($className));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve documents via a comparison on JSON fields, returning only the first result
|
||||
*
|
||||
@@ -75,4 +107,35 @@ class Find
|
||||
return Custom::single(Query\Find::byFields($tableName, $namedFields, $match),
|
||||
Parameters::addFields($namedFields, []), new DocumentMapper($className));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve documents via a JSON containment query (`@>`), returning only the first result (PostgreSQL only)
|
||||
*
|
||||
* @template TDoc The type of document to be retrieved
|
||||
* @param string $tableName The name of the table from which documents should be retrieved
|
||||
* @param array|object $criteria The criteria for the JSON containment query
|
||||
* @param class-string<TDoc> $className The name of the class to be retrieved
|
||||
* @return false|TDoc The first document matching the JSON containment query if any is found, false otherwise
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function firstByContains(string $tableName, array|object $criteria, string $className): mixed
|
||||
{
|
||||
return Custom::single(Query\Find::byContains($tableName), Parameters::json(':criteria', $criteria),
|
||||
new DocumentMapper($className));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve documents via a JSON Path match query (`@?`), returning only the first result (PostgreSQL only)
|
||||
*
|
||||
* @template TDoc The type of document to be retrieved
|
||||
* @param string $tableName The name of the table from which documents should be retrieved
|
||||
* @param string $path The JSON Path match string
|
||||
* @param class-string<TDoc> $className The name of the class to be retrieved
|
||||
* @return false|TDoc The first document matching the JSON Path if any is found, false otherwise
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function firstByJsonPath(string $tableName, string $path, string $className): mixed
|
||||
{
|
||||
return Custom::single(Query\Find::byJsonPath($tableName), [':path' => $path], new DocumentMapper($className));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ class Parameters
|
||||
{
|
||||
switch (Configuration::$mode) {
|
||||
case Mode::PgSQL:
|
||||
return [$paramName => "ARRAY['" . implode("','", $fieldNames) . "']"];
|
||||
return [$paramName => "{" . implode(",", $fieldNames) . "}"];
|
||||
case Mode::SQLite:
|
||||
$it = [];
|
||||
$idx = 0;
|
||||
|
||||
@@ -17,7 +17,7 @@ class Patch
|
||||
*/
|
||||
public static function byId(string $tableName, mixed $docId, array|object $patch): void
|
||||
{
|
||||
Custom::nonQuery(Query\Patch::byId($tableName),
|
||||
Custom::nonQuery(Query\Patch::byId($tableName, $docId),
|
||||
array_merge(Parameters::id($docId), Parameters::json(':data', $patch)));
|
||||
}
|
||||
|
||||
@@ -37,4 +37,32 @@ class Patch
|
||||
Custom::nonQuery(Query\Patch::byFields($tableName, $namedFields, $match),
|
||||
Parameters::addFields($namedFields, Parameters::json(':data', $patch)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON containment query (`@>`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The table in which documents should be patched
|
||||
* @param array|object $criteria The JSON containment query values to match
|
||||
* @param array|object $patch The object with which the documents should be patched (will be JSON-encoded)
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byContains(string $tableName, array|object $criteria, array|object $patch): void
|
||||
{
|
||||
Custom::nonQuery(Query\Patch::byContains($tableName),
|
||||
array_merge(Parameters::json(':criteria', $criteria), Parameters::json(':data', $patch)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Patch documents using a JSON Path match query (`@?`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The table in which documents should be patched
|
||||
* @param string $path The JSON Path match string
|
||||
* @param array|object $patch The object with which the documents should be patched (will be JSON-encoded)
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byJsonPath(string $tableName, string $path, array|object $patch): void
|
||||
{
|
||||
Custom::nonQuery(Query\Patch::byJsonPath($tableName),
|
||||
array_merge([':path' => $path], Parameters::json(':data', $patch)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +38,44 @@ class Query
|
||||
* Create a WHERE clause fragment to implement an ID-based query
|
||||
*
|
||||
* @param string $paramName The parameter name where the value of the ID will be provided (optional; default @id)
|
||||
* @param mixed $docId The ID of the document to be retrieved; used to determine type for potential JSON field
|
||||
* casts (optional; string ID assumed if no value is provided)
|
||||
* @return string The WHERE clause fragment to match by ID
|
||||
* @throws DocumentException If the database mode has not been set
|
||||
*/
|
||||
public static function whereById(string $paramName = ':id'): string
|
||||
public static function whereById(string $paramName = ':id', mixed $docId = null): string
|
||||
{
|
||||
return self::whereByFields([Field::EQ(Configuration::$idField, 0, $paramName)]);
|
||||
return self::whereByFields([Field::EQ(Configuration::$idField, $docId ?? '', $paramName)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WHERE clause fragment to implement a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param string $paramName The name of the parameter (optional; defaults to `:criteria`)
|
||||
* @return string The WHERE clause fragment for a JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function whereDataContains(string $paramName = ':criteria'): string
|
||||
{
|
||||
if (Configuration::$mode <> Mode::PgSQL) {
|
||||
throw new DocumentException('JSON containment is only supported on PostgreSQL');
|
||||
}
|
||||
return "data @> $paramName";
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WHERE clause fragment to implement a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param string $paramName The name of the parameter (optional; defaults to `:path`)
|
||||
* @return string The WHERE clause fragment for a JSON Path match query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function whereJsonPathMatches(string $paramName = ':path'): string
|
||||
{
|
||||
if (Configuration::$mode <> Mode::PgSQL) {
|
||||
throw new DocumentException('JSON Path matching is only supported on PostgreSQL');
|
||||
}
|
||||
return "jsonb_path_exists(data, $paramName::jsonpath)";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -68,10 +100,11 @@ class Query
|
||||
},
|
||||
Mode::PgSQL => match ($autoId ?? AutoId::None) {
|
||||
AutoId::None => ':data',
|
||||
AutoId::Number => ":data || ('{\"$id\":' || "
|
||||
. "(SELECT COALESCE(MAX(data->>'$id'), 0) + 1 FROM $tableName) || '}')",
|
||||
AutoId::UUID => ":data || '{\"$id\":\"" . AutoId::generateUUID() . "\"}'",
|
||||
AutoId::RandomString => ":data || '{\"$id\":\"" . AutoId::generateRandom() . "\"}'",
|
||||
AutoId::Number => ":data::jsonb || ('{\"$id\":' || "
|
||||
. "(SELECT COALESCE(MAX((data->>'$id')::numeric), 0) + 1 "
|
||||
. "FROM $tableName) || '}')::jsonb",
|
||||
AutoId::UUID => ":data::jsonb || '{\"$id\":\"" . AutoId::generateUUID() . "\"}'",
|
||||
AutoId::RandomString => ":data::jsonb || '{\"$id\":\"" . AutoId::generateRandom() . "\"}'",
|
||||
},
|
||||
default =>
|
||||
throw new DocumentException('Database mode not set; cannot generate auto-ID INSERT statement'),
|
||||
|
||||
@@ -33,4 +33,28 @@ class Count
|
||||
{
|
||||
return self::all($tableName) . ' WHERE ' . Query::whereByFields($fields, $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to count matching documents using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be counted
|
||||
* @return string The query to count documents using a JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byContains(string $tableName): string
|
||||
{
|
||||
return self::all($tableName) . ' WHERE ' . Query::whereDataContains();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to count matching documents using a JSON Path match (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be counted
|
||||
* @return string The query to count documents using a JSON Path match
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byJsonPath(string $tableName): string
|
||||
{
|
||||
return self::all($tableName) . ' WHERE ' . Query::whereJsonPathMatches();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace BitBadger\PDODocument\Query;
|
||||
|
||||
use BitBadger\PDODocument\{Configuration, DocumentException, Mode};
|
||||
use BitBadger\PDODocument\{Configuration, DocumentException, DocumentIndex, Mode};
|
||||
|
||||
/**
|
||||
* Queries to define tables and indexes
|
||||
@@ -68,4 +68,25 @@ class Definition
|
||||
{
|
||||
return str_replace('INDEX', 'UNIQUE INDEX', self::ensureIndexOn($tableName, 'key', [Configuration::$idField]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a document-wide index on a table (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table on which the document index should be created
|
||||
* @param DocumentIndex $indexType The type of index to be created
|
||||
* @return string The SQL statement to create an index on JSON documents in the specified table
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function ensureDocumentIndexOn(string $tableName, DocumentIndex $indexType): string
|
||||
{
|
||||
if (Configuration::$mode <> Mode::PgSQL) {
|
||||
throw new DocumentException('Document indexes are only supported on PostgreSQL');
|
||||
}
|
||||
[, $tbl] = self::splitSchemaAndTable($tableName);
|
||||
$extraOps = match ($indexType) {
|
||||
DocumentIndex::Full => '',
|
||||
DocumentIndex::Optimized => ' jsonb_path_ops'
|
||||
};
|
||||
return "CREATE INDEX IF NOT EXISTS idx_{$tbl}_document ON $tableName USING GIN (data$extraOps)";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,13 @@ class Delete
|
||||
* Query to delete a document by its ID
|
||||
*
|
||||
* @param string $tableName The name of the table from which a document should be deleted
|
||||
* @param mixed $docId The ID of the document to be deleted (optional; string ID assumed)
|
||||
* @return string The DELETE statement to delete a document by its ID
|
||||
* @throws DocumentException If the database mode has not been set
|
||||
*/
|
||||
public static function byId(string $tableName): string
|
||||
public static function byId(string $tableName, mixed $docId = null): string
|
||||
{
|
||||
return "DELETE FROM $tableName WHERE " . Query::whereById();
|
||||
return "DELETE FROM $tableName WHERE " . Query::whereById(docId: $docId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,4 +35,28 @@ class Delete
|
||||
{
|
||||
return "DELETE FROM $tableName WHERE " . Query::whereByFields($fields, $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to delete documents using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table from which documents should be deleted
|
||||
* @return string The DELETE statement to delete documents via a JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byContains(string $tableName): string
|
||||
{
|
||||
return "DELETE FROM $tableName WHERE " . Query::whereDataContains();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to delete documents using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table from which documents should be deleted
|
||||
* @return string The DELETE statement to delete documents via a JSON Path match
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byJsonPath(string $tableName): string
|
||||
{
|
||||
return "DELETE FROM $tableName WHERE " . Query::whereJsonPathMatches();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,13 @@ class Exists
|
||||
* Query to determine if a document exists for the given ID
|
||||
*
|
||||
* @param string $tableName The name of the table in which document existence should be checked
|
||||
* @param mixed $docId The ID of the document whose existence should be checked (optional; string ID assumed)
|
||||
* @return string The query to determine document existence by ID
|
||||
* @throws DocumentException If the database mode has not been set
|
||||
*/
|
||||
public static function byId(string $tableName): string
|
||||
public static function byId(string $tableName, mixed $docId = null): string
|
||||
{
|
||||
return self::query($tableName, Query::whereById());
|
||||
return self::query($tableName, Query::whereById(docId: $docId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,4 +47,28 @@ class Exists
|
||||
{
|
||||
return self::query($tableName, Query::whereByFields($fields, $match));
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to determine if documents exist using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which document existence should be checked
|
||||
* @return string The query to determine document existence by a JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byContains(string $tableName): string
|
||||
{
|
||||
return self::query($tableName, Query::whereDataContains());
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to determine if documents exist using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which document existence should be checked
|
||||
* @return string The query to determine document existence by a JSON Path match
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byJsonPath(string $tableName): string
|
||||
{
|
||||
return self::query($tableName, Query::whereJsonPathMatches());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,12 +13,13 @@ class Find
|
||||
* Query to retrieve a document by its ID
|
||||
*
|
||||
* @param string $tableName The name of the table from which a document should be retrieved
|
||||
* @param mixed $docId The ID of the document to be retrieved (optional; string ID assumed)
|
||||
* @return string The SELECT statement to retrieve a document by its ID
|
||||
* @throws DocumentException If the database mode has not been set
|
||||
*/
|
||||
public static function byId(string $tableName): string
|
||||
public static function byId(string $tableName, mixed $docId = null): string
|
||||
{
|
||||
return Query::selectFromTable($tableName) . ' WHERE ' . Query::whereById();
|
||||
return Query::selectFromTable($tableName) . ' WHERE ' . Query::whereById(docId: $docId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,4 +35,28 @@ class Find
|
||||
{
|
||||
return Query::selectFromTable($tableName) . ' WHERE ' . Query::whereByFields($fields, $match);
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to retrieve documents using a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table from which documents should be retrieved
|
||||
* @return string The SELECT statement to retrieve documents by a JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byContains(string $tableName): string
|
||||
{
|
||||
return Query::selectFromTable($tableName) . ' WHERE ' . Query::whereDataContains();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to retrieve documents using a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table from which documents should be retrieved
|
||||
* @return string The SELECT statement to retrieve documents by a JSON Path match
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byJsonPath(string $tableName): string
|
||||
{
|
||||
return Query::selectFromTable($tableName) . ' WHERE ' . Query::whereJsonPathMatches();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,13 @@ class Patch
|
||||
* Query to patch (partially update) a document by its ID
|
||||
*
|
||||
* @param string $tableName The name of the table in which a document should be patched
|
||||
* @param mixed $docId The ID of the document to be patched (optional; string ID assumed)
|
||||
* @return string The query to patch a document by its ID
|
||||
* @throws DocumentException If the database mode has not been set
|
||||
*/
|
||||
public static function byId(string $tableName): string
|
||||
public static function byId(string $tableName, mixed $docId = null): string
|
||||
{
|
||||
return self::update($tableName, Query::whereById());
|
||||
return self::update($tableName, Query::whereById(docId: $docId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,4 +53,28 @@ class Patch
|
||||
{
|
||||
return self::update($tableName, Query::whereByFields($field, $match));
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to patch (partially update) a document via a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be patched
|
||||
* @return string The query to patch documents via a JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byContains(string $tableName): string
|
||||
{
|
||||
return self::update($tableName, Query::whereDataContains());
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to patch (partially update) a document via a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be patched
|
||||
* @return string The query to patch documents via a JSON Path match
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byJsonPath(string $tableName): string
|
||||
{
|
||||
return self::update($tableName, Query::whereJsonPathMatches());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,8 @@ class RemoveFields
|
||||
{
|
||||
switch (Configuration::$mode) {
|
||||
case Mode::PgSQL:
|
||||
return "UPDATE $tableName SET data = data - " . array_keys($parameters)[0] . " WHERE $whereClause";
|
||||
return "UPDATE $tableName SET data = data - " . array_keys($parameters)[0]
|
||||
. "::text[] WHERE $whereClause";
|
||||
case Mode::SQLite:
|
||||
$paramNames = implode(', ', array_keys($parameters));
|
||||
return "UPDATE $tableName SET data = json_remove(data, $paramNames) WHERE $whereClause";
|
||||
@@ -40,12 +41,13 @@ class RemoveFields
|
||||
*
|
||||
* @param string $tableName The name of the table in which the document should be manipulated
|
||||
* @param array $parameters The parameter list for the query
|
||||
* @param mixed $docId The ID of the document from which fields should be removed (optional; string ID assumed)
|
||||
* @return string The UPDATE statement to remove fields from a document by its ID
|
||||
* @throws DocumentException If the database mode has not been set
|
||||
*/
|
||||
public static function byId(string $tableName, array $parameters): string
|
||||
public static function byId(string $tableName, array $parameters, mixed $docId = null): string
|
||||
{
|
||||
return self::update($tableName, $parameters, Query::whereById());
|
||||
return self::update($tableName, $parameters, Query::whereById(docId: $docId));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,4 +65,30 @@ class RemoveFields
|
||||
{
|
||||
return self::update($tableName, $parameters, Query::whereByFields($fields, $match));
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to remove fields from documents via a JSON containment query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be manipulated
|
||||
* @param array $parameters The parameter list for the query
|
||||
* @return string The UPDATE statement to remove fields from documents via a JSON containment query
|
||||
* @throws DocumentException If the database mode is not PostgreSQL
|
||||
*/
|
||||
public static function byContains(string $tableName, array $parameters): string
|
||||
{
|
||||
return self::update($tableName, $parameters, Query::whereDataContains());
|
||||
}
|
||||
|
||||
/**
|
||||
* Query to remove fields from documents via a JSON Path match query (PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The name of the table in which documents should be manipulated
|
||||
* @param array $parameters The parameter list for the query
|
||||
* @return string The UPDATE statement to remove fields from documents via a JSON Path match
|
||||
* @throws DocumentException
|
||||
*/
|
||||
public static function byJsonPath(string $tableName, array $parameters): string
|
||||
{
|
||||
return self::update($tableName, $parameters, Query::whereJsonPathMatches());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class RemoveFields
|
||||
public static function byId(string $tableName, mixed $docId, array $fieldNames): void
|
||||
{
|
||||
$nameParams = Parameters::fieldNames(':name', $fieldNames);
|
||||
Custom::nonQuery(Query\RemoveFields::byId($tableName, $nameParams),
|
||||
Custom::nonQuery(Query\RemoveFields::byId($tableName, $nameParams, $docId),
|
||||
array_merge(Parameters::id($docId), $nameParams));
|
||||
}
|
||||
|
||||
@@ -39,4 +39,34 @@ class RemoveFields
|
||||
Custom::nonQuery(Query\RemoveFields::byFields($tableName, $namedFields, $nameParams, $match),
|
||||
Parameters::addFields($namedFields, $nameParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove fields from documents via a JSON containment query (`@>`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The table in which documents should have fields removed
|
||||
* @param array|object $criteria The JSON containment query values
|
||||
* @param array|string[] $fieldNames The names of the fields to be removed
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byContains(string $tableName, array|object $criteria, array $fieldNames): void
|
||||
{
|
||||
$nameParams = Parameters::fieldNames(':name', $fieldNames);
|
||||
Custom::nonQuery(Query\RemoveFields::byContains($tableName, $nameParams),
|
||||
array_merge(Parameters::json(':criteria', $criteria), $nameParams));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove fields from documents via a JSON Path match query (`@?`; PostgreSQL only)
|
||||
*
|
||||
* @param string $tableName The table in which documents should have fields removed
|
||||
* @param string $path The JSON Path match string
|
||||
* @param array|string[] $fieldNames The names of the fields to be removed
|
||||
* @throws DocumentException If the database mode is not PostgreSQL, or if an error occurs
|
||||
*/
|
||||
public static function byJsonPath(string $tableName, string $path, array $fieldNames): void
|
||||
{
|
||||
$nameParams = Parameters::fieldNames(':name', $fieldNames);
|
||||
Custom::nonQuery(Query\RemoveFields::byJsonPath($tableName, $nameParams),
|
||||
array_merge([':path' => $path], $nameParams));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user