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.

Tokens
59.6K
Snippets
413
Records
499
Agent score
57%

What's inside Ottoman

  1. Overview of Ottoman ODM for Couchbase

    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.
  2. 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.
  3. Initialize a project using the Ottoman CLI

    Use the ottoman-cli to 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 generate
  4. Understand the relationship between Documents and Models in Ottoman

    In Ottoman, a Document represents a one-to-one mapping to a document stored in Couchbase Server. A Model is a subclass of Document. 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; // true
  5. Understand the relationship between Documents and Models in Ottoman

    In Ottoman, a Document is a one-to-one mapping to a document stored in Couchbase Server. A Model is a subclass of Document. When you create a new instance of a Model, you are creating a new document. You do not need to instantiate the Document class 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; // true
  6. Define relationships between schemas using ReferenceType

    The Reference type 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);
  7. CoreType class overview

    The CoreType class is the base implementation for schema types in OttomanJS. It extends IOttomanType and is inherited by specific type implementations including StringType, NumberType, BooleanType, DateType, ArrayType, EmbedType, ReferenceType, and MixedType.
  8. Perform multi-document ACID transactions in Ottoman

    To perform a transaction in Ottoman (version 2.5.0+), use the $transactions method. 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: ctx option. 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
    }
  9. Define a data model and persist documents using Ottoman

    Use ottoman.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 call ottoman.connect() with your connection details and ottoman.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();
  10. 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 a populate option 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);
  11. Manage concurrent operations in transactions

    Ottoman allows concurrent operations within a transaction to improve performance, provided two rules are followed:

    1. The first mutation must be performed serially (alone) to trigger the creation of transaction metadata.
    2. 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.
  12. Manage multiple Ottoman instances for different databases

    When an application needs to connect to multiple databases, you must instantiate the Ottoman class 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 of model()).
    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();