diff --git a/.optimize-cache.json b/.optimize-cache.json index f926af4a597..babde58ee40 100644 --- a/.optimize-cache.json +++ b/.optimize-cache.json @@ -1455,6 +1455,14 @@ "static/images/docs/databases/dark/scale-custom-policies.png": "0013e987e9b8b917cb9be4c28048f851f2d188f3bfa5ff17a11a7ac7cf9c3ade", "static/images/docs/databases/dark/scale-policies.png": "9ca9523f2e20e9aa993f0ad933cdf1dcd12adbaa35ecb2a0b8b3d2fd65877e1f", "static/images/docs/databases/databases.png": "0278a6bc5672684653f74bcf3c0d022fdd82a08d7a7fd438b28e21bd81b5e5d5", + "static/images/docs/databases/documentsdb/backup-policy.png": "913cacbe957847f8a71efa2d2e704754e52cf9c276dce8978f6c473a9ba6595b", + "static/images/docs/databases/documentsdb/backups-tab.png": "231163729496618d26863c6fa7ad88069875f78a3c320033d4bddc3c2f8da376", + "static/images/docs/databases/documentsdb/create-database.png": "49ba017dcec96631a87f924b13b6b93478add7b516cc62947e87230b5a0d5de1", + "static/images/docs/databases/documentsdb/dark/backup-policy.png": "a29fad13b50374b3de55824f1332fa4be9c2c7a0c4c63f550d03306ffd546551", + "static/images/docs/databases/documentsdb/dark/backups-tab.png": "dedb6cfcdd9217e814e58cb14db3d8309f53e3071cb9f12edd9a0d5f9bad1d7c", + "static/images/docs/databases/documentsdb/dark/create-database.png": "fe4c1340226d31fb3fa1d655563c9c8fdd150b4d0aac3e217fdd2af40718c272", + "static/images/docs/databases/documentsdb/dark/manual-backup.png": "880f077a3a598c35c9939a1cfcab4bc82a2a88e730be1b4157359657f0a47dcd", + "static/images/docs/databases/documentsdb/manual-backup.png": "0f3432e170b68503b00fac4cf65ed7c5e5a04f34031b5011fe1c80f1eda2bf1a", "static/images/docs/databases/manual-backup.png": "a5854158c5350e333ae14b699db4626c5c26e17b529f3acd137cfae8cb08e9c4", "static/images/docs/databases/pro-policy.png": "b0d35de73c334614dc3f644459dea2bbc56d0da3157db985f24897597ae26302", "static/images/docs/databases/restore.png": "97611c54c654631d2a86a8453a2ea3603c32e85888c065668e945eeeeb894df0", diff --git a/src/redirects.json b/src/redirects.json index 17340a23b84..bc286d8f2d0 100644 --- a/src/redirects.json +++ b/src/redirects.json @@ -97,15 +97,15 @@ }, { "link": "/docs/databases-queries", - "redirect": "/docs/products/databases/queries" + "redirect": "/docs/products/databases/tablesdb/queries" }, { "link": "/docs/databases-pagination", - "redirect": "/docs/products/databases/pagination" + "redirect": "/docs/products/databases/tablesdb/pagination" }, { "link": "/docs/databases-relationships", - "redirect": "/docs/products/databases/relationships" + "redirect": "/docs/products/databases/tablesdb/relationships" }, { "link": "/docs/storage", @@ -213,11 +213,11 @@ }, { "link": "/docs/queries", - "redirect": "/docs/products/databases/queries" + "redirect": "/docs/products/databases/tablesdb/queries" }, { "link": "/docs/pagination", - "redirect": "/docs/products/databases/pagination" + "redirect": "/docs/products/databases/tablesdb/pagination" }, { "link": "/docs/webhooks", @@ -915,5 +915,141 @@ { "link": "/heroes", "redirect": "/" + }, + { + "link": "/docs/products/databases/ai-suggestions", + "redirect": "/docs/products/databases/tablesdb/ai-suggestions" + }, + { + "link": "/docs/products/databases/atomic-numeric-operations", + "redirect": "/docs/products/databases/tablesdb/atomic-numeric-operations" + }, + { + "link": "/docs/products/databases/backups", + "redirect": "/docs/products/databases/tablesdb/backups" + }, + { + "link": "/docs/products/databases/bulk-operations", + "redirect": "/docs/products/databases/tablesdb/bulk-operations" + }, + { + "link": "/docs/products/databases/csv-exports", + "redirect": "/docs/products/databases/tablesdb/csv-exports" + }, + { + "link": "/docs/products/databases/csv-imports", + "redirect": "/docs/products/databases/tablesdb/csv-imports" + }, + { + "link": "/docs/products/databases/databases", + "redirect": "/docs/products/databases/tablesdb/databases" + }, + { + "link": "/docs/products/databases/geo-queries", + "redirect": "/docs/products/databases/tablesdb/geo-queries" + }, + { + "link": "/docs/products/databases/legacy", + "redirect": "/docs/products/databases/tablesdb/legacy" + }, + { + "link": "/docs/products/databases/legacy/atomic-numeric-operations", + "redirect": "/docs/products/databases/tablesdb/legacy/atomic-numeric-operations" + }, + { + "link": "/docs/products/databases/legacy/bulk-operations", + "redirect": "/docs/products/databases/tablesdb/legacy/bulk-operations" + }, + { + "link": "/docs/products/databases/legacy/collections", + "redirect": "/docs/products/databases/tablesdb/legacy/collections" + }, + { + "link": "/docs/products/databases/legacy/databases", + "redirect": "/docs/products/databases/tablesdb/legacy/databases" + }, + { + "link": "/docs/products/databases/legacy/documents", + "redirect": "/docs/products/databases/tablesdb/legacy/documents" + }, + { + "link": "/docs/products/databases/legacy/order", + "redirect": "/docs/products/databases/tablesdb/legacy/order" + }, + { + "link": "/docs/products/databases/legacy/pagination", + "redirect": "/docs/products/databases/tablesdb/legacy/pagination" + }, + { + "link": "/docs/products/databases/legacy/permissions", + "redirect": "/docs/products/databases/tablesdb/legacy/permissions" + }, + { + "link": "/docs/products/databases/legacy/queries", + "redirect": "/docs/products/databases/tablesdb/legacy/queries" + }, + { + "link": "/docs/products/databases/legacy/quick-start", + "redirect": "/docs/products/databases/tablesdb/legacy/quick-start" + }, + { + "link": "/docs/products/databases/legacy/relationships", + "redirect": "/docs/products/databases/tablesdb/legacy/relationships" + }, + { + "link": "/docs/products/databases/legacy/type-generation", + "redirect": "/docs/products/databases/tablesdb/legacy/type-generation" + }, + { + "link": "/docs/products/databases/offline", + "redirect": "/docs/products/databases/tablesdb/offline" + }, + { + "link": "/docs/products/databases/operators", + "redirect": "/docs/products/databases/tablesdb/operators" + }, + { + "link": "/docs/products/databases/order", + "redirect": "/docs/products/databases/tablesdb/order" + }, + { + "link": "/docs/products/databases/pagination", + "redirect": "/docs/products/databases/tablesdb/pagination" + }, + { + "link": "/docs/products/databases/permissions", + "redirect": "/docs/products/databases/tablesdb/permissions" + }, + { + "link": "/docs/products/databases/queries", + "redirect": "/docs/products/databases/tablesdb/queries" + }, + { + "link": "/docs/products/databases/quick-start", + "redirect": "/docs/products/databases/tablesdb/quick-start" + }, + { + "link": "/docs/products/databases/relationships", + "redirect": "/docs/products/databases/tablesdb/relationships" + }, + { + "link": "/docs/products/databases/rows", + "redirect": "/docs/products/databases/tablesdb/rows" + }, + { + "link": "/docs/products/databases/tables", + "redirect": "/docs/products/databases/tablesdb/tables" + }, + { + "link": "/docs/products/databases/timestamp-overrides", + "redirect": "/docs/products/databases/tablesdb/timestamp-overrides" + }, + { + "link": "/docs/products/databases/transactions", + "redirect": "/docs/products/databases/tablesdb/transactions" + }, + { + "link": "/docs/products/databases/type-generation", + "redirect": "/docs/products/databases/tablesdb/type-generation" } ] diff --git a/src/routes/docs/products/databases/(overview)/+layout.svelte b/src/routes/docs/products/databases/(overview)/+layout.svelte new file mode 100644 index 00000000000..ae270c5d9e4 --- /dev/null +++ b/src/routes/docs/products/databases/(overview)/+layout.svelte @@ -0,0 +1,64 @@ + + + + + + {@render children()} + diff --git a/src/routes/docs/products/databases/(overview)/+page.markdoc b/src/routes/docs/products/databases/(overview)/+page.markdoc new file mode 100644 index 00000000000..635ec2fd952 --- /dev/null +++ b/src/routes/docs/products/databases/(overview)/+page.markdoc @@ -0,0 +1,40 @@ +--- +layout: article +title: Databases +description: Store and query your application data with Appwrite Databases. Choose between Appwrite databases with managed APIs and dedicated native databases with direct access. +--- + +Appwrite Databases provide performant and scalable storage for your application, business, and user data. Choose the database that fits your use case, from managed APIs with permissions and realtime to dedicated native engines you connect to directly. + +{% info title="Looking for file storage?" %} +Databases store data. If you need to store files like images, PDFs, or videos, use [Appwrite Storage](/docs/products/storage). +{% /info %} + +# Appwrite databases {% #appwrite-databases %} + +Managed databases with an Appwrite API on top, including permissions, indexes, queries, and realtime. Available on shared and dedicated infrastructure. + +{% cards %} +{% cards_item href="/docs/products/databases/tablesdb" title="TablesDB" %} +Structured, relational data with typed columns, rows, relationships, and indexes. +{% /cards_item %} +{% cards_item href="/docs/products/databases/documentsdb" title="DocumentsDB" %} +Schemaless document storage for flexible, JSON-style data. +{% /cards_item %} +{% cards_item href="/docs/products/databases/vectorsdb" title="VectorsDB" %} +Store embeddings and run similarity search to power AI features. +{% /cards_item %} +{% /cards %} + +# Native databases {% #native-databases %} + +Native database engines provisioned for your project with direct connection access and no abstraction layer. Available on dedicated infrastructure. + +{% cards %} +{% cards_item href="/docs/products/databases/postgresql" title="PostgreSQL" %} +A dedicated, native PostgreSQL database you connect to directly. +{% /cards_item %} +{% cards_item href="/docs/products/databases/mysql" title="MySQL" %} +A dedicated, native MySQL database you connect to directly. +{% /cards_item %} +{% /cards %} diff --git a/src/routes/docs/products/databases/documentsdb/+layout.svelte b/src/routes/docs/products/databases/documentsdb/+layout.svelte new file mode 100644 index 00000000000..3f63cab9b0a --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/+layout.svelte @@ -0,0 +1,91 @@ + + + + + + {@render children()} + diff --git a/src/routes/docs/products/databases/documentsdb/+page.markdoc b/src/routes/docs/products/databases/documentsdb/+page.markdoc new file mode 100644 index 00000000000..cb74d847aff --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/+page.markdoc @@ -0,0 +1,18 @@ +--- +layout: article +title: DocumentsDB +description: Store and query schemaless documents with Appwrite DocumentsDB. Collections give you flexible, JSON-style storage for your application, business, and user data. +--- + +Appwrite DocumentsDB lets you store and query schemaless documents. +Collections hold documents as flexible JSON, so you can add fields as your data evolves without defining a schema up front. + +{% info title="Looking for file storage?" %} +Databases store data, if you need to store files like images, PDFs or videos, use [Appwrite Storage](/docs/products/storage). +{% /info %} + +You can organize data into databases, collections, and documents. You can also paginate, order, and query documents. + +{% arrow_link href="/docs/products/databases/documentsdb/quick-start" %} +Quick start +{% /arrow_link %} diff --git a/src/routes/docs/products/databases/documentsdb/atomic-numeric-operations/+page.markdoc b/src/routes/docs/products/databases/documentsdb/atomic-numeric-operations/+page.markdoc new file mode 100644 index 00000000000..9dd095b1a31 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/atomic-numeric-operations/+page.markdoc @@ -0,0 +1,867 @@ +--- +layout: article +title: Atomic numeric operations +description: Safely increment and decrement numeric fields without race conditions. Perfect for counters, quotas, inventory, and usage metrics in high-concurrency applications. +--- + +Atomic numeric operations allow you to safely increase or decrease numeric fields without fetching the full document. This eliminates race conditions and reduces bandwidth usage when updating any numeric values that need to be modified atomically, such as counters, scores, balances, and other fast-moving numeric data. + +These operations work on numeric document fields, whether the value is an integer or a floating-point number. + +# How atomic operations work {% #how-atomic-operations-work %} + +Instead of the traditional read-modify-write pattern, atomic numeric operations use dedicated methods to modify values directly on the server. The server applies the change atomically under concurrency control and returns the new value. + +**Traditional approach:** +1. Fetch document → `{ likes: 42 }` +2. Update client-side → `likes: 43` +3. Write back → `{ likes: 43 }` + +**Atomic approach:** +1. Call `incrementDocumentAttribute()` with the attribute key and the value to increment by +2. Server applies atomically → `likes: 43` + +# When to use atomic operations {% #when-to-use-atomic-operations %} + +Atomic numeric operations work well for: + +- **Social features**: Likes, follows, comment counts +- **Usage metering**: API credits, storage quotas, request limits +- **Game state**: Scores, lives, currency, experience points +- **E-commerce**: Stock counts, inventory levels +- **Workflow tracking**: Retry counts, progress indicators +- **Rate limiting**: Request counters, usage tracking + +# Perform atomic operations {% #perform-atomic-operations %} + +Use the `incrementDocumentAttribute` and `decrementDocumentAttribute` methods to perform atomic numeric operations. The server will apply these changes atomically under concurrency control. + +## Increment a field {% #increment-field %} + +{% multicode %} +```client-web +import { Client, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const documentsDB = new DocumentsDB(client); + +const result = await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', // attribute + value: 1 // value +}); +``` +```client-flutter +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final documentsDB = DocumentsDB(client); + +final document = await documentsDB.incrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1 +); +``` +```client-apple +import Appwrite +import AppwriteModels + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let documentsDB = DocumentsDB(client) + +let document = try await documentsDB.incrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "likes", + value: 1 +) +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client(applicationContext) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val documentsDB = DocumentsDB(client) + +val document = documentsDB.incrementDocumentAttribute( + databaseId = "", + collectionId = "", + documentId = "", + attribute = "likes", + value = 1 +) +``` +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', // attribute + value: 1 // value +}); +``` +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.increment_document_attribute( + database_id = '', + collection_id = '', + document_id = '', + attribute = 'likes', # attribute + value = 1 # value +) +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; + +let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + +let documents_db = DocumentsDB::new(&client); + +let result = documents_db.increment_document_attribute( + "", + "", + "", + "likes", // attribute + Some(1.0), // value + None, // max + None, // transaction_id +).await?; +``` +```graphql +mutation { + documentsDBIncrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "likes", + value: 1 + ) { + _id + _collectionId + _databaseId + _createdAt + _updatedAt + _permissions + data + } +} +``` +{% /multicode %} + +## Decrement a field {% #decrement-field %} + +Use the `decrementDocumentAttribute` method to decrease numeric fields: + +{% multicode %} +```client-web +import { Client, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +const documentsDB = new DocumentsDB(client); + +const result = await documentsDB.decrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', // attribute + value: 5 // value +}); +``` +```client-flutter +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +final documentsDB = DocumentsDB(client); + +final document = await documentsDB.decrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', + value: 5 +); +``` +```client-apple +import Appwrite +import AppwriteModels + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +let documentsDB = DocumentsDB(client) + +let document = try await documentsDB.decrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "credits", + value: 5 +) +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client(applicationContext) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +val documentsDB = DocumentsDB(client) + +val document = documentsDB.decrementDocumentAttribute( + databaseId = "", + collectionId = "", + documentId = "", + attribute = "credits", + value = 5 +) +``` +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.decrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', // attribute + value: 5 // value +}); +``` +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.decrement_document_attribute( + database_id = '', + collection_id = '', + document_id = '', + attribute = 'credits', # attribute + value = 5 # value +) +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; + +let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + +let documents_db = DocumentsDB::new(&client); + +let result = documents_db.decrement_document_attribute( + "", + "", + "", + "credits", // attribute + Some(5.0), // value + None, // min + None, // transaction_id +).await?; +``` +```graphql +mutation { + documentsDBDecrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "credits", + value: 5 + ) { + _id + _collectionId + _databaseId + _createdAt + _updatedAt + _permissions + data + } +} +``` +{% /multicode %} + +# Set constraints and bounds {% #set-constraints-and-bounds %} + +You can set minimum and maximum bounds for individual operations to prevent invalid values. Use the optional `min` and `max` parameters to ensure the final value stays within acceptable limits. If an operation would move the value past the bound, the request is rejected. + +## Example with constraints {% #example-with-constraints %} + +{% multicode %} +```client-web +// Increment with maximum constraint +const result = await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', // attribute + value: 100, // value + max: 1000 // max (optional) +}); + +// Decrement with minimum constraint +const result2 = await documentsDB.decrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', // attribute + value: 50, // value + min: 0 // min (optional) +}); +``` +```client-flutter +// Increment with maximum constraint +final document = await documentsDB.incrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', + value: 100, + max: 1000 +); + +// Decrement with minimum constraint +final document2 = await documentsDB.decrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', + value: 50, + min: 0 +); +``` +```client-apple +// Increment with maximum constraint +let document = try await documentsDB.incrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "credits", + value: 100, + max: 1000 +) + +// Decrement with minimum constraint +let document2 = try await documentsDB.decrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "credits", + value: 50, + min: 0 +) +``` +```client-android-kotlin +// Increment with maximum constraint +val document = documentsDB.incrementDocumentAttribute( + databaseId = "", + collectionId = "", + documentId = "", + attribute = "credits", + value = 100, + max = 1000 +) + +// Decrement with minimum constraint +val document2 = documentsDB.decrementDocumentAttribute( + databaseId = "", + collectionId = "", + documentId = "", + attribute = "credits", + value = 50, + min = 0 +) +``` +```server-nodejs +// Increment with maximum constraint +const result = await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', // attribute + value: 100, // value + max: 1000 // max (optional) +}); + +// Decrement with minimum constraint +const result2 = await documentsDB.decrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'credits', // attribute + value: 50, // value + min: 0 // min (optional) +}); +``` +```server-python +# Increment with maximum constraint +result = documents_db.increment_document_attribute( + database_id = '', + collection_id = '', + document_id = '', + attribute = 'credits', # attribute + value = 100, # value + max = 1000 # max (optional) +) + +# Decrement with minimum constraint +result2 = documents_db.decrement_document_attribute( + database_id = '', + collection_id = '', + document_id = '', + attribute = 'credits', # attribute + value = 50, # value + min = 0 # min (optional) +) +``` +```rust +// Increment with maximum constraint +let result = documents_db.increment_document_attribute( + "", + "", + "", + "credits", // attribute + Some(100.0), // value + Some(1000.0), // max (optional) + None, // transaction_id +).await?; + +// Decrement with minimum constraint +let result2 = documents_db.decrement_document_attribute( + "", + "", + "", + "credits", // attribute + Some(50.0), // value + Some(0.0), // min (optional) + None, // transaction_id +).await?; +``` +{% /multicode %} + +# Follow best practices {% #follow-best-practices %} + +## Use for high-concurrency scenarios {% #use-for-high-concurrency-scenarios %} + +Atomic numeric operations are most beneficial when multiple users or processes might update the same numeric field simultaneously. + +## Combine with regular updates {% #combine-with-regular-updates %} + +For complex updates that include both atomic operations and regular field changes, you'll need to use separate API calls: + +{% multicode %} +```client-web +// First, increment the likes atomically +const likeResult = await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', // attribute + value: 1 // value +}); + +// Then, update other fields +const updateResult = await documentsDB.updateDocument({ + databaseId: '', + collectionId: '', + documentId: '', + data: { + lastLikedBy: userId, + lastLikedAt: new Date().toISOString() + } +}); +``` +```client-flutter +// First, increment the likes atomically +final likeResult = await documentsDB.incrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1 +); + +// Then, update other fields +final updateResult = await documentsDB.updateDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: { + 'lastLikedBy': userId, + 'lastLikedAt': DateTime.now().toIso8601String() + } +); +``` +```client-apple +// First, increment the likes atomically +let likeResult = try await documentsDB.incrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "likes", + value: 1 +) + +// Then, update other fields +let updateResult = try await documentsDB.updateDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: [ + "lastLikedBy": userId, + "lastLikedAt": ISO8601DateFormatter().string(from: Date()) + ] +) +``` +```client-android-kotlin +// First, increment the likes atomically +val likeResult = documentsDB.incrementDocumentAttribute( + databaseId = "", + collectionId = "", + documentId = "", + attribute = "likes", + value = 1 +) + +// Then, update other fields +val updateResult = documentsDB.updateDocument( + databaseId = "", + collectionId = "", + documentId = "", + data = mapOf( + "lastLikedBy" to userId, + "lastLikedAt" to Instant.now().toString() + ) +) +``` +```server-nodejs +// First, increment the likes atomically +const likeResult = await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', // attribute + value: 1 // value +}); + +// Then, update other fields +const updateResult = await documentsDB.updateDocument({ + databaseId: '', + collectionId: '', + documentId: '', + data: { + lastLikedBy: userId, + lastLikedAt: new Date().toISOString() + } +}); +``` +```server-python +# First, increment the likes atomically +like_result = documents_db.increment_document_attribute( + database_id = '', + collection_id = '', + document_id = '', + attribute = 'likes', # attribute + value = 1 # value +) + +# Then, update other fields +update_result = documents_db.update_document( + database_id = '', + collection_id = '', + document_id = '', + data = { + 'lastLikedBy': user_id, + 'lastLikedAt': datetime.now().isoformat() + } +) +``` +```rust +use serde_json::json; + +// First, increment the likes atomically +let like_result = documents_db.increment_document_attribute( + "", + "", + "", + "likes", // attribute + Some(1.0), // value + None, // max + None, // transaction_id +).await?; + +// Then, update other fields +let update_result = documents_db.update_document( + "", + "", + "", + Some(json!({ + "lastLikedBy": user_id, + "lastLikedAt": chrono::Utc::now().to_rfc3339() + })), + None, // permissions + None, // transaction_id +).await?; +``` +{% /multicode %} + +# Use transactions {% #use-transactions %} + +Atomic numeric operations accept `transactionId`. When provided, increments/decrements are staged and applied on commit. + +{% multicode %} +```client-web +await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1, + transactionId: '' +}); +``` +```client-flutter +await documentsDB.incrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1, + transactionId: '' +); +``` +```client-apple +try await documentsDB.incrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "likes", + value: 1, + transactionId: "" +) +``` +```client-android-kotlin +documentsDB.incrementDocumentAttribute( + databaseId = "", + collectionId = "", + documentId = "", + attribute = "likes", + value = 1, + transactionId = "" +) +``` +```client-android-java +documentsDB.incrementDocumentAttribute( + "", + "", + "", + "likes", + 1, + "", + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(result); + return null; + }) +); +``` +```client-react-native +await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1, + transactionId: '' +}); +``` +```server-nodejs +await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1, + transactionId: '' +}); +``` +```server-deno +await documentsDB.incrementDocumentAttribute({ + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1, + transactionId: '' +}); +``` +```server-python +documents_db.increment_document_attribute( + database_id = '', + collection_id = '', + document_id = '', + attribute = 'likes', + value = 1, + transaction_id = '' +) +``` +```rust +documents_db.increment_document_attribute( + "", + "", + "", + "likes", + Some(1.0), + None, // max + Some(""), // transaction_id +).await?; +``` +```server-php +$documentsDB->incrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1, + transactionId: '' +); +``` +```server-ruby +documents_db.increment_document_attribute( + database_id: '', + collection_id: '', + document_id: '', + attribute: 'likes', + value: 1, + transaction_id: '' +) +``` +```server-dotnet +await documentsDB.IncrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "likes", + value: 1, + transactionId: "" +); +``` +```server-dart +await documentsDB.incrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: 'likes', + value: 1, + transactionId: '' +); +``` +```server-swift +try await documentsDB.incrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "likes", + value: 1, + transactionId: "" +) +``` +```server-kotlin +documentsDB.incrementDocumentAttribute( + databaseId = "", + collectionId = "", + documentId = "", + attribute = "likes", + value = 1, + transactionId = "" +) +``` +```server-java +documentsDB.incrementDocumentAttribute( + "", + "", + "", + "likes", + 1, + "", + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(result); + return null; + }) +); +``` +{% /multicode %} + +## Explore related features + +- [Bulk operations](/docs/products/databases/documentsdb/bulk-operations) - Update multiple documents at once +- [Permissions](/docs/products/databases/documentsdb/permissions) - Control access to documents +- [Queries](/docs/products/databases/documentsdb/queries) - Find documents to update diff --git a/src/routes/docs/products/databases/documentsdb/backups/+page.markdoc b/src/routes/docs/products/databases/documentsdb/backups/+page.markdoc new file mode 100644 index 00000000000..a5930313c71 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/backups/+page.markdoc @@ -0,0 +1,89 @@ +--- +layout: article +title: Backups +description: Learn how to back up and restore your DocumentsDB databases on Appwrite Cloud, ensuring data security and seamless recovery. +--- + +Appwrite Backups enable seamless, **encrypted** database backups on Cloud. +All backups are **hot** backups, ensuring zero downtime and fast recovery. + +{% info title="Backups are available on Appwrite Cloud for all Pro and Enterprise customers." %} +{% /info %} + +You manage backups from a database's **Backups** tab, where you can automate backups with policies or create manual backups on demand. + +{% only_dark %} +![Backups tab](/images/docs/databases/documentsdb/dark/backups-tab.avif) +{% /only_dark %} +{% only_light %} +![Backups tab](/images/docs/databases/documentsdb/backups-tab.avif) +{% /only_light %} + +# Backup policies {% #backup-policies %} + +Backup policies automate your backups on a schedule. To create one, open your database's **Backups** tab and click **Create policy**, then choose a preset policy or add a custom one. + +{% only_dark %} +![Create backup policy](/images/docs/databases/documentsdb/dark/backup-policy.avif) +{% /only_dark %} +{% only_light %} +![Create backup policy](/images/docs/databases/documentsdb/backup-policy.avif) +{% /only_light %} + +The available options depend on your plan: + +- On the **Pro** plan, you get a **Daily** backup policy retained for 7 days. +- On the **Enterprise** plan, you get access to additional preset policies and custom policies, where you control how often backups run and how long they are retained. + +Click **Create** to save the policy. Your database is now set up for automated backups. + +# Manual backups {% #manual-backups %} + +You can create an on-demand backup whenever necessary. In your database's **Backups** tab, click **Manual backup**, then click **Create**. + +{% only_dark %} +![Manual backup](/images/docs/databases/documentsdb/dark/manual-backup.avif) +{% /only_dark %} +{% only_light %} +![Manual backup](/images/docs/databases/documentsdb/manual-backup.avif) +{% /only_light %} + +Manual backups are retained until you delete them. Depending on the size of your database, the backup may take some time to complete. You can monitor its progress in the backups list. + +# Restoring backups {% #restoring-backups %} + +To restore a database, you need an existing backup. + +1. Open your database's **Backups** tab. +2. In the backups list, open the **Actions** menu for the backup you want to restore. +3. Click **Restore**. +4. Enter a name for the new database and an optional database ID. +5. Click **Restore**. + +Depending on the size of your database, the restoration may take some time. The restore creates a new database from the backup, leaving the original untouched. + +# Backup security & performance {% #backup-security-and-performance %} + +All backups created with Appwrite are: + +1. **Encrypted**: + All backups are securely encrypted to ensure your data remains protected at all times. + +2. **Remotely stored**: + Backups are stored in a remote location, providing an additional layer of security and ensuring your data is always recoverable. + +3. **Hot backups**: + Backups are hot, meaning they occur with zero downtime, allowing you to recover data quickly without interrupting your projects and services. + +# Best practices {% #best-practices %} + +To ensure your backups are robust and effective, consider the following best practices: + +1. **Schedule regular backups**: + Add backup policies based on the frequency of database changes. Daily backups are often sufficient for most use cases. + +2. **Retain critical backups longer**: + Use custom policies with longer retention to keep backups of critical data for extended periods, ensuring historical records are available when needed. + +3. **Optimize backup policies based on data sensitivity**: + Tailor your backup frequency and retention settings according to the sensitivity and importance of the data. diff --git a/src/routes/docs/products/databases/documentsdb/bulk-operations/+page.markdoc b/src/routes/docs/products/databases/documentsdb/bulk-operations/+page.markdoc new file mode 100644 index 00000000000..6522e3bde64 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/bulk-operations/+page.markdoc @@ -0,0 +1,567 @@ +--- +layout: article +title: Bulk operations +description: Perform bulk operations on documents within your collections for efficient data handling. +--- + +Appwrite Databases supports bulk operations for documents, allowing you to create, update, or delete multiple documents in a single request. This can significantly improve performance for apps as it allows you to reduce the number of API calls needed while working with large data sets. + +Bulk operations can only be performed via the server-side SDKs. The client-side SDKs do not support bulk operations by design to prevent abuse and protect against unexpected costs. This ensures that only trusted server environments can perform large-scale data operations. + +For client applications that need bulk-like functionality, consider using [Appwrite Functions](/docs/products/functions) with proper rate limiting and validation. + +{% info title="Important notes" %} +Bulk operations trigger Functions, Webhooks, or Realtime events for each document manipulated. Rather than a single event for the entire bulk operation, each document generates a separate event on the existing realtime channels for its operation type. +{% /info %} + +# Atomic behavior {% #atomic-behavior %} + +Bulk operations in Appwrite are **atomic**, meaning they follow an all-or-nothing approach. Either all documents in your bulk request succeed, or all documents fail. + +This atomicity ensures: +- **Data consistency**: Your database remains in a consistent state even if some operations would fail. +- **Race condition prevention**: Multiple clients can safely perform bulk operations simultaneously. +- **Simplified error handling**: You only need to handle complete success or complete failure scenarios. + +For example, if you attempt to create 100 documents and one fails due to a validation error, none of the 100 documents will be created. + +# Plan limits {% #plan-limits %} + +Bulk operations have different limits based on your Appwrite plan: + +| Plan | Documents per request | +|------|----------------------| +| Free | 100 | +| Pro | 1,000 | + +These limits apply to all bulk operations including create, update, upsert, and delete operations. If you need higher limits than what the Pro plan offers, you can [inquire](/contact-us/enterprise) about a custom plan. + +# Create documents {% #create-documents %} + +You can create multiple documents in a single request using the `createDocuments` method. + +{% info title="Custom timestamps" %} +When creating, updating or upserting in bulk, you can set `$createdAt` and `$updatedAt` for each document in the payload. Values must be ISO 8601 date-time strings. If omitted, Appwrite sets them automatically. +{% /info %} + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.createDocuments({ + databaseId: '', + collectionId: '', + documents: [ + { + $id: sdk.ID.unique(), + name: 'Document 1' + }, + { + $id: sdk.ID.unique(), + name: 'Document 2' + } + ] +}); +``` + +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.id import ID + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +documents_db = DocumentsDB(client) + +result = documents_db.create_documents( + database_id = '', + collection_id = '', + documents = [ + { + '$id': ID.unique(), + 'name': 'Document 1' + }, + { + '$id': ID.unique(), + 'name': 'Document 2' + } + ] +) +``` + +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::id::ID; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_documents( + "", + "", + vec![ + json!({ + "$id": ID::unique(), + "name": "Document 1" + }), + json!({ + "$id": ID::unique(), + "name": "Document 2" + }), + ], + None, + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +# Update documents {% #update-documents %} + +{% info title="Permissions required" %} +You must grant **update** permissions to users at the **collection level** before users can update documents. +[Learn more about permissions](/docs/products/databases/documentsdb/permissions) +{% /info %} + +You can update multiple documents in a single request using the `updateDocuments` method. + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.updateDocuments({ + databaseId: '', + collectionId: '', + data: { + status: 'published' + }, + queries: [ + sdk.Query.equal('status', 'draft') + ] +}); +``` + +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.query import Query + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +documents_db = DocumentsDB(client) + +result = documents_db.update_documents( + database_id = '', + collection_id = '', + data = { + 'status': 'published' + }, + queries = [ + Query.equal('status', 'draft') + ] +) +``` + +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::query::Query; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.update_documents( + "", + "", + Some(json!({ + "status": "published" + })), + Some(vec![ + Query::equal("status", "draft").to_string(), + ]), + None, + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +# Upsert documents {% #upsert-documents %} + +{% info title="Permissions required" %} +You must grant **create** and **update** permissions to users at the **collection level** before users can create documents. +[Learn more about permissions](/docs/products/databases/documentsdb/permissions) +{% /info %} + +You can upsert multiple documents in a single request using the `upsertDocuments` method. + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.upsertDocuments({ + databaseId: '', + collectionId: '', + documents: [ + { + $id: sdk.ID.unique(), + name: 'New Document 1' + }, + { + $id: 'document-id-2', // Existing document ID + name: 'New Document 2' + } + ] +}); +``` + +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.id import ID + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +documents_db = DocumentsDB(client) + +result = documents_db.upsert_documents( + database_id = '', + collection_id = '', + documents = [ + { + '$id': ID.unique(), + 'name': 'New Document 1' + }, + { + '$id': 'document-id-2', # Existing document ID + 'name': 'New Document 2' + } + ] +) +``` + +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::id::ID; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.upsert_documents( + "", + "", + vec![ + json!({ + "$id": ID::unique(), + "name": "New Document 1" + }), + json!({ + "$id": "document-id-2", // Existing document ID + "name": "New Document 2" + }), + ], + None, + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +# Delete documents {% #delete-documents %} + +{% info title="Permissions required" %} +You must grant **delete** permissions to users at the **collection level** before users can delete documents. +[Learn more about permissions](/docs/products/databases/documentsdb/permissions) +{% /info %} + +You can delete multiple documents in a single request using the `deleteDocuments` method. + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.deleteDocuments({ + databaseId: '', + collectionId: '', + queries: [ + sdk.Query.equal('status', 'archived') + ] +}); +``` + +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.query import Query + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +documents_db = DocumentsDB(client) + +result = documents_db.delete_documents( + database_id = '', + collection_id = '', + queries = [ + Query.equal('status', 'archived') + ] +) +``` + +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::query::Query; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.delete_documents( + "", + "", + Some(vec![ + Query::equal("status", "archived").to_string(), + ]), + None, + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +{% info title="Queries for deletion" %} + +When deleting documents, you must specify queries to filter which documents to delete. +If no queries are provided, all documents in the collection will be deleted. +[Learn more about queries](/docs/products/databases/documentsdb/queries). + +{% /info %} + +# Use transactions {% #use-transactions %} + +All bulk operations accept `transactionId`. When provided, Appwrite stages the bulk request and applies it on commit. See [Transactions](/docs/products/databases/documentsdb/transactions). + +{% multicode %} +```server-nodejs +await documentsDB.createDocuments({ + databaseId: '', + collectionId: '', + documents: [ + { $id: sdk.ID.unique(), name: 'One' }, + { $id: sdk.ID.unique(), name: 'Two' } + ], + transactionId: '' +}); +``` +```server-python +documents_db.create_documents( + database_id = '', + collection_id = '', + documents = [ + { '$id': ID.unique(), 'name': 'One' }, + { '$id': ID.unique(), 'name': 'Two' } + ], + transaction_id = '' +) +``` +```server-deno +await documentsDB.createDocuments({ + databaseId: '', + collectionId: '', + documents: [ + { $id: sdk.ID.unique(), name: 'One' }, + { $id: sdk.ID.unique(), name: 'Two' } + ], + transactionId: '' +}); +``` +```server-php +$documentsDB->createDocuments( + databaseId: '', + collectionId: '', + documents: [ + [ '$id' => ID::unique(), 'name' => 'One' ], + [ '$id' => ID::unique(), 'name' => 'Two' ] + ], + transactionId: '' +); +``` +```server-ruby +documents_db.create_documents( + database_id: '', + collection_id: '', + documents: [ + { '$id' => ID.unique(), 'name' => 'One' }, + { '$id' => ID.unique(), 'name' => 'Two' } + ], + transaction_id: '' +) +``` +```server-dotnet +await documentsDB.CreateDocuments( + databaseId: "", + collectionId: "", + documents: new List> + { + new Dictionary + { + ["$id"] = ID.Unique(), + ["name"] = "One" + }, + new Dictionary + { + ["$id"] = ID.Unique(), + ["name"] = "Two" + } + }, + transactionId: "" +); +``` +```server-dart +await documentsDB.createDocuments( + databaseId: '', + collectionId: '', + documents: [ + { '\$id': ID.unique(), 'name': 'One' }, + { '\$id': ID.unique(), 'name': 'Two' } + ], + transactionId: '' +); +``` +```server-swift +try await documentsDB.createDocuments( + databaseId: "", + collectionId: "", + documents: [ + ["$id": ID.unique(), "name": "One"], + ["$id": ID.unique(), "name": "Two"] + ], + transactionId: "" +) +``` +```server-kotlin +documentsDB.createDocuments( + databaseId = "", + collectionId = "", + documents = listOf( + mapOf("\$id" to ID.unique(), "name" to "One"), + mapOf("\$id" to ID.unique(), "name" to "Two") + ), + transactionId = "" +) +``` +```server-java +documentsDB.createDocuments( + "", + "", + Arrays.asList( + Map.of( + "$id", ID.unique(), + "name", "One" + ), + Map.of( + "$id", ID.unique(), + "name", "Two" + ) + ), + "", + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(result); + return null; + }) +); +``` +```rust +let result = documents_db.create_documents( + "", + "", + vec![ + json!({ + "$id": ID::unique(), + "name": "One" + }), + json!({ + "$id": ID::unique(), + "name": "Two" + }), + ], + Some(""), +).await?; +``` +{% /multicode %} diff --git a/src/routes/docs/products/databases/documentsdb/collections/+page.markdoc b/src/routes/docs/products/databases/documentsdb/collections/+page.markdoc new file mode 100644 index 00000000000..09ec098633d --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/collections/+page.markdoc @@ -0,0 +1,526 @@ +--- +layout: article +title: Collections +description: Organize documents with Appwrite DocumentsDB collections. Learn how to create collections, configure permissions, and add indexes for fast queries. +--- +Appwrite uses collections as containers of documents. +Collections are schemaless, so documents in the same collection can hold different fields. You shape data in your application instead of defining columns up front. + +# Create collection {% #create-collection %} +You can create collections using the Appwrite Console, a [Server SDK](/docs/sdks#server), or using the [CLI](/docs/tooling/command-line/installation). +{% tabs %} + +{% tabsitem #console title="Console" %} +Head to the **Databases** page, open a [database](/docs/products/databases/documentsdb/databases), and click **Create collection**. + +{% /tabsitem %} + +{% tabsitem #server-sdk title="Server SDK" %} +You can also create collections programmatically using a [Server SDK](/docs/sdks#server). Appwrite [Server SDKs](/docs/sdks#server) require an [API key](/docs/advanced/platform/api-keys). + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.createCollection({ + databaseId: '', + collectionId: '', + name: '', + permissions: [sdk.Permission.read(sdk.Role.any())], // optional + documentSecurity: false // optional +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.createCollection({ + databaseId: '', + collectionId: '', + name: '', + permissions: [sdk.Permission.read(sdk.Role.any())], // optional + documentSecurity: false // optional +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->createCollection( + databaseId: '', + collectionId: '', + name: '', + permissions: [Permission::read(Role::any())], // optional + documentSecurity: false // optional +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.permission import Permission +from appwrite.role import Role + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.create_collection( + database_id = '', + collection_id = '', + name = '', + permissions = [Permission.read(Role.any())], # optional + document_security = False # optional +) +``` +```ruby +require 'appwrite' + +include Appwrite +include Appwrite::Permission +include Appwrite::Role + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.create_collection( + database_id: '', + collection_id: '', + name: '', + permissions: [Permission.read(Role.any())], # optional + document_security: false # optional +) +``` +```csharp +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +Collection result = await documentsDB.CreateCollection( + databaseId: "", + collectionId: "", + name: "", + permissions: new List { Permission.Read(Role.Any()) }, // optional + documentSecurity: false // optional +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; +import 'package:dart_appwrite/permission.dart'; +import 'package:dart_appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +Collection result = await documentsDB.createCollection( + databaseId: '', + collectionId: '', + name: '', + permissions: [Permission.read(Role.any())], // (optional) + documentSecurity: false, // (optional) +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.Permission +import io.appwrite.Role +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +val response = documentsDB.createCollection( + databaseId = "", + collectionId = "", + name = "", + permissions = listOf(Permission.read(Role.any())), // optional + documentSecurity = false, // optional +) +``` +```java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.createCollection( + "", + "", + "", + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); +``` +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +let collection = try await documentsDB.createCollection( + databaseId: "", + collectionId: "", + name: "", + permissions: [Permission.read(Role.any())], // optional + documentSecurity: false // optional +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; +use appwrite::permission::Permission; +use appwrite::role::Role; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_collection( + "", + "", + "", + Some(vec![Permission::read(Role::any()).to_string()]), // permissions (optional) + Some(false), // documentSecurity (optional) + None, // enabled (optional) + None, // attributes (optional) + None, // indexes (optional) + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} +{% /tabsitem %} +{% /tabs %} + +# Permissions {% #permissions %} +Appwrite uses permissions to control data access. +For security, only users that are granted permissions can access a resource. + +By default, Appwrite doesn't grant permissions to any users when a new collection is created. +This means users can't create documents or read, update, and delete existing documents until you grant access. + +Set `documentSecurity` to `true` on a collection to configure permissions on individual documents. A user then needs either collection level or document level permissions to access a document. + +[Learn about configuring permissions](/docs/products/databases/documentsdb/permissions). + +# Indexes {% #indexes %} +Databases use indexes to quickly locate data without scanning every document. +To ensure the best performance, Appwrite recommends an index for every field you query. +If you plan to query multiple fields in a single query, creating an index with **all** queried fields will yield optimal performance. + +The following indexes are currently supported: + +| Type | Description | +|------------|--------------------------------------------------------------------------------------------------------------| +| `key` | Plain index to allow queries. | +| `unique` | Unique index to disallow duplicates. | +| `fulltext` | For searching within text fields. Required for the [search query method](/docs/products/databases/documentsdb/queries). | + +You can create an index by navigating to your collection's **Indexes** tab or by using your favorite [Server SDK](/docs/sdks#server). + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.createIndex({ + databaseId: '', + collectionId: '', + key: 'title_index', + type: sdk.DocumentsDBIndexType.Key, + attributes: ['title'] +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.createIndex({ + databaseId: '', + collectionId: '', + key: 'title_index', + type: sdk.DocumentsDBIndexType.Key, + attributes: ['title'] +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->createIndex( + databaseId: '', + collectionId: '', + key: 'title_index', + type: DocumentsDBIndexType::KEY(), + attributes: ['title'] +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.enums import DocumentsDBIndexType + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.create_index( + database_id = '', + collection_id = '', + key = 'title_index', + type = DocumentsDBIndexType.KEY, + attributes = ['title'] +) +``` +```ruby +require 'appwrite' + +include Appwrite +include Appwrite::Enums + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.create_index( + database_id: '', + collection_id: '', + key: 'title_index', + type: DocumentsDBIndexType::KEY, + attributes: ['title'] +) +``` +```csharp +using Appwrite; +using Appwrite.Enums; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +Index result = await documentsDB.CreateIndex( + databaseId: "", + collectionId: "", + key: "title_index", + type: DocumentsDBIndexType.Key, + attributes: new List { "title" } +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; +import 'package:dart_appwrite/enums.dart' as enums; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +Index result = await documentsDB.createIndex( + databaseId: '', + collectionId: '', + key: 'title_index', + type: enums.DocumentsDBIndexType.key, + attributes: ['title'], +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.enums.DocumentsDBIndexType +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +val response = documentsDB.createIndex( + databaseId = "", + collectionId = "", + key = "title_index", + type = DocumentsDBIndexType.KEY, + attributes = listOf("title"), +) +``` +```java +import io.appwrite.Client; +import io.appwrite.enums.DocumentsDBIndexType; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; +import java.util.List; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.createIndex( + "", + "", + "title_index", + DocumentsDBIndexType.KEY, + List.of("title"), + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); +``` +```swift +import Appwrite +import AppwriteEnums + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +let index = try await documentsDB.createIndex( + databaseId: "", + collectionId: "", + key: "title_index", + type: .key, + attributes: ["title"] +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; +use appwrite::enums::DocumentsDBIndexType; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_index( + "", + "", + "title_index", + DocumentsDBIndexType::Key, + vec!["title"], + None, // orders (optional) + None, // lengths (optional) + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} diff --git a/src/routes/docs/products/databases/documentsdb/databases/+page.markdoc b/src/routes/docs/products/databases/documentsdb/databases/+page.markdoc new file mode 100644 index 00000000000..f2f21db6e45 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/databases/+page.markdoc @@ -0,0 +1,218 @@ +--- +layout: article +title: Databases +description: Dive deeper into Appwrite DocumentsDB and database configuration. Learn how to create and manage multiple databases for your application. +--- +Databases are the largest organizational unit in Appwrite. +Each database contains a group of [collections](/docs/products/databases/documentsdb/collections). + +# Create in Console {% #create-in-console %} +The easiest way to create a database is using the Appwrite Console. +Navigate to the **Databases** page and click **Create database**, choose **DocumentsDB** as the database type, and select your preferred tier. + +{% only_dark %} +![Create database](/images/docs/databases/documentsdb/dark/create-database.avif) +{% /only_dark %} +{% only_light %} +![Create database](/images/docs/databases/documentsdb/create-database.avif) +{% /only_light %} + +# Create using Server SDKs {% #create-using-server-sdks %} +You can programmatically create databases using a [Server SDK](/docs/sdks#server). Appwrite [Server SDKs](/docs/sdks#server) require an [API key](/docs/advanced/platform/api-keys). + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.create({ + databaseId: '', + name: '' +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.create({ + databaseId: '', + name: '' +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->create( + databaseId: '', + name: '' +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.create( + database_id = '', + name = '' +) +``` +```ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.create( + database_id: '', + name: '' +) +``` +```csharp +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +Database result = await documentsDB.Create( + databaseId: "", + name: "" +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +Database result = await documentsDB.create( + databaseId: '', + name: '', +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +val response = documentsDB.create( + databaseId = "", + name = "", +) +``` +```java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.create( + "", + "", + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); +``` +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +let database = try await documentsDB.create( + databaseId: "", + name: "" +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create( + "", + "", + None, // enabled (optional) + None, // dedicatedDatabaseId (optional) + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +{% /multicode %} + diff --git a/src/routes/docs/products/databases/documentsdb/documents/+page.markdoc b/src/routes/docs/products/databases/documentsdb/documents/+page.markdoc new file mode 100644 index 00000000000..6bb47042249 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/documents/+page.markdoc @@ -0,0 +1,1189 @@ +--- +layout: article +title: Documents +description: Create, read, update, and delete documents in Appwrite DocumentsDB. Learn how to work with schemaless JSON documents in your collections. +--- +Each piece of data in Appwrite DocumentsDB is a document. +Documents are schemaless JSON, so each document in a collection can hold its own set of fields. + +# Create documents {% #create-documents %} +{% info title="Permissions required" %} +You must grant _create_ permissions to users at the _collection level_ before users can create documents. +[Learn more about permissions](#permissions) +{% /info %} + +Use the `createDocument` method to add a document to a collection. Appwrite [Server SDKs](/docs/sdks#server) require an [API key](/docs/advanced/platform/api-keys). + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.createDocument({ + databaseId: '', + collectionId: '', + documentId: sdk.ID.unique(), + data: { title: 'Hamlet', year: 1601 }, + permissions: [sdk.Permission.read(sdk.Role.any())] // optional +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.createDocument({ + databaseId: '', + collectionId: '', + documentId: sdk.ID.unique(), + data: { title: 'Hamlet', year: 1601 }, + permissions: [sdk.Permission.read(sdk.Role.any())] // optional +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->createDocument( + databaseId: '', + collectionId: '', + documentId: ID::unique(), + data: ['title' => 'Hamlet', 'year' => 1601], + permissions: [Permission::read(Role::any())] // optional +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.id import ID +from appwrite.permission import Permission +from appwrite.role import Role + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.create_document( + database_id = '', + collection_id = '', + document_id = ID.unique(), + data = { "title": "Hamlet", "year": 1601 }, + permissions = [Permission.read(Role.any())] # optional +) +``` +```ruby +require 'appwrite' + +include Appwrite +include Appwrite::Permission +include Appwrite::Role + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.create_document( + database_id: '', + collection_id: '', + document_id: ID.unique(), + data: { "title" => "Hamlet", "year" => 1601 }, + permissions: [Permission.read(Role.any())] # optional +) +``` +```csharp +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +Document result = await documentsDB.CreateDocument( + databaseId: "", + collectionId: "", + documentId: ID.Unique(), + data: new { title = "Hamlet", year = 1601 }, + permissions: new List { Permission.Read(Role.Any()) } // optional +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; +import 'package:dart_appwrite/permission.dart'; +import 'package:dart_appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.createDocument( + databaseId: '', + collectionId: '', + documentId: ID.unique(), + data: { "title": "Hamlet", "year": 1601 }, + permissions: [Permission.read(Role.any())], // (optional) +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.ID +import io.appwrite.Permission +import io.appwrite.Role +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +val response = documentsDB.createDocument( + databaseId = "", + collectionId = "", + documentId = ID.unique(), + data = mapOf("title" to "Hamlet", "year" to 1601), + permissions = listOf(Permission.read(Role.any())), // optional +) +``` +```java +import io.appwrite.Client; +import io.appwrite.ID; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; +import java.util.Map; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.createDocument( + "", + "", + ID.unique(), + Map.of("title", "Hamlet", "year", 1601), + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); +``` +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +let document = try await documentsDB.createDocument( + databaseId: "", + collectionId: "", + documentId: ID.unique(), + data: ["title": "Hamlet", "year": 1601], + permissions: [Permission.read(Role.any())] // optional +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; +use appwrite::id::ID; +use appwrite::permission::Permission; +use appwrite::role::Role; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_document( + "", + "", + ID::unique(), + serde_json::json!({ "title": "Hamlet", "year": 1601 }), + Some(vec![Permission::read(Role::any()).to_string()]), // optional + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +```bash +appwrite documents-db create-document \ + --database-id \ + --collection-id \ + --document-id 'unique()' \ + --data '{ "title": "Hamlet", "year": 1601 }' +``` +{% /multicode %} + +# List documents {% #list-documents %} +Use the `listDocuments` method to read documents from a collection. Pass [queries](/docs/products/databases/documentsdb/queries) to filter, order, and paginate the results. + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + sdk.Query.equal('title', 'Hamlet') + ] +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + sdk.Query.equal('title', 'Hamlet') + ] +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query::equal('title', ['Hamlet']) + ] +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.query import Query + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.list_documents( + database_id = '', + collection_id = '', + queries = [ + Query.equal('title', 'Hamlet') + ] +) +``` +```ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.list_documents( + database_id: '', + collection_id: '', + queries: [ + Query.equal('title', ['Hamlet']) + ] +) +``` +```csharp +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +DocumentList result = await documentsDB.ListDocuments( + databaseId: "", + collectionId: "", + queries: new List { + Query.Equal("title", new List { "Hamlet" }) + } +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +DocumentList result = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.equal('title', 'Hamlet') + ], +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +val response = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.equal("title", "Hamlet") + ), +) +``` +```java +import io.appwrite.Client; +import io.appwrite.Query; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; +import java.util.List; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.listDocuments( + "", + "", + List.of( + Query.equal("title", List.of("Hamlet")) + ), + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); +``` +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +let documentList = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.equal("title", value: "Hamlet") + ] +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; +use appwrite::query::Query; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.list_documents( + "", + "", + Some(vec![Query::equal("title", vec!["Hamlet"])]), + None, // transactionId (optional) + None, // total (optional) + None, // ttl (optional) + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +```bash +appwrite documents-db list-documents \ + --database-id \ + --collection-id \ + --queries '{"method":"equal","attribute":"title","values":["Hamlet"]}' +``` +{% /multicode %} + +# Update document {% #update-document %} +Use the `updateDocument` method to update a document. With the patch behavior, you only pass the fields you want to change. + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.updateDocument({ + databaseId: '', + collectionId: '', + documentId: '', + data: { year: 1602 } +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.updateDocument({ + databaseId: '', + collectionId: '', + documentId: '', + data: { year: 1602 } +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->updateDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: ['year' => 1602] +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.update_document( + database_id = '', + collection_id = '', + document_id = '', + data = { "year": 1602 } +) +``` +```ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.update_document( + database_id: '', + collection_id: '', + document_id: '', + data: { "year" => 1602 } +) +``` +```csharp +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +Document result = await documentsDB.UpdateDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: new { year = 1602 } +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.updateDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: { "year": 1602 }, +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +val response = documentsDB.updateDocument( + databaseId = "", + collectionId = "", + documentId = "", + data = mapOf("year" to 1602), +) +``` +```java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; +import java.util.Map; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.updateDocument( + "", + "", + "", + Map.of("year", 1602), + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); +``` +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +let document = try await documentsDB.updateDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: ["year": 1602] +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.update_document( + "", + "", + "", + Some(serde_json::json!({ "year": 1602 })), + None, // permissions (optional) + None, // transactionId (optional) + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +```bash +appwrite documents-db update-document \ + --database-id \ + --collection-id \ + --document-id \ + --data '{ "year": 1602 }' +``` +{% /multicode %} + +# Upsert documents {% #upsert-documents %} +Use the `upsertDocument` method to create a document if it doesn't exist, or update it if it does. + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.upsertDocument({ + databaseId: '', + collectionId: '', + documentId: '', + data: { title: 'Hamlet', year: 1603 } +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.upsertDocument({ + databaseId: '', + collectionId: '', + documentId: '', + data: { title: 'Hamlet', year: 1603 } +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->upsertDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: ['title' => 'Hamlet', 'year' => 1603] +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.upsert_document( + database_id = '', + collection_id = '', + document_id = '', + data = { "title": "Hamlet", "year": 1603 } +) +``` +```ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.upsert_document( + database_id: '', + collection_id: '', + document_id: '', + data: { "title" => "Hamlet", "year" => 1603 } +) +``` +```csharp +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +Document result = await documentsDB.UpsertDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: new { title = "Hamlet", year = 1603 } +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.upsertDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: { "title": "Hamlet", "year": 1603 }, +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +val response = documentsDB.upsertDocument( + databaseId = "", + collectionId = "", + documentId = "", + data = mapOf("title" to "Hamlet", "year" to 1603), +) +``` +```java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; +import java.util.Map; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.upsertDocument( + "", + "", + "", + Map.of("title", "Hamlet", "year", 1603), + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println(result); + }) +); +``` +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +let document = try await documentsDB.upsertDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: ["title": "Hamlet", "year": 1603] +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.upsert_document( + "", + "", + "", + Some(serde_json::json!({ "title": "Hamlet", "year": 1603 })), + None, // permissions (optional) + None, // transactionId (optional) + ).await?; + + println!("{:?}", result); + Ok(()) +} +``` +```bash +appwrite documents-db upsert-document \ + --database-id \ + --collection-id \ + --document-id \ + --data '{ "title": "Hamlet", "year": 1603 }' +``` +{% /multicode %} + +# Delete document {% #delete-document %} +Use the `deleteDocument` method to remove a document from a collection. + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.deleteDocument({ + databaseId: '', + collectionId: '', + documentId: '' +}); +``` +```deno +import * as sdk from "npm:node-appwrite"; + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +const documentsDB = new sdk.DocumentsDB(client); + +const result = await documentsDB.deleteDocument({ + databaseId: '', + collectionId: '', + documentId: '' +}); +``` +```php +setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + ->setProject('') // Your project ID + ->setKey(''); // Your secret API key + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->deleteDocument( + databaseId: '', + collectionId: '', + documentId: '' +); +``` +```python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint +client.set_project('') # Your project ID +client.set_key('') # Your secret API key + +documents_db = DocumentsDB(client) + +result = documents_db.delete_document( + database_id = '', + collection_id = '', + document_id = '' +) +``` +```ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') # Your API Endpoint + .set_project('') # Your project ID + .set_key('') # Your secret API key + +documents_db = DocumentsDB.new(client) + +result = documents_db.delete_document( + database_id: '', + collection_id: '', + document_id: '' +) +``` +```csharp +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndPoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .SetProject("") // Your project ID + .SetKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +await documentsDB.DeleteDocument( + databaseId: "", + collectionId: "", + documentId: "" +); +``` +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +DocumentsDB documentsDB = DocumentsDB(client); + +await documentsDB.deleteDocument( + databaseId: '', + collectionId: '', + documentId: '', +); +``` +```kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client(context) + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +val documentsDB = DocumentsDB(client) + +documentsDB.deleteDocument( + databaseId = "", + collectionId = "", + documentId = "", +) +``` +```java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; + +Client client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey(""); // Your secret API key + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.deleteDocument( + "", + "", + "", + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + + System.out.println("Deleted"); + }) +); +``` +```swift +import Appwrite + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") // Your API Endpoint + .setProject("") // Your project ID + .setKey("") // Your secret API key + +let documentsDB = DocumentsDB(client) + +try await documentsDB.deleteDocument( + databaseId: "", + collectionId: "", + documentId: "" +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::DocumentsDB; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new(); + client.set_endpoint("https://.cloud.appwrite.io/v1"); // Your API Endpoint + client.set_project(""); // Your project ID + client.set_key(""); // Your secret API key + + let documents_db = DocumentsDB::new(&client); + + documents_db.delete_document( + "", + "", + "", + None, // transactionId (optional) + ).await?; + + Ok(()) +} +``` +```bash +appwrite documents-db delete-document \ + --database-id \ + --collection-id \ + --document-id +``` +{% /multicode %} + +# Permissions {% #permissions %} +To access documents through a [Client SDK](/docs/sdks#client), you must grant the relevant permissions. +By default, Appwrite doesn't grant any user permissions when a new collection is created. + +You can configure permissions at the collection level, or enable `documentSecurity` on the collection to also set permissions on individual documents. + +[Learn about configuring permissions](/docs/products/databases/documentsdb/permissions). diff --git a/src/routes/docs/products/databases/documentsdb/order/+page.markdoc b/src/routes/docs/products/databases/documentsdb/order/+page.markdoc new file mode 100644 index 00000000000..c8dfeba16c6 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/order/+page.markdoc @@ -0,0 +1,336 @@ +--- +layout: article +title: Order +description: Understand how to do data ordering in Appwrite Databases. Learn how to order and sort your database records for efficient data retrieval. +--- + +You can order results returned by Appwrite Databases by using an order query. +For best performance, create an [index](/docs/products/databases/documentsdb/collections#indexes) on the field you plan to order by. + +# Ordering one field {% #one-field %} + +When querying using the [listDocuments](/docs/products/databases/documentsdb/documents#list-documents) endpoint, +you can specify the order of the documents returned using the `Query.orderAsc()` and `Query.orderDesc()` query methods. + +{% multicode %} +```client-web +import { Client, Query, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const documentsDB = new DocumentsDB(client); + +documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.orderAsc('title'), + ] +}); +``` + +```client-flutter +import 'package:appwrite/appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + final documentsDB = DocumentsDB(client); + + try { + final documents = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.orderAsc('title') + ] + ); + } on AppwriteException catch(e) { + print(e); + } +} +``` + +```client-apple +import Appwrite +import AppwriteModels + +func main() async throws { + let client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + let documentsDB = DocumentsDB(client) + + do { + let documents = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.orderAsc("title") + ] + ) + } catch { + print(error.localizedDescription) + } +} +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client(applicationContext) + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + val documentsDB = DocumentsDB(client) + + try { + val documents = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = [ + Query.orderAsc("title") + ] + ) + } catch (e: AppwriteException) { + Log.e("Appwrite", e.message) + } +} +``` + +```graphql +query { + documentsDBListDocuments( + databaseId: "", + collectionId: "" + queries: ["orderAsc(\"title\")"] + ) { + total + documents { + _id + data + } + } +} +``` + +{% /multicode %} + +# Multiple fields {% #multiple-fields %} +To sort based on multiple fields, simply provide multiple query methods. +For better performance, create an index on the first field that you order by. + +In the example below, the movies returned will be first sorted by `title` in ascending order, then sorted by `year` in descending order. +{% multicode %} + +```js +// Web SDK code example for sorting based on multiple fields +// ... + +// List documents and sort based on multiple fields +documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.orderAsc('title'), // Order first by title in ascending order + Query.orderDesc('year'), // Then, order by year in descending order + ] +}); +``` +```dart +// Flutter SDK code example for sorting based on multiple fields +// ... + +// List documents and sort based on multiple fields +try { + final documents = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.orderAsc('title'), // Order by title in ascending order + Query.orderDesc('year') // Order by year in descending order + ] + ); +} on AppwriteException catch(e) { + print(e); +} +``` +```kotlin +// Android SDK code example for sorting based on multiple fields +// ... + +// List documents and sort based on multiple fields +try { + val documents = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = [ + Query.orderAsc("title"), // Order by title in ascending order + Query.orderDesc("year") // Order by year in descending order + ] + ); +} catch (e: AppwriteException) { + Log.e("Appwrite", e.message); +} +``` +```swift +// Apple SDK code example for sorting based on multiple fields +// ... + +// List documents and sort based on multiple fields +do { + let documents = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.orderAsc("title"), // Order by title in ascending order + Query.orderDesc("year") // Order by year in descending order + ] + ); +} catch { + print(error.localizedDescription); +} +``` +```graphql +query { + documentsDBListDocuments( + databaseId: "", + collectionId: "", + queries: ["orderAsc(\"title\")", "orderDesc(\"year\")"] + ) { + total + documents { + _id + data + } + } +} +``` +{% /multicode %} + +# Ordering by sequence {% #sequence-ordering %} + +For ordering based on insertion order, you can use the `$sequence` field, which Appwrite automatically adds to all documents. Sorting by `$sequence` returns documents in the order they were created. + +{% multicode %} +```client-web +import { Client, Query, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const documentsDB = new DocumentsDB(client); + +documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.orderAsc('$sequence'), + ] +}); +``` + +```client-flutter +import 'package:appwrite/appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + final documentsDB = DocumentsDB(client); + + try { + final documents = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.orderAsc('\$sequence') + ] + ); + } on AppwriteException catch(e) { + print(e); + } +} +``` + +```client-apple +import Appwrite +import AppwriteModels + +func main() async throws { + let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + let documentsDB = DocumentsDB(client) + + do { + let documents = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.orderAsc("$sequence") + ] + ) + } catch { + print(error.localizedDescription) + } +} +``` + +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client(applicationContext) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + val documentsDB = DocumentsDB(client) + + try { + val documents = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.orderAsc("\$sequence") + ) + ) + } catch (e: AppwriteException) { + Log.e("Appwrite", e.message) + } +} +``` + +```graphql +query { + documentsDBListDocuments( + databaseId: "", + collectionId: "" + queries: ["orderAsc(\"$sequence\")"] + ) { + total + documents { + _id + data + } + } +} +``` +{% /multicode %} + +The `$sequence` field is useful when you need: +- Consistent ordering for pagination, especially with high-frequency inserts +- Reliable insertion order tracking when timestamps might not be precise enough +- Simple insertion-order sorting without managing custom counter fields diff --git a/src/routes/docs/products/databases/documentsdb/pagination/+page.markdoc b/src/routes/docs/products/databases/documentsdb/pagination/+page.markdoc new file mode 100644 index 00000000000..1f310e8ffeb --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/pagination/+page.markdoc @@ -0,0 +1,540 @@ +--- +layout: article +title: Pagination +description: Implement pagination for large data sets in Appwrite Databases. Explore techniques for splitting and displaying data across multiple pages. +--- + +As your collection grows in size, you'll need to paginate the documents returned. +Pagination improves performance by returning a subset of documents that match a query at a time, called a page. + +By default, list operations return 25 documents per page, which can be changed using the `Query.limit()` query method. +There is no hard limit on the number of documents you can request. However, beware that **large pages can degrade performance**. + +# Offset pagination {% #offset-pagination %} + +Offset pagination divides documents into pages of `N` documents each. +To read page number `P`, skip `offset = N * (P - 1)` documents, then read the next `N`. + +Using `Query.limit()` and `Query.offset()` you can achieve offset pagination. +With `Query.limit()` you define how many documents can be returned from one request. +The `Query.offset()` is the number of documents you wish to skip before selecting documents. + +{% multicode %} +```client-web +import { Client, Query, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const documentsDB = new DocumentsDB(client); + +// Page 1 +const page1 = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25), + Query.offset(0) + ] +}); + +// Page 2 +const page2 = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25), + Query.offset(25) + ] +}); +``` +```client-flutter +import 'package:appwrite/appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + final documentsDB = DocumentsDB(client); + + final page1 = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25), + Query.offset(0) + ] + ); + + final page2 = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25), + Query.offset(25) + ] + ); +} +``` +```client-apple +import Appwrite +import AppwriteModels + +func main() async throws { + let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + let documentsDB = DocumentsDB(client) + + let page1 = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.limit(25), + Query.offset(0) + ] + ) + + let page2 = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.limit(25), + Query.offset(25) + ] + ) +} +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client(applicationContext) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + val documentsDB = DocumentsDB(client) + + val page1 = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.limit(25), + Query.offset(0) + ) + ) + + val page2 = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.limit(25), + Query.offset(25) + ) + ) +} +``` + +{% /multicode %} + +{% info title="Drawbacks" %} +While traditional offset pagination is familiar, it comes with some drawbacks. +The request gets slower as the offset increases because the database has to skip over all the preceding documents before it can start selecting data. +If the data changes frequently, offset pagination will also produce **missing and duplicate** results. +{% /info %} + +# Cursor pagination {% #cursor-pagination %} + +The cursor is a unique identifier for a document that points to where the next page should start. +After reading a page of documents, pass the last document's ID into the `Query.cursorAfter(lastId)` query method to get the next page of documents. +Pass the first document's ID into the `Query.cursorBefore(firstId)` query method to retrieve the previous page. + +{% multicode %} + +```client-web +import { Client, Query, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject(""); + +const documentsDB = new DocumentsDB(client); + +// Page 1 +const page1 = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25) + ] +}); + +const lastId = page1.documents[page1.documents.length - 1].$id; + +// Page 2 +const page2 = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25), + Query.cursorAfter(lastId) + ] +}); +``` + +```client-flutter +import 'package:appwrite/appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + final documentsDB = DocumentsDB(client); + + final page1 = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25) + ] + ); + + final lastId = page1.documents[page1.documents.length - 1].$id; + + final page2 = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25), + Query.cursorAfter(lastId) + ] + ); +} +``` +```client-apple +import Appwrite +import AppwriteModels + +func main() async throws { + let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + let documentsDB = DocumentsDB(client) + + let page1 = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.limit(25) + ] + ) + + let lastId = page1.documents[page1.documents.count - 1].$id + + let page2 = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.limit(25), + Query.cursorAfter(lastId) + ] + ) +} +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client(applicationContext) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + val documentsDB = DocumentsDB(client) + + val page1 = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.limit(25) + ) + ) + + val lastId = page1.documents[page1.documents.size - 1].$id + + val page2 = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.limit(25), + Query.cursorAfter(lastId) + ) + ) +} +``` + +{% /multicode %} + +# When to use what? {% #when-to-use %} +Offset pagination should be used for collections that rarely change. +Offset pagination lets you build an indicator of the current page number and the total page count. +For example, a list with up to 20 pages or static data like a list of countries or currencies. +Using offset pagination on large and frequently updated collections may result in slow performance and **missing and duplicate** results. + +Cursor pagination should be used for frequently updated collections. +It is best suited for lazy-loaded pages with infinite scrolling. +For example, a feed, comment section, chat history, or high volume datasets. + +# Cache list responses {% #cache-list-responses %} + +You can cache list responses by passing a `ttl` (time-to-live) value in seconds. Subsequent identical requests return the cached result until the TTL expires. The cache is permission-aware, so users with different roles never see each other's cached data. + +Set `ttl` between `1` and `86400` (24 hours). The default is `0` (caching disabled). The response includes an `X-Appwrite-Cache` header with value `hit` or `miss`. + +{% multicode %} +```client-web +import { Client, Query, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const documentsDB = new DocumentsDB(client); + +const page = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25) + ], + ttl: 60 // Cache for 60 seconds +}); +``` +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const documentsDB = new sdk.DocumentsDB(client); + +const page = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + sdk.Query.limit(25) + ], + ttl: 60 // Cache for 60 seconds +}); +``` +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.query import Query + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +documents_db = DocumentsDB(client) + +page = documents_db.list_documents( + database_id='', + collection_id='', + queries=[ + Query.limit(25) + ], + ttl=60 # Cache for 60 seconds +) +``` +```server-ruby +require 'appwrite' + +client = Appwrite::Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +documents_db = Appwrite::DocumentsDB.new(client) + +page = documents_db.list_documents( + database_id: '', + collection_id: '', + queries: [ + Appwrite::Query.limit(25) + ], + ttl: 60 # Cache for 60 seconds +) +``` +```server-deno +import { Client, Query, DocumentsDB } from "https://deno.land/x/appwrite/mod.ts"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const documentsDB = new DocumentsDB(client); + +const page = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25) + ], + ttl: 60 // Cache for 60 seconds +}); +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$documentsDB = new DocumentsDB($client); + +$page = $documentsDB->listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query::limit(25) + ], + ttl: 60 // Cache for 60 seconds +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + + final documentsDB = DocumentsDB(client); + + final page = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.limit(25) + ], + ttl: 60 // Cache for 60 seconds + ); +} +``` +```server-swift +import Appwrite +import AppwriteModels + +func main() async throws { + let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + + let documentsDB = DocumentsDB(client) + + let page = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.limit(25) + ], + ttl: 60 // Cache for 60 seconds + ) +} +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + + val documentsDB = DocumentsDB(client) + + val page = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.limit(25) + ), + ttl = 60 // Cache for 60 seconds + ) +} +``` +```server-rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::query::Query; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let page = documents_db.list_documents( + "", + "", + Some(vec![ + Query::limit(25).to_string(), + ]), + None, // transaction_id + None, // total + Some(60), // ttl - Cache for 60 seconds + ).await?; + + println!("{:?}", page); + Ok(()) +} +``` +```graphql +query { + documentsDBListDocuments( + databaseId: "", + collectionId: "", + queries: ["limit(25)"], + ttl: 60 + ) { + total + documents { + _id + data + } + } +} +``` +```http +GET /v1/documentsdb//collections//documents?ttl=60 HTTP/1.1 +Content-Type: application/json +X-Appwrite-Project: +``` +{% /multicode %} + +Document writes do **not** invalidate the cache, so cached responses may contain stale data until the TTL expires. Use a short TTL for collections that change often, or skip caching entirely when you always need the latest documents. diff --git a/src/routes/docs/products/databases/documentsdb/permissions/+page.markdoc b/src/routes/docs/products/databases/documentsdb/permissions/+page.markdoc new file mode 100644 index 00000000000..5ccd0905b35 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/permissions/+page.markdoc @@ -0,0 +1,35 @@ +--- +layout: article +title: Database permissions +description: Control access to your DocumentsDB data with permissions. Learn how to set collection level and document level access rules. +--- + +Permissions define who can access documents in a collection. By default **no permissions** are granted to any users, so no user can access any documents. +Permissions exist at two levels, collection level and document level permissions. + +In Appwrite, permissions are **granted**, meaning a user has no access by default and receives access when granted. +A user with access granted at either collection level or document level will be able to access a document. +Users **don't need access at both levels** to access documents. + +# Collection level {% #collection-level %} +Collection level permissions apply to every document in the collection. +If a user has read, create, update, or delete permissions at the collection level, the user can access **all documents** inside the collection. + +Configure collection level permissions by navigating to **Your collection** > **Security** > **Permissions**. + +[Learn more about permissions and roles](/docs/advanced/platform/permissions) + +# Document level {% #document-level %} +Document level permissions grant access to individual documents. +If a user has read, create, update, or delete permissions at the document level, the user can access the **individual document**. + +Document level permissions are only applied if row level security is enabled in the security settings of your collection. +Enable document level permissions by navigating to **Your collection** > **Security** > **Row level security (RLS)**. + +Document level permissions are configured on individual documents. + +[Learn more about permissions and roles](/docs/advanced/platform/permissions) + +# Common use cases {% #common-use-cases %} + +For examples of how to implement common permission patterns, including creating private documents that are only accessible to their creators, see the [permissions examples](/docs/advanced/platform/permissions#examples) in our platform documentation. diff --git a/src/routes/docs/products/databases/documentsdb/queries/+page.markdoc b/src/routes/docs/products/databases/documentsdb/queries/+page.markdoc new file mode 100644 index 00000000000..34d9f057694 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/queries/+page.markdoc @@ -0,0 +1,1855 @@ +--- +layout: article +title: Queries +description: Harness the power of querying with Appwrite DocumentsDB. Discover various query options, filtering, sorting, and advanced querying techniques. +--- + +Many list endpoints in Appwrite allow you to filter, sort, and paginate results using queries. Appwrite provides a common set of syntax to build queries. + +# Query class {% #query-class %} + +Appwrite SDKs provide a `Query` class to help you build queries. The `Query` class has methods for each type of supported query operation. + +# Building queries {% #building-queries %} + +Queries are passed to an endpoint through the `queries` parameter as an array of query strings, which can be generated using the `Query` class. + +Each query method is logically separated via `AND` operations. For `OR` operation, pass multiple values into the query method separated by commas. +For example `Query.equal('title', ['Avatar', 'Lord of the Rings'])` will fetch the movies `Avatar` or `Lord of the Rings`. + +{% info title="Default pagination behavior" %} +By default, results are limited to the **first 25 items**. +You can change this through [pagination](/docs/products/databases/documentsdb/pagination). +{% /info %} + +{% multicode %} + +```client-web +import { Client, Query, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const documentsDB = new DocumentsDB(client); + +documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.equal('title', ['Avatar', 'Lord of the Rings']), + Query.greaterThan('year', 1999) + ] +}); +``` +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client(); + +const documentsDB = new sdk.DocumentsDB(client); + +client + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey('') +; + +const promise = documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + sdk.Query.equal('title', ['Avatar', 'Lord of the Rings']), + sdk.Query.greaterThan('year', 1999) + ] +}); + +promise.then(function (response) { + console.log(response); +}, function (error) { + console.log(error); +}); +``` +```client-flutter +import 'package:appwrite/appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + final documentsDB = DocumentsDB(client); + + try { + final documents = await documentsDB.listDocuments( + '', + '', + [ + Query.equal('title', ['Avatar', 'Lord of the Rings']), + Query.greaterThan('year', 1999) + ] + ); + } on AppwriteException catch(e) { + print(e); + } +} +``` +```client-apple +import Appwrite +import AppwriteModels + +func main() async throws { + let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + let documentsDB = DocumentsDB(client) + + do { + let documents = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.equal("title", value: ["Avatar", "Lord of the Rings"]), + Query.greaterThan("year", value: 1999) + ] + ) + } catch { + print(error.localizedDescription) + } +} +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client(applicationContext) + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + val documentsDB = DocumentsDB(client) + + try { + val documents = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.equal("title", listOf("Avatar", "Lord of the Rings")), + Query.greaterThan("year", 1999) + ) + ) + } catch (e: AppwriteException) { + Log.e("Appwrite", e.message) + } +} +``` +```server-go +package main + +import ( + "fmt" + "log" + + "github.com/appwrite/sdk-for-go/appwrite" + "github.com/appwrite/sdk-for-go/query" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://.cloud.appwrite.io/v1"), + appwrite.WithProject(""), + appwrite.WithKey(""), + ) + + documentsDB := appwrite.NewDocumentsDB(client) + + documents, err := documentsDB.ListDocuments( + "", + "", + documentsDB.WithListDocumentsQueries([]string{ + query.Equal("title", []string{"Avatar", "Lord of the Rings"}), + query.GreaterThan("year", 1999), + }), + ) + if err != nil { + log.Fatal(err) + } + + fmt.Printf("Documents: %+v\n", documents) +} +``` +```server-rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::query::Query; +use serde_json::Value; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let documents = documents_db.list_documents( + "", + "", + Some(vec![ + Query::equal("title", Value::Array(vec![ + Value::String("Avatar".to_string()), + Value::String("Lord of the Rings".to_string()), + ])).to_string(), + Query::greater_than("year", 1999).to_string(), + ]), + None, + None, + None, + ).await?; + + println!("{:?}", documents); + Ok(()) +} +``` +```server-php +setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey('') +; + +$documentsDB = new DocumentsDB($client); + +$result = $documentsDB->listDocuments( + '', + '', + [ + Query::equal('title', ['Avatar', 'Lord of the Rings']), + Query::greaterThan('year', 1999) + ] +); +``` +```server-python +from appwrite.client import Client +from appwrite.query import Query +from appwrite.services.documents_db import DocumentsDB + +client = Client() + +(client + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') +) + +documentsDB = DocumentsDB(client) + +result = documentsDB.list_documents( + '', + '', + [ + Query.equal('title', ['Avatar', 'Lord of the Rings']), + Query.greater_than('year', 1999) + ] +) +``` +```graphql +query { + documentsDBListDocuments( + databaseId: "", + collectionId: "" + queries: [ + "{\"method\":\"equal\",\"attribute\":\"title\",\"values\":[\"Avatar\",\"Lord of the Rings\"]}", + "{\"method\":\"greaterThan\",\"attribute\":\"year\",\"values\":[1999]}" + ] + ) { + total + documents { + _id + data + } + } +} +``` +```http +GET /v1/documentsdb//collections//documents?queries[]=%7B%22method%22%3A%22equal%22%2C%22attribute%22%3A%22title%22%2C%22values%22%3A%5B%22Avatar%22%2C%22Lord%20of%20the%20Rings%22%5D%7D&queries[]=%7B%22method%22%3A%22greaterThan%22%2C%22attribute%22%3A%22year%22%2C%22values%22%3A%5B1999%5D%7D HTTP/1.1 +Content-Type: application/json +X-Appwrite-Project: +``` +```bash +appwrite documents-db list-documents \ + --database-id \ + --collection-id \ + --queries '{"method":"equal","attribute":"title","values":["Avatar","Lord of the Rings"]}' '{"method":"greaterThan","attribute":"year","values":[1999]}' +``` +{% /multicode %} + +# Query operators {% #query-operators %} + +## Select {% #select %} + +The `select` operator allows you to specify which fields should be returned from a document. This optimizes response size and retrieves only the data you need. + +{% multicode %} +```client-web +Query.select(["name", "title"]) +``` +```client-flutter +Query.select(["name", "title"]) +``` +```server-python +Query.select(["name", "title"]) +``` +```server-ruby +Query.select(["name", "title"]) +``` +```server-deno +Query.select(["name", "title"]) +``` +```server-php +Query::select(["name", "title"]) +``` +```client-apple +Query.select(["name", "title"]) +``` +```server-go +query.Select([]string{"name", "title"}) +``` +```server-rust +Query::select(vec!["name", "title"]).to_string() +``` +```http +{"method":"select","values":["name","title"]} +``` +{% /multicode %} + +### Use selection patterns {% #select-patterns %} + +| Pattern | Description | Use case | +|---------|-------------|----------| +| `["field1", "field2"]` | Specific fields only | Minimize response size | +| `["*"]` | All document fields | Get complete document data | + +### Optimize performance {% #select-performance %} + +**Optimize response size** - Only select the fields you actually need. Smaller responses are faster to transfer and parse. + +**Reduce database load** - Selecting fewer fields reduces database processing time, especially for large documents. + +## Comparison operators {% #comparison %} + +### Equal {% #equal %} + +Returns document if field is equal to any value in the provided array. + +{% multicode %} +```client-web +Query.equal("title", ["Iron Man"]) +``` +```client-flutter +Query.equal("title", ["Iron Man"]) +``` +```server-python +Query.equal("title", ["Iron Man"]) +``` +```server-ruby +Query.equal("title", ["Iron Man"]) +``` +```server-deno +Query.equal("title", ["Iron Man"]) +``` +```server-php +Query::equal("title", ["Iron Man"]) +``` +```client-apple +Query.equal("title", value: ["Iron Man"]) +``` +```server-go +query.Equal("title", []string{"Iron Man"}) +``` +```server-rust +Query::equal("title", Value::Array(vec![Value::String("Iron Man".to_string())])).to_string() +``` +```http +{"method":"equal","attribute":"title","values":["Iron Man"]} +``` +{% /multicode %} + +### Not equal {% #not-equal %} + +Returns document if field is not equal to any value in the provided array. + +{% multicode %} +```client-web +Query.notEqual("title", "Iron Man") +``` +```client-flutter +Query.notEqual("title", "Iron Man") +``` +```server-python +Query.not_equal("title", "Iron Man") +``` +```server-ruby +Query.not_equal("title", "Iron Man") +``` +```server-deno +Query.notEqual("title", "Iron Man") +``` +```server-php +Query::notEqual("title", "Iron Man") +``` +```client-apple +Query.notEqual("title", value: "Iron Man") +``` +```server-go +query.NotEqual("title", "Iron Man") +``` +```server-rust +Query::not_equal("title", "Iron Man").to_string() +``` +```http +{"method":"notEqual","attribute":"title","values":"Iron Man"} +``` +{% /multicode %} + +### Less than {% #less-than %} + +Returns document if field is less than the provided value. + +{% multicode %} +```client-web +Query.lessThan("score", 10) +``` +```client-flutter +Query.lessThan("score", 10) +``` +```server-python +Query.less_than("score", 10) +``` +```server-ruby +Query.less_than("score", 10) +``` +```server-deno +Query.lessThan("score", 10) +``` +```server-php +Query::lessThan("score", 10) +``` +```client-apple +Query.lessThan("score", value: 10) +``` +```server-go +query.LessThan("score", 10) +``` +```server-rust +Query::less_than("score", 10).to_string() +``` +```http +{"method":"lessThan","attribute":"score","values":[10]} +``` +{% /multicode %} + +### Less than or equal {% #less-than-equal %} + +Returns document if field is less than or equal to the provided value. + +{% multicode %} +```client-web +Query.lessThanEqual("score", 10) +``` +```client-flutter +Query.lessThanEqual("score", 10) +``` +```server-python +Query.less_than_equal("score", 10) +``` +```server-ruby +Query.less_than_equal("score", 10) +``` +```server-deno +Query.lessThanEqual("score", 10) +``` +```server-php +Query::lessThanEqual("score", 10) +``` +```client-apple +Query.lessThanEqual("score", value: 10) +``` +```server-go +query.LessThanEqual("score", 10) +``` +```server-rust +Query::less_than_equal("score", 10).to_string() +``` +```http +{"method":"lessThanEqual","attribute":"score","values":[10]} +``` +{% /multicode %} + +### Greater than {% #greater-than %} + +Returns document if field is greater than the provided value. + +{% multicode %} +```client-web +Query.greaterThan("score", 10) +``` +```client-flutter +Query.greaterThan("score", 10) +``` +```server-python +Query.greater_than("score", 10) +``` +```server-ruby +Query.greater_than("score", 10) +``` +```server-deno +Query.greaterThan("score", 10) +``` +```server-php +Query::greaterThan("score", 10) +``` +```client-apple +Query.greaterThan("score", value: 10) +``` +```server-go +query.GreaterThan("score", 10) +``` +```server-rust +Query::greater_than("score", 10).to_string() +``` +```http +{"method":"greaterThan","attribute":"score","values":[10]} +``` +{% /multicode %} + +### Greater than or equal {% #greater-than-equal %} + +Returns document if field is greater than or equal to the provided value. + +{% multicode %} +```client-web +Query.greaterThanEqual("score", 10) +``` +```client-flutter +Query.greaterThanEqual("score", 10) +``` +```server-python +Query.greater_than_equal("score", 10) +``` +```server-ruby +Query.greater_than_equal("score", 10) +``` +```server-deno +Query.greaterThanEqual("score", 10) +``` +```server-php +Query::greaterThanEqual("score", 10) +``` +```client-apple +Query.greaterThanEqual("score", value: 10) +``` +```server-go +query.GreaterThanEqual("score", 10) +``` +```server-rust +Query::greater_than_equal("score", 10).to_string() +``` +```http +{"method":"greaterThanEqual","attribute":"score","values":[10]} +``` +{% /multicode %} + +### Between {% #between %} + +Returns document if field value falls between the two values. The boundary values are inclusive and can be strings or numbers. + +{% multicode %} +```client-web +Query.between("price", 5, 10) +``` +```client-flutter +Query.between("price", 5, 10) +``` +```server-python +Query.between("price", 5, 10) +``` +```server-ruby +Query.between("price", 5, 10) +``` +```server-deno +Query.between("price", 5, 10) +``` +```server-php +Query::between("price", 5, 10) +``` +```client-apple +Query.between("price", start: 5, end: 10) +``` +```server-go +query.Between("price", 5, 10) +``` +```server-rust +Query::between("price", 5, 10).to_string() +``` +```http +{"method":"between","attribute":"price","values":[5,10]} +``` +{% /multicode %} + +### Not between {% #not-between %} + +Returns documents if the field value is outside the range defined by the two values (strictly less than start OR strictly greater than end). +Works with strings or numbers. Boundary values are excluded. + +{% multicode %} +```client-web +Query.notBetween("price", 5, 10) +``` +```client-flutter +Query.notBetween("price", 5, 10) +``` +```client-apple +Query.notBetween("price", start: 5, end: 10) +``` +```client-android-kotlin +Query.notBetween("price", 5, 10) +``` +```client-android-java +Query.notBetween("price", 5, 10) +``` +```server-python +Query.not_between("price", 5, 10) +``` +```server-ruby +Query.not_between("price", 5, 10) +``` +```server-deno +Query.notBetween("price", 5, 10) +``` +```server-nodejs +Query.notBetween("price", 5, 10) +``` +```server-php +Query::notBetween("price", 5, 10) +``` +```server-swift +Query.notBetween("price", start: 5, end: 10) +``` +```server-rust +Query::not_between("price", 5, 10).to_string() +``` +```http +{"method":"notBetween","attribute":"price","values":[5,10]} +``` +{% /multicode %} + +## Null checks {% #null-checks %} + +### Is null {% #is-null %} + +Returns documents where field value is null. + +{% multicode %} +```client-web +Query.isNull("name") +``` +```client-flutter +Query.isNull("name") +``` +```server-python +Query.is_null("name") +``` +```server-ruby +Query.is_null("name") +``` +```server-deno +Query.isNull("name") +``` +```server-php +Query::isNull("name") +``` +```client-apple +Query.isNull("name") +``` +```server-go +query.IsNull("name") +``` +```server-rust +Query::is_null("name").to_string() +``` +```http +{"method":"isNull","attribute":"name"} +``` +{% /multicode %} + +### Is not null {% #is-not-null %} + +Returns documents where field value is **not** null. + +{% multicode %} +```client-web +Query.isNotNull("name") +``` +```client-flutter +Query.isNotNull("name") +``` +```server-python +Query.is_not_null("name") +``` +```server-ruby +Query.is_not_null("name") +``` +```server-deno +Query.isNotNull("name") +``` +```server-php +Query::isNotNull("name") +``` +```client-apple +Query.isNotNull("name") +``` +```server-go +query.IsNotNull("name") +``` +```server-rust +Query::is_not_null("name").to_string() +``` +```http +{"method":"isNotNull","attribute":"name"} +``` +{% /multicode %} + +## String operations {% #string-operations %} + +### Starts with {% #starts-with %} + +Returns documents if a string field starts with a substring. + +{% multicode %} +```client-web +Query.startsWith("name", "Once upon a time") +``` +```client-flutter +Query.startsWith("name", "Once upon a time") +``` +```server-python +Query.starts_with("name", "Once upon a time") +``` +```server-ruby +Query.starts_with("name", "Once upon a time") +``` +```server-deno +Query.startsWith("name", "Once upon a time") +``` +```server-php +Query::startsWith("name", "Once upon a time") +``` +```client-apple +Query.startsWith("name", value: "Once upon a time") +``` +```server-go +query.StartsWith("name", "Once upon a time") +``` +```server-rust +Query::starts_with("name", "Once upon a time").to_string() +``` +```http +{"method":"startsWith","attribute":"name","values":["Once upon a time"]} +``` +{% /multicode %} + +### Not starts with {% #not-starts-with %} + +Returns documents if a string field does not start with a substring. + +{% multicode %} +```client-web +Query.notStartsWith("name", "Once upon a time") +``` +```client-flutter +Query.notStartsWith("name", "Once upon a time") +``` +```client-apple +Query.notStartsWith("name", value: "Once upon a time") +``` +```client-android-kotlin +Query.notStartsWith("name", "Once upon a time") +``` +```client-android-java +Query.notStartsWith("name", "Once upon a time") +``` +```server-python +Query.not_starts_with("name", "Once upon a time") +``` +```server-ruby +Query.not_starts_with("name", "Once upon a time") +``` +```server-deno +Query.notStartsWith("name", "Once upon a time") +``` +```server-nodejs +Query.notStartsWith("name", "Once upon a time") +``` +```server-php +Query::notStartsWith("name", "Once upon a time") +``` +```server-swift +Query.notStartsWith("name", value: "Once upon a time") +``` +```server-rust +Query::not_starts_with("name", "Once upon a time").to_string() +``` +```http +{"method":"notStartsWith","attribute":"name","values":["Once upon a time"]} +``` +{% /multicode %} + +### Ends with {% #ends-with %} + +Returns documents if a string field ends with a substring. + +{% multicode %} +```client-web +Query.endsWith("name", "happily ever after.") +``` +```client-flutter +Query.endsWith("name", "happily ever after.") +``` +```server-python +Query.ends_with("name", "happily ever after.") +``` +```server-ruby +Query.ends_with("name", "happily ever after.") +``` +```server-deno +Query.endsWith("name", "happily ever after.") +``` +```server-php +Query::endsWith("name", "happily ever after.") +``` +```client-apple +Query.endsWith("name", value: "happily ever after.") +``` +```server-go +query.EndsWith("name", "happily ever after.") +``` +```server-rust +Query::ends_with("name", "happily ever after.").to_string() +``` +```http +{"method":"endsWith","attribute":"name","values":["happily ever after."]} +``` +{% /multicode %} + +### Not ends with {% #not-ends-with %} + +Returns documents if a string field does not end with a substring. + +{% multicode %} +```client-web +Query.notEndsWith("name", "happily ever after.") +``` +```client-flutter +Query.notEndsWith("name", "happily ever after.") +``` +```client-apple +Query.notEndsWith("name", value: "happily ever after.") +``` +```client-android-kotlin +Query.notEndsWith("name", "happily ever after.") +``` +```client-android-java +Query.notEndsWith("name", "happily ever after.") +``` +```server-python +Query.not_ends_with("name", "happily ever after.") +``` +```server-ruby +Query.not_ends_with("name", "happily ever after.") +``` +```server-deno +Query.notEndsWith("name", "happily ever after.") +``` +```server-nodejs +Query.notEndsWith("name", "happily ever after.") +``` +```server-php +Query::notEndsWith("name", "happily ever after.") +``` +```server-swift +Query.notEndsWith("name", value: "happily ever after.") +``` +```server-rust +Query::not_ends_with("name", "happily ever after.").to_string() +``` +```http +{"method":"notEndsWith","attribute":"name","values":["happily ever after."]} +``` +{% /multicode %} + +### Contains {% #contains %} + +Returns documents if the array field contains the specified elements or if a string field contains the specified substring. + +{% multicode %} +```client-web +// For arrays +Query.contains("ingredients", ['apple', 'banana']) + +// For strings +Query.contains("name", "Tom") +``` +```client-flutter +// For arrays +Query.contains("ingredients", ['apple', 'banana']) + +// For strings +Query.contains("name", "Tom") +``` +```server-python +# For arrays +Query.contains("ingredients", ['apple', 'banana']) + +# For strings +Query.contains("name", "Tom") +``` +```server-ruby +# For arrays +Query.contains("ingredients", ['apple', 'banana']) + +# For strings +Query.contains("name", "Tom") +``` +```server-deno +// For arrays +Query.contains("ingredients", ['apple', 'banana']) + +// For strings +Query.contains("name", "Tom") +``` +```server-php +// For arrays +Query::contains("ingredients", ['apple', 'banana']) + +// For strings +Query::contains("name", "Tom") +``` +```client-apple +// For arrays +Query.contains("ingredients", value: ["apple", "banana"]) + +// For strings +Query.contains("name", value: "Tom") +```server-go +// For arrays +query.Contains("ingredients", []string{"apple", "banana"}) + +// For strings +query.Contains("name", "Tom") +``` +```server-rust +// For arrays +Query::contains("ingredients", Value::Array(vec![ + Value::String("apple".to_string()), + Value::String("banana".to_string()), +])).to_string() + +// For strings +Query::contains("name", "Tom").to_string() +``` +```http +# For arrays +{"method":"contains","attribute":"ingredients","values":["apple","banana"]} + +# For strings +{"method":"contains","attribute":"name","values":["Tom"]} +``` +{% /multicode %} + +### Not contains {% #not-contains %} + +Returns documents if the array field does not contain the specified +elements, or if a string field does not contain the specified +substring. + +{% multicode %} +```client-web +// For arrays +Query.notContains("ingredients", ['apple', 'banana']) + +// For strings +Query.notContains("name", "Tom") +``` +```client-flutter +// For arrays +Query.notContains("ingredients", ['apple', 'banana']) + +// For strings +Query.notContains("name", "Tom") +``` +```client-react-native +// For arrays +Query.notContains("ingredients", ['apple', 'banana']) + +// For strings +Query.notContains("name", "Tom") +``` +```client-apple +// For arrays +Query.notContains("ingredients", value: ['apple', 'banana']) + +// For strings +Query.notContains("name", value: "Tom") +``` +```client-android-kotlin +// For arrays +Query.notContains("ingredients", ['apple', 'banana']) + +// For strings +Query.notContains("name", "Tom") +``` +```client-android-java +// For arrays +Query.notContains("ingredients", Arrays.asList("apple", "banana")) + +// For strings +Query.notContains("name", "Tom") +``` +```server-python +# For arrays +Query.not_contains("ingredients", ['apple', 'banana']) + +# For strings +Query.not_contains("name", "Tom") +``` +```server-ruby +# For arrays +Query.not_contains("ingredients", ['apple', 'banana']) + +# For strings +Query.not_contains("name", "Tom") +``` +```server-deno +// For arrays +Query.notContains("ingredients", ['apple', 'banana']) + +// For strings +Query.notContains("name", "Tom") +``` +```server-nodejs +// For arrays +Query.notContains("ingredients", ['apple', 'banana']) + +// For strings +Query.notContains("name", "Tom") +``` +```server-php +// For arrays +Query::notContains("ingredients", ['apple', 'banana']) + +// For strings +Query::notContains("name", "Tom") +``` +```server-dotnet +// For arrays +Query.NotContains("ingredients", new List { "apple", "banana" }) + +// For strings +Query.NotContains("name", "Tom") +``` +```server-go +// For arrays +query.NotContains("ingredients", []string{"apple", "banana"}) + +// For strings +query.NotContains("name", "Tom") +```server-dart +// For arrays +Query.notContains("ingredients", ['apple', 'banana']) + +// For strings +Query.notContains("name", "Tom") +``` +```server-swift +// For arrays +Query.notContains("ingredients", value: ['apple', 'banana']) + +// For strings +Query.notContains("name", value: "Tom") +``` +```server-kotlin +// For arrays +Query.notContains("ingredients", listOf("apple", "banana")) + +// For strings +Query.notContains("name", "Tom") +``` +```server-rust +// For arrays +Query::not_contains("ingredients", Value::Array(vec![ + Value::String("apple".to_string()), + Value::String("banana".to_string()), +])).to_string() + +// For strings +Query::not_contains("name", "Tom").to_string() +``` +```http +# For arrays +{"method":"notContains","attribute":"ingredients","values":["apple","banana"]} + +# For strings +{"method":"notContains","attribute":"name","values":["Tom"]} +``` +{% /multicode %} + +### Search {% #search %} + +Searches string fields for provided keywords. Requires a [full-text index](/docs/products/databases/documentsdb/collections#indexes) on queried fields. + +{% multicode %} +```client-web +Query.search("text", "key words") +``` +```client-flutter +Query.search("text", "key words") +``` +```server-python +Query.search("text", "key words") +``` +```server-ruby +Query.search("text", "key words") +``` +```server-deno +Query.search("text", "key words") +``` +```server-php +Query::search("text", "key words") +``` +```client-apple +Query.search("text", value: "key words") +``` +```server-go +query.Search("text", "key words") +``` +```server-rust +Query::search("text", "key words").to_string() +``` +```http +{"method":"search","attribute":"text","values":["key words"]} +``` +{% /multicode %} + +### Not search {% #not-search %} + +Returns documents if a string field does not match the full-text search +query. Requires a [full-text index](/docs/products/databases/documentsdb/collections#indexes) +on queried fields. + +{% multicode %} +```client-web +Query.notSearch("text", "key words") +``` +```client-flutter +Query.notSearch("text", "key words") +``` +```client-apple +Query.notSearch("text", value: "key words") +``` +```client-android-kotlin +Query.notSearch("text", "key words") +``` +```client-android-java +Query.notSearch("text", "key words") +``` +```server-python +Query.not_search("text", "key words") +``` +```server-ruby +Query.not_search("text", "key words") +``` +```server-deno +Query.notSearch("text", "key words") +``` +```server-nodejs +Query.notSearch("text", "key words") +``` +```server-php +Query::notSearch("text", "key words") +``` +```server-swift +Query.notSearch("text", value: "key words") +``` +```server-rust +Query::not_search("text", "key words").to_string() +``` +```http +{"method":"notSearch","attribute":"text","values":["key words"]} +``` +{% /multicode %} + +## Logical operators {% #logical-operators %} + +### AND {% #and %} + +Returns document if it matches all of the nested sub-queries in the array passed in. + +{% multicode %} +```client-web +Query.and([ + Query.lessThan("size", 10), + Query.greaterThan("size", 5) +]) +``` +```client-flutter +Query.and([ + Query.lessThan("size", 10), + Query.greaterThan("size", 5) +]) +``` +```server-python +Query.and_queries([ + Query.less_than("size", 10), + Query.greater_than("size", 5) +]) +``` +```server-ruby +Query.and([ + Query.less_than("size", 10), + Query.greater_than("size", 5) +]) +``` +```server-deno +Query.and([ + Query.lessThan("size", 10), + Query.greaterThan("size", 5) +]) +``` +```server-php +Query::and([ + Query::lessThan("size", 10), + Query::greaterThan("size", 5) +]) +``` +```client-apple +Query.and([ + Query.lessThan("size", value: 10), + Query.greaterThan("size", value: 5) +]) +``` +```server-go +query.And([]string{ + query.LessThan("size", 10), + query.GreaterThan("size", 5), +}) +``` +```server-rust +Query::and(vec![ + Query::less_than("size", 10).to_string(), + Query::greater_than("size", 5).to_string(), +]).to_string() +``` +```http +{"method":"and","values":[{"method":"lessThan","attribute":"size","values":[10]},{"method":"greaterThan","attribute":"size","values":[5]}]} +``` +{% /multicode %} + +### OR {% #or %} + +Returns document if it matches any of the nested sub-queries in the array passed in. + +{% multicode %} +```client-web +Query.or([ + Query.lessThan("size", 5), + Query.greaterThan("size", 10) +]) +``` +```client-flutter +Query.or([ + Query.lessThan("size", 5), + Query.greaterThan("size", 10) +]) +``` +```server-python +Query.or_queries([ + Query.less_than("size", 5), + Query.greater_than("size", 10) +]) +``` +```server-ruby +Query.or([ + Query.less_than("size", 5), + Query.greater_than("size", 10) +]) +``` +```server-deno +Query.or([ + Query.lessThan("size", 5), + Query.greaterThan("size", 10) +]) +``` +```server-php +Query::or([ + Query::lessThan("size", 5), + Query::greaterThan("size", 10) +]) +``` +```client-apple +Query.or([ + Query.lessThan("size", value: 5), + Query.greaterThan("size", value: 10) +]) +``` +```server-go +query.Or([]string{ + query.LessThan("size", 5), + query.GreaterThan("size", 10), +}) +``` +```server-rust +Query::or(vec![ + Query::less_than("size", 5).to_string(), + Query::greater_than("size", 10).to_string(), +]).to_string() +``` +```http +{"method":"or","values":[{"method":"lessThan","attribute":"size","values":[5]},{"method":"greaterThan","attribute":"size","values":[10]}]} +``` +{% /multicode %} + +## Ordering {% #ordering %} + +### Order descending {% #order-desc %} + +Orders results in descending order by field. + +{% multicode %} +```client-web +Query.orderDesc("field") +``` +```client-flutter +Query.orderDesc("field") +``` +```server-python +Query.order_desc("field") +``` +```server-ruby +Query.order_desc("field") +``` +```server-nodejs +Query.orderDesc("field") +``` +```server-php +Query::orderDesc("field") +``` +```client-apple +Query.orderDesc("field") +``` +```server-go +query.OrderDesc("attribute") +``` +```server-rust +Query::order_desc("field").to_string() +``` +```http +{"method":"orderDesc","attribute":"field"} +``` +{% /multicode %} + +### Order ascending {% #order-asc %} + +Orders results in ascending order by field. + +{% multicode %} +```client-web +Query.orderAsc("field") +``` +```client-flutter +Query.orderAsc("field") +``` +```server-python +Query.order_asc("field") +``` +```server-ruby +Query.order_asc("field") +``` +```server-nodejs +Query.orderAsc("field") +``` +```server-php +Query::orderAsc("field") +``` +```client-apple +Query.orderAsc("field") +``` +```server-go +query.OrderAsc("attribute") +``` +```server-rust +Query::order_asc("field").to_string() +``` +```http +{"method":"orderAsc","attribute":"field"} +``` +{% /multicode %} + +## Pagination {% #pagination %} + +### Limit {% #limit %} + +Limits the number of results returned by the query. Used for [pagination](/docs/products/databases/documentsdb/pagination). + +{% multicode %} +```client-web +Query.limit(25) +``` +```client-flutter +Query.limit(25) +``` +```server-python +Query.limit(25) +``` +```server-ruby +Query.limit(25) +``` +```server-deno +Query.limit(25) +``` +```server-php +Query::limit(25) +``` +```client-apple +Query.limit(25) +``` +```server-go +query.Limit(25) +``` +```server-rust +Query::limit(25).to_string() +``` +```http +{"method":"limit","values":[25]} +``` +{% /multicode %} + +### Offset {% #offset %} + +Offset the results returned by skipping some of the results. Used for [pagination](/docs/products/databases/documentsdb/pagination). + +{% multicode %} +```client-web +Query.offset(0) +``` +```client-flutter +Query.offset(0) +``` +```server-python +Query.offset(0) +``` +```server-ruby +Query.offset(0) +``` +```server-deno +Query.offset(0) +``` +```server-php +Query::offset(0) +``` +```client-apple +Query.offset(0) +``` +```server-go +query.Offset(0) +``` +```server-rust +Query::offset(0).to_string() +``` +```http +{"method":"offset","values":[0]} +``` +{% /multicode %} + +### Cursor after {% #cursor-after %} + +Places the cursor after the specified resource ID. Used for [pagination](/docs/products/databases/documentsdb/pagination). + +{% multicode %} +```client-web +Query.cursorAfter("62a7...f620") +``` +```client-flutter +Query.cursorAfter("62a7...f620") +``` +```server-python +Query.cursor_after("62a7...f620") +``` +```server-ruby +Query.cursor_after("62a7...f620") +``` +```server-deno +Query.cursorAfter("62a7...f620") +``` +```server-php +Query::cursorAfter("62a7...f620") +``` +```client-apple +Query.cursorAfter("62a7...f620") +``` +```server-go +query.CursorAfter("62a7...f620") +``` +```server-rust +Query::cursor_after("62a7...f620").to_string() +``` +```http +{"method":"cursorAfter","values":["62a7...f620"]} +``` +{% /multicode %} + +### Cursor before {% #cursor-before %} + +Places the cursor before the specified resource ID. Used for [pagination](/docs/products/databases/documentsdb/pagination). + +{% multicode %} +```client-web +Query.cursorBefore("62a7...a600") +``` +```client-flutter +Query.cursorBefore("62a7...a600") +``` +```server-python +Query.cursor_before("62a7...a600") +``` +```server-ruby +Query.cursor_before("62a7...a600") +``` +```server-deno +Query.cursorBefore("62a7...a600") +``` +```server-php +Query::cursorBefore("62a7...a600") +``` +```client-apple +Query.cursorBefore("62a7...a600") +``` +```server-go +query.CursorBefore("62a7...a600") +``` +```server-rust +Query::cursor_before("62a7...a600").to_string() +``` +```http +{"method":"cursorBefore","values":["62a7...a600"]} +``` +{% /multicode %} + +# Time helpers {% #time-helpers %} + +Built-in helpers for filtering by creation and update timestamps using +ISO 8601 date-time strings (for example, "2025-01-01T00:00:00Z"). + +### Created before {% #created-before %} + +Returns documents created before the given date. + +{% multicode %} +```client-web +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```client-flutter +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```client-apple +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```client-android-kotlin +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```client-android-java +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```server-python +Query.created_before("2025-01-01T00:00:00Z") +``` +```server-ruby +Query.created_before("2025-01-01T00:00:00Z") +``` +```server-deno +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```server-nodejs +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```server-php +Query::createdBefore("2025-01-01T00:00:00Z") +``` +```server-swift +Query.createdBefore("2025-01-01T00:00:00Z") +``` +```server-rust +Query::created_before("2025-01-01T00:00:00Z").to_string() +``` +```http +{"method":"createdBefore","values":["2025-01-01T00:00:00Z"]} +``` +{% /multicode %} + +### Created after {% #created-after %} + +Returns documents created after the given date. + +{% multicode %} +```client-web +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```client-flutter +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```client-apple +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```client-android-kotlin +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```client-android-java +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```server-python +Query.created_after("2025-01-01T00:00:00Z") +``` +```server-ruby +Query.created_after("2025-01-01T00:00:00Z") +``` +```server-deno +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```server-nodejs +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```server-php +Query::createdAfter("2025-01-01T00:00:00Z") +``` +```server-swift +Query.createdAfter("2025-01-01T00:00:00Z") +``` +```server-rust +Query::created_after("2025-01-01T00:00:00Z").to_string() +``` +```http +{"method":"createdAfter","values":["2025-01-01T00:00:00Z"]} +``` +{% /multicode %} + +### Updated before {% #updated-before %} + +Returns documents updated before the given date. + +{% multicode %} +```client-web +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```client-flutter +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```client-apple +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```client-android-kotlin +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```client-android-java +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```server-python +Query.updated_before("2025-01-01T00:00:00Z") +``` +```server-ruby +Query.updated_before("2025-01-01T00:00:00Z") +``` +```server-deno +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```server-nodejs +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```server-php +Query::updatedBefore("2025-01-01T00:00:00Z") +``` +```server-swift +Query.updatedBefore("2025-01-01T00:00:00Z") +``` +```server-rust +Query::updated_before("2025-01-01T00:00:00Z").to_string() +``` +```http +{"method":"updatedBefore","values":["2025-01-01T00:00:00Z"]} +``` +{% /multicode %} + +### Updated after {% #updated-after %} + +Returns documents updated after the given date. + +{% multicode %} +```client-web +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```client-flutter +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```client-apple +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```client-android-kotlin +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```client-android-java +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```server-python +Query.updated_after("2025-01-01T00:00:00Z") +``` +```server-ruby +Query.updated_after("2025-01-01T00:00:00Z") +``` +```server-deno +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```server-nodejs +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```server-php +Query::updatedAfter("2025-01-01T00:00:00Z") +``` +```server-swift +Query.updatedAfter("2025-01-01T00:00:00Z") +``` +```server-rust +Query::updated_after("2025-01-01T00:00:00Z").to_string() +``` +```http +{"method":"updatedAfter","values":["2025-01-01T00:00:00Z"]} +``` +{% /multicode %} +# Complex queries {% #complex-queries %} + +You can create complex queries by combining AND and OR operations. For example, to find items that are either books under $20 or magazines under $10: + +{% multicode %} +```client-web +const results = await documentsDB.listDocuments({ + databaseId: '', + collectionId: '', + queries: [ + Query.or([ + Query.and([ + Query.equal('category', ['books']), + Query.lessThan('price', 20) + ]), + Query.and([ + Query.equal('category', ['magazines']), + Query.lessThan('price', 10) + ]) + ]) + ] +}); +``` +```client-flutter +final results = await documentsDB.listDocuments( + '', + '', + [ + Query.or([ + Query.and([ + Query.equal('category', ['books']), + Query.lessThan('price', 20) + ]), + Query.and([ + Query.equal('category', ['magazines']), + Query.lessThan('price', 10) + ]) + ]) + ] +); +``` +```server-python +results = documentsDB.list_documents( + database_id='', + collection_id='', + queries=[ + Query.or_queries([ + Query.and_queries([ + Query.equal('category', ['books']), + Query.less_than('price', 20) + ]), + Query.and_queries([ + Query.equal('category', ['magazines']), + Query.less_than('price', 10) + ]) + ]) + ] +) +``` +```server-go +documents, err := documentsDB.ListDocuments( + "", + "", + documentsDB.WithListDocumentsQueries([]string{ + query.Or([]string{ + query.And([]string{ + query.Equal("category", []string{"books"}), + query.LessThan("price", 20), + }), + query.And([]string{ + query.Equal("category", []string{"magazines"}), + query.LessThan("price", 10), + }), + }), + }), +) +if err != nil { + log.Fatal(err) +} +``` +```server-rust +let documents = documents_db.list_documents( + "", + "", + Some(vec![ + Query::or(vec![ + Query::and(vec![ + Query::equal("category", Value::Array(vec![Value::String("books".to_string())])).to_string(), + Query::less_than("price", 20).to_string(), + ]).to_string(), + Query::and(vec![ + Query::equal("category", Value::Array(vec![Value::String("magazines".to_string())])).to_string(), + Query::less_than("price", 10).to_string(), + ]).to_string(), + ]).to_string(), + ]), + None, + None, + None, +).await?; +``` +```http +{"method":"or","values":[{"method":"and","values":[{"method":"equal","attribute":"category","values":["books"]},{"method":"lessThan","attribute":"price","values":[20]}]},{"method":"and","values":[{"method":"equal","attribute":"category","values":["magazines"]},{"method":"lessThan","attribute":"price","values":[10]}]}]} +``` +{% /multicode %} + +This example demonstrates how to combine `OR` and `AND` operations. The query uses `Query.or()` to match either condition: books under $20 OR magazines under $10. +Each condition within the OR is composed of two AND conditions - one for the category and one for the price threshold. The database will return documents that match either of these combined conditions. + diff --git a/src/routes/docs/products/databases/documentsdb/quick-start/+page.markdoc b/src/routes/docs/products/databases/documentsdb/quick-start/+page.markdoc new file mode 100644 index 00000000000..92c5f30cf8b --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/quick-start/+page.markdoc @@ -0,0 +1,243 @@ +--- +layout: article +title: Start with DocumentsDB +description: Get started with Appwrite DocumentsDB. Follow a step-by-step guide to create your first database, add a collection, and perform basic document operations. +--- + + +{% section #create-database step=1 title="Create database" %} +Head to your [Appwrite Console](https://cloud.appwrite.io/console/) and click **Create database**. Name it `Oscar` and choose **DocumentsDB** as the database type. +Optionally, add a custom database ID. Select your preferred tier, then click **Create database**. +{% /section %} + +{% section #create-collection step=2 title="Create collection" %} +In the `Oscar` database, click **Create collection** and name it `My books`. Optionally, add a custom collection ID. + +Collections are schemaless, so there are no columns to define. Each document holds its own fields as flexible JSON. + +Open the collection's **Security** tab. Under **Permissions**, add a new role **Any** and check **Create** and **Read**, so anyone can create and read documents. Click **Update** to save. +{% /section %} + + +{% section #create-documents step=3 title="Create documents" %} +To create a document use the `createDocument` method. + +In the **Settings** menu, find your project ID and replace `` in the example. + +Navigate to the `Oscar` database, copy the database ID, and replace ``. +Then, in the `My books` collection, copy the collection ID, and replace ``. + +{% multicode %} +```client-web +import { Client, ID, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + +const documentsDB = new DocumentsDB(client); + +const promise = documentsDB.createDocument({ + databaseId: '', + collectionId: '', + documentId: ID.unique(), + data: { title: "Hamlet" } +}); + +promise.then(function (response) { + console.log(response); +}, function (error) { + console.log(error); +}); +``` +```client-flutter +import 'package:appwrite/appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject(''); + + final documentsDB = DocumentsDB(client); + + try { + final document = documentsDB.createDocument( + databaseId: '', + collectionId: '', + documentId: ID.unique(), + data: { "title": "Hamlet" } + ); + } on AppwriteException catch(e) { + print(e); + } +} +``` +```client-apple +import Appwrite +import AppwriteModels + +func main() async throws { + let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + let documentsDB = DocumentsDB(client) + + do { + let document = try await documentsDB.createDocument( + databaseId: "", + collectionId: "", + documentId: ID.unique(), + data: ["title": "Hamlet"] + ) + } catch { + print(error.localizedDescription) + } +} +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.ID +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client(applicationContext) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + val documentsDB = DocumentsDB(client) + + try { + val document = documentsDB.createDocument( + databaseId = "", + collectionId = "", + documentId = ID.unique(), + data = mapOf("title" to "Hamlet"), + ) + } catch (e: Exception) { + Log.e("Appwrite", "Error: " + e.message) + } +} +``` +{% /multicode %} + +The response should look similar to this. + +```json +{ + "title": "Hamlet", + "$id": "6a423aa70000c39da0be", + "$sequence": "019f12b5-1c89-7094-8d12-5b7e7a09f025", + "$permissions": [], + "$createdAt": "2026-06-29T09:28:07.047+00:00", + "$updatedAt": "2026-06-29T09:28:07.047+00:00", + "$databaseId": "650125c64b3c25ce4bc4", + "$collectionId": "650125cff227cf9f95ad" +} +``` + +{% /section %} + +{% section #list-documents step=4 title="List documents" %} +To read and query data from your collection, use the `listDocuments` endpoint. + +Like the previous step, replace ``, ``, and `` with their respective IDs. +{% multicode %} +```client-web +import { Client, Query, DocumentsDB } from "appwrite"; + +const client = new Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + +const documentsDB = new DocumentsDB(client); + +const promise = documentsDB.listDocuments({ + databaseId: "", + collectionId: "", + queries: [ + Query.equal('title', 'Hamlet') + ] +}); + +promise.then(function (response) { + console.log(response); +}, function (error) { + console.log(error); +}); +``` +```client-flutter +import 'package:appwrite/appwrite.dart'; + +void main() async { + final client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + final documentsDB = DocumentsDB(client); + + try { + final documents = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [ + Query.equal('title', 'Hamlet') + ] + ); + } on AppwriteException catch(e) { + print(e); + } +} +``` +```client-apple +import Appwrite +import AppwriteModels + +func main() async throws{ + let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + let documentsDB = DocumentsDB(client) + + do { + let documents = try await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + queries: [ + Query.equal("title", value: "Hamlet") + ] + ) + } catch { + print(error.localizedDescription) + } +} +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.Query +import io.appwrite.services.DocumentsDB + +suspend fun main() { + val client = Client(applicationContext) + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + + val documentsDB = DocumentsDB(client) + + try { + val documents = documentsDB.listDocuments( + databaseId = "", + collectionId = "", + queries = listOf( + Query.equal("title", "Hamlet") + ) + ) + } catch (e: AppwriteException) { + Log.e("Appwrite", "Error: " + e.message) + } +} +``` + +{% /multicode %} +{% /section %} diff --git a/src/routes/docs/products/databases/documentsdb/timestamp-overrides/+page.markdoc b/src/routes/docs/products/databases/documentsdb/timestamp-overrides/+page.markdoc new file mode 100644 index 00000000000..0549b732ab4 --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/timestamp-overrides/+page.markdoc @@ -0,0 +1,1266 @@ +--- +layout: article +title: Timestamp overrides +description: Set custom $createdAt and $updatedAt timestamps for your documents when using server SDKs. +--- + +When creating or updating documents, Appwrite automatically sets `$createdAt` and `$updatedAt` timestamps. However, there are scenarios where you might need to set these timestamps manually, such as when migrating data from another system or backfilling historical records. + +{% info title="Server SDKs required" %} +To manually set `$createdAt` and `$updatedAt`, you must use a **server SDK** with an **API key**. These attributes can be passed inside the `data` parameter on any of the create, update, or upsert routes (single or bulk). +{% /info %} + +# Setting custom timestamps {% #setting-custom-timestamps %} + +You can override a document's timestamps by providing ISO 8601 strings (for example, `2025-08-10T12:34:56.000Z`) in the `data` payload. If these attributes are not provided, Appwrite will set them automatically. + +Custom timestamps work with all document operations: create, update, upsert, and their bulk variants. + +## Single document operations {% #single-document-operations %} + +When working with individual documents, you can set custom timestamps during create, update, and upsert operations. + +### Create with custom timestamps {% #create-custom %} + +{% multicode %} +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +const documentsDB = new sdk.DocumentsDB(client); + +await documentsDB.createDocument({ + databaseId: '', + collectionId: '', + documentId: sdk.ID.unique(), + data: { + '$createdAt': new Date('2025-08-10T12:34:56.000Z').toISOString(), + '$updatedAt': new Date('2025-08-10T12:34:56.000Z').toISOString(), + // ...your attributes + } +}); +``` +```server-php +use Appwrite\Client; +use Appwrite\ID; +use Appwrite\Services\DocumentsDB; + +$client = (new Client()) + ->setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$documentsDB = new DocumentsDB($client); + +$documentsDB->createDocument( + databaseId: '', + collectionId: '', + documentId: ID::unique(), + data: [ + '$createdAt' => (new DateTime(''))->format(DATE_ATOM), + '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), + // ...your attributes + ] +); +``` +```server-swift +import Appwrite +import Foundation + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let documentsDB = DocumentsDB(client) + +let isoFormatter = ISO8601DateFormatter() +isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] +let customDate = isoFormatter.date(from: "") ?? Date() +let createdAt = isoFormatter.string(from: customDate) +let updatedAt = isoFormatter.string(from: customDate) + +do { + let created = try await documentsDB.createDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: [ + "$createdAt": createdAt, + "$updatedAt": updatedAt, + // ...your attributes + ] + ) + print("Created:", created) +} catch { + print("Create error:", error) +} +``` +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB +from appwrite.id import ID +from datetime import datetime, timezone + +client = Client() +client.set_endpoint('https://.cloud.appwrite.io/v1') +client.set_project('') +client.set_key('') + +documents_db = DocumentsDB(client) + +iso = datetime(2025, 8, 10, 12, 34, 56, tzinfo=timezone.utc).isoformat() + +documents_db.create_document( + database_id='', + collection_id='', + document_id=ID.unique(), + data={ + '$createdAt': iso, + '$updatedAt': iso, + # ...your attributes + } +) +``` +```server-ruby +require 'appwrite' +require 'time' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +documents_db = DocumentsDB.new(client) + +custom_date = Time.parse('2025-08-10T12:34:56.000Z').iso8601 + +documents_db.create_document( + database_id: '', + collection_id: '', + document_id: ID.unique(), + data: { + '$createdAt' => custom_date, + '$updatedAt' => custom_date, + # ...your attributes + } +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndpoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +DocumentsDB documentsDB = new DocumentsDB(client); + +string customDate = DateTimeOffset.Parse("2025-08-10T12:34:56.000Z").ToString("O"); + +await documentsDB.CreateDocument( + databaseId: "", + collectionId: "", + documentId: ID.Unique(), + data: new Dictionary + { + ["$createdAt"] = customDate, + ["$updatedAt"] = customDate, + // ...your attributes + } +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +DocumentsDB documentsDB = DocumentsDB(client); + +String customDate = DateTime.parse('2025-08-10T12:34:56.000Z').toIso8601String(); + +await documentsDB.createDocument( + databaseId: '', + collectionId: '', + documentId: ID.unique(), + data: { + '\$createdAt': customDate, + '\$updatedAt': customDate, + // ...your attributes + }, +); +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::id::ID; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_document( + "", + "", + &ID::unique(), + json!({ + "$createdAt": "2025-08-10T12:34:56.000Z", + "$updatedAt": "2025-08-10T12:34:56.000Z" + // ...your attributes + }), + None, + None, + ).await?; + + println!("Created: {:?}", result); + Ok(()) +} +``` +{% /multicode %} + +### Update with custom timestamps {% #update-custom %} + +When updating documents, you can also set a custom `$updatedAt` timestamp: + +{% multicode %} +```server-nodejs +await documentsDB.updateDocument({ + databaseId: '', + collectionId: '', + documentId: '', + data: { + '$updatedAt': new Date('2025-08-10T12:34:56.000Z').toISOString(), + // ...your attributes + } +}); +``` +```server-php +$documentsDB->updateDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: [ + '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), + // ...your attributes + ] +); +``` +```server-python +from datetime import datetime, timezone + +documents_db.update_document( + database_id='', + collection_id='', + document_id='', + data={ + '$updatedAt': datetime(2025, 8, 10, 12, 34, 56, tzinfo=timezone.utc).isoformat(), + # ...your attributes + } +) +``` +```server-swift +import Appwrite +import Foundation + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let documentsDB = DocumentsDB(client) + +let isoFormatter = ISO8601DateFormatter() +isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] +let updatedAt = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) + +do { + let updated = try await documentsDB.updateDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: [ + "$updatedAt": updatedAt, + // ...your attributes + ] + ) + print("Updated:", updated) +} catch { + print("Update error:", error) +} +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +documents_db = DocumentsDB.new(client) + +custom_date = Time.parse('').iso8601 + +documents_db.update_document( + database_id: '', + collection_id: '', + document_id: '', + data: { + '$updatedAt' => custom_date, + # ...your attributes + } +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndpoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +DocumentsDB documentsDB = new DocumentsDB(client); + +string customDate = DateTimeOffset.Parse("").ToString("O"); + +await documentsDB.UpdateDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: new Dictionary + { + ["$updatedAt"] = customDate, + // ...your attributes + } +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +DocumentsDB documentsDB = DocumentsDB(client); + +String customDate = DateTime.parse('').toIso8601String(); + +await documentsDB.updateDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: { + '\$updatedAt': customDate, + // ...your attributes + }, +); +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.update_document( + "", + "", + "", + Some(json!({ + "$updatedAt": "2025-08-10T12:34:56.000Z" + // ...your attributes + })), + None, + None, + ).await?; + + println!("Updated: {:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## Bulk operations {% #bulk-operations %} + +Custom timestamps also work with bulk operations, allowing you to set different timestamps for each document in the batch: + +### Bulk create {% #bulk-create %} + +{% multicode %} +```server-nodejs +await documentsDB.createDocuments({ + databaseId: '', + collectionId: '', + documents: [ + { + '$id': sdk.ID.unique(), + '$createdAt': new Date('2024-01-01T00:00:00.000Z').toISOString(), + '$updatedAt': new Date('2024-01-01T00:00:00.000Z').toISOString(), + // ...your attributes + }, + { + '$id': sdk.ID.unique(), + '$createdAt': new Date('2024-02-01T00:00:00.000Z').toISOString(), + '$updatedAt': new Date('2024-02-01T00:00:00.000Z').toISOString(), + // ...your attributes + } + ] +}); +``` +```server-python +documents_db.create_documents( + database_id='', + collection_id='', + documents=[ + { + '$id': ID.unique(), + '$createdAt': datetime(2024, 1, 1, tzinfo=timezone.utc).isoformat(), + '$updatedAt': datetime(2024, 1, 1, tzinfo=timezone.utc).isoformat(), + # ...your attributes + }, + { + '$id': ID.unique(), + '$createdAt': datetime(2024, 2, 1, tzinfo=timezone.utc).isoformat(), + '$updatedAt': datetime(2024, 2, 1, tzinfo=timezone.utc).isoformat(), + # ...your attributes + } + ] +) +``` +```server-php +use Appwrite\Client; +use Appwrite\ID; +use Appwrite\Services\DocumentsDB; + +$client = (new Client()) + ->setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$documentsDB = new DocumentsDB($client); + +$documentsDB->createDocuments( + databaseId: '', + collectionId: '', + documents: [ + [ + '$id' => ID::unique(), + '$createdAt' => (new DateTime(''))->format(DATE_ATOM), + '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), + // ...your attributes + ], + [ + '$id' => ID::unique(), + '$createdAt' => (new DateTime(''))->format(DATE_ATOM), + '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), + // ...your attributes + ], + ] +); +``` +```server-swift +import Appwrite +import Foundation + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let documentsDB = DocumentsDB(client) + +let isoFormatter = ISO8601DateFormatter() +isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] + +let first = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) +let second = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) + +do { + let bulkCreated = try await documentsDB.createDocuments( + databaseId: "", + collectionId: "", + documents: [ + [ + "$id": ID.unique(), + "$createdAt": first, + "$updatedAt": first, + // ...your attributes + ], + [ + "$id": ID.unique(), + "$createdAt": second, + "$updatedAt": second, + // ...your attributes + ] + ] + ) + print("Bulk create:", bulkCreated) +} catch { + print("Bulk create error:", error) +} +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +documents_db = DocumentsDB.new(client) + +first = Time.parse('').iso8601 +second = Time.parse('').iso8601 + +documents_db.create_documents( + database_id: '', + collection_id: '', + documents: [ + { + '$id' => ID.unique(), + '$createdAt' => first, + '$updatedAt' => first, + # ...your attributes + }, + { + '$id' => ID.unique(), + '$createdAt' => second, + '$updatedAt' => second, + # ...your attributes + } + ] +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndpoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +DocumentsDB documentsDB = new DocumentsDB(client); + +string first = DateTimeOffset.Parse("").ToString("O"); +string second = DateTimeOffset.Parse("").ToString("O"); + +await documentsDB.CreateDocuments( + databaseId: "", + collectionId: "", + documents: new List + { + new Dictionary + { + ["$id"] = ID.Unique(), + ["$createdAt"] = first, + ["$updatedAt"] = first, + // ...your attributes + }, + new Dictionary + { + ["$id"] = ID.Unique(), + ["$createdAt"] = second, + ["$updatedAt"] = second, + // ...your attributes + } + } +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +DocumentsDB documentsDB = DocumentsDB(client); + +String first = DateTime.parse('').toIso8601String(); +String second = DateTime.parse('').toIso8601String(); + +await documentsDB.createDocuments( + databaseId: '', + collectionId: '', + documents: [ + { + '\$id': ID.unique(), + '\$createdAt': first, + '\$updatedAt': first, + // ...your attributes + }, + { + '\$id': ID.unique(), + '\$createdAt': second, + '\$updatedAt': second, + // ...your attributes + } + ], +); +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::id::ID; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_documents( + "", + "", + vec![ + json!({ + "$id": ID::unique(), + "$createdAt": "2024-01-01T00:00:00.000Z", + "$updatedAt": "2024-01-01T00:00:00.000Z" + // ...your attributes + }), + json!({ + "$id": ID::unique(), + "$createdAt": "2024-02-01T00:00:00.000Z", + "$updatedAt": "2024-02-01T00:00:00.000Z" + // ...your attributes + }), + ], + None, + ).await?; + + println!("Bulk create: {:?}", result); + Ok(()) +} +``` +{% /multicode %} + +### Bulk upsert {% #bulk-upsert %} + +{% multicode %} +```server-nodejs +await documentsDB.upsertDocuments({ + databaseId: '', + collectionId: '', + documents: [ + { + '$id': '', + '$createdAt': new Date('2024-01-01T00:00:00.000Z').toISOString(), + '$updatedAt': new Date('2025-01-01T00:00:00.000Z').toISOString(), + // ...your attributes + } + ] +}); +``` +```server-python +documents_db.upsert_documents( + database_id='', + collection_id='', + documents=[ + { + '$id': '', + '$createdAt': datetime(2024, 1, 1, tzinfo=timezone.utc).isoformat(), + '$updatedAt': datetime(2025, 1, 1, tzinfo=timezone.utc).isoformat(), + # ...your attributes + } + ] +) +``` +```server-php +use Appwrite\Client; +use Appwrite\ID; +use Appwrite\Services\DocumentsDB; + +$client = (new Client()) + ->setEndpoint('https://.cloud.appwrite.io/v1') + ->setProject('') + ->setKey(''); + +$documentsDB = new DocumentsDB($client); + +$documentsDB->upsertDocuments( + databaseId: '', + collectionId: '', + documents: [ + [ + '$id' => '', + '$createdAt' => (new DateTime(''))->format(DATE_ATOM), + '$updatedAt' => (new DateTime(''))->format(DATE_ATOM), + // ...your attributes + ], + ] +); +``` +```server-swift +import Appwrite +import Foundation + +let client = Client() + .setEndpoint("https://.cloud.appwrite.io/v1") + .setProject("") + .setKey("") + +let documentsDB = DocumentsDB(client) + +let isoFormatter = ISO8601DateFormatter() +isoFormatter.formatOptions = [.withInternetDateTime, .withFractionalSeconds] +let createdAt = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) +let updatedAt = isoFormatter.string(from: isoFormatter.date(from: "") ?? Date()) + +do { + let bulkUpserted = try await documentsDB.upsertDocuments( + databaseId: "", + collectionId: "", + documents: [ + [ + "$id": "", + "$createdAt": createdAt, + "$updatedAt": updatedAt, + // ...your attributes + ] + ] + ) + print("Bulk upsert:", bulkUpserted) +} catch { + print("Bulk upsert error:", error) +} +``` +```server-ruby +require 'appwrite' +require 'time' + +include Appwrite + +client = Client.new + .set_endpoint('https://.cloud.appwrite.io/v1') + .set_project('') + .set_key('') + +documents_db = DocumentsDB.new(client) + +custom_date = Time.parse('').iso8601 + +documents_db.upsert_documents( + database_id: '', + collection_id: '', + documents: [ + { + '$id' => '', + '$createdAt' => custom_date, + '$updatedAt' => custom_date, + # ...your attributes + } + ] +) +``` +```server-dotnet +using Appwrite; +using Appwrite.Models; +using Appwrite.Services; + +Client client = new Client() + .SetEndpoint("https://.cloud.appwrite.io/v1") + .SetProject("") + .SetKey(""); + +DocumentsDB documentsDB = new DocumentsDB(client); + +string createdAt = DateTimeOffset.Parse("").ToString("O"); +string updatedAt = DateTimeOffset.Parse("").ToString("O"); + +await documentsDB.UpsertDocuments( + databaseId: "", + collectionId: "", + documents: new List + { + new Dictionary + { + ["$id"] = "", + ["$createdAt"] = createdAt, + ["$updatedAt"] = updatedAt, + // ...your attributes + } + } +); +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') + .setProject('') + .setKey(''); + +DocumentsDB documentsDB = DocumentsDB(client); + +String createdAt = DateTime.parse('').toIso8601String(); +String updatedAt = DateTime.parse('').toIso8601String(); + +await documentsDB.upsertDocuments( + databaseId: '', + collectionId: '', + documents: [ + { + '\$id': '', + '\$createdAt': createdAt, + '\$updatedAt': updatedAt, + // ...your attributes + } + ], +); +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let client = Client::new() + .set_endpoint("https://.cloud.appwrite.io/v1") + .set_project("") + .set_key(""); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.upsert_documents( + "", + "", + vec![ + json!({ + "$id": "", + "$createdAt": "2024-01-01T00:00:00.000Z", + "$updatedAt": "2025-01-01T00:00:00.000Z" + // ...your attributes + }), + ], + None, + ).await?; + + println!("Bulk upsert: {:?}", result); + Ok(()) +} +``` +{% /multicode %} + +# Common use cases {% #use-cases %} + +Custom timestamps are particularly useful in several scenarios: + +## Data migration {% #data-migration %} +When migrating existing data from another system, you can preserve the original +creation and modification times: + +{% multicode %} +```server-nodejs +await documentsDB.createDocument({ + databaseId: '', + collectionId: 'blog_posts', + documentId: sdk.ID.unique(), + data: { + '$createdAt': '', + '$updatedAt': '', + title: '', + content: '<CONTENT>' + } +}); +``` +```server-php +$documentsDB->createDocument( + databaseId: '<DATABASE_ID>', + collectionId: 'blog_posts', + documentId: ID::unique(), + data: [ + '$createdAt' => '<ORIGINAL_CREATED_AT_ISO>', + '$updatedAt' => '<LAST_MODIFIED_ISO>', + 'title' => '<TITLE>', + 'content' => '<CONTENT>' + ] +); +``` +```server-swift +let _ = try await documentsDB.createDocument( + databaseId: "<DATABASE_ID>", + collectionId: "blog_posts", + documentId: ID.unique(), + data: [ + "$createdAt": "<ORIGINAL_CREATED_AT_ISO>", + "$updatedAt": "<LAST_MODIFIED_ISO>", + "title": "<TITLE>", + "content": "<CONTENT>" + ] +) +``` +```server-python +documents_db.create_document( + database_id='<DATABASE_ID>', + collection_id='blog_posts', + document_id=ID.unique(), + data={ + '$createdAt': '<ORIGINAL_CREATED_AT_ISO>', + '$updatedAt': '<LAST_MODIFIED_ISO>', + 'title': '<TITLE>', + 'content': '<CONTENT>' + } +) +``` +```server-ruby +documents_db.create_document( + database_id: '<DATABASE_ID>', + collection_id: 'blog_posts', + document_id: ID.unique(), + data: { + '$createdAt' => '<ORIGINAL_CREATED_AT_ISO>', + '$updatedAt' => '<LAST_MODIFIED_ISO>', + 'title' => '<TITLE>', + 'content' => '<CONTENT>' + } +) +``` +```server-dotnet +await documentsDB.CreateDocument( + databaseId: "<DATABASE_ID>", + collectionId: "blog_posts", + documentId: ID.Unique(), + data: new Dictionary<string, object> + { + ["$createdAt"] = "<ORIGINAL_CREATED_AT_ISO>", + ["$updatedAt"] = "<LAST_MODIFIED_ISO>", + ["title"] = "<TITLE>", + ["content"] = "<CONTENT>" + } +); +``` +```server-dart +await documentsDB.createDocument( + databaseId: '<DATABASE_ID>', + collectionId: 'blog_posts', + documentId: ID.unique(), + data: { + '\$createdAt': '<ORIGINAL_CREATED_AT_ISO>', + '\$updatedAt': '<LAST_MODIFIED_ISO>', + 'title': '<TITLE>', + 'content': '<CONTENT>' + }, +); +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::id::ID; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let client = Client::new() + .set_endpoint("https://<REGION>.cloud.appwrite.io/v1") + .set_project("<PROJECT_ID>") + .set_key("<API_KEY>"); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_document( + "<DATABASE_ID>", + "blog_posts", + &ID::unique(), + json!({ + "$createdAt": "<ORIGINAL_CREATED_AT_ISO>", + "$updatedAt": "<LAST_MODIFIED_ISO>", + "title": "<TITLE>", + "content": "<CONTENT>" + }), + None, + None, + ).await?; + + println!("Created: {:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## Backdating records {% #backdating %} +For historical data entry or when creating records that represent past events: + +{% multicode %} +```server-nodejs +await documentsDB.createDocument({ + databaseId: '<DATABASE_ID>', + collectionId: 'transactions', + documentId: sdk.ID.unique(), + data: { + '$createdAt': '2023-12-31T23:59:59.000Z', + '$updatedAt': '2023-12-31T23:59:59.000Z', + amount: 1000, + type: 'year-end-bonus' + } +}); +``` +```server-php +$documentsDB->createDocument( + databaseId: '<DATABASE_ID>', + collectionId: 'transactions', + documentId: ID::unique(), + data: [ + '$createdAt' => '2023-12-31T23:59:59.000Z', + '$updatedAt' => '2023-12-31T23:59:59.000Z', + 'amount' => 1000, + 'type' => 'year-end-bonus' + ] +); +``` +```server-swift +let _ = try await documentsDB.createDocument( + databaseId: "<DATABASE_ID>", + collectionId: "transactions", + documentId: ID.unique(), + data: [ + "$createdAt": "2023-12-31T23:59:59.000Z", + "$updatedAt": "2023-12-31T23:59:59.000Z", + "amount": 1000, + "type": "year-end-bonus" + ] +) +``` +```server-python +documents_db.create_document( + database_id='<DATABASE_ID>', + collection_id='transactions', + document_id=ID.unique(), + data={ + '$createdAt': '2023-12-31T23:59:59.000Z', + '$updatedAt': '2023-12-31T23:59:59.000Z', + 'amount': 1000, + 'type': 'year-end-bonus' + } +) +``` +```server-ruby +documents_db.create_document( + database_id: '<DATABASE_ID>', + collection_id: 'transactions', + document_id: ID.unique(), + data: { + '$createdAt' => '2023-12-31T23:59:59.000Z', + '$updatedAt' => '2023-12-31T23:59:59.000Z', + 'amount' => 1000, + 'type' => 'year-end-bonus' + } +) +``` +```server-dotnet +await documentsDB.CreateDocument( + databaseId: "<DATABASE_ID>", + collectionId: "transactions", + documentId: ID.Unique(), + data: new Dictionary<string, object> + { + ["$createdAt"] = "2023-12-31T23:59:59.000Z", + ["$updatedAt"] = "2023-12-31T23:59:59.000Z", + ["amount"] = 1000, + ["type"] = "year-end-bonus" + } +); +``` +```server-dart +await documentsDB.createDocument( + databaseId: '<DATABASE_ID>', + collectionId: 'transactions', + documentId: ID.unique(), + data: { + '\$createdAt': '2023-12-31T23:59:59.000Z', + '\$updatedAt': '2023-12-31T23:59:59.000Z', + 'amount': 1000, + 'type': 'year-end-bonus' + }, +); +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use appwrite::id::ID; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let client = Client::new() + .set_endpoint("https://<REGION>.cloud.appwrite.io/v1") + .set_project("<PROJECT_ID>") + .set_key("<API_KEY>"); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.create_document( + "<DATABASE_ID>", + "transactions", + &ID::unique(), + json!({ + "$createdAt": "2023-12-31T23:59:59.000Z", + "$updatedAt": "2023-12-31T23:59:59.000Z", + "amount": 1000, + "type": "year-end-bonus" + }), + None, + None, + ).await?; + + println!("Created: {:?}", result); + Ok(()) +} +``` +{% /multicode %} + +## Synchronization {% #synchronization %} +When synchronizing data between systems while maintaining timestamp consistency: + +{% multicode %} +```server-nodejs +await documentsDB.upsertDocument({ + databaseId: '<DATABASE_ID>', + collectionId: 'users', + documentId: '<DOCUMENT_ID_OR_NEW_ID>', + data: { + '$updatedAt': '<EXTERNAL_LAST_MODIFIED_ISO>', + profile: '<PROFILE_DATA>' + } +}); +``` +```server-php +$documentsDB->upsertDocument( + databaseId: '<DATABASE_ID>', + collectionId: 'users', + documentId: '<DOCUMENT_ID_OR_NEW_ID>', + data: [ + '$updatedAt' => '<EXTERNAL_LAST_MODIFIED_ISO>', + 'profile' => '<PROFILE_DATA>' + ] +); +``` +```server-swift +let _ = try await documentsDB.upsertDocument( + databaseId: "<DATABASE_ID>", + collectionId: "users", + documentId: "<DOCUMENT_ID_OR_NEW_ID>", + data: [ + "$updatedAt": "<EXTERNAL_LAST_MODIFIED_ISO>", + "profile": "<PROFILE_DATA>" + ] +) +``` +```server-python +documents_db.upsert_document( + database_id='<DATABASE_ID>', + collection_id='users', + document_id='<DOCUMENT_ID_OR_NEW_ID>', + data={ + '$updatedAt': '<EXTERNAL_LAST_MODIFIED_ISO>', + 'profile': '<PROFILE_DATA>' + } +) +``` +```server-ruby +documents_db.upsert_document( + database_id: '<DATABASE_ID>', + collection_id: 'users', + document_id: '<DOCUMENT_ID_OR_NEW_ID>', + data: { + '$updatedAt' => '<EXTERNAL_LAST_MODIFIED_ISO>', + 'profile' => '<PROFILE_DATA>' + } +) +``` +```server-dotnet +await documentsDB.UpsertDocument( + databaseId: "<DATABASE_ID>", + collectionId: "users", + documentId: "<DOCUMENT_ID_OR_NEW_ID>", + data: new Dictionary<string, object> + { + ["$updatedAt"] = "<EXTERNAL_LAST_MODIFIED_ISO>", + ["profile"] = "<PROFILE_DATA>" + } +); +``` +```server-dart +await documentsDB.upsertDocument( + databaseId: '<DATABASE_ID>', + collectionId: 'users', + documentId: '<DOCUMENT_ID_OR_NEW_ID>', + data: { + '\$updatedAt': '<EXTERNAL_LAST_MODIFIED_ISO>', + 'profile': '<PROFILE_DATA>' + }, +); +``` +```rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let client = Client::new() + .set_endpoint("https://<REGION>.cloud.appwrite.io/v1") + .set_project("<PROJECT_ID>") + .set_key("<API_KEY>"); + + let documents_db = DocumentsDB::new(&client); + + let result = documents_db.upsert_document( + "<DATABASE_ID>", + "users", + "<DOCUMENT_ID_OR_NEW_ID>", + Some(json!({ + "$updatedAt": "<EXTERNAL_LAST_MODIFIED_ISO>", + "profile": "<PROFILE_DATA>" + })), + None, + None, + ).await?; + + println!("Upserted: {:?}", result); + Ok(()) +} +``` +{% /multicode %} + +{% info title="Timestamp format and usage" %} +- Values must be valid ISO 8601 date-time strings (UTC recommended). Using `toISOString()` (JavaScript) or `datetime.isoformat()` (Python) is a good default. +- You can set either or both attributes as needed. If omitted, Appwrite sets them automatically. +{% /info %} diff --git a/src/routes/docs/products/databases/documentsdb/transactions/+page.markdoc b/src/routes/docs/products/databases/documentsdb/transactions/+page.markdoc new file mode 100644 index 00000000000..be64977d0ad --- /dev/null +++ b/src/routes/docs/products/databases/documentsdb/transactions/+page.markdoc @@ -0,0 +1,1274 @@ +--- +layout: article +title: Transactions +description: Stage multiple database operations and commit them atomically. Group changes across databases and collections with ordering, isolation, and conflict detection. +--- + +Transactions let you stage multiple database operations and apply them together, atomically. Use transactions to keep related changes consistent, even when they span multiple databases and collections. + +# How transactions work {% #how-transactions-work %} + +1. Call the [createTransaction](#create-a-transaction) method to create a transaction. This will return a transaction model, including its ID. +2. Stage operations by passing the `transactionId` parameter to supported document, bulk, and atomic numeric methods. You can stage many operations at once with the [createOperations](#create-operations) method. +3. Call the [updateTransaction](#update-transaction) method to commit or roll back. + +On commit, Appwrite replays all staged logs in order inside a real database transaction. Staged operations see earlier staged changes (read your own writes). If any affected document changed outside your transaction, the commit fails with a conflict. + +{% info title="Scope and limitations" %} +You can stage operations across any database and collection within the same transaction. Schema operations (for example, creating or deleting indexes) are not included in transactions. +{% /info %} + +# Limits {% #limits %} + +The maximum number of operations you can stage per transaction depends on your plan: + +| Plan | Max operations per transaction | +|------|-------------------------------| +| Free | 100 | +| Pro | 1,000 | + +# Create a transaction {% #create-a-transaction %} + +Call the `createTransaction` method to begin. It returns a transaction model that includes `$id`. Pass this ID as `transactionId` to subsequent operations. + +{% multicode %} +```client-web +import { Client, DocumentsDB } from 'appwrite'; + +const client = new Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') + .setProject('<PROJECT_ID>'); + +const documentsDB = new DocumentsDB(client); + +const tx = await documentsDB.createTransaction(); +// tx.$id is your transactionId +``` +```client-react-native +import { Client, DocumentsDB } from 'react-native-appwrite'; + +const client = new Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') + .setProject('<PROJECT_ID>'); + +const documentsDB = new DocumentsDB(client); + +const tx = await documentsDB.createTransaction(); +// tx.$id is your transactionId +``` +```client-flutter +import 'package:appwrite/appwrite.dart'; + +final client = Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') + .setProject('<PROJECT_ID>'); + +final documentsDB = DocumentsDB(client); + +final tx = await documentsDB.createTransaction(); +// tx.$id is your transactionId +``` +```client-apple +import Appwrite + +let client = Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") + .setProject("<PROJECT_ID>") + +let documentsDB = DocumentsDB(client) + +let tx = try await documentsDB.createTransaction() +// tx.$id is your transactionId +``` +```client-android-kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client(applicationContext) + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") + .setProject("<PROJECT_ID>") + +val documentsDB = DocumentsDB(client) + +val tx = documentsDB.createTransaction() +// tx.$id is your transactionId +``` +```client-android-java +import io.appwrite.Client; +import io.appwrite.services.DocumentsDB; + +Client client = new Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") + .setProject("<PROJECT_ID>"); + +DocumentsDB documentsDB = new DocumentsDB(client); + +// Create a transaction (asynchronous) +documentsDB.createTransaction(new CoroutineCallback<>((tx, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(tx); +})); +``` +```server-nodejs +const sdk = require('node-appwrite'); + +const client = new sdk.Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') + .setProject('<PROJECT_ID>') + .setKey('<API_KEY>'); + +const documentsDB = new sdk.DocumentsDB(client); + +const tx = await documentsDB.createTransaction(); +// tx.$id is your transactionId +``` +```server-deno +import * as sdk from 'npm:node-appwrite'; + +const client = new sdk.Client() + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') + .setProject('<PROJECT_ID>') + .setKey('<API_KEY>'); + +const documentsDB = new sdk.DocumentsDB(client); + +const tx = await documentsDB.createTransaction(); +// tx.$id is your transactionId +``` +```server-python +from appwrite.client import Client +from appwrite.services.documents_db import DocumentsDB + +client = Client() +client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') +client.set_project('<PROJECT_ID>') +client.set_key('<API_KEY>') + +documents_db = DocumentsDB(client) + +tx = documents_db.create_transaction() +# tx.$id is your transactionId +``` +```server-php +<?php + +use Appwrite\Client; +use Appwrite\Services\DocumentsDB; + +$client = new Client(); + +$client + ->setEndpoint('https://<REGION>.cloud.appwrite.io/v1') + ->setProject('<PROJECT_ID>') + ->setKey('<API_KEY>') +; + +$documentsDB = new DocumentsDB($client); + +$tx = $documentsDB->createTransaction(); +// $tx->\$id is your transactionId +``` +```server-ruby +require 'appwrite' + +include Appwrite + +client = Client.new + .set_endpoint('https://<REGION>.cloud.appwrite.io/v1') + .set_project('<PROJECT_ID>') + .set_key('<API_KEY>') + +documents_db = DocumentsDB.new(client) + +tx = documents_db.create_transaction +# tx['$id'] is your transactionId +``` +```server-dotnet +using Appwrite; +using Appwrite.Services; + +var client = new Client() + .SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") + .SetProject("<PROJECT_ID>") + .SetKey("<API_KEY>"); + +var documentsDB = new DocumentsDB(client); + +var tx = await documentsDB.CreateTransaction(); +// tx.$id is your transactionId +``` +```server-dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +void main() async { + Client client = Client(); + DocumentsDB documentsDB = DocumentsDB(client); + + client + .setEndpoint('https://<REGION>.cloud.appwrite.io/v1') + .setProject('<PROJECT_ID>') + .setKey('<API_KEY>'); + + final tx = await documentsDB.createTransaction(); + // tx contains the transaction ID +} +``` +```server-go +package main + +import ( + "log" + "github.com/appwrite/sdk-for-go/appwrite" +) + +func main() { + client := appwrite.NewClient( + appwrite.WithEndpoint("https://<REGION>.cloud.appwrite.io/v1"), + appwrite.WithProject("<PROJECT_ID>"), + appwrite.WithKey("<API_KEY>"), + ) + + documentsDB := appwrite.NewDocumentsDB(client) + + tx, err := documentsDB.CreateTransaction() + if err != nil { log.Fatal(err) } + _ = tx +} +``` +```server-swift +import Appwrite + +let client = Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") + .setProject("<PROJECT_ID>") + .setKey("<API_KEY>") + +let documentsDB = DocumentsDB(client) + +let tx = try await documentsDB.createTransaction() +// tx.$id is your transactionId +``` +```server-kotlin +import io.appwrite.Client +import io.appwrite.services.DocumentsDB + +val client = Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") + .setProject("<PROJECT_ID>") + .setKey("<API_KEY>") + +val documentsDB = DocumentsDB(client) + +val tx = documentsDB.createTransaction() +// tx.$id is your transactionId +``` +```server-java +import io.appwrite.Client; +import io.appwrite.coroutines.CoroutineCallback; +import io.appwrite.services.DocumentsDB; + +Client client = new Client() + .setEndpoint("https://<REGION>.cloud.appwrite.io/v1") + .setProject("<PROJECT_ID>") + .setKey("<API_KEY>"); + +DocumentsDB documentsDB = new DocumentsDB(client); + +documentsDB.createTransaction(new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return; + } + System.out.println(result); +})); +``` +```server-rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let client = Client::new() + .set_endpoint("https://<REGION>.cloud.appwrite.io/v1") + .set_project("<PROJECT_ID>") + .set_key("<API_KEY>"); + + let documents_db = DocumentsDB::new(&client); + + let tx = documents_db.create_transaction(None).await?; + // tx.id is your transaction_id + + Ok(()) +} +``` +{% /multicode %} + +# Stage operations {% #stage-operations %} + +Add the `transactionId` parameter to supported methods to stage them instead of immediately persisting. + +When you pass `transactionId`, Appwrite writes the operation to an internal staging area. The target collection is not modified until you commit the transaction. + +## Stage single operations {% #stage-single-operations %} + +Create, update, upsert, delete, and atomic numeric operations accept `transactionId`, as well as their bulk versions (createDocuments, updateDocuments, upsertDocuments, deleteDocuments). + +{% multicode %} +```client-web +// Create inside a transaction +await documentsDB.createDocument({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + data: { name: 'Walter' }, + transactionId: tx.$id +}); + +// Increment inside a transaction +await documentsDB.incrementDocumentAttribute({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + attribute: 'credits', + value: 1, + transactionId: tx.$id +}); +``` +```client-flutter +// Create inside a transaction +await documentsDB.createDocument( + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + data: { 'name': 'Walter' }, + transactionId: tx.$id +); + +// Increment inside a transaction +await documentsDB.incrementDocumentAttribute( + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + attribute: 'credits', + value: 1, + transactionId: tx.$id +); +``` +```client-apple +// Create inside a transaction +try await documentsDB.createDocument( + databaseId: "<DATABASE_ID>", + collectionId: "<COLLECTION_ID>", + documentId: "<DOCUMENT_ID>", + data: ["name": "Walter"], + transactionId: tx.$id +) + +// Increment inside a transaction +try await documentsDB.incrementDocumentAttribute( + databaseId: "<DATABASE_ID>", + collectionId: "<COLLECTION_ID>", + documentId: "<DOCUMENT_ID>", + attribute: "credits", + value: 1, + transactionId: tx.$id +) +``` +```server-kotlin +// Create inside a transaction +documentsDB.createDocument( + databaseId = "<DATABASE_ID>", + collectionId = "<COLLECTION_ID>", + documentId = "<DOCUMENT_ID>", + data = mapOf("name" to "Walter"), + transactionId = tx.$id +) + +// Increment inside a transaction +documentsDB.incrementDocumentAttribute( + databaseId = "<DATABASE_ID>", + collectionId = "<COLLECTION_ID>", + documentId = "<DOCUMENT_ID>", + attribute = "credits", + value = 1, + transactionId = tx.$id +) +``` +```server-java +// Create inside a transaction (asynchronous) +documentsDB.createDocument( + "<DATABASE_ID>", + "<COLLECTION_ID>", + "<DOCUMENT_ID>", + Map.of("name", "Walter"), + "<TRANSACTION_ID>", + new CoroutineCallback<>((document, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(document); + return null; + }) +); + +// Increment inside a transaction (asynchronous) +documentsDB.incrementDocumentAttribute( + "<DATABASE_ID>", + "<COLLECTION_ID>", + "<DOCUMENT_ID>", + "credits", + 1, + "<TRANSACTION_ID>", + new CoroutineCallback<>((document, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(document); + return null; + }) +); +``` +```client-react-native +// Create inside a transaction +await documentsDB.createDocument({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + data: { name: 'Walter' }, + transactionId: tx.$id +}); + +// Increment inside a transaction +await documentsDB.incrementDocumentAttribute({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + attribute: 'credits', + value: 1, + transactionId: tx.$id +}); +``` +```server-nodejs +// Update inside a transaction +await documentsDB.updateDocument({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + data: { plan: 'pro' }, + transactionId: tx.$id +}); + +// Delete inside a transaction +await documentsDB.deleteDocument({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + transactionId: tx.$id +}); +``` +```server-python +# Upsert inside a transaction +documents_db.upsert_document( + database_id = '<DATABASE_ID>', + collection_id = '<COLLECTION_ID>', + document_id = '<DOCUMENT_ID>', + data = { 'name': 'Walter' }, + transaction_id = tx.id +) + +# Decrement inside a transaction +documents_db.decrement_document_attribute( + database_id = '<DATABASE_ID>', + collection_id = '<COLLECTION_ID>', + document_id = '<DOCUMENT_ID>', + attribute = 'credits', + value = 1, + transaction_id = tx.id +) +``` +```server-php +// Create inside a transaction +$documentsDB->createDocument( + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + data: ['name' => 'Walter'], + transactionId: $tx['$id'] +); + +// Increment inside a transaction +$documentsDB->incrementDocumentAttribute( + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + attribute: 'credits', + value: 1, + transactionId: $tx['$id'] +); +``` +```server-ruby +# Create inside a transaction +documents_db.create_document( + database_id: '<DATABASE_ID>', + collection_id: '<COLLECTION_ID>', + document_id: '<DOCUMENT_ID>', + data: { 'name' => 'Walter' }, + transaction_id: tx['$id'] +) + +# Increment inside a transaction +documents_db.increment_document_attribute( + database_id: '<DATABASE_ID>', + collection_id: '<COLLECTION_ID>', + document_id: '<DOCUMENT_ID>', + attribute: 'credits', + value: 1, + transaction_id: tx['$id'] +) +``` +```server-dotnet +// Create inside a transaction +await documentsDB.CreateDocument( + databaseId: "<DATABASE_ID>", + collectionId: "<COLLECTION_ID>", + documentId: "<DOCUMENT_ID>", + data: new Dictionary<string, object> { ["name"] = "Walter" }, + transactionId: tx.Id +); + +// Increment inside a transaction +await documentsDB.IncrementDocumentAttribute( + databaseId: "<DATABASE_ID>", + collectionId: "<COLLECTION_ID>", + documentId: "<DOCUMENT_ID>", + attribute: "credits", + value: 1, + transactionId: tx.Id +); +``` +```server-dart +// Create inside a transaction +await documentsDB.createDocument( + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + data: { 'name': 'Walter' }, + transactionId: tx.Id +); + +// Increment inside a transaction +await documentsDB.incrementDocumentAttribute( + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + attribute: 'credits', + value: 1, + transactionId: tx.Id +); +``` +```server-deno +// Create inside a transaction +await documentsDB.createDocument({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + data: { name: 'Walter' }, + transactionId: tx.$id +}); + +// Increment inside a transaction +await documentsDB.incrementDocumentAttribute({ + databaseId: '<DATABASE_ID>', + collectionId: '<COLLECTION_ID>', + documentId: '<DOCUMENT_ID>', + attribute: 'credits', + value: 1, + transactionId: tx.$id +}); +``` +```server-swift +// Create inside a transaction +try await documentsDB.createDocument( + databaseId: "<DATABASE_ID>", + collectionId: "<COLLECTION_ID>", + documentId: "<DOCUMENT_ID>", + data: ["name": "Walter"], + transactionId: tx.$id +) + +// Increment inside a transaction +try await documentsDB.incrementDocumentAttribute( + databaseId: "<DATABASE_ID>", + collectionId: "<COLLECTION_ID>", + documentId: "<DOCUMENT_ID>", + attribute: "credits", + value: 1, + transactionId: tx.$id +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let client = Client::new() + .set_endpoint("https://<REGION>.cloud.appwrite.io/v1") + .set_project("<PROJECT_ID>") + .set_key("<API_KEY>"); + + let documents_db = DocumentsDB::new(&client); + + let tx = documents_db.create_transaction(None).await?; + + // Update inside a transaction + documents_db.update_document( + "<DATABASE_ID>", + "<COLLECTION_ID>", + "<DOCUMENT_ID>", + Some(json!({ "plan": "pro" })), + None, + Some(&tx.id), + ).await?; + + // Delete inside a transaction + documents_db.delete_document( + "<DATABASE_ID>", + "<COLLECTION_ID>", + "<DOCUMENT_ID>", + Some(&tx.id), + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +## Stage many with createOperations {% #create-operations %} + +Use the `createOperations` method to stage multiple operations across databases and collections in a single request. Provide an array of operation objects: + +```json +[ + { + "action": "create|update|upsert|increment|decrement|delete|bulkCreate|bulkUpdate|bulkUpsert|bulkDelete", + "databaseId": "<DATABASE_ID>", + "collectionId": "<COLLECTION_ID>", + "documentId": "<DOCUMENT_ID>", + "data": {} + } +] +``` + +### Provide data for each action (createOperations) {% #provide-data-for-each-action %} + +### Create, update, and upsert {% #create-update-upsert %} +Pass a raw data object. +```json +{ "name": "Walter" } +``` + +### Increment and decrement {% #increment-decrement %} +Pass a value and optionally `min`/`max` bounds. +```json +{ "value": 1, "min": 0, "max": 1000, "attribute": "<ATTRIBUTE_NAME>" } +``` + +### Bulk create and bulk upsert {% #bulk-create-upsert %} +Pass an array of raw data objects. +```json +[{ "$id": "123", "name": "Walter" }] +``` + +### Bulk update {% #bulk-update %} +Pass queries and the data to apply. +```json +{ "queries": [{"method": "equal", "attribute": "status", "values": ["draft"]}], "data": { "status": "published" } } +``` + +### Bulk delete {% #bulk-delete %} +Pass queries to select documents to delete. +```json +{ "queries": [{"method": "equal", "attribute": "archived", "values": [true]}] } +``` + +{% multicode %} +```server-nodejs +// Stage multiple operations at once +await documentsDB.createOperations({ + transactionId: tx.$id, + operations: [ + { + action: 'create', + databaseId: '<DB_A>', + collectionId: '<COLLECTION_1>', + documentId: 'u1', + data: { name: 'Walter' } + }, + { + action: 'increment', + databaseId: '<DB_B>', + collectionId: '<COLLECTION_2>', + documentId: 'u2', + data: { value: 1, min: 0, attribute: 'credits' } + } + ] +}); +``` +```server-python +documents_db.create_operations( + transaction_id = tx.id, + operations = [ + { + 'action': 'create', + 'databaseId': '<DB_A>', + 'collectionId': '<COLLECTION_1>', + 'documentId': 'u1', + 'data': { 'name': 'Walter' } + }, + { + 'action': 'increment', + 'databaseId': '<DB_B>', + 'collectionId': '<COLLECTION_2>', + 'documentId': 'u2', + 'data': { 'value': 1, 'min': 0, 'attribute': 'credits' } + } + ] +) +``` +```client-web +await documentsDB.createOperations({ + transactionId: tx.$id, + operations: [ + { + action: 'create', + databaseId: '<DB_A>', + collectionId: '<COLLECTION_1>', + documentId: 'u1', + data: { name: 'Walter' } + }, + { + action: 'increment', + databaseId: '<DB_B>', + collectionId: '<COLLECTION_2>', + documentId: 'u2', + data: { value: 1, min: 0, attribute: 'credits' } + } + ] +}); +``` +```client-flutter +await documentsDB.createOperations( + transactionId: tx.$id, + operations: [ + { + 'action': 'create', + 'databaseId': '<DB_A>', + 'collectionId': '<COLLECTION_1>', + 'documentId': 'u1', + 'data': { 'name': 'Walter' } + }, + { + 'action': 'increment', + 'databaseId': '<DB_B>', + 'collectionId': '<COLLECTION_2>', + 'documentId': 'u2', + 'data': { 'value': 1, 'min': 0, 'attribute': 'credits' } + } + ], +); +``` +```client-apple +try await documentsDB.createOperations( + transactionId: tx.$id, + operations: [ + [ + "action": "create", + "databaseId": "<DB_A>", + "collectionId": "<COLLECTION_1>", + "documentId": "u1", + "data": ["name": "Walter"] + ], + [ + "action": "increment", + "databaseId": "<DB_B>", + "collectionId": "<COLLECTION_2>", + "documentId": "u2", + "data": ["value": 1, "min": 0, "attribute": "credits"] + ] + ] +) +``` +```server-kotlin +documentsDB.createOperations( + transactionId = tx.$id, + operations = listOf( + mapOf( + "action" to "create", + "databaseId" to "<DB_A>", + "collectionId" to "<COLLECTION_1>", + "documentId" to "u1", + "data" to mapOf("name" to "Walter") + ), + mapOf( + "action" to "increment", + "databaseId" to "<DB_B>", + "collectionId" to "<COLLECTION_2>", + "documentId" to "u2", + "data" to mapOf("value" to 1, "min" to 0, "attribute" to "credits") + ) + ) +) +``` +```server-java +// Stage multiple operations at once (asynchronous) +List<Map<String, Object>> operations = Arrays.asList( + Map.of( + "action", "create", + "databaseId", "<DB_A>", + "collectionId", "<COLLECTION_1>", + "documentId", "u1", + "data", Map.of("name", "Walter") + ), + Map.of( + "action", "increment", + "databaseId", "<DB_B>", + "collectionId", "<COLLECTION_2>", + "documentId", "u2", + "data", Map.of("value", 1, "min", 0, "attribute", "credits") + ) +); + +documentsDB.createOperations( + "<TRANSACTION_ID>", + operations, + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(result); + return null; + }) +); +``` +```client-react-native +await documentsDB.createOperations({ + transactionId: tx.$id, + operations: [ + { + action: 'create', + databaseId: '<DB_A>', + collectionId: '<COLLECTION_1>', + documentId: 'u1', + data: { name: 'Walter' } + }, + { + action: 'increment', + databaseId: '<DB_B>', + collectionId: '<COLLECTION_2>', + documentId: 'u2', + data: { value: 1, min: 0, attribute: 'credits' } + } + ] +}); +``` +```server-deno +await documentsDB.createOperations({ + transactionId: tx.$id, + operations: [ + { + action: 'create', + databaseId: '<DB_A>', + collectionId: '<COLLECTION_1>', + documentId: 'u1', + data: { name: 'Walter' } + }, + { + action: 'increment', + databaseId: '<DB_B>', + collectionId: '<COLLECTION_2>', + documentId: 'u2', + data: { value: 1, min: 0, attribute: 'credits' } + } + ] +}); +``` +```server-php +$documentsDB->createOperations( + transactionId: $tx['$id'], + operations: [ + [ + 'action' => 'create', + 'databaseId' => '<DB_A>', + 'collectionId' => '<COLLECTION_1>', + 'documentId' => 'u1', + 'data' => [ 'name' => 'Walter' ] + ], + [ + 'action' => 'increment', + 'databaseId' => '<DB_B>', + 'collectionId' => '<COLLECTION_2>', + 'documentId' => 'u2', + 'data' => [ 'value' => 1, 'min' => 0, 'attribute' => 'credits' ] + ] + ] +); +``` +```server-ruby +documents_db.create_operations( + transaction_id: tx['$id'], + operations: [ + { + 'action' => 'create', + 'databaseId' => '<DB_A>', + 'collectionId' => '<COLLECTION_1>', + 'documentId' => 'u1', + 'data' => { 'name' => 'Walter' } + }, + { + 'action' => 'increment', + 'databaseId' => '<DB_B>', + 'collectionId' => '<COLLECTION_2>', + 'documentId' => 'u2', + 'data' => { 'value' => 1, 'min' => 0, 'attribute' => 'credits' } + } + ] +) +``` +```server-dotnet +await documentsDB.CreateOperations( + transactionId: tx.Id, + operations: new List<Dictionary<string, object>> + { + new Dictionary<string, object> + { + ["action"] = "create", + ["databaseId"] = "<DB_A>", + ["collectionId"] = "<COLLECTION_1>", + ["documentId"] = "u1", + ["data"] = new Dictionary<string, object> { ["name"] = "Walter" } + }, + new Dictionary<string, object> + { + ["action"] = "increment", + ["databaseId"] = "<DB_B>", + ["collectionId"] = "<COLLECTION_2>", + ["documentId"] = "u2", + ["data"] = new Dictionary<string, object> { ["value"] = 1, ["min"] = 0, ["attribute"] = "credits" } + } + } +); +``` +```server-dart +await documentsDB.createOperations( + transactionId: tx.Id, + operations: [ + { + 'action': 'create', + 'databaseId': '<DB_A>', + 'collectionId': '<COLLECTION_1>', + 'documentId': 'u1', + 'data': { 'name': 'Walter' } + }, + { + 'action': 'increment', + 'databaseId': '<DB_B>', + 'collectionId': '<COLLECTION_2>', + 'documentId': 'u2', + 'data': { 'value': 1, 'min': 0, 'attribute': 'credits' } + } + ] +); +``` +```server-swift +try await documentsDB.createOperations( + transactionId: tx.$id, + operations: [ + [ + "action": "create", + "databaseId": "<DB_A>", + "collectionId": "<COLLECTION_1>", + "documentId": "u1", + "data": ["name": "Walter"] + ], + [ + "action": "increment", + "databaseId": "<DB_B>", + "collectionId": "<COLLECTION_2>", + "documentId": "u2", + "data": ["value": 1, "min": 0, "attribute": "credits"] + ] + ] +) +``` +```server-rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; +use serde_json::json; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let client = Client::new() + .set_endpoint("https://<REGION>.cloud.appwrite.io/v1") + .set_project("<PROJECT_ID>") + .set_key("<API_KEY>"); + + let documents_db = DocumentsDB::new(&client); + + let tx = documents_db.create_transaction(None).await?; + + // Stage multiple operations at once + documents_db.create_operations( + &tx.id, + Some(vec![ + json!({ + "action": "create", + "databaseId": "<DB_A>", + "collectionId": "<COLLECTION_1>", + "documentId": "u1", + "data": { "name": "Walter" } + }), + json!({ + "action": "increment", + "databaseId": "<DB_B>", + "collectionId": "<COLLECTION_2>", + "documentId": "u2", + "data": { "value": 1, "min": 0, "attribute": "credits" } + }), + ]), + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +# Commit or roll back {% #commit-or-rollback %} + +When you are done staging operations, call the `updateTransaction` method to finalize the transaction. + +{% multicode %} +```client-web +// Commit +await documentsDB.updateTransaction({ + transactionId: tx.$id, + commit: true +}); + +// Or roll back +await documentsDB.updateTransaction({ + transactionId: tx.$id, + rollback: true +}); +``` +```client-flutter +// Commit +await documentsDB.updateTransaction( + transactionId: tx.$id, + commit: true +); + +// Roll back +await documentsDB.updateTransaction( + transactionId: tx.$id, + rollback: true +); +``` +```client-apple +// Commit +try await documentsDB.updateTransaction( + transactionId: tx.$id, + commit: true +) + +// Roll back +try await documentsDB.updateTransaction( + transactionId: tx.$id, + rollback: true +) +``` +```server-kotlin +// Commit +documentsDB.updateTransaction( + transactionId = tx.$id, + commit = true +) + +// Roll back +documentsDB.updateTransaction( + transactionId = tx.$id, + rollback = true +) +``` +```server-java +// Commit (asynchronous) +documentsDB.updateTransaction( + "<TRANSACTION_ID>", + true, + false, + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(result); + return null; + }) +); + +// Roll back (asynchronous) +documentsDB.updateTransaction( + "<TRANSACTION_ID>", + false, + true, + new CoroutineCallback<>((result, error) -> { + if (error != null) { + error.printStackTrace(); + return null; + } + System.out.println(result); + return null; + }) +); +``` +```client-react-native +// Commit +await documentsDB.updateTransaction({ + transactionId: tx.$id, + commit: true +}); + +// Roll back +await documentsDB.updateTransaction({ + transactionId: tx.$id, + rollback: true +}); +``` +```server-nodejs +// Commit +await documentsDB.updateTransaction({ + transactionId: tx.$id, + commit: true +}); + +// Roll back +await documentsDB.updateTransaction({ + transactionId: tx.$id, + rollback: true +}); +``` +```server-python +# Commit +documents_db.update_transaction( + transaction_id = tx.id, + commit = True +) + +# Roll back +documents_db.update_transaction( + transaction_id = tx.id, + rollback = True +) +``` +```server-php +// Commit +$documentsDB->updateTransaction( + transactionId: $tx['$id'], + commit: true +); + +// Roll back +$documentsDB->updateTransaction( + transactionId: $tx['$id'], + rollback: true +); +``` +```server-ruby +# Commit +documents_db.update_transaction( + transaction_id: tx['$id'], + commit: true +) + +# Roll back +documents_db.update_transaction( + transaction_id: tx['$id'], + rollback: true +) +``` +```server-dotnet +// Commit +await documentsDB.UpdateTransaction( + transactionId: tx.Id, + commit: true +); + +// Roll back +await documentsDB.UpdateTransaction( + transactionId: tx.Id, + rollback: true +); +``` +```server-dart +// Commit +await documentsDB.updateTransaction( + transactionId: tx.Id, + commit: true +); + +// Roll back +await documentsDB.updateTransaction( + transactionId: tx.Id, + rollback: true +); +``` +```server-rust +use appwrite::Client; +use appwrite::services::documents_db::DocumentsDB; + +#[tokio::main] +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let client = Client::new() + .set_endpoint("https://<REGION>.cloud.appwrite.io/v1") + .set_project("<PROJECT_ID>") + .set_key("<API_KEY>"); + + let documents_db = DocumentsDB::new(&client); + + // Commit + documents_db.update_transaction( + "<TRANSACTION_ID>", + Some(true), + None, + ).await?; + + // Roll back + documents_db.update_transaction( + "<TRANSACTION_ID>", + None, + Some(true), + ).await?; + + Ok(()) +} +``` +{% /multicode %} + +# Handle conflicts {% #handle-conflicts %} + +On commit, Appwrite verifies that documents affected by your transaction haven't changed externally since they were staged. If a conflicting change is detected, the commit fails with a conflict error. Resolve the conflict (for example, refetch and re-stage) and try again. + +{% info title="Best practices" %} +Keep transactions short-lived to reduce the likelihood of conflicts. Stage related updates in the order they must be applied. Prefer `createOperations` when you need to stage many changes across multiple collections. +{% /info %} + +{% arrow_link href="/docs/references" %} +Explore the API references +{% /arrow_link %} diff --git a/src/routes/docs/products/databases/mysql/+layout.svelte b/src/routes/docs/products/databases/mysql/+layout.svelte new file mode 100644 index 00000000000..f6fe631eea8 --- /dev/null +++ b/src/routes/docs/products/databases/mysql/+layout.svelte @@ -0,0 +1,29 @@ +<script lang="ts"> + import Docs from '$lib/layouts/Docs.svelte'; + import Sidebar, { type NavParent, type NavTree } from '$lib/layouts/Sidebar.svelte'; + + let { children } = $props(); + + const parent: NavParent = { + href: '/docs/products/databases', + label: 'Databases' + }; + + const navigation: NavTree = [ + { + label: 'Getting started', + items: [ + { + label: 'Overview', + href: '/docs/products/databases/mysql' + } + ] + } + ]; +</script> + +<Docs variant="two-side-navs"> + <Sidebar {navigation} {parent} /> + + {@render children()} +</Docs> diff --git a/src/routes/docs/products/databases/mysql/+page.markdoc b/src/routes/docs/products/databases/mysql/+page.markdoc new file mode 100644 index 00000000000..de42e29c3bd --- /dev/null +++ b/src/routes/docs/products/databases/mysql/+page.markdoc @@ -0,0 +1,11 @@ +--- +layout: article +title: MySQL +description: Run a dedicated, native MySQL database provisioned for your project and connect to it directly with standard MySQL clients. +--- + +Run a dedicated, native MySQL database provisioned for your project. Connect to it directly with standard MySQL clients and tools, with no Appwrite abstraction layer in between. Native databases are available on dedicated infrastructure. + +{% info title="Documentation in progress" %} +Detailed MySQL setup and connection guides are on the way. +{% /info %} diff --git a/src/routes/docs/products/databases/postgresql/+layout.svelte b/src/routes/docs/products/databases/postgresql/+layout.svelte new file mode 100644 index 00000000000..de7adff91d5 --- /dev/null +++ b/src/routes/docs/products/databases/postgresql/+layout.svelte @@ -0,0 +1,29 @@ +<script lang="ts"> + import Docs from '$lib/layouts/Docs.svelte'; + import Sidebar, { type NavParent, type NavTree } from '$lib/layouts/Sidebar.svelte'; + + let { children } = $props(); + + const parent: NavParent = { + href: '/docs/products/databases', + label: 'Databases' + }; + + const navigation: NavTree = [ + { + label: 'Getting started', + items: [ + { + label: 'Overview', + href: '/docs/products/databases/postgresql' + } + ] + } + ]; +</script> + +<Docs variant="two-side-navs"> + <Sidebar {navigation} {parent} /> + + {@render children()} +</Docs> diff --git a/src/routes/docs/products/databases/postgresql/+page.markdoc b/src/routes/docs/products/databases/postgresql/+page.markdoc new file mode 100644 index 00000000000..4e7c251d8da --- /dev/null +++ b/src/routes/docs/products/databases/postgresql/+page.markdoc @@ -0,0 +1,11 @@ +--- +layout: article +title: PostgreSQL +description: Run a dedicated, native PostgreSQL database provisioned for your project and connect to it directly with standard PostgreSQL clients. +--- + +Run a dedicated, native PostgreSQL database provisioned for your project. Connect to it directly with standard PostgreSQL clients and tools, with no Appwrite abstraction layer in between. Native databases are available on dedicated infrastructure. + +{% info title="Documentation in progress" %} +Detailed PostgreSQL setup and connection guides are on the way. +{% /info %} diff --git a/src/routes/docs/products/databases/+layout.svelte b/src/routes/docs/products/databases/tablesdb/+layout.svelte similarity index 70% rename from src/routes/docs/products/databases/+layout.svelte rename to src/routes/docs/products/databases/tablesdb/+layout.svelte index 87f1ffb5a32..ca05c54b16a 100644 --- a/src/routes/docs/products/databases/+layout.svelte +++ b/src/routes/docs/products/databases/tablesdb/+layout.svelte @@ -10,7 +10,7 @@ let { children } = $props(); const parent: NavParent = { - href: '/docs', + href: '/docs/products/databases', label: 'Databases' }; @@ -20,11 +20,11 @@ items: [ { label: 'Overview', - href: '/docs/products/databases' + href: '/docs/products/databases/tablesdb' }, { label: 'Quick start', - href: '/docs/products/databases/quick-start' + href: '/docs/products/databases/tablesdb/quick-start' } ] }, @@ -33,45 +33,45 @@ items: [ { label: 'Databases', - href: '/docs/products/databases/databases' + href: '/docs/products/databases/tablesdb/databases' }, { label: 'Tables', - href: '/docs/products/databases/tables' + href: '/docs/products/databases/tablesdb/tables' }, { label: 'Rows', - href: '/docs/products/databases/rows' + href: '/docs/products/databases/tablesdb/rows' }, { label: 'Permissions', - href: '/docs/products/databases/permissions' + href: '/docs/products/databases/tablesdb/permissions' }, { label: 'Relationships', - href: '/docs/products/databases/relationships' + href: '/docs/products/databases/tablesdb/relationships' }, { label: 'Queries', - href: '/docs/products/databases/queries' + href: '/docs/products/databases/tablesdb/queries' }, { label: 'Order', - href: '/docs/products/databases/order' + href: '/docs/products/databases/tablesdb/order' }, { label: 'Operators', - href: '/docs/products/databases/operators', + href: '/docs/products/databases/tablesdb/operators', new: isNewUntil('31 Dec 2025') }, { label: 'Geo queries', - href: '/docs/products/databases/geo-queries', + href: '/docs/products/databases/tablesdb/geo-queries', new: isNewUntil('30 Sep 2025') }, { label: 'Backups', - href: '/docs/products/databases/backups' + href: '/docs/products/databases/tablesdb/backups' } ] }, @@ -80,50 +80,50 @@ items: [ { label: 'Pagination', - href: '/docs/products/databases/pagination' + href: '/docs/products/databases/tablesdb/pagination' }, { label: 'Transactions', - href: '/docs/products/databases/transactions', + href: '/docs/products/databases/tablesdb/transactions', new: isNewUntil('31 Oct 2025') }, { label: 'Type generation', - href: '/docs/products/databases/type-generation', + href: '/docs/products/databases/tablesdb/type-generation', new: isNewUntil('31 Jul 2025') }, { label: 'Offline sync', - href: '/docs/products/databases/offline' + href: '/docs/products/databases/tablesdb/offline' }, { label: 'Bulk operations', - href: '/docs/products/databases/bulk-operations', + href: '/docs/products/databases/tablesdb/bulk-operations', new: isNewUntil('31 Jul 2025') }, { label: 'Atomic numeric operations', - href: '/docs/products/databases/atomic-numeric-operations', + href: '/docs/products/databases/tablesdb/atomic-numeric-operations', new: isNewUntil('31 Jul 2025') }, { label: 'CSV imports', - href: '/docs/products/databases/csv-imports', + href: '/docs/products/databases/tablesdb/csv-imports', new: isNewUntil('31 Jul 2025') }, { label: 'CSV exports', - href: '/docs/products/databases/csv-exports', + href: '/docs/products/databases/tablesdb/csv-exports', new: isNewUntil('28 Feb 2026') }, { label: 'AI suggestions', - href: '/docs/products/databases/ai-suggestions', + href: '/docs/products/databases/tablesdb/ai-suggestions', new: isNewUntil('31 Dec 2025') }, { label: 'Timestamp overrides', - href: '/docs/products/databases/timestamp-overrides' + href: '/docs/products/databases/tablesdb/timestamp-overrides' } ] }, @@ -145,16 +145,16 @@ const legacyUrl = $derived( page.url.pathname - .replace('/products/databases', '/products/databases/legacy') - .replace('rows', 'documents') - .replace('tables', 'collections') + .replace('/tablesdb/', '/tablesdb/legacy/') + .replace(/\/rows(\/|$)/, '/documents$1') + .replace(/\/tables(\/|$)/, '/collections$1') ); const hideSubtitleRoutes = ['offline', 'backups', 'csv-imports', 'csv-exports']; const shouldShowSubtitle = $derived( !hideSubtitleRoutes.some((segment) => page.route.id?.includes(segment)) && - !page.url.pathname.endsWith('products/databases') + !page.url.pathname.endsWith('products/databases/tablesdb') ); const headerSectionInfoAlert = writable<HeaderSectionInfoAlert | null>(null); diff --git a/src/routes/docs/products/databases/+page.markdoc b/src/routes/docs/products/databases/tablesdb/+page.markdoc similarity index 70% rename from src/routes/docs/products/databases/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/+page.markdoc index 9217bde1e68..e9f01089844 100644 --- a/src/routes/docs/products/databases/+page.markdoc +++ b/src/routes/docs/products/databases/tablesdb/+page.markdoc @@ -1,7 +1,7 @@ --- layout: article -title: Databases -description: Store and query structured data with Appwrite Databases. Databases provide performant and scalable storage for your application, business, and user data. +title: TablesDB +description: Store and query structured data with Appwrite TablesDB. Tables provide performant and scalable storage for your application, business, and user data. --- Appwrite Databases let you store and query structured data. @@ -14,6 +14,6 @@ Databases store data, if you need to store files like images, PDFs or videos, us You can organize data into databases, tables, and rows. You can also paginate, order, and query rows. For complex business logic, Appwrite supports relationships to help you model your data. -{% arrow_link href="/docs/products/databases/quick-start" %} +{% arrow_link href="/docs/products/databases/tablesdb/quick-start" %} Quick start {% /arrow_link %} \ No newline at end of file diff --git a/src/routes/docs/products/databases/ai-suggestions/+page.markdoc b/src/routes/docs/products/databases/tablesdb/ai-suggestions/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/ai-suggestions/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/ai-suggestions/+page.markdoc diff --git a/src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc b/src/routes/docs/products/databases/tablesdb/atomic-numeric-operations/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/atomic-numeric-operations/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/atomic-numeric-operations/+page.markdoc diff --git a/src/routes/docs/products/databases/backups/+page.markdoc b/src/routes/docs/products/databases/tablesdb/backups/+page.markdoc similarity index 87% rename from src/routes/docs/products/databases/backups/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/backups/+page.markdoc index 202133529f6..6dca288f054 100644 --- a/src/routes/docs/products/databases/backups/+page.markdoc +++ b/src/routes/docs/products/databases/tablesdb/backups/+page.markdoc @@ -8,14 +8,14 @@ Appwrite Backups enable seamless, **encrypted** database backups on Cloud. All backups are **hot** backups, ensuring zero downtime and fast recovery. Learn how to efficiently back up your databases to ensure data security and smooth recovery. -{% info title="Backups are available on Appwrite Cloud for all Pro, Scale, and Enterprise customers." %} +{% info title="Backups are available on Appwrite Cloud for all Pro and Enterprise customers." %} {% /info %} Appwrite Backups allow you to automate database backups using backup policies, supporting pre-defined, custom retention & other options. You can also create manual backups whenever necessary. # Backup policies {% #backup-policies %} -Backup policies allow you to automate your backup process. The Scale and Enterprise plans allow for more customization and offer options like how often backups should occur, how long they should be retained, and when they should run. +Backup policies allow you to automate your backup process. The Enterprise plan allows for more customization and offers options like how often backups should occur, how long they should be retained, and when they should run. ## Creating a backup policy {% #creating-backup-policy %} @@ -40,21 +40,21 @@ To automate your database backups, you need to create backup policies that run a ![Pro plan policy](/images/docs/databases/pro-policy.avif) {% /only_light %} - * On **Scale** and **Enterprise** plans, you get access to more & custom policies\ + * On the **Enterprise** plan, you get access to more & custom policies\   * Select a pre-defined policy {% only_dark %} - ![Scale plan policies](/images/docs/databases/dark/scale-policies.avif) + ![Backup policies](/images/docs/databases/dark/scale-policies.avif) {% /only_dark %} {% only_light %} - ![Scale plan policies](/images/docs/databases/scale-policies.avif) + ![Backup policies](/images/docs/databases/scale-policies.avif) {% /only_light %} * Or create a custom policy and adjust the settings as you like {% only_dark %} - ![Custom policies for Scale plan](/images/docs/databases/dark/scale-custom-policies.avif) + ![Custom backup policies](/images/docs/databases/dark/scale-custom-policies.avif) {% /only_dark %} {% only_light %} - ![Custom policies for Scale plan](/images/docs/databases/scale-custom-policies.avif) + ![Custom backup policies](/images/docs/databases/scale-custom-policies.avif) {% /only_light %} 4. Click on **Create** diff --git a/src/routes/docs/products/databases/bulk-operations/+page.markdoc b/src/routes/docs/products/databases/tablesdb/bulk-operations/+page.markdoc similarity index 96% rename from src/routes/docs/products/databases/bulk-operations/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/bulk-operations/+page.markdoc index 07a93db88c0..d1574894303 100644 --- a/src/routes/docs/products/databases/bulk-operations/+page.markdoc +++ b/src/routes/docs/products/databases/tablesdb/bulk-operations/+page.markdoc @@ -56,10 +56,10 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.createRows( - '<DATABASE_ID>', - '<TABLE_ID>', - [ +const result = await tablesDB.createRows({ + databaseId: '<DATABASE_ID>', + tableId: '<TABLE_ID>', + rows: [ { $id: sdk.ID.unique(), name: 'Row 1' @@ -69,7 +69,7 @@ const result = await tablesDB.createRows( name: 'Row 2' } ] -); +}); ``` ```server-python @@ -155,16 +155,16 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.updateRows( - '<DATABASE_ID>', - '<TABLE_ID>', - { +const result = await tablesDB.updateRows({ + databaseId: '<DATABASE_ID>', + tableId: '<TABLE_ID>', + data: { status: 'published' }, - [ + queries: [ sdk.Query.equal('status', 'draft') ] -); +}); ``` ```server-python @@ -242,10 +242,10 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.upsertRows( - '<DATABASE_ID>', - '<TABLE_ID>', - [ +const result = await tablesDB.upsertRows({ + databaseId: '<DATABASE_ID>', + tableId: '<TABLE_ID>', + rows: [ { $id: sdk.ID.unique(), name: 'New Row 1' @@ -255,7 +255,7 @@ const result = await tablesDB.upsertRows( name: 'New Row 2' } ] -); +}); ``` ```server-python @@ -342,13 +342,13 @@ const client = new sdk.Client() const tablesDB = new sdk.TablesDB(client); -const result = await tablesDB.deleteRows( - '<DATABASE_ID>', - '<TABLE_ID>', - [ +const result = await tablesDB.deleteRows({ + databaseId: '<DATABASE_ID>', + tableId: '<TABLE_ID>', + queries: [ sdk.Query.equal('status', 'archived') ] -); +}); ``` ```server-python diff --git a/src/routes/docs/products/databases/csv-exports/+page.markdoc b/src/routes/docs/products/databases/tablesdb/csv-exports/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/csv-exports/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/csv-exports/+page.markdoc diff --git a/src/routes/docs/products/databases/csv-imports/+page.markdoc b/src/routes/docs/products/databases/tablesdb/csv-imports/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/csv-imports/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/csv-imports/+page.markdoc diff --git a/src/routes/docs/products/databases/databases/+page.markdoc b/src/routes/docs/products/databases/tablesdb/databases/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/databases/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/databases/+page.markdoc diff --git a/src/routes/docs/products/databases/geo-queries/+page.markdoc b/src/routes/docs/products/databases/tablesdb/geo-queries/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/geo-queries/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/geo-queries/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/+layout.svelte b/src/routes/docs/products/databases/tablesdb/legacy/+layout.svelte similarity index 100% rename from src/routes/docs/products/databases/legacy/+layout.svelte rename to src/routes/docs/products/databases/tablesdb/legacy/+layout.svelte diff --git a/src/routes/docs/products/databases/legacy/+page.ts b/src/routes/docs/products/databases/tablesdb/legacy/+page.ts similarity index 100% rename from src/routes/docs/products/databases/legacy/+page.ts rename to src/routes/docs/products/databases/tablesdb/legacy/+page.ts diff --git a/src/routes/docs/products/databases/legacy/atomic-numeric-operations/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/atomic-numeric-operations/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/atomic-numeric-operations/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/atomic-numeric-operations/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/bulk-operations/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/bulk-operations/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/bulk-operations/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/bulk-operations/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/collections/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/collections/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/collections/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/collections/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/databases/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/databases/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/databases/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/databases/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/documents/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/documents/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/documents/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/documents/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/order/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/order/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/order/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/order/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/pagination/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/pagination/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/pagination/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/pagination/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/permissions/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/permissions/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/permissions/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/permissions/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/queries/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/queries/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/queries/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/queries/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/quick-start/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/quick-start/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/quick-start/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/quick-start/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/relationships/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/relationships/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/relationships/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/relationships/+page.markdoc diff --git a/src/routes/docs/products/databases/legacy/type-generation/+page.markdoc b/src/routes/docs/products/databases/tablesdb/legacy/type-generation/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/legacy/type-generation/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/legacy/type-generation/+page.markdoc diff --git a/src/routes/docs/products/databases/offline/+page.markdoc b/src/routes/docs/products/databases/tablesdb/offline/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/offline/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/offline/+page.markdoc diff --git a/src/routes/docs/products/databases/operators/+page.markdoc b/src/routes/docs/products/databases/tablesdb/operators/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/operators/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/operators/+page.markdoc diff --git a/src/routes/docs/products/databases/order/+page.markdoc b/src/routes/docs/products/databases/tablesdb/order/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/order/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/order/+page.markdoc diff --git a/src/routes/docs/products/databases/pagination/+page.markdoc b/src/routes/docs/products/databases/tablesdb/pagination/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/pagination/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/pagination/+page.markdoc diff --git a/src/routes/docs/products/databases/permissions/+page.markdoc b/src/routes/docs/products/databases/tablesdb/permissions/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/permissions/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/permissions/+page.markdoc diff --git a/src/routes/docs/products/databases/queries/+page.markdoc b/src/routes/docs/products/databases/tablesdb/queries/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/queries/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/queries/+page.markdoc diff --git a/src/routes/docs/products/databases/quick-start/+page.markdoc b/src/routes/docs/products/databases/tablesdb/quick-start/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/quick-start/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/quick-start/+page.markdoc diff --git a/src/routes/docs/products/databases/relationships/+page.markdoc b/src/routes/docs/products/databases/tablesdb/relationships/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/relationships/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/relationships/+page.markdoc diff --git a/src/routes/docs/products/databases/rows/+page.markdoc b/src/routes/docs/products/databases/tablesdb/rows/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/rows/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/rows/+page.markdoc diff --git a/src/routes/docs/products/databases/tables/+page.markdoc b/src/routes/docs/products/databases/tablesdb/tables/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/tables/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/tables/+page.markdoc diff --git a/src/routes/docs/products/databases/timestamp-overrides/+page.markdoc b/src/routes/docs/products/databases/tablesdb/timestamp-overrides/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/timestamp-overrides/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/timestamp-overrides/+page.markdoc diff --git a/src/routes/docs/products/databases/transactions/+page.markdoc b/src/routes/docs/products/databases/tablesdb/transactions/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/transactions/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/transactions/+page.markdoc diff --git a/src/routes/docs/products/databases/type-generation/+page.markdoc b/src/routes/docs/products/databases/tablesdb/type-generation/+page.markdoc similarity index 100% rename from src/routes/docs/products/databases/type-generation/+page.markdoc rename to src/routes/docs/products/databases/tablesdb/type-generation/+page.markdoc diff --git a/src/routes/docs/products/databases/vectorsdb/+layout.svelte b/src/routes/docs/products/databases/vectorsdb/+layout.svelte new file mode 100644 index 00000000000..bd502da28b6 --- /dev/null +++ b/src/routes/docs/products/databases/vectorsdb/+layout.svelte @@ -0,0 +1,29 @@ +<script lang="ts"> + import Docs from '$lib/layouts/Docs.svelte'; + import Sidebar, { type NavParent, type NavTree } from '$lib/layouts/Sidebar.svelte'; + + let { children } = $props(); + + const parent: NavParent = { + href: '/docs/products/databases', + label: 'Databases' + }; + + const navigation: NavTree = [ + { + label: 'Getting started', + items: [ + { + label: 'Overview', + href: '/docs/products/databases/vectorsdb' + } + ] + } + ]; +</script> + +<Docs variant="two-side-navs"> + <Sidebar {navigation} {parent} /> + + {@render children()} +</Docs> diff --git a/src/routes/docs/products/databases/vectorsdb/+page.markdoc b/src/routes/docs/products/databases/vectorsdb/+page.markdoc new file mode 100644 index 00000000000..f0391bc33d3 --- /dev/null +++ b/src/routes/docs/products/databases/vectorsdb/+page.markdoc @@ -0,0 +1,11 @@ +--- +layout: article +title: VectorsDB +description: Store vector embeddings and run similarity search with Appwrite VectorsDB to power semantic search, recommendations, and other AI features. +--- + +Appwrite VectorsDB stores vector embeddings and runs similarity search, so you can build semantic search, recommendations, and other AI features. It also exposes an endpoint to generate text embeddings, and shares the permissions, indexes, transactions, and realtime concepts used across Appwrite Databases. + +{% info title="Documentation in progress" %} +Detailed VectorsDB guides are on the way. For now, the concepts in [TablesDB](/docs/products/databases/tablesdb) are the closest reference. +{% /info %} diff --git a/static/images/docs/databases/documentsdb/backup-policy.avif b/static/images/docs/databases/documentsdb/backup-policy.avif new file mode 100644 index 00000000000..b44ed9afa6b Binary files /dev/null and b/static/images/docs/databases/documentsdb/backup-policy.avif differ diff --git a/static/images/docs/databases/documentsdb/backups-tab.avif b/static/images/docs/databases/documentsdb/backups-tab.avif new file mode 100644 index 00000000000..29aec532bc7 Binary files /dev/null and b/static/images/docs/databases/documentsdb/backups-tab.avif differ diff --git a/static/images/docs/databases/documentsdb/create-database.avif b/static/images/docs/databases/documentsdb/create-database.avif new file mode 100644 index 00000000000..4a201bf9554 Binary files /dev/null and b/static/images/docs/databases/documentsdb/create-database.avif differ diff --git a/static/images/docs/databases/documentsdb/dark/backup-policy.avif b/static/images/docs/databases/documentsdb/dark/backup-policy.avif new file mode 100644 index 00000000000..3e152f6732d Binary files /dev/null and b/static/images/docs/databases/documentsdb/dark/backup-policy.avif differ diff --git a/static/images/docs/databases/documentsdb/dark/backups-tab.avif b/static/images/docs/databases/documentsdb/dark/backups-tab.avif new file mode 100644 index 00000000000..b975ab60a2a Binary files /dev/null and b/static/images/docs/databases/documentsdb/dark/backups-tab.avif differ diff --git a/static/images/docs/databases/documentsdb/dark/create-database.avif b/static/images/docs/databases/documentsdb/dark/create-database.avif new file mode 100644 index 00000000000..8cfe52aa5ed Binary files /dev/null and b/static/images/docs/databases/documentsdb/dark/create-database.avif differ diff --git a/static/images/docs/databases/documentsdb/dark/manual-backup.avif b/static/images/docs/databases/documentsdb/dark/manual-backup.avif new file mode 100644 index 00000000000..091f73911ed Binary files /dev/null and b/static/images/docs/databases/documentsdb/dark/manual-backup.avif differ diff --git a/static/images/docs/databases/documentsdb/manual-backup.avif b/static/images/docs/databases/documentsdb/manual-backup.avif new file mode 100644 index 00000000000..0dc1a93e6db Binary files /dev/null and b/static/images/docs/databases/documentsdb/manual-backup.avif differ