mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 08:58:35 +00:00
commit
5727b0fb45
255 changed files with 31292 additions and 1827 deletions
|
|
@ -2521,4 +2521,128 @@ return [
|
|||
],
|
||||
],
|
||||
],
|
||||
|
||||
'transactions' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('transactions'),
|
||||
'name' => 'Transactions',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('status'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 16, // pending | committing | committed | failed
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => 'pending',
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('operations'),
|
||||
'type' => Database::VAR_INTEGER,
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => true,
|
||||
'default' => 0,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('expiresAt'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_expiresAt'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['expiresAt'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_DESC],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
'transactionLogs' => [
|
||||
'$collection' => ID::custom(Database::METADATA),
|
||||
'$id' => ID::custom('transactionLogs'),
|
||||
'name' => 'Transaction Logs',
|
||||
'attributes' => [
|
||||
[
|
||||
'$id' => ID::custom('transactionInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('databaseInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('collectionInternalId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('documentId'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => Database::LENGTH_KEY,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('action'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 32, // create | update | upsert | increment | decrement | delete | bulkCreate | bulkUpdate | bulkUpsert | bulkDelete
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('data'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'size' => 5_000_000, // Allow large payloads for bulk operations
|
||||
'signed' => false,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['json'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
'$id' => ID::custom('_key_transaction'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['transactionInternalId'],
|
||||
'lengths' => [],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -981,6 +981,48 @@ return [
|
|||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Transactions */
|
||||
Exception::TRANSACTION_NOT_FOUND => [
|
||||
'name' => Exception::TRANSACTION_NOT_FOUND,
|
||||
'description' => 'Transaction with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::TRANSACTION_ALREADY_EXISTS => [
|
||||
'name' => Exception::TRANSACTION_ALREADY_EXISTS,
|
||||
'description' => 'Transaction with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::TRANSACTION_INVALID => [
|
||||
'name' => Exception::TRANSACTION_INVALID,
|
||||
'description' => 'The transaction is invalid. Please check the transaction state and try again.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::TRANSACTION_FAILED => [
|
||||
'name' => Exception::TRANSACTION_FAILED,
|
||||
'description' => 'The transaction has errored. Please check the transaction data and try again.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::TRANSACTION_EXPIRED => [
|
||||
'name' => Exception::TRANSACTION_EXPIRED,
|
||||
'description' => 'The transaction has expired. Please create a new transaction and try again.',
|
||||
'code' => 410,
|
||||
],
|
||||
Exception::TRANSACTION_CONFLICT => [
|
||||
'name' => Exception::TRANSACTION_CONFLICT,
|
||||
'description' => 'The transaction has a conflict. Please resolve the conflict and try again.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::TRANSACTION_LIMIT_EXCEEDED => [
|
||||
'name' => Exception::TRANSACTION_LIMIT_EXCEEDED,
|
||||
'description' => 'The maximum number of operations per transaction has been exceeded.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::TRANSACTION_NOT_READY => [
|
||||
'name' => Exception::TRANSACTION_NOT_READY,
|
||||
'description' => 'The transaction is not ready yet. Please try again later.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Project Errors */
|
||||
Exception::PROJECT_NOT_FOUND => [
|
||||
'name' => Exception::PROJECT_NOT_FOUND,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ return [
|
|||
'gitUrl' => 'git@github.com:appwrite/sdk-for-web.git',
|
||||
'gitRepoName' => 'sdk-for-web',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'gitBranch' => 'feat-txn',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'),
|
||||
'demos' => [
|
||||
[
|
||||
|
|
@ -275,7 +275,7 @@ return [
|
|||
'gitUrl' => 'git@github.com:appwrite/sdk-for-node.git',
|
||||
'gitRepoName' => 'sdk-for-node',
|
||||
'gitUserName' => 'appwrite',
|
||||
'gitBranch' => 'dev',
|
||||
'gitBranch' => 'feat-txn',
|
||||
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/nodejs/CHANGELOG.md'),
|
||||
],
|
||||
[
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ $member = [
|
|||
'subscribers.write',
|
||||
'subscribers.read',
|
||||
'assistant.read',
|
||||
'rules.read'
|
||||
'rules.read',
|
||||
];
|
||||
|
||||
$admins = [
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1253,7 +1253,7 @@ App::error()
|
|||
}
|
||||
|
||||
/**
|
||||
* If its not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
|
||||
* If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
|
||||
*/
|
||||
if (!$publish && $project->getId() !== 'console') {
|
||||
if (!Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
|
|
@ -1355,6 +1355,7 @@ App::error()
|
|||
case 409: // Error allowed publicly
|
||||
case 412: // Error allowed publicly
|
||||
case 416: // Error allowed publicly
|
||||
case 422: // Error allowed publicly
|
||||
case 429: // Error allowed publicly
|
||||
case 451: // Error allowed publicly
|
||||
case 501: // Error allowed publicly
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate
|
|||
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
|
||||
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
|
||||
const APP_LIMIT_DATABASE_BATCH = 100; // Default maximum batch size for database operations
|
||||
const APP_LIMIT_DATABASE_TRANSACTION = 100; // Default maximum operations per transaction
|
||||
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
|
||||
|
|
@ -55,6 +56,10 @@ const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
|
|||
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
|
||||
const APP_DATABASE_QUERY_MAX_VALUES = 500;
|
||||
const APP_DATABASE_ENCRYPT_SIZE_MIN = 150;
|
||||
const APP_DATABASE_TXN_TTL_MIN = 60; // 1 minute
|
||||
const APP_DATABASE_TXN_TTL_MAX = 3600; // 1 hour
|
||||
const APP_DATABASE_TXN_TTL_DEFAULT = 300; // 5 minutes
|
||||
const APP_DATABASE_TXN_MAX_OPERATIONS = 100; // Maximum operations per transaction
|
||||
const APP_STORAGE_UPLOADS = '/storage/uploads';
|
||||
const APP_STORAGE_SITES = '/storage/sites';
|
||||
const APP_STORAGE_FUNCTIONS = '/storage/functions';
|
||||
|
|
@ -105,8 +110,11 @@ const BUILD_TYPE_RETRY = 'retry';
|
|||
|
||||
// Deletion Types
|
||||
const DELETE_TYPE_DATABASES = 'databases';
|
||||
|
||||
const DELETE_TYPE_DOCUMENT = 'document';
|
||||
const DELETE_TYPE_COLLECTIONS = 'collections';
|
||||
const DELETE_TYPE_TRANSACTION = 'transaction';
|
||||
const DELETE_TYPE_EXPIRED_TRANSACTIONS = 'expired_transactions';
|
||||
const DELETE_TYPE_PROJECTS = 'projects';
|
||||
const DELETE_TYPE_SITES = 'sites';
|
||||
const DELETE_TYPE_FUNCTIONS = 'functions';
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ use Ahc\Jwt\JWT;
|
|||
use Ahc\Jwt\JWTException;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Key;
|
||||
use Appwrite\Databases\TransactionState;
|
||||
use Appwrite\Event\Audit;
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Certificate;
|
||||
|
|
@ -1041,3 +1042,7 @@ App::setResource('httpReferrerSafe', function (Request $request, string $httpRef
|
|||
$referrer = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
|
||||
return $referrer;
|
||||
}, ['request', 'httpReferrer', 'platforms', 'dbForPlatform', 'project', 'utopia']);
|
||||
|
||||
App::setResource('transactionState', function (Database $dbForProject) {
|
||||
return new TransactionState($dbForProject);
|
||||
}, ['dbForProject']);
|
||||
|
|
|
|||
1
composer.lock
generated
1
composer.lock
generated
|
|
@ -5127,6 +5127,7 @@
|
|||
"issues": "https://github.com/doctrine/annotations/issues",
|
||||
"source": "https://github.com/doctrine/annotations/tree/2.0.2"
|
||||
},
|
||||
"abandoned": true,
|
||||
"time": "2024-09-05T10:17:24+00:00"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ const result = await databases.createDocument({
|
|||
"age": 30,
|
||||
"isAdmin": false
|
||||
},
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.createOperations({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
operations: [
|
||||
{
|
||||
"action": "create",
|
||||
"databaseId": "<DATABASE_ID>",
|
||||
"collectionId": "<COLLECTION_ID>",
|
||||
"documentId": "<DOCUMENT_ID>",
|
||||
"data": {
|
||||
"name": "Walter O'Brien"
|
||||
}
|
||||
}
|
||||
] // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.createTransaction({
|
||||
ttl: 60 // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -12,7 +12,8 @@ const result = await databases.decrementDocumentAttribute({
|
|||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: null, // optional
|
||||
min: null // optional
|
||||
min: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const databases = new Databases(client);
|
|||
const result = await databases.deleteDocument({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>'
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.deleteTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -10,7 +10,8 @@ const result = await databases.getDocument({
|
|||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.getTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -12,7 +12,8 @@ const result = await databases.incrementDocumentAttribute({
|
|||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: null, // optional
|
||||
max: null // optional
|
||||
max: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const databases = new Databases(client);
|
|||
const result = await databases.listDocuments({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.listTransactions({
|
||||
queries: [] // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -11,7 +11,8 @@ const result = await databases.updateDocument({
|
|||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
data: {}, // optional
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { Client, Databases } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const databases = new Databases(client);
|
||||
|
||||
const result = await databases.updateTransaction({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
commit: false, // optional
|
||||
rollback: false // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -11,7 +11,8 @@ const result = await databases.upsertDocument({
|
|||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
data: {},
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
import { Client, TablesDB } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const tablesDB = new TablesDB(client);
|
||||
|
||||
const result = await tablesDB.createOperations({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
operations: [
|
||||
{
|
||||
"action": "create",
|
||||
"databaseId": "<DATABASE_ID>",
|
||||
"tableId": "<TABLE_ID>",
|
||||
"rowId": "<ROW_ID>",
|
||||
"data": {
|
||||
"name": "Walter O'Brien"
|
||||
}
|
||||
}
|
||||
] // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -17,7 +17,8 @@ const result = await tablesDB.createRow({
|
|||
"age": 30,
|
||||
"isAdmin": false
|
||||
},
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, TablesDB } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const tablesDB = new TablesDB(client);
|
||||
|
||||
const result = await tablesDB.createTransaction({
|
||||
ttl: 60 // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -12,7 +12,8 @@ const result = await tablesDB.decrementRowColumn({
|
|||
rowId: '<ROW_ID>',
|
||||
column: '',
|
||||
value: null, // optional
|
||||
min: null // optional
|
||||
min: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client);
|
|||
const result = await tablesDB.deleteRow({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>'
|
||||
rowId: '<ROW_ID>',
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, TablesDB } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const tablesDB = new TablesDB(client);
|
||||
|
||||
const result = await tablesDB.deleteTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -10,7 +10,8 @@ const result = await tablesDB.getRow({
|
|||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, TablesDB } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const tablesDB = new TablesDB(client);
|
||||
|
||||
const result = await tablesDB.getTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -12,7 +12,8 @@ const result = await tablesDB.incrementRowColumn({
|
|||
rowId: '<ROW_ID>',
|
||||
column: '',
|
||||
value: null, // optional
|
||||
max: null // optional
|
||||
max: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ const tablesDB = new TablesDB(client);
|
|||
const result = await tablesDB.listRows({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import { Client, TablesDB } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const tablesDB = new TablesDB(client);
|
||||
|
||||
const result = await tablesDB.listTransactions({
|
||||
queries: [] // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -11,7 +11,8 @@ const result = await tablesDB.updateRow({
|
|||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>',
|
||||
data: {}, // optional
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { Client, TablesDB } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const tablesDB = new TablesDB(client);
|
||||
|
||||
const result = await tablesDB.updateTransaction({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
commit: false, // optional
|
||||
rollback: false // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
@ -11,7 +11,8 @@ const result = await tablesDB.upsertRow({
|
|||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>',
|
||||
data: {}, // optional
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
|
|
|
|||
|
|
@ -18,5 +18,6 @@ const result = await databases.createDocument({
|
|||
"age": 30,
|
||||
"isAdmin": false
|
||||
},
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const databases = new sdk.Databases(client);
|
|||
const result = await databases.createDocuments({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documents: []
|
||||
documents: [],
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const result = await databases.createOperations({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
operations: [
|
||||
{
|
||||
"action": "create",
|
||||
"databaseId": "<DATABASE_ID>",
|
||||
"collectionId": "<COLLECTION_ID>",
|
||||
"documentId": "<DOCUMENT_ID>",
|
||||
"data": {
|
||||
"name": "Walter O'Brien"
|
||||
}
|
||||
}
|
||||
] // optional
|
||||
});
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const result = await databases.createTransaction({
|
||||
ttl: 60 // optional
|
||||
});
|
||||
|
|
@ -13,5 +13,6 @@ const result = await databases.decrementDocumentAttribute({
|
|||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: null, // optional
|
||||
min: null // optional
|
||||
min: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const databases = new sdk.Databases(client);
|
|||
const result = await databases.deleteDocument({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>'
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const databases = new sdk.Databases(client);
|
|||
const result = await databases.deleteDocuments({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const result = await databases.deleteTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
|
@ -11,5 +11,6 @@ const result = await databases.getDocument({
|
|||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const result = await databases.getTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
|
@ -13,5 +13,6 @@ const result = await databases.incrementDocumentAttribute({
|
|||
documentId: '<DOCUMENT_ID>',
|
||||
attribute: '',
|
||||
value: null, // optional
|
||||
max: null // optional
|
||||
max: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const databases = new sdk.Databases(client);
|
|||
const result = await databases.listDocuments({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const result = await databases.listTransactions({
|
||||
queries: [] // optional
|
||||
});
|
||||
|
|
@ -12,5 +12,6 @@ const result = await databases.updateDocument({
|
|||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
data: {}, // optional
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@ const result = await databases.updateDocuments({
|
|||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
data: {}, // optional
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const databases = new sdk.Databases(client);
|
||||
|
||||
const result = await databases.updateTransaction({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
commit: false, // optional
|
||||
rollback: false // optional
|
||||
});
|
||||
|
|
@ -12,5 +12,6 @@ const result = await databases.upsertDocument({
|
|||
collectionId: '<COLLECTION_ID>',
|
||||
documentId: '<DOCUMENT_ID>',
|
||||
data: {},
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const databases = new sdk.Databases(client);
|
|||
const result = await databases.upsertDocuments({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
collectionId: '<COLLECTION_ID>',
|
||||
documents: []
|
||||
documents: [],
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const result = await messaging.createPush({
|
|||
targets: [], // optional
|
||||
data: {}, // optional
|
||||
action: '<ACTION>', // optional
|
||||
image: '[ID1:ID2]', // optional
|
||||
image: '<ID1:ID2>', // optional
|
||||
icon: '<ICON>', // optional
|
||||
sound: '<SOUND>', // optional
|
||||
color: '<COLOR>', // optional
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ const result = await messaging.updatePush({
|
|||
body: '<BODY>', // optional
|
||||
data: {}, // optional
|
||||
action: '<ACTION>', // optional
|
||||
image: '[ID1:ID2]', // optional
|
||||
image: '<ID1:ID2>', // optional
|
||||
icon: '<ICON>', // optional
|
||||
sound: '<SOUND>', // optional
|
||||
color: '<COLOR>', // optional
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const tablesDB = new sdk.TablesDB(client);
|
||||
|
||||
const result = await tablesDB.createOperations({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
operations: [
|
||||
{
|
||||
"action": "create",
|
||||
"databaseId": "<DATABASE_ID>",
|
||||
"tableId": "<TABLE_ID>",
|
||||
"rowId": "<ROW_ID>",
|
||||
"data": {
|
||||
"name": "Walter O'Brien"
|
||||
}
|
||||
}
|
||||
] // optional
|
||||
});
|
||||
|
|
@ -18,5 +18,6 @@ const result = await tablesDB.createRow({
|
|||
"age": 30,
|
||||
"isAdmin": false
|
||||
},
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client);
|
|||
const result = await tablesDB.createRows({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
rows: []
|
||||
rows: [],
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const tablesDB = new sdk.TablesDB(client);
|
||||
|
||||
const result = await tablesDB.createTransaction({
|
||||
ttl: 60 // optional
|
||||
});
|
||||
|
|
@ -13,5 +13,6 @@ const result = await tablesDB.decrementRowColumn({
|
|||
rowId: '<ROW_ID>',
|
||||
column: '',
|
||||
value: null, // optional
|
||||
min: null // optional
|
||||
min: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client);
|
|||
const result = await tablesDB.deleteRow({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>'
|
||||
rowId: '<ROW_ID>',
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client);
|
|||
const result = await tablesDB.deleteRows({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const tablesDB = new sdk.TablesDB(client);
|
||||
|
||||
const result = await tablesDB.deleteTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
|
@ -11,5 +11,6 @@ const result = await tablesDB.getRow({
|
|||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const tablesDB = new sdk.TablesDB(client);
|
||||
|
||||
const result = await tablesDB.getTransaction({
|
||||
transactionId: '<TRANSACTION_ID>'
|
||||
});
|
||||
|
|
@ -13,5 +13,6 @@ const result = await tablesDB.incrementRowColumn({
|
|||
rowId: '<ROW_ID>',
|
||||
column: '',
|
||||
value: null, // optional
|
||||
max: null // optional
|
||||
max: null, // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client);
|
|||
const result = await tablesDB.listRows({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const tablesDB = new sdk.TablesDB(client);
|
||||
|
||||
const result = await tablesDB.listTransactions({
|
||||
queries: [] // optional
|
||||
});
|
||||
|
|
@ -12,5 +12,6 @@ const result = await tablesDB.updateRow({
|
|||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>',
|
||||
data: {}, // optional
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -11,5 +11,6 @@ const result = await tablesDB.updateRows({
|
|||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
data: {}, // optional
|
||||
queries: [] // optional
|
||||
queries: [], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,14 @@
|
|||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setKey('<YOUR_API_KEY>'); // Your secret API key
|
||||
|
||||
const tablesDB = new sdk.TablesDB(client);
|
||||
|
||||
const result = await tablesDB.updateTransaction({
|
||||
transactionId: '<TRANSACTION_ID>',
|
||||
commit: false, // optional
|
||||
rollback: false // optional
|
||||
});
|
||||
|
|
@ -12,5 +12,6 @@ const result = await tablesDB.upsertRow({
|
|||
tableId: '<TABLE_ID>',
|
||||
rowId: '<ROW_ID>',
|
||||
data: {}, // optional
|
||||
permissions: ["read("any")"] // optional
|
||||
permissions: ["read("any")"], // optional
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
|
|
@ -10,5 +10,6 @@ const tablesDB = new sdk.TablesDB(client);
|
|||
const result = await tablesDB.upsertRows({
|
||||
databaseId: '<DATABASE_ID>',
|
||||
tableId: '<TABLE_ID>',
|
||||
rows: []
|
||||
rows: [],
|
||||
transactionId: '<TRANSACTION_ID>' // optional
|
||||
});
|
||||
|
|
|
|||
1
docs/references/databases/create-operations.md
Normal file
1
docs/references/databases/create-operations.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create multiple operations in a single transaction.
|
||||
1
docs/references/databases/create-transaction.md
Normal file
1
docs/references/databases/create-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a new transaction.
|
||||
1
docs/references/databases/delete-transaction.md
Normal file
1
docs/references/databases/delete-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Delete a transaction by its unique ID.
|
||||
|
|
@ -1 +1 @@
|
|||
Get index by ID.
|
||||
Get an index by its unique ID.
|
||||
1
docs/references/databases/get-transaction.md
Normal file
1
docs/references/databases/get-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Get a transaction by its unique ID.
|
||||
1
docs/references/databases/list-transactions.md
Normal file
1
docs/references/databases/list-transactions.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
List transactions across all databases.
|
||||
1
docs/references/databases/update-transaction.md
Normal file
1
docs/references/databases/update-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a transaction, to either commit or roll back its operations.
|
||||
1
docs/references/tablesdb/create-operations.md
Normal file
1
docs/references/tablesdb/create-operations.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create multiple operations in a single transaction.
|
||||
1
docs/references/tablesdb/create-transaction.md
Normal file
1
docs/references/tablesdb/create-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Create a new transaction.
|
||||
1
docs/references/tablesdb/delete-transaction.md
Normal file
1
docs/references/tablesdb/delete-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Delete a transaction by its unique ID.
|
||||
1
docs/references/tablesdb/get-transaction.md
Normal file
1
docs/references/tablesdb/get-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Get a transaction by its unique ID.
|
||||
1
docs/references/tablesdb/list-transactions.md
Normal file
1
docs/references/tablesdb/list-transactions.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
List transactions across all databases.
|
||||
1
docs/references/tablesdb/update-transaction.md
Normal file
1
docs/references/tablesdb/update-transaction.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Update a transaction, to either commit or roll back its operations.
|
||||
745
src/Appwrite/Databases/TransactionState.php
Normal file
745
src/Appwrite/Databases/TransactionState.php
Normal file
|
|
@ -0,0 +1,745 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Databases;
|
||||
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception;
|
||||
use Utopia\Database\Exception\Timeout;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
/**
|
||||
* Service for managing transaction state and providing transaction-aware document operations
|
||||
*
|
||||
* This class provides methods to:
|
||||
* - Query documents with transaction awareness (getDocument, listDocuments, countDocuments)
|
||||
* - Apply bulk operations to transaction state for cross-operation visibility
|
||||
* - Replay transaction operations to build current state
|
||||
*/
|
||||
class TransactionState
|
||||
{
|
||||
private Database $dbForProject;
|
||||
|
||||
public function __construct(Database $dbForProject)
|
||||
{
|
||||
$this->dbForProject = $dbForProject;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a document with transaction-aware logic
|
||||
*
|
||||
* @param string $collectionId Collection ID
|
||||
* @param string $documentId Document ID
|
||||
* @param string|null $transactionId Optional transaction ID
|
||||
* @param array $queries Optional query filters
|
||||
* @return Document
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
* @throws Timeout
|
||||
*/
|
||||
public function getDocument(
|
||||
string $collectionId,
|
||||
string $documentId,
|
||||
?string $transactionId = null,
|
||||
array $queries = []
|
||||
): Document {
|
||||
if ($transactionId === null) {
|
||||
return $this->dbForProject->getDocument($collectionId, $documentId, $queries);
|
||||
}
|
||||
|
||||
$state = $this->getTransactionState($transactionId);
|
||||
|
||||
if (isset($state[$collectionId][$documentId])) {
|
||||
$docState = $state[$collectionId][$documentId];
|
||||
|
||||
if (!$docState['exists']) {
|
||||
return new Document();
|
||||
}
|
||||
|
||||
if ($docState['action'] === 'create') {
|
||||
return $this->applyProjection($docState['document'], $queries);
|
||||
}
|
||||
|
||||
if ($docState['action'] === 'update' || $docState['action'] === 'upsert') {
|
||||
// Merge with committed version
|
||||
$committedDoc = $this->dbForProject->getDocument($collectionId, $documentId, $queries);
|
||||
if (!$committedDoc->isEmpty()) {
|
||||
foreach ($docState['document']->getAttributes() as $key => $value) {
|
||||
if ($key !== '$id') {
|
||||
$committedDoc->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
// Reapply projection in case transaction added new fields
|
||||
return $this->applyProjection($committedDoc, $queries);
|
||||
} elseif ($docState['action'] === 'upsert') {
|
||||
return $this->applyProjection($docState['document'], $queries);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->dbForProject->getDocument($collectionId, $documentId, $queries);
|
||||
}
|
||||
|
||||
/**
|
||||
* List documents with transaction-aware logic
|
||||
*
|
||||
* @param string $collectionId Collection ID
|
||||
* @param string|null $transactionId Optional transaction ID
|
||||
* @param array $queries Optional query filters
|
||||
* @return array Array of Document objects
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
* @throws Timeout
|
||||
*/
|
||||
public function listDocuments(
|
||||
string $collectionId,
|
||||
?string $transactionId = null,
|
||||
array $queries = []
|
||||
): array {
|
||||
// If no transaction, use normal database retrieval
|
||||
if ($transactionId === null) {
|
||||
return $this->dbForProject->find($collectionId, $queries);
|
||||
}
|
||||
|
||||
$state = $this->getTransactionState($transactionId);
|
||||
$committedDocs = $this->dbForProject->find($collectionId, $queries);
|
||||
$documentMap = [];
|
||||
|
||||
// Build map of committed documents
|
||||
foreach ($committedDocs as $doc) {
|
||||
$documentMap[$doc->getId()] = $doc;
|
||||
}
|
||||
|
||||
// Apply transaction state changes
|
||||
if (isset($state[$collectionId])) {
|
||||
foreach ($state[$collectionId] as $docId => $docState) {
|
||||
if (!$docState['exists']) {
|
||||
// Document was deleted, remove from results
|
||||
unset($documentMap[$docId]);
|
||||
} elseif ($docState['action'] === 'create') {
|
||||
// Document was created, add to results with projection
|
||||
$documentMap[$docId] = $this->applyProjection($docState['document'], $queries);
|
||||
} elseif ($docState['action'] === 'update' || $docState['action'] === 'upsert') {
|
||||
if (isset($documentMap[$docId])) {
|
||||
// Update existing document
|
||||
foreach ($docState['document']->getAttributes() as $key => $value) {
|
||||
if ($key !== '$id') {
|
||||
$documentMap[$docId]->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
// Reapply projection in case transaction added new fields
|
||||
$documentMap[$docId] = $this->applyProjection($documentMap[$docId], $queries);
|
||||
} elseif ($docState['action'] === 'upsert') {
|
||||
// Upsert created a new document, apply projection
|
||||
$documentMap[$docId] = $this->applyProjection($docState['document'], $queries);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($documentMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Count documents with transaction-aware logic
|
||||
*
|
||||
* @param string $collectionId Collection ID
|
||||
* @param string|null $transactionId Optional transaction ID
|
||||
* @param array $queries Optional query filters
|
||||
* @return int Document count
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
* @throws Timeout
|
||||
*/
|
||||
public function countDocuments(
|
||||
string $collectionId,
|
||||
?string $transactionId = null,
|
||||
array $queries = []
|
||||
): int {
|
||||
if ($transactionId === null) {
|
||||
return $this->dbForProject->count($collectionId, $queries, APP_LIMIT_COUNT);
|
||||
}
|
||||
|
||||
$state = $this->getTransactionState($transactionId);
|
||||
|
||||
$baseCount = $this->dbForProject->count($collectionId, $queries, APP_LIMIT_COUNT);
|
||||
|
||||
if (!isset($state[$collectionId])) {
|
||||
return $baseCount;
|
||||
}
|
||||
|
||||
$committedDocs = $this->dbForProject->find($collectionId, $queries);
|
||||
$committedDocIds = [];
|
||||
foreach ($committedDocs as $doc) {
|
||||
$committedDocIds[$doc->getId()] = true;
|
||||
}
|
||||
|
||||
$adjustedCount = $baseCount;
|
||||
|
||||
$filters = $this->extractFilters($queries);
|
||||
|
||||
foreach ($state[$collectionId] as $docId => $docState) {
|
||||
if (!$docState['exists']) {
|
||||
if (isset($committedDocIds[$docId])) {
|
||||
$adjustedCount--;
|
||||
}
|
||||
} elseif ($docState['action'] === 'create') {
|
||||
if ($this->documentMatchesFilters($docState['document'], $filters)) {
|
||||
$adjustedCount++;
|
||||
}
|
||||
} elseif ($docState['action'] === 'update' || $docState['action'] === 'upsert') {
|
||||
$wasInResults = isset($committedDocIds[$docId]);
|
||||
$nowMatches = $this->documentMatchesFilters($docState['document'], $filters);
|
||||
|
||||
if (!$wasInResults && $nowMatches && $docState['action'] === 'upsert') {
|
||||
$adjustedCount++;
|
||||
} elseif ($wasInResults && !$nowMatches) {
|
||||
$adjustedCount--;
|
||||
} elseif (!$wasInResults && $nowMatches) {
|
||||
// Update shouldn't add a new doc, but upsert might have
|
||||
if ($docState['action'] === 'upsert') {
|
||||
$adjustedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max(0, $adjustedCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a document exists with transaction-aware logic
|
||||
*
|
||||
* @param string $collectionId Collection ID
|
||||
* @param string $documentId Document ID
|
||||
* @param string|null $transactionId Optional transaction ID
|
||||
* @return bool True if document exists
|
||||
*/
|
||||
public function documentExists(
|
||||
string $collectionId,
|
||||
string $documentId,
|
||||
?string $transactionId = null
|
||||
): bool {
|
||||
$doc = $this->getDocument($collectionId, $documentId, $transactionId);
|
||||
return !$doc->isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply bulk update to documents in transaction state that match queries
|
||||
*
|
||||
* This allows bulk operations within a transaction to see each other's changes.
|
||||
*
|
||||
* @param string $collectionId Collection ID
|
||||
* @param Document $updateData Document with update values
|
||||
* @param array $queries Query filters to match documents
|
||||
* @param array &$state Transaction state (passed by reference)
|
||||
* @return void
|
||||
*/
|
||||
public function applyBulkUpdateToState(
|
||||
string $collectionId,
|
||||
Document $updateData,
|
||||
array $queries,
|
||||
array &$state
|
||||
): void {
|
||||
if (!isset($state[$collectionId])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filters = $this->extractFilters($queries);
|
||||
|
||||
foreach ($state[$collectionId] as $docId => $doc) {
|
||||
if ($this->documentMatchesFilters($doc, $filters)) {
|
||||
foreach ($updateData->getArrayCopy() as $key => $value) {
|
||||
if ($key !== '$id') {
|
||||
$doc->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply bulk delete to documents in transaction state that match queries
|
||||
*
|
||||
* This allows bulk operations within a transaction to see each other's changes.
|
||||
*
|
||||
* @param string $collectionId Collection ID
|
||||
* @param array $queries Query filters to match documents
|
||||
* @param array &$state Transaction state (passed by reference)
|
||||
* @return void
|
||||
*/
|
||||
public function applyBulkDeleteToState(
|
||||
string $collectionId,
|
||||
array $queries,
|
||||
array &$state
|
||||
): void {
|
||||
if (!isset($state[$collectionId])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filters = $this->extractFilters($queries);
|
||||
|
||||
foreach ($state[$collectionId] as $docId => $doc) {
|
||||
if ($this->documentMatchesFilters($doc, $filters)) {
|
||||
unset($state[$collectionId][$docId]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply bulk upsert to documents in transaction state
|
||||
*
|
||||
* This merges partial upsert data with full documents from transaction state,
|
||||
* preventing validation errors when upserting documents created in the same transaction.
|
||||
*
|
||||
* @param string $collectionId Collection ID
|
||||
* @param array $documents Array of Document objects to upsert (can be partial)
|
||||
* @param array &$state Transaction state (passed by reference)
|
||||
* @return array Merged documents ready for database upsert
|
||||
*/
|
||||
public function applyBulkUpsertToState(
|
||||
string $collectionId,
|
||||
array $documents,
|
||||
array &$state
|
||||
): array {
|
||||
$mergedDocuments = [];
|
||||
|
||||
foreach ($documents as $doc) {
|
||||
if (!($doc instanceof Document)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$docId = $doc->getId();
|
||||
if (!$docId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($state[$collectionId][$docId])) {
|
||||
foreach ($doc->getArrayCopy() as $key => $value) {
|
||||
if ($key !== '$id') {
|
||||
$state[$collectionId][$docId]->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
$mergedDocuments[] = $state[$collectionId][$docId];
|
||||
} else {
|
||||
$mergedDocuments[] = $doc;
|
||||
}
|
||||
}
|
||||
|
||||
return $mergedDocuments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current state of a transaction by replaying its operations
|
||||
*
|
||||
* @param string $transactionId Transaction ID
|
||||
* @return array State array with structure: [collectionId => [docId => ['action' => ..., 'document' => ..., 'exists' => ...]]]
|
||||
* @throws Exception
|
||||
* @throws Exception\Query
|
||||
* @throws Timeout
|
||||
*/
|
||||
private function getTransactionState(string $transactionId): array
|
||||
{
|
||||
$transaction = Authorization::skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId));
|
||||
if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') {
|
||||
return [];
|
||||
}
|
||||
|
||||
$operations = Authorization::skip(fn () => $this->dbForProject->find('transactionLogs', [
|
||||
Query::equal('transactionInternalId', [$transaction->getSequence()]),
|
||||
Query::orderAsc(),
|
||||
Query::limit(PHP_INT_MAX)
|
||||
]));
|
||||
|
||||
$state = [];
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
$databaseInternalId = $operation['databaseInternalId'];
|
||||
$collectionInternalId = $operation['collectionInternalId'];
|
||||
$collectionId = "database_{$databaseInternalId}_collection_{$collectionInternalId}";
|
||||
$documentId = $operation['documentId'];
|
||||
$action = $operation['action'];
|
||||
$data = $operation['data'];
|
||||
|
||||
if ($data instanceof Document) {
|
||||
$data = $data->getArrayCopy();
|
||||
}
|
||||
|
||||
switch ($action) {
|
||||
case 'create':
|
||||
$docId = $documentId ?? ($data['$id'] ?? null);
|
||||
if ($docId) {
|
||||
if (!isset($data['$id'])) {
|
||||
$data['$id'] = $docId;
|
||||
}
|
||||
$state[$collectionId][$docId] = [
|
||||
'action' => 'create',
|
||||
'document' => new Document($data),
|
||||
'exists' => true
|
||||
];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'update':
|
||||
if (isset($state[$collectionId][$documentId])) {
|
||||
$existingDocument = $state[$collectionId][$documentId]['document'];
|
||||
foreach ($data as $key => $value) {
|
||||
if ($key !== '$id') {
|
||||
$existingDocument->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
// Only set action to 'update' if it's not already 'create' or 'upsert'
|
||||
$currentAction = $state[$collectionId][$documentId]['action'];
|
||||
if ($currentAction !== 'create' && $currentAction !== 'upsert') {
|
||||
$state[$collectionId][$documentId]['action'] = 'update';
|
||||
}
|
||||
} else {
|
||||
$state[$collectionId][$documentId] = [
|
||||
'action' => 'update',
|
||||
'document' => new Document($data),
|
||||
'exists' => true
|
||||
];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'upsert':
|
||||
$docId = $documentId ?? ($data['$id'] ?? null);
|
||||
if (!$docId) {
|
||||
break;
|
||||
}
|
||||
$state[$collectionId][$docId] = [
|
||||
'action' => 'upsert',
|
||||
'document' => new Document($data),
|
||||
'exists' => true
|
||||
];
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
$state[$collectionId][$documentId] = [
|
||||
'action' => 'delete',
|
||||
'exists' => false
|
||||
];
|
||||
break;
|
||||
|
||||
case 'increment':
|
||||
case 'decrement':
|
||||
$attribute = $data['attribute'] ?? null;
|
||||
$value = $data['value'] ?? 1;
|
||||
|
||||
if ($attribute) {
|
||||
if (isset($state[$collectionId][$documentId])) {
|
||||
$existingDocument = $state[$collectionId][$documentId]['document'];
|
||||
$currentValue = $existingDocument->getAttribute($attribute, 0);
|
||||
$newValue = $action === 'increment' ? $currentValue + $value : $currentValue - $value;
|
||||
$existingDocument->setAttribute($attribute, $newValue);
|
||||
|
||||
$currentAction = $state[$collectionId][$documentId]['action'];
|
||||
if ($currentAction !== 'create' && $currentAction !== 'upsert') {
|
||||
$state[$collectionId][$documentId]['action'] = 'update';
|
||||
}
|
||||
} else {
|
||||
$newValue = $action === 'increment' ? $value : -$value;
|
||||
$state[$collectionId][$documentId] = [
|
||||
'action' => 'update',
|
||||
'document' => new Document([$attribute => $newValue]),
|
||||
'exists' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkCreate':
|
||||
if (\is_array($data)) {
|
||||
foreach ($data as $doc) {
|
||||
if ($doc instanceof Document) {
|
||||
$doc = $doc->getArrayCopy();
|
||||
}
|
||||
$state[$collectionId][$doc['$id']] = [
|
||||
'action' => 'create',
|
||||
'document' => new Document($doc),
|
||||
'exists' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkUpdate':
|
||||
if (isset($data['queries']) && isset($data['data'])) {
|
||||
$queries = Query::parseQueries($data['queries'] ?? []);
|
||||
$updateData = $data['data'];
|
||||
|
||||
foreach ($state[$collectionId] ?? [] as $docId => $entry) {
|
||||
if (!$entry['exists']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$document = $entry['document'];
|
||||
$filters = $this->extractFilters($queries);
|
||||
|
||||
if ($this->documentMatchesFilters($document, $filters)) {
|
||||
foreach ($updateData as $key => $value) {
|
||||
if ($key !== '$id') {
|
||||
$document->setAttribute($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$currentAction = $state[$collectionId][$docId]['action'];
|
||||
if ($currentAction !== 'create' && $currentAction !== 'upsert') {
|
||||
$state[$collectionId][$docId]['action'] = 'update';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkUpsert':
|
||||
if (\is_array($data)) {
|
||||
foreach ($data as $doc) {
|
||||
if ($doc instanceof Document) {
|
||||
$doc = $doc->getArrayCopy();
|
||||
}
|
||||
|
||||
$docId = $doc['$id'] ?? null;
|
||||
if (!$docId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isset($state[$collectionId][$docId])) {
|
||||
$existingDocument = $state[$collectionId][$docId]['document'];
|
||||
foreach ($doc as $key => $value) {
|
||||
$existingDocument->setAttribute($key, $value);
|
||||
}
|
||||
} else {
|
||||
$state[$collectionId][$docId] = [
|
||||
'action' => 'upsert',
|
||||
'document' => new Document($doc),
|
||||
'exists' => true
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'bulkDelete':
|
||||
if (isset($data['queries'])) {
|
||||
$queries = Query::parseQueries($data['queries'] ?? []);
|
||||
$filters = $this->extractFilters($queries);
|
||||
|
||||
foreach ($state[$collectionId] ?? [] as $docId => $entry) {
|
||||
if (!$entry['exists']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$document = $entry['document'];
|
||||
if ($this->documentMatchesFilters($document, $filters)) {
|
||||
$state[$collectionId][$docId] = [
|
||||
'action' => 'delete',
|
||||
'exists' => false
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply projection (select) semantics from queries to a document
|
||||
*
|
||||
* @param Document $doc Document to apply projection to
|
||||
* @param array $queries Query array that may contain select queries
|
||||
* @return Document Projected document
|
||||
*/
|
||||
private function applyProjection(Document $doc, array $queries): Document
|
||||
{
|
||||
if (empty($queries)) {
|
||||
return $doc;
|
||||
}
|
||||
|
||||
$selections = [];
|
||||
foreach ($queries as $query) {
|
||||
if ($query->getMethod() === Query::TYPE_SELECT) {
|
||||
$values = $query->getValues();
|
||||
foreach ($values as $value) {
|
||||
// Skip relationship selections (containing '.')
|
||||
if (!\str_contains($value, '.')) {
|
||||
$selections[] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($selections) || \in_array('*', $selections)) {
|
||||
return $doc;
|
||||
}
|
||||
|
||||
// Create a new document with only selected attributes
|
||||
$projected = new Document();
|
||||
|
||||
// Always preserve internal attributes
|
||||
$projected->setAttribute('$id', $doc->getId());
|
||||
$projected->setAttribute('$collection', $doc->getCollection());
|
||||
$projected->setAttribute('$createdAt', $doc->getCreatedAt());
|
||||
$projected->setAttribute('$updatedAt', $doc->getUpdatedAt());
|
||||
if ($doc->offsetExists('$permissions')) {
|
||||
$projected->setAttribute('$permissions', $doc->getPermissions());
|
||||
}
|
||||
|
||||
// Add selected attributes
|
||||
foreach ($selections as $attribute) {
|
||||
if ($doc->offsetExists($attribute)) {
|
||||
$projected->setAttribute($attribute, $doc->getAttribute($attribute));
|
||||
}
|
||||
}
|
||||
|
||||
return $projected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract only filter queries from a query array
|
||||
*
|
||||
* @param array $queries Query array
|
||||
* @return array Filtered queries
|
||||
*/
|
||||
private function extractFilters(array $queries): array
|
||||
{
|
||||
$filters = [];
|
||||
foreach ($queries as $query) {
|
||||
$method = $query->getMethod();
|
||||
if (!\in_array($method, [
|
||||
Query::TYPE_LIMIT,
|
||||
Query::TYPE_OFFSET,
|
||||
Query::TYPE_CURSOR_AFTER,
|
||||
Query::TYPE_CURSOR_BEFORE,
|
||||
Query::TYPE_SELECT,
|
||||
Query::TYPE_ORDER_ASC,
|
||||
Query::TYPE_ORDER_DESC
|
||||
])) {
|
||||
$filters[] = $query;
|
||||
}
|
||||
}
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a document matches filter queries
|
||||
*
|
||||
* @param Document $doc Document to check
|
||||
* @param array $filters Pre-filtered Query filters (use extractFilters first)
|
||||
* @return bool True if document matches all filters
|
||||
*/
|
||||
private function documentMatchesFilters(Document $doc, array $filters): bool
|
||||
{
|
||||
if (empty($filters)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
foreach ($filters as $filter) {
|
||||
$attribute = $filter->getAttribute();
|
||||
$values = $filter->getValues();
|
||||
$docValue = $doc->getAttribute($attribute);
|
||||
|
||||
switch ($filter->getMethod()) {
|
||||
case Query::TYPE_EQUAL:
|
||||
if (!\in_array($docValue, $values)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_NOT_EQUAL:
|
||||
if (\in_array($docValue, $values)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_CONTAINS:
|
||||
$matches = false;
|
||||
foreach ($values as $value) {
|
||||
if (\is_array($docValue) && \in_array($value, $docValue)) {
|
||||
$matches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$matches) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_STARTS_WITH:
|
||||
$matches = false;
|
||||
foreach ($values as $value) {
|
||||
if (\is_string($docValue) && \str_starts_with($docValue, $value)) {
|
||||
$matches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$matches) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_ENDS_WITH:
|
||||
$matches = false;
|
||||
foreach ($values as $value) {
|
||||
if (\is_string($docValue) && \str_ends_with($docValue, $value)) {
|
||||
$matches = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$matches) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_GREATER:
|
||||
if (!($docValue > $values[0])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_GREATER_EQUAL:
|
||||
if (!($docValue >= $values[0])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_LESSER:
|
||||
if (!($docValue < $values[0])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_LESSER_EQUAL:
|
||||
if (!($docValue <= $values[0])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_IS_NULL:
|
||||
if (!\is_null($docValue)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_IS_NOT_NULL:
|
||||
if (\is_null($docValue)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
|
||||
case Query::TYPE_BETWEEN:
|
||||
if (!($docValue >= $values[0] && $docValue <= $values[1])) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue