UNPKG

s3db.js

Version:

Use AWS S3, the world's most reliable document storage, as a database with this ORM.

111 lines (99 loc) 4.03 kB
import { calculateTotalSize } from '../concerns/calculator.js'; import { tryFn, tryFnSync } from '../concerns/try-fn.js'; /** * Body Only Behavior Configuration Documentation * * The `body-only` behavior stores all data in the S3 object body as JSON, keeping only * the version field (`_v`) in metadata. This allows for unlimited data size since S3 * objects can be up to 5TB, but requires reading the full object body for any operation. * * ## Purpose & Use Cases * - For large objects that exceed S3 metadata limits * - When you need to store complex nested data structures * - For objects that will be read infrequently (higher latency) * - When you want to avoid metadata size constraints entirely * * ## How It Works * - Keeps only the `_v` (version) field in S3 metadata * - Serializes all other data as JSON in the object body * - Requires full object read for any data access * - No size limits on data (only S3 object size limit of 5TB) * * ## Performance Considerations * - Higher latency for read operations (requires full object download) * - Higher bandwidth usage for read operations * - No metadata-based filtering or querying possible * - Best for large, infrequently accessed data * * @example * // Create a resource with body-only behavior * const resource = await db.createResource({ * name: 'large_documents', * attributes: { ... }, * behavior: 'body-only' * }); * * // All data goes to body, only _v stays in metadata * const doc = await resource.insert({ * title: 'Large Document', * content: 'Very long content...', * metadata: { ... } * }); * * ## Comparison to Other Behaviors * | Behavior | Metadata Usage | Body Usage | Size Limits | Performance | * |------------------|----------------|------------|-------------|-------------| * | body-only | Minimal (_v) | All data | 5TB | Slower reads | * | body-overflow | Optimized | Overflow | 2KB metadata | Balanced | * | truncate-data | All (truncated)| None | 2KB metadata | Fast reads | * | enforce-limits | All (limited) | None | 2KB metadata | Fast reads | * | user-managed | All (unlimited)| None | S3 limit | Fast reads | * * @typedef {Object} BodyOnlyBehaviorConfig * @property {boolean} [enabled=true] - Whether the behavior is active */ export async function handleInsert({ resource, data, mappedData }) { // Keep only the version field in metadata const metadataOnly = { '_v': mappedData._v || String(resource.version) }; metadataOnly._map = JSON.stringify(resource.schema.map); // Use the original object for the body const body = JSON.stringify(mappedData); return { mappedData: metadataOnly, body }; } export async function handleUpdate({ resource, id, data, mappedData }) { // For updates, we need to merge with existing data // Since we can't easily read the existing body during update, // we'll put the update data in the body and let the resource handle merging // Keep only the version field in metadata const metadataOnly = { '_v': mappedData._v || String(resource.version) }; metadataOnly._map = JSON.stringify(resource.schema.map); // Use the original object for the body const body = JSON.stringify(mappedData); return { mappedData: metadataOnly, body }; } export async function handleUpsert({ resource, id, data, mappedData }) { // Same as insert for body-only behavior return handleInsert({ resource, data, mappedData }); } export async function handleGet({ resource, metadata, body }) { // Parse the body to get the actual data let bodyData = {}; if (body && body.trim() !== '') { const [ok, err, parsed] = tryFnSync(() => JSON.parse(body)); if (ok) { bodyData = parsed; } else { bodyData = {}; } } // Merge metadata (which contains _v) with body data const mergedData = { ...bodyData, ...metadata // metadata contains _v }; return { metadata: mergedData, body }; }