mirror of
https://github.com/appwrite/appwrite
synced 2026-05-24 01:18:37 +00:00
add: upsert documents/rows.
This commit is contained in:
parent
ad34cf3a62
commit
44b8054ed7
4 changed files with 219 additions and 0 deletions
|
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk;
|
||||
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Conflict as ConflictException;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Relationship as RelationshipException;
|
||||
use Utopia\Database\Exception\Structure as StructureException;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\JSON;
|
||||
|
||||
class Upsert extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'upsertDocuments';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_DOCUMENT_LIST;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT)
|
||||
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/documents')
|
||||
->desc('Create or update documents')
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].collections.[collectionId].documents.[documentId].create')
|
||||
->label('scope', 'documents.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'document.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', [
|
||||
new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/upsert-documents.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_CREATED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON,
|
||||
)
|
||||
])
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('collectionId', '', new UID(), 'Collection ID.')
|
||||
->param('documents', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of document data as JSON objects. May contain partial documents.', false, ['plan'])
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('plan')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $databaseId, string $collectionId, array $documents, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, array $plan): void
|
||||
{
|
||||
$database = $dbForProject->getDocument('databases', $databaseId);
|
||||
if ($database->isEmpty()) {
|
||||
throw new Exception(Exception::DATABASE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$collection = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId);
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception($this->getParentNotFoundException());
|
||||
}
|
||||
|
||||
$hasRelationships = \array_filter(
|
||||
$collection->getAttribute('attributes', []),
|
||||
fn ($attribute) => $attribute->getAttribute('type') === Database::VAR_RELATIONSHIP
|
||||
);
|
||||
|
||||
if ($hasRelationships) {
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk upsert is not supported for ' . $this->getSdkNamespace() . ' with relationship attributes');
|
||||
}
|
||||
|
||||
foreach ($documents as $key => $document) {
|
||||
$documents[$key] = new Document($document);
|
||||
}
|
||||
|
||||
$upserted = [];
|
||||
|
||||
try {
|
||||
$modified = $dbForProject->createOrUpdateDocuments(
|
||||
'database_' . $database->getSequence() . '_collection_' . $collection->getSequence(),
|
||||
$documents,
|
||||
onNext: function (Document $document) use ($plan, &$upserted) {
|
||||
if (\count($upserted) < ($plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH)) {
|
||||
$upserted[] = $document;
|
||||
}
|
||||
},
|
||||
);
|
||||
} catch (ConflictException) {
|
||||
throw new Exception($this->getConflictException());
|
||||
} catch (DuplicateException) {
|
||||
throw new Exception($this->getDuplicateException());
|
||||
} catch (RelationshipException $e) {
|
||||
throw new Exception(Exception::RELATIONSHIP_VALUE_INVALID, $e->getMessage());
|
||||
} catch (StructureException $e) {
|
||||
throw new Exception($this->getInvalidStructureException(), $e->getMessage());
|
||||
}
|
||||
|
||||
foreach ($upserted as $document) {
|
||||
$document->setAttribute('$databaseId', $database->getId());
|
||||
$document->setAttribute('$collectionId', $collection->getId());
|
||||
}
|
||||
|
||||
$queueForStatsUsage
|
||||
->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, \max(1, $modified))
|
||||
->addMetric(str_replace('{databaseInternalId}', $database->getSequence(), METRIC_DATABASE_ID_OPERATIONS_WRITES), \max(1, $modified));
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $modified,
|
||||
$this->getSdkGroup() => $upserted
|
||||
]), $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk;
|
||||
|
||||
use Appwrite\Platform\Modules\Databases\Context;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Upsert as DocumentsUpsert;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\JSON;
|
||||
|
||||
class Upsert extends DocumentsUpsert
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName(): string
|
||||
{
|
||||
return 'upsertRows';
|
||||
}
|
||||
|
||||
protected function getResponseModel(): string
|
||||
{
|
||||
return UtopiaResponse::MODEL_ROW_LIST;
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->setContext(Context::DATABASE_ROWS);
|
||||
|
||||
$this
|
||||
->setHttpMethod(self::HTTP_REQUEST_METHOD_PUT)
|
||||
->setHttpPath('/v1/databases/:databaseId/tables/:tableId/rows')
|
||||
->desc('Create or update rows')
|
||||
->groups(['api', 'database'])
|
||||
->label('event', 'databases.[databaseId].tables.[tableId].rows.[rowId].create')
|
||||
->label('scope', 'documents.write')
|
||||
->label('resourceType', RESOURCE_TYPE_DATABASES)
|
||||
->label('audits.event', 'document.create')
|
||||
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
|
||||
->label('abuse-key', 'ip:{ip},method:{method},url:{url},userId:{userId}')
|
||||
->label('abuse-limit', APP_LIMIT_WRITE_RATE_DEFAULT * 2)
|
||||
->label('abuse-time', APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT)
|
||||
->label('sdk', [
|
||||
new Method(
|
||||
namespace: $this->getSdkNamespace(),
|
||||
group: $this->getSdkGroup(),
|
||||
name: self::getName(),
|
||||
description: '/docs/references/databases/upsert-documents.md',
|
||||
auth: [AuthType::ADMIN, AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: SwooleResponse::STATUS_CODE_CREATED,
|
||||
model: $this->getResponseModel(),
|
||||
)
|
||||
],
|
||||
contentType: ContentType::JSON,
|
||||
)
|
||||
])
|
||||
->param('databaseId', '', new UID(), 'Database ID.')
|
||||
->param('tableId', '', new UID(), 'Table ID.')
|
||||
->param('rows', [], fn (array $plan) => new ArrayList(new JSON(), $plan['databasesBatchSize'] ?? APP_LIMIT_DATABASE_BATCH), 'Array of row data as JSON objects. May contain partial rows.', false, ['plan'])
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('plan')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
}
|
||||
|
|
@ -27,6 +27,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\UR
|
|||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\XList as ListAttributes;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Create as CreateCollection;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Delete as DeleteCollection;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Bulk\Upsert as UpsertDocuments;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Create as CreateDocument;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Delete as DeleteDocument;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Documents\Get as GetDocument;
|
||||
|
|
@ -80,6 +81,7 @@ class Collections extends Base
|
|||
$service->addAction(GetDocument::getName(), new GetDocument());
|
||||
$service->addAction(UpdateDocument::getName(), new UpdateDocument());
|
||||
$service->addAction(UpsertDocument::getName(), new UpsertDocument());
|
||||
$service->addAction(UpsertDocuments::getName(), new UpsertDocuments());
|
||||
$service->addAction(DeleteDocument::getName(), new DeleteDocument());
|
||||
$service->addAction(ListDocuments::getName(), new ListDocuments());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\Delete as
|
|||
use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\Get as GetColumnIndex;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Indexes\XList as ListColumnIndexes;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Logs\XList as ListTableLogs;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Bulk\Upsert as UpsertRows;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Create as CreateRow;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Delete as DeleteRow;
|
||||
use Appwrite\Platform\Modules\Databases\Http\Databases\Tables\Rows\Get as GetRow;
|
||||
|
|
@ -137,6 +138,7 @@ class Tables extends Base
|
|||
$service->addAction(GetRow::getName(), new GetRow());
|
||||
$service->addAction(UpdateRow::getName(), new UpdateRow());
|
||||
$service->addAction(UpsertRow::getName(), new UpsertRow());
|
||||
$service->addAction(UpsertRows::getName(), new UpsertRows());
|
||||
$service->addAction(DeleteRow::getName(), new DeleteRow());
|
||||
$service->addAction(ListRows::getName(), new ListRows());
|
||||
$service->addAction(ListRowLogs::getName(), new ListRowLogs());
|
||||
|
|
|
|||
Loading…
Reference in a new issue