Community edition generated from private repo via sync pipeline. Includes 9 plugins (WordPress, WooCommerce, WP Advanced, Gitea, n8n, Supabase, OpenPanel, Appwrite, Directus) with ~587 tools. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
753 lines
29 KiB
Python
753 lines
29 KiB
Python
"""
|
|
Documents Handler - manages Appwrite document CRUD operations
|
|
|
|
Phase I.1: 12 tools
|
|
- Documents CRUD: 5 (list, get, create, update, delete)
|
|
- Bulk Operations: 3 (bulk_create, bulk_update, bulk_delete)
|
|
- Query/Count: 4 (search, count, get_by_query, list_with_cursor)
|
|
"""
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
from plugins.appwrite.client import AppwriteClient
|
|
|
|
# =====================
|
|
# QUERY HELPERS (Appwrite 1.7.4 JSON format)
|
|
# =====================
|
|
|
|
def _query_limit(value: int) -> str:
|
|
"""Build limit query in JSON format."""
|
|
return json.dumps({"method": "limit", "values": [value]})
|
|
|
|
def _query_offset(value: int) -> str:
|
|
"""Build offset query in JSON format."""
|
|
return json.dumps({"method": "offset", "values": [value]})
|
|
|
|
def _query_order_asc(attribute: str) -> str:
|
|
"""Build orderAsc query in JSON format."""
|
|
return json.dumps({"method": "orderAsc", "values": [attribute]})
|
|
|
|
def _query_order_desc(attribute: str) -> str:
|
|
"""Build orderDesc query in JSON format."""
|
|
return json.dumps({"method": "orderDesc", "values": [attribute]})
|
|
|
|
def _query_cursor_after(document_id: str) -> str:
|
|
"""Build cursorAfter query in JSON format."""
|
|
return json.dumps({"method": "cursorAfter", "values": [document_id]})
|
|
|
|
def _query_cursor_before(document_id: str) -> str:
|
|
"""Build cursorBefore query in JSON format."""
|
|
return json.dumps({"method": "cursorBefore", "values": [document_id]})
|
|
|
|
def _query_search(attribute: str, value: str) -> str:
|
|
"""Build search query in JSON format."""
|
|
return json.dumps({"method": "search", "attribute": attribute, "values": [value]})
|
|
|
|
def _query_equal(attribute: str, values: list[Any]) -> str:
|
|
"""Build equal query in JSON format."""
|
|
return json.dumps({"method": "equal", "attribute": attribute, "values": values})
|
|
|
|
def get_tool_specifications() -> list[dict[str, Any]]:
|
|
"""Return tool specifications for ToolGenerator (12 tools)"""
|
|
return [
|
|
# =====================
|
|
# DOCUMENT CRUD (5)
|
|
# =====================
|
|
{
|
|
"name": "list_documents",
|
|
"method_name": "list_documents",
|
|
"description": """List documents in a collection with powerful query support.
|
|
|
|
Queries must be JSON objects (Appwrite 1.7.4 format):
|
|
- {"method": "equal", "attribute": "status", "values": ["active"]}
|
|
- {"method": "notEqual", "attribute": "type", "values": ["draft"]}
|
|
- {"method": "greaterThan", "attribute": "price", "values": [100]}
|
|
- {"method": "search", "attribute": "title", "values": ["keyword"]}
|
|
- {"method": "orderAsc", "values": ["createdAt"]}
|
|
- {"method": "orderDesc", "values": ["createdAt"]}
|
|
- {"method": "limit", "values": [25]}
|
|
- {"method": "offset", "values": [50]}
|
|
- {"method": "cursorAfter", "values": ["documentId"]}""",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"queries": {
|
|
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
|
"description": "Query JSON strings for filtering, sorting, and pagination",
|
|
"examples": [
|
|
[
|
|
'{"method":"equal","attribute":"status","values":["active"]}',
|
|
'{"method":"limit","values":[25]}',
|
|
]
|
|
],
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id"],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "get_document",
|
|
"method_name": "get_document",
|
|
"description": "Get a single document by ID.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"document_id": {"type": "string", "description": "Document ID"},
|
|
"queries": {
|
|
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
|
"description": "Optional queries for selecting specific attributes",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id", "document_id"],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "create_document",
|
|
"method_name": "create_document",
|
|
"description": "Create a new document. Use 'unique()' as document_id for auto-generated ID.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"document_id": {
|
|
"type": "string",
|
|
"description": "Unique document ID. Use 'unique()' for auto-generation",
|
|
},
|
|
"data": {
|
|
"type": "object",
|
|
"description": "Document data (key-value pairs matching collection attributes)",
|
|
},
|
|
"permissions": {
|
|
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
|
"description": "Document permissions (e.g., 'read(\"user:123\")', 'write(\"team:456\")')",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id", "document_id", "data"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
{
|
|
"name": "update_document",
|
|
"method_name": "update_document",
|
|
"description": "Update an existing document. Only provided fields will be updated.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"document_id": {"type": "string", "description": "Document ID"},
|
|
"data": {
|
|
"anyOf": [{"type": "object"}, {"type": "null"}],
|
|
"description": "Fields to update",
|
|
},
|
|
"permissions": {
|
|
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
|
"description": "New permissions (replaces existing)",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id", "document_id"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
{
|
|
"name": "delete_document",
|
|
"method_name": "delete_document",
|
|
"description": "Delete a document by ID.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"document_id": {"type": "string", "description": "Document ID to delete"},
|
|
},
|
|
"required": ["database_id", "collection_id", "document_id"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
# =====================
|
|
# BULK OPERATIONS (3)
|
|
# =====================
|
|
{
|
|
"name": "bulk_create_documents",
|
|
"method_name": "bulk_create_documents",
|
|
"description": """Create multiple documents at once. More efficient than creating one by one.
|
|
|
|
Example documents array:
|
|
[
|
|
{"document_id": "unique()", "data": {"title": "Doc 1", "status": "active"}},
|
|
{"document_id": "doc-2", "data": {"title": "Doc 2", "status": "draft"}, "permissions": ["read(\\"any\\")"]}
|
|
]
|
|
|
|
Each document must have:
|
|
- document_id: Use 'unique()' for auto-generated ID or provide your own
|
|
- data: Object with field values matching collection attributes""",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"documents": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"document_id": {
|
|
"type": "string",
|
|
"description": "Document ID. Use 'unique()' for auto-generation",
|
|
},
|
|
"data": {
|
|
"type": "object",
|
|
"description": "Document data (key-value pairs matching collection attributes)",
|
|
},
|
|
"permissions": {
|
|
"anyOf": [
|
|
{"type": "array", "items": {"type": "string"}},
|
|
{"type": "null"},
|
|
],
|
|
"description": "Optional permissions for this document",
|
|
},
|
|
},
|
|
"required": ["document_id", "data"],
|
|
},
|
|
"description": "Array of documents to create. Each must have document_id and data.",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id", "documents"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
{
|
|
"name": "bulk_update_documents",
|
|
"method_name": "bulk_update_documents",
|
|
"description": "Update multiple documents at once.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"updates": {
|
|
"type": "array",
|
|
"items": {
|
|
"type": "object",
|
|
"properties": {
|
|
"document_id": {"type": "string"},
|
|
"data": {"type": "object"},
|
|
"permissions": {
|
|
"anyOf": [
|
|
{"type": "array", "items": {"type": "string"}},
|
|
{"type": "null"},
|
|
]
|
|
},
|
|
},
|
|
"required": ["document_id", "data"],
|
|
},
|
|
"description": "Array of document updates",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id", "updates"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
{
|
|
"name": "bulk_delete_documents",
|
|
"method_name": "bulk_delete_documents",
|
|
"description": "Delete multiple documents at once.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"document_ids": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
"description": "Array of document IDs to delete",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id", "document_ids"],
|
|
},
|
|
"scope": "write",
|
|
},
|
|
# =====================
|
|
# QUERY/SEARCH (4)
|
|
# =====================
|
|
{
|
|
"name": "search_documents",
|
|
"method_name": "search_documents",
|
|
"description": "Full-text search in documents. Requires a fulltext index on the searched attribute.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"attribute": {
|
|
"type": "string",
|
|
"description": "Attribute name to search in (must have fulltext index)",
|
|
},
|
|
"query": {"type": "string", "description": "Search query string"},
|
|
"limit": {"type": "integer", "description": "Maximum results", "default": 25},
|
|
},
|
|
"required": ["database_id", "collection_id", "attribute", "query"],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "count_documents",
|
|
"method_name": "count_documents",
|
|
"description": "Count documents in a collection with optional filters.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"queries": {
|
|
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
|
"description": "Optional filter queries",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id"],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "get_documents_by_ids",
|
|
"method_name": "get_documents_by_ids",
|
|
"description": "Get multiple documents by their IDs.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"document_ids": {
|
|
"type": "array",
|
|
"items": {"type": "string"},
|
|
"description": "Array of document IDs to fetch",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id", "document_ids"],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
{
|
|
"name": "list_documents_paginated",
|
|
"method_name": "list_documents_paginated",
|
|
"description": "List documents with cursor-based pagination for efficient large dataset traversal.",
|
|
"schema": {
|
|
"type": "object",
|
|
"properties": {
|
|
"database_id": {"type": "string", "description": "Database ID"},
|
|
"collection_id": {"type": "string", "description": "Collection ID"},
|
|
"limit": {
|
|
"type": "integer",
|
|
"description": "Results per page",
|
|
"default": 25,
|
|
"maximum": 100,
|
|
},
|
|
"cursor": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Cursor from previous response for next page",
|
|
},
|
|
"cursor_direction": {
|
|
"type": "string",
|
|
"enum": ["after", "before"],
|
|
"description": "Cursor direction",
|
|
"default": "after",
|
|
},
|
|
"order_attribute": {
|
|
"anyOf": [{"type": "string"}, {"type": "null"}],
|
|
"description": "Attribute to order by (default: $id)",
|
|
},
|
|
"order_type": {
|
|
"type": "string",
|
|
"enum": ["ASC", "DESC"],
|
|
"description": "Sort order",
|
|
"default": "DESC",
|
|
},
|
|
"filters": {
|
|
"anyOf": [{"type": "array", "items": {"type": "string"}}, {"type": "null"}],
|
|
"description": "Additional filter queries",
|
|
},
|
|
},
|
|
"required": ["database_id", "collection_id"],
|
|
},
|
|
"scope": "read",
|
|
},
|
|
]
|
|
|
|
# =====================
|
|
# HANDLER FUNCTIONS
|
|
# =====================
|
|
|
|
async def list_documents(
|
|
client: AppwriteClient, database_id: str, collection_id: str, queries: list[str] | None = None
|
|
) -> str:
|
|
"""List documents with queries."""
|
|
try:
|
|
result = await client.list_documents(
|
|
database_id=database_id, collection_id=collection_id, queries=queries
|
|
)
|
|
documents = result.get("documents", [])
|
|
|
|
response = {
|
|
"success": True,
|
|
"database_id": database_id,
|
|
"collection_id": collection_id,
|
|
"total": result.get("total", len(documents)),
|
|
"documents": documents,
|
|
}
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def get_document(
|
|
client: AppwriteClient,
|
|
database_id: str,
|
|
collection_id: str,
|
|
document_id: str,
|
|
queries: list[str] | None = None,
|
|
) -> str:
|
|
"""Get document by ID."""
|
|
try:
|
|
result = await client.get_document(
|
|
database_id=database_id,
|
|
collection_id=collection_id,
|
|
document_id=document_id,
|
|
queries=queries,
|
|
)
|
|
return json.dumps({"success": True, "document": result}, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def create_document(
|
|
client: AppwriteClient,
|
|
database_id: str,
|
|
collection_id: str,
|
|
document_id: str,
|
|
data: dict[str, Any],
|
|
permissions: list[str] | None = None,
|
|
) -> str:
|
|
"""Create a new document."""
|
|
try:
|
|
result = await client.create_document(
|
|
database_id=database_id,
|
|
collection_id=collection_id,
|
|
document_id=document_id,
|
|
data=data,
|
|
permissions=permissions,
|
|
)
|
|
return json.dumps(
|
|
{"success": True, "message": "Document created successfully", "document": result},
|
|
indent=2,
|
|
ensure_ascii=False,
|
|
)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def update_document(
|
|
client: AppwriteClient,
|
|
database_id: str,
|
|
collection_id: str,
|
|
document_id: str,
|
|
data: dict[str, Any] | None = None,
|
|
permissions: list[str] | None = None,
|
|
) -> str:
|
|
"""Update document."""
|
|
try:
|
|
result = await client.update_document(
|
|
database_id=database_id,
|
|
collection_id=collection_id,
|
|
document_id=document_id,
|
|
data=data,
|
|
permissions=permissions,
|
|
)
|
|
return json.dumps(
|
|
{"success": True, "message": "Document updated successfully", "document": result},
|
|
indent=2,
|
|
ensure_ascii=False,
|
|
)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def delete_document(
|
|
client: AppwriteClient, database_id: str, collection_id: str, document_id: str
|
|
) -> str:
|
|
"""Delete document."""
|
|
try:
|
|
await client.delete_document(database_id, collection_id, document_id)
|
|
return json.dumps(
|
|
{"success": True, "message": f"Document '{document_id}' deleted successfully"}, indent=2
|
|
)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def bulk_create_documents(
|
|
client: AppwriteClient, database_id: str, collection_id: str, documents: list[dict[str, Any]]
|
|
) -> str:
|
|
"""Create multiple documents."""
|
|
try:
|
|
results = []
|
|
errors = []
|
|
|
|
for doc in documents:
|
|
try:
|
|
result = await client.create_document(
|
|
database_id=database_id,
|
|
collection_id=collection_id,
|
|
document_id=doc.get("document_id", "unique()"),
|
|
data=doc["data"],
|
|
permissions=doc.get("permissions"),
|
|
)
|
|
results.append({"id": result.get("$id"), "success": True})
|
|
except Exception as e:
|
|
errors.append({"document_id": doc.get("document_id"), "error": str(e)})
|
|
|
|
response = {
|
|
"success": len(errors) == 0,
|
|
"created": len(results),
|
|
"failed": len(errors),
|
|
"results": results,
|
|
}
|
|
if errors:
|
|
response["errors"] = errors
|
|
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def bulk_update_documents(
|
|
client: AppwriteClient, database_id: str, collection_id: str, updates: list[dict[str, Any]]
|
|
) -> str:
|
|
"""Update multiple documents."""
|
|
try:
|
|
results = []
|
|
errors = []
|
|
|
|
for update in updates:
|
|
try:
|
|
result = await client.update_document(
|
|
database_id=database_id,
|
|
collection_id=collection_id,
|
|
document_id=update["document_id"],
|
|
data=update.get("data"),
|
|
permissions=update.get("permissions"),
|
|
)
|
|
results.append({"id": result.get("$id"), "success": True})
|
|
except Exception as e:
|
|
errors.append({"document_id": update["document_id"], "error": str(e)})
|
|
|
|
response = {
|
|
"success": len(errors) == 0,
|
|
"updated": len(results),
|
|
"failed": len(errors),
|
|
"results": results,
|
|
}
|
|
if errors:
|
|
response["errors"] = errors
|
|
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def bulk_delete_documents(
|
|
client: AppwriteClient, database_id: str, collection_id: str, document_ids: list[str]
|
|
) -> str:
|
|
"""Delete multiple documents."""
|
|
try:
|
|
results = []
|
|
errors = []
|
|
|
|
for doc_id in document_ids:
|
|
try:
|
|
await client.delete_document(database_id, collection_id, doc_id)
|
|
results.append({"id": doc_id, "success": True})
|
|
except Exception as e:
|
|
errors.append({"document_id": doc_id, "error": str(e)})
|
|
|
|
response = {
|
|
"success": len(errors) == 0,
|
|
"deleted": len(results),
|
|
"failed": len(errors),
|
|
"results": results,
|
|
}
|
|
if errors:
|
|
response["errors"] = errors
|
|
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def search_documents(
|
|
client: AppwriteClient,
|
|
database_id: str,
|
|
collection_id: str,
|
|
attribute: str,
|
|
query: str,
|
|
limit: int = 25,
|
|
) -> str:
|
|
"""Full-text search in documents."""
|
|
try:
|
|
# Appwrite 1.7.4 JSON format for queries
|
|
queries = [_query_search(attribute, query), _query_limit(limit)]
|
|
result = await client.list_documents(
|
|
database_id=database_id, collection_id=collection_id, queries=queries
|
|
)
|
|
documents = result.get("documents", [])
|
|
|
|
response = {
|
|
"success": True,
|
|
"query": query,
|
|
"attribute": attribute,
|
|
"total": result.get("total", len(documents)),
|
|
"documents": documents,
|
|
}
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
error_msg = str(e)
|
|
if "fulltext" in error_msg.lower() or "index" in error_msg.lower():
|
|
error_msg += " (Hint: Make sure you have a fulltext index on the searched attribute)"
|
|
return json.dumps({"success": False, "error": error_msg}, indent=2)
|
|
|
|
async def count_documents(
|
|
client: AppwriteClient, database_id: str, collection_id: str, queries: list[str] | None = None
|
|
) -> str:
|
|
"""Count documents in collection."""
|
|
try:
|
|
# Build query list: start with user filters, add limit(1) to minimize data
|
|
count_queries = []
|
|
|
|
# Add user-provided filter queries (make a copy to avoid mutating input)
|
|
if queries and len(queries) > 0:
|
|
count_queries.extend(queries)
|
|
|
|
# Always add limit(1) to minimize data transfer - Appwrite 1.7.4 JSON format
|
|
count_queries.append(_query_limit(1))
|
|
|
|
result = await client.list_documents(
|
|
database_id=database_id, collection_id=collection_id, queries=count_queries
|
|
)
|
|
|
|
response = {
|
|
"success": True,
|
|
"database_id": database_id,
|
|
"collection_id": collection_id,
|
|
"count": result.get("total", 0),
|
|
}
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def get_documents_by_ids(
|
|
client: AppwriteClient, database_id: str, collection_id: str, document_ids: list[str]
|
|
) -> str:
|
|
"""Get multiple documents by IDs."""
|
|
try:
|
|
documents = []
|
|
errors = []
|
|
|
|
for doc_id in document_ids:
|
|
try:
|
|
doc = await client.get_document(
|
|
database_id=database_id, collection_id=collection_id, document_id=doc_id
|
|
)
|
|
documents.append(doc)
|
|
except Exception as e:
|
|
errors.append({"document_id": doc_id, "error": str(e)})
|
|
|
|
response = {
|
|
"success": len(errors) == 0,
|
|
"found": len(documents),
|
|
"not_found": len(errors),
|
|
"documents": documents,
|
|
}
|
|
if errors:
|
|
response["errors"] = errors
|
|
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|
|
|
|
async def list_documents_paginated(
|
|
client: AppwriteClient,
|
|
database_id: str,
|
|
collection_id: str,
|
|
limit: int = 25,
|
|
cursor: str | None = None,
|
|
cursor_direction: str = "after",
|
|
order_attribute: str | None = None,
|
|
order_type: str = "DESC",
|
|
filters: list[str] | None = None,
|
|
) -> str:
|
|
"""List documents with cursor pagination."""
|
|
try:
|
|
# Build query list (don't mutate input) - Appwrite 1.7.4 JSON format
|
|
queries = []
|
|
|
|
# Add user-provided filter queries
|
|
if filters and len(filters) > 0:
|
|
queries.extend(filters)
|
|
|
|
# Add ordering only if explicitly specified
|
|
if order_attribute:
|
|
if order_type == "DESC":
|
|
queries.append(_query_order_desc(order_attribute))
|
|
else:
|
|
queries.append(_query_order_asc(order_attribute))
|
|
|
|
# Add cursor
|
|
if cursor:
|
|
if cursor_direction == "after":
|
|
queries.append(_query_cursor_after(cursor))
|
|
else:
|
|
queries.append(_query_cursor_before(cursor))
|
|
|
|
# Add limit only if queries exist or limit differs from default
|
|
if queries or limit != 25:
|
|
queries.append(_query_limit(min(limit, 100)))
|
|
|
|
result = await client.list_documents(
|
|
database_id=database_id,
|
|
collection_id=collection_id,
|
|
queries=queries if queries else None,
|
|
)
|
|
documents = result.get("documents", [])
|
|
|
|
# Determine next cursor
|
|
next_cursor = None
|
|
if documents and len(documents) == limit:
|
|
next_cursor = documents[-1].get("$id")
|
|
|
|
response = {
|
|
"success": True,
|
|
"total": result.get("total", 0),
|
|
"count": len(documents),
|
|
"documents": documents,
|
|
"pagination": {
|
|
"limit": limit,
|
|
"cursor": cursor,
|
|
"next_cursor": next_cursor,
|
|
"has_more": next_cursor is not None,
|
|
},
|
|
}
|
|
return json.dumps(response, indent=2, ensure_ascii=False)
|
|
|
|
except Exception as e:
|
|
return json.dumps({"success": False, "error": str(e)}, indent=2)
|