A newer version of this documentation is available.

View Latest

Documents

    Description — Couchbase Lite concepts — Data model — Documents
    Related Content — Databases | Blobs | Indexing |

    Overview

    Document Structure

    In Couchbase Lite the term 'document' refers to an entry in the database. You can compare it to a record, or a row in a table.

    Each document has an ID or unique identifier. This ID is similar to a primary key in other databases.

    You can specify the ID programmatically. If you omit it, it will be automatically generated as a UUID.

    Couchbase documents are assigned to a Collection. The ID of a document must be unique within the Collection it is written to. You cannot change it after you have written the document.

    The document also has a value which contains the actual application data. This value is stored as a dictionary of key-value (k-v) pairs. The values can be made of up several different Data Types such as numbers, strings, arrays, and nested objects.

    Data Encoding

    The document body is stored in an internal, efficient, binary form called Fleece. This internal form can be easily converted into a manageable native dictionary format for manipulation in applications.

    Fleece data is stored in the smallest format that will hold the value whilst maintaining the integrity of the value.

    Data Types

    The Document class offers a set of property accessors for various scalar types, such as:

    • Boolean

    • Date

    • Double

    • Float

    • Int

    • Long

    • String

    These accessors take care of converting to/from JSON encoding, and make sure you get the type you expect.

    In addition to these basic data types Couchbase Lite provides for the following:

    Dictionary

    represents a read-only key-value pair collection

    MutableDictionary

    represents a writeable key-value pair collection

    Array

    represents a readonly ordered collection of objects

    MutableArray

    represents a writeable collection of objects

    Blob

    represents an arbitrary piece of binary data

    JSON

    Couchbase Lite also provides for the direct handling of JSON data implemented in most cases by the provision of a toJSON() method on appropriate API classes (for example, on MutableDocument, Dictionary, Blob and Array) — see Working with JSON Data.

    Constructing a Document

    An individual document often represents a single instance of an object in application code.

    You can consider a document as the equivalent of a 'row' in a relational table, with each of the document’s attributes being equivalent to a 'column'.

    Documents can contain nested structures. This allows developers to express many-to-many relationships without requiring a reference or join table, and is naturally expressive of hierarchical data.

    Most apps will work with one or more documents, persisting them to a local database and optionally syncing them, either centrally or to the cloud.

    In this section we provide an example of how you might create a hotel document, which provides basic contact details and price data.

    Data Model
    hotel: {
      type: string (value = `hotel`)
      name: string
      address: dictionary {
        street: string
        city: string
        state: string
        country: string
        code: string
      }
      phones: array
      rate: float
    }

    Open a Database

    First open your database. If the database does not already exist, Couchbase Lite will create it for you.

    Couchbase documents are assigned to a Collection. All the CRUD examples in this document operate on a collection object (here, the Default Collection).

    // Get the database (and create it if it doesn’t exist).
    
    NSError *error;
    CBLDatabase *database = [[CBLDatabase alloc] initWithName:@"hoteldb" error:&error];
    CBLCollection *collection = [database defaultCollection:&error];

    See Databases for more information

    Create a Document

    Now create a new document to hold your application’s data.

    Use the mutable form, so that you can add data to the document.

    // Create your new document
    // The lack of 'const' indicates this document is mutable
    CBLMutableDocument *mutableDoc = [[CBLMutableDocument alloc] init];

    For more on using Documents, see Document Initializers and Mutability.

    Create a Dictionary

    Now create a mutable dictionary (address).

    Each element of the dictionary value will be directly accessible via its own key.

    // Create and populate mutable dictionary
    // Create a new mutable dictionary and populate some keys/values
    CBLMutableDictionary *address = [[CBLMutableDictionary alloc] init];
    [address setString:@"1 Main st" forKey:@"street"];
    [address setString:@"San Francisco" forKey:@"city"];
    [address setString:@"CA" forKey:@"state"];
    [address setString:@"USA" forKey:@"country"];
    [address setString:@"90210" forKey:@"code"];

    Learn more about Using Dictionaries.

    Create an Array

    Since the hotel may have multiple contact numbers, provide a field (phones) as a mutable array.

    // Create and populate mutable array
    CBLMutableArray *phones = [[CBLMutableArray alloc] init];
    [phones addString:@"650-000-0000"];
    [phones addString:@"650-000-0001"];

    Learn more about Using Arrays

    Populate a Document

    Now add your data to the mutable document created earlier. Each data item is stored as a key-value pair.

    // Initialize and populate the document
    
    // Add document type and hotel name as string
    [mutableDoc setString:@"hotel" forKey:@"type"];
    [mutableDoc setString:@"Hotel Java Mo" forKey:@"name"];
    
    // Add average room rate (float)
    [mutableDoc setFloat:121.75 forKey:@"room_rate"];
    
    // Add address (dictionary)
    [mutableDoc setDictionary:address forKey:@"address"];
    
    // Add phone numbers(array)
    [mutableDoc setArray:phones forKey:@"phones"];
    Couchbase recommend using a type attribute to define each logical document type.

    Save a Document

    Now persist the populated document to your Couchbase Lite database. This will auto-generate the document id.

    [collection saveDocument:mutableDoc error:&error];

    Close the Database

    With your document saved, you can now close our Couchbase Lite database.

    if (![self.database close:&error])
        NSLog(@"Error closing db:%@", error);

    Working with Data

    Date accessors

    Couchbase Lite offers Date accessors as a convenience. Dates are a common data type, but JSON doesn’t natively support them, so the convention is to store them as strings in ISO-8601 format.

    Example 1. Date Getter

    This example sets the date on the createdAt property and reads it back using the dateForKey: accessor method.

    [doc setValue:[NSDate date] forKey:@"createdAt"];
    NSDate *date = [doc dateForKey:@"createdAt"];

    Using Dictionaries

    Example 2. Read Only
    CBLDocument *doc = [collection documentWithID:@"doc1" error:&error];
    
    // Getting a dictionary value from the document
    CBLDictionary *dict = [doc dictionaryForKey:@"address"];
    
    // Access a value from the dictionary
    NSString *street = [dict stringForKey:@"street"];
    NSLog(@"Street:: %@", street);
    
    // Iterate dictionary
    for (NSString *key in dict) {
        id value = [dict valueForKey:key];
        NSLog(@"Value:: %@", value);
    }
    
    // Create a mutable copy
    CBLMutableDictionary *mutableDict = [dict toMutable];
    [mutableDict setString:@"1 Great sts" forKey:@"street"];
    Example 3. Mutable
    
    // Create a new mutable dictionary and populate some keys/values
    CBLMutableDictionary *dict = [[CBLMutableDictionary alloc] init];
    [dict setString:@"1 Main st" forKey:@"street"];
    [dict setString:@"San Francisco" forKey:@"city"];
    
    // Set the dictionary to a document and save the document
    CBLMutableDocument *doc = [[CBLMutableDocument alloc] init];
    [doc setDictionary:dict forKey:@"address"];
    NSError *error;
    [collection saveDocument:doc error:&error];

    Using Arrays

    API References
    Example 4. Read Only
    NSError *error;
    CBLDocument *doc = [collection documentWithID:@"doc1" error:&error];
    
    // Getting an array value from the document
    CBLArray *array = [doc arrayForKey:@"phones"];
    
    // Get element count
    NSUInteger count = array.count;
    NSLog(@"Count:: %lu", (unsigned long)count);
    
    // Access an array element by index
    if (count > 0) {
        id value = [array valueAtIndex:0];
        NSLog(@"Value:: %@", value);
    }
    
    // Iterate the array
    for (id value in array) {
        NSLog(@"Value:: %@", value);
    }
    
    // Create a mutable copy
    CBLMutableArray *mutableArray = [array toMutable];
    [mutableArray addString:@"650-000-0002"];
    Example 5. Mutable
    // Create a new mutable array and populate data into the array
    CBLMutableArray *array = [[CBLMutableArray alloc] init];
    [array addString:@"650-000-0000"];
    [array addString:@"650-000-0001"];
    
    // Set the array to a document and save the document
    CBLMutableDocument *doc = [[CBLMutableDocument alloc] init];
    [doc setArray:array forKey:@"address"];
    NSError *error;
    [collection saveDocument:doc error:&error];

    Using Blobs

    For more on working with blobs, see Blobs

    Document Initializers

    You can use the following methods/initializers:

    • Use the (nonnull instancetype)init; initializer to create a new document where the document ID is randomly generated by the database.

    • Use the (nonnull instancetype)initWithID:(nullable NSString *)documentID; initializer to create a new document with a specific ID.

    • Use the {url-api-method-collection-getdocument} method to get a document. If the document doesn’t exist in the collection, the method will return null. You can use this behavior to check if a document with a given ID already exists in the collection.

    Example 6. Persist a document

    The following code example creates a document and persists it to the database.

    CBLMutableDocument *doc = [[CBLMutableDocument alloc] init];
    [doc setString:@"task" forKey:@"task"];
    [doc setString:@"todo" forKey:@"owner"];
    [doc setString:@"task" forKey:@"createdAt"];
    [collection saveDocument:doc error:&error];

    Mutability

    By default, a document is immutable when it is read from the database. Use the (nonnull CBLMutableDocument *)toMutable; to create an updatable instance of the document.

    Example 7. Make a mutable document

    Changes to the document are persisted to the database when the save method is called.

    CBLDocument *doc = [collection documentWithID:@"xyz" error:&error];
    CBLMutableDocument *mutableDocument = [doc toMutable];
    [mutableDocument setString:@"apples" forKey:@"name"];
    [collection saveDocument:mutableDocument error:&error];
    Any user change to the value of reserved keys (_id, _rev or _deleted) will be detected when a document is saved and will result in an exception (Error Code 5 — CorruptRevisionData) — see also Document Constraints.

    Batch operations

    If you’re making multiple changes to a database at once, it’s faster to group them together. The following example persists a few documents in batch.

    Example 8. Batch operations
    [database inBatch:&error usingBlock:^{
        for (int i = 0; i < 10; i++) {
            CBLMutableDocument *doc = [[CBLMutableDocument alloc] init];
            [doc setValue:@"user" forKey:@"type"];
            [doc setValue:[NSString stringWithFormat:@"user %d", i] forKey:@"name"];
            [doc setBoolean:NO forKey:@"admin"];
    
            NSError *err = nil;
            [collection saveDocument:doc error:&err];
        }
    }];

    At the local level this operation is still transactional: no other Database instances, including ones managed by the replicator can make changes during the execution of the block, and other instances will not see partial changes. But Couchbase Mobile is a distributed system, and due to the way replication works, there’s no guarantee that Sync Gateway or other devices will receive your changes all at once.

    Document change events

    You can register for document changes. The following example registers for changes to the document with ID user.john and prints the verified_account property when a change is detected.

    Example 9. Document change events
    [collection addDocumentChangeListenerWithID:@"user.john" listener:^(CBLDocumentChange  *change) {
        NSError *error;
        CBLDocument *doc = [wCollection documentWithID:change.documentID error:&error];
        NSLog(@"Status ::%@)", [doc stringForKey:@"verified_account"]);
    }];

    Document Expiration

    Document expiration allows users to set the expiration date for a document. When the document expires, it is purged from the database. The purge is not replicated to Sync Gateway.

    Example 10. Set document expiration

    This example sets the TTL for a document to 1 day from the current time.

    // Purge the document one day from now
    NSDate *ttl = [[NSCalendar currentCalendar] dateByAddingUnit:NSCalendarUnitDay
                                                           value:1
                                                          toDate:[NSDate date]
                                                         options:0];
    [collection setDocumentExpirationWithID:@"doc123" expiration:ttl error:&error];
    
    // Reset expiration
    [collection setDocumentExpirationWithID:@"doc1" expiration:nil error:&error];
    
    // Query documents that will be expired in less than five minutes
    NSTimeInterval fiveMinutesFromNow = [[NSDate dateWithTimeIntervalSinceNow:60 * 5] timeIntervalSince1970];
    CBLQuery *query = [CBLQueryBuilder select:@[[CBLQuerySelectResult expression:[CBLQueryMeta id]]]
                                         from:[CBLQueryDataSource collection:collection]
                                        where:[[CBLQueryMeta expiration]
                                                lessThan:[CBLQueryExpression double:fiveMinutesFromNow]]];

    You can set expiration for a whole Collection

    Document Constraints

    Couchbase Lite APIs do not explicitly disallow the use of attributes with the underscore prefix at the top level of document. This is to facilitate the creation of documents for use either in local only mode where documents are not synced, or when used exclusively in peer-to-peer sync.

    "_id", :"_rev" and "_sequence" are reserved keywords and must not be used as top-level attributes — see Example 11.

    Users are cautioned that any attempt to sync such documents to Sync Gateway will result in an error. To be future proof, you are advised to avoid creating such documents. Use of these attributes for user-level data may result in undefined system behavior.

    For more guidance — see: Sync Gateway - data modeling guidelines

    Example 11. Reserved Keys List
    • _attachments

    • _deleted [1]

    • _id [1]

    • _removed

    • _rev [1]

    • _sequence

    Working with JSON Data

    The toJSON() typed-accessor means you can easily work with JSON data, native and Couchbase Lite objects.

    Arrays

    Convert an ArrayObject to and from JSON using the toJSON() and toArray methods — see Example 4.

    Additionally you can:

    • Initialize a 'MutableArrayObject' using data supplied as a JSON string. This is done using the init(json) constructor — see: Example 4

    • Convert an ArrayFragment object to a JSON String

    • Set data with a JSON string using setJSON()

    Example 12. Arrays as JSON strings
    NSString *json = @"[\"1000\",\"1001\",\"1002\",\"1003\"]";
    
    CBLMutableArray *array = [[CBLMutableArray alloc] initWithJSON:json error:&error];
    
    for (NSString *item in array) {
        NSLog(@"%@", item);
    }

    Blobs

    Convert a Blob to JSON using the toJSON method — see Example 13.

    You can use isBlob() to check whether a given dictionary object is a blob or not — see Example 13.

    Note that the blob object must first be saved to the database (generating the required metadata) before you can use the toJSON method.

    Example 13. Blobs as JSON strings
    
    CBLDocument *doc = [collection documentWithID:@"doc-1000" error:&error];
    CBLBlob *blob = [doc blobForKey:@"avatar"];
    NSString *json = [blob toJSON];
    NSLog(@"json string is %@", json);

    See also: Blobs

    Dictionaries

    Convert a DictionaryObject to and from JSON using the toJSON and toDictionary methods — see Example 14.

    Additionally you can:

    • Initialize a 'MutableDictionaryObject' using data supplied as a JSON string. This is done using the init(json) constructor-- see: Example 14

    • Set data with a JSON string using setJSON()

    Example 14. Dictionaries as JSON strings
    NSString *json = @"{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\","
    "\"city\":\"Balmain\",\"country\":\"Australia\",\"description\":\"Undefined description for Hotel Ned\"}";
    
    
    CBLMutableDictionary *dict = [[CBLMutableDictionary alloc] initWithJSON:json
                                                                      error:&error];
    
    NSString *name = [dict stringForKey:@"name"];
    
    for (NSString *key in dict) {
        NSLog(@"%@ %@", key, [dict valueForKey:key]);
    }

    Documents

    Convert a Document to and from JSON strings using the toJSON() and setJSON() methods — see Example 15.

    Additionally you can:

    • Initialize a 'MutableDocument' using data supplied as a JSON string. This is done using the init(json) or init(id: json:) constructor — see: Example 15

    • Set data with a JSON string using setJSON()

    Example 15. Documents as JSON strings
    CBLCollection *collection = [self.database createCollectionWithName:@"hotel"
                                                                  scope:nil
                                                                  error:&error];
    CBLQuery *query = [CBLQueryBuilder select:@[[CBLQuerySelectResult expression:[CBLQueryMeta id]
                                                                              as:@"metaId"]]
                                         from:[CBLQueryDataSource collection:collection]];
    
    
    CBLDocument *doc = [collection documentWithID:@"doc-1000" error:&error];
    NSString *json = [doc toJSON];
    NSLog(@"json %@", json);

    Query Results as JSON

    Convert a Query Result to JSON using its {to-JSON} accessor method.

    Example 16. Using JSON Results

    Use CBLResult.toJSON to transform your result string into a JSON string, which can easily be serialized or used as required in your application. See Example 16 for a working example.

    CBLQueryResultSet *rs = [query execute:&error];
    for (CBLQueryResult *result in rs) {
    
        // Get result as a JSON string
        NSString *json = [result toJSON];
    
        // Get an native Obj-C object from the Json String
        NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:[json dataUsingEncoding:NSUTF8StringEncoding]
                                                                         options:NSJSONReadingAllowFragments
                                                                           error:&error];
    
        // Log generated Json and Native objects
        // For demo/example purposes
        NSLog(@"Json String %@", json);
        NSLog(@"Native Object %@", dict);
    
    }; // end for
    JSON String Format

    If your query selects ALL then the JSON format will be:

    {
      database-name: {
        key1: "value1",
        keyx: "valuex"
      }
    }

    If your query selects a sub-set of available properties then the JSON format will be:

    {
      key1: "value1",
      keyx: "valuex"
    }

    1. Any change to this reserved key will be detected when it is saved and will result in a Couchbase exception (Error Code 5 — CorruptRevisionData)