Ottoman ODM for Couchbase
website·Indexed 32 days ago
https://ottomanjs.com/An Object Data Modeler (ODM) for the Couchbase Node.js SDK that provides JSON schema and validation for NoSQL. It allows developers to define schemas, compile models, manage document instances, and construct complex N1QL queries using a built-in Query Builder.
What's inside Ottoman
- Ottoman is an Object Document Mapper (ODM) for Couchbase that leverages the Couchbase Node.js SDK 3.x. It allows developers to define the structure of documents using Schemas and Models, providing a way to map database documents to application objects. Key benefits include schema validation, quality control for JSON data, and abstraction of complexities found in the official Node.js SDK.
Overview of Ottoman ODM for Couchbase
Ottoman is an Object Data Modeler (ODM) for the Couchbase Node.js SDK. It provides JSON schema definition and validation for NoSQL documents, allowing developers to enforce data structures and business logic at the application level before persisting data to Couchbase. It is conceptually similar to Mongoose for MongoDB.Initialize a project using the Ottoman CLI
Use theottoman-clito quickly generate and spin up a new project. This tool leverages Couchbase as the underlying JSON database, providing a memory-first architecture for high-performance read and write operations while combining the benefits of NoSQL document databases and relational databases.$ npx ottoman-cli generateUnderstand the relationship between Documents and Models in Ottoman
In Ottoman, aDocumentrepresents a one-to-one mapping to a document stored in Couchbase Server. AModelis a subclass ofDocument. When you use a Model constructor, you create a new document instance. In practice, a 'document' in Ottoman refers to an instance of a model.const MyModel = ottoman.model('Test', new Schema({ name: String })); const doc = new MyModel(); doc instanceof MyModel; // true doc instanceof ottoman.Model; // true doc instanceof ottoman.Document; // trueUnderstand the relationship between Documents and Models in Ottoman
In Ottoman, aDocumentis a one-to-one mapping to a document stored in Couchbase Server. AModelis a subclass ofDocument. When you create a new instance of a Model, you are creating a new document. You do not need to instantiate theDocumentclass directly; instead, use the Model constructor.const MyModel = ottoman.model('Test', new Schema({ name: String })); const doc = new MyModel(); doc instanceof MyModel; // true doc instanceof ottoman.Model; // true doc instanceof ottoman.Document; // trueDefine relationships between schemas using ReferenceType
TheReferencetype in Ottoman is used to create a relationship between two schemas. This allows a field in one model to reference a document in another model.const Cat = model('Cat', {name: String}); const schema = new Schema({ type: String, isActive: Boolean, name: String, cats: [{ type: CatSchema, ref: 'Cat' }], }); const User = model('User', schema);CoreType class overview
TheCoreTypeclass is the base implementation for schema types in OttomanJS. It extendsIOttomanTypeand is inherited by specific type implementations includingStringType,NumberType,BooleanType,DateType,ArrayType,EmbedType,ReferenceType, andMixedType.Perform multi-document ACID transactions in Ottoman
To perform a transaction in Ottoman (version 2.5.0+), use the
$transactionsmethod. You must provide an arrow function containing the transaction logic. The transaction automatically commits if the function completes successfully; otherwise, it rolls back and may retry.Crucially, every operation inside the transaction must include the
transactionContext: ctxoption. Omitting this context will result in operations being performed non-transactionally, which can lead to unexpected results and side effects since the transaction function may be executed multiple times.const schema = new Schema({ name: String, age: Number }); const Swan = model('Swan', schema); await start(); try { await ottoman.$transactions(async (ctx: TransactionAttemptContext) => { const odette = new Swan({ name: 'Odette', age: 30 }); // Pass the transaction context to the save method await odette.save(false, { transactionContext: ctx }); // Use the context to find the document within the same transaction const doc = await Swan.findById(odette.id, { transactionContext: ctx }); console.log(doc); }); } catch (error) { // Handle transaction errors }Define a data model and persist documents using Ottoman
Useottoman.model()to define a schema for your documents. You can then instantiate this model to create new documents and call the.save()method to persist them to the Couchbase bucket. Before performing these operations, you must callottoman.connect()with your connection details andottoman.start()to initialize the connection.const ottoman = require('ottoman'); // Define the model const User = ottoman.model('User', { firstName: String, lastName: String, email: String, tagline: String }); const main = async () => { // Connect to Couchbase await ottoman.connect({ connectionString: 'couchbase://localhost', bucketName: 'default', username: 'Administrator', password: 'password' }); await ottoman.start(); // Create and save a document const perry = new User({ firstName: 'Perry', lastName: 'Mason', email: 'perry.mason@example.com', tagLine : 'Who can we get on the case?' }); await perry.save(); }; main();Populate references in Ottoman documents
Population replaces reference IDs in a document with the actual documents they point to. This can be done on an existing document instance using_populate()or during a query by passing apopulateoption to the Model method.// 1. Populate using a document instance const story = await Story.findById('storyId'); await story._populate('author'); console.log(story.author.name); // 2. Populate using a Model query const stories = await Story.find({ title: 'Casino Royale' }, { populate: 'author' }); const story = stories[0]; console.log(story.author.name);Manage concurrent operations in transactions
Ottoman allows concurrent operations within a transaction to improve performance, provided two rules are followed:
- The first mutation must be performed serially (alone) to trigger the creation of transaction metadata.
- All concurrent operations must be allowed to complete. If an error occurs during a concurrent operation, the application should record the error and throw it at the end of the concurrent block to trigger a transaction retry.
Manage multiple Ottoman instances for different databases
When an application needs to connect to multiple databases, you must instantiate theOttomanclass multiple times. To ensure models and configurations are registered to the correct database, avoid standalone functions and instead call methods directly on the specific instance (e.g.,instance.model()instead ofmodel()).import { Ottoman } from 'ottoman'; const main = async () => { const ottoman1 = new Ottoman(); await ottoman1.connect('couchbase://localhost/travel-sample@admin:password'); const ottoman2 = new Ottoman(); await ottoman2.connect('couchbase://localhost/other-bucket@admin:password'); // Explicitly register models to specific instances const UserModel = ottoman1.model('User', { name: String }); const CatModel = ottoman2.model('Cat', { age: Number }); } main();