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; a record, or row in a table if you like.

      Each document has an ID (primary key in other databases) by which it can be located. This ID can be automatically generated (as a UUID) or specified programmatically; the only constraints are that it must be unique within the database, and it can’t be changed. The document also has a value which contains the actual application data. This value is stored as a dictionary collection of key-value (k-v) pairs where the values themselves may comprise different types of data such as numbers, strings, arrays or even nested objects — see: Data Types

      Data Encoding

      The document body is stored in an internal, efficient, binary form ( Fleece ).
      This internal form is 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, including boolean, integers, floating-point and strings. These accessors take care of converting to/from JSON encoding, and make sure you get the type you’re expecting.

      So your document content may well comprise one or more supporting data types such as:

      • Boolean

      • Date

      • Double

      • Float

      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. A document might be considered equivalent to 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 junction 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 we open your database. If the database does not already exist, Couchbase Lite will create it for us.

      // Get the database (and create it if it doesn’t exist).
      var database = new Database("hoteldb");

      See: Databases for more information

      Create a Document

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

      Because we will be adding data to the document we must use its mutable form.

      // Create your new document
      
      // Add the dictionary to a document's properties and save the document
      var doc = new MutableDocument("hoteldoc");

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

      Create a Dictionary

      Here we create a dictionary (address). Because we want to add values into the dictionary, we must create it in mutable form.

      When the dictionary is retrieved, each element’s value is directly accessible via its own key.

      // Create and populate mutable dictionary
      var address = new MutableDictionaryObject();
      address.SetString("street", "1 Main st.");
      address.SetString("city", "San Francisco");
      address.SetString("state", "CA");
      address.SetString("country", "USA");
      address.SetString("code", "90210");

      For more on using Dictionaries, see: Using Dictionaries

      Create an Array

      Since our hotel may have multiple lines we provide an array (phones) to hold contact numbers. Again, because we want to add values into the array, we create it in mutable form.

      // Create and populate mutable array
      var phones = MutableArrayObject();
      phones.AddString("650-000-0000");
      phones.AddString("650-000-0001");

      For more on using Arrays, see: Using Arrays

      Populate a Document

      Here we add our data to the mutable document we created earlier. Each data item is stored as a key-value pair.

      // Initialize and populate the document
      
      // Add document type to document properties (1)
      doc.SetString("type", "hotel");
      
      // Add hotel name string to document properties (2)
      doc.SetString("name", "Hotel Java Mo");
      
      // Add float to document properties (3)
      doc.SetFloat("room_rate", 121.75);
      
      // Add dictionary to document's properties (4)
      doc.SetDictionary("address", address);
      
      // Add array to document's properties (5)
      doc.SetArray("phones", phones);
      1 Add hotel name (string)
      2 Add average room rate (float)
      3 Add document type (string)
      Couchbase recommend using a type attribute to define each logical document type.
      4 Add address (dictionary) The address dictionary is added to the document and stored with the key address. We will use this to retrieve it when needed.
      5 Add phone numbers (array) The phones arrary is added to the document and stored with the key phones. We will use this to retrieve it when needed.

      Save a Document

      With the document now populated, we can persist to our Couchbase Lite database, auto-generating the document id.

      // Save the document changes (1)
      database.Save(doc);

      Close the Database

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

      // Close the database (1)
      database.Close();

      Working with Data

      Checking a Document’s Properties

      To check whether a given property exists in the document, you should use the Document.Contains(String key) method.

      If the property doesn’t exist, the call will return the default value for that getter method (0 for Document.GetInt() 0.0 for Document.GetFloat() etc.).

      Date accessors

      As a convenience Couchbase Lite offers Date accessors. 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 Document.GetDate() accessor method.

      newTask.SetValue("createdAt", DateTimeOffset.UtcNow);
      var date = newTask.GetDate("createdAt");

      Using Dictionaries

      Example 2. Read Only
      // NOTE: No error handling, for brevity (see getting started)
      var document = database.GetDocument("doc1");
      
      // Getting a dictionary from the document's properties
      var dict = document.GetDictionary("address");
      
      // Access a value with a key from the dictionary
      var street = dict.GetString("street");
      
      // Iterate dictionary
      foreach (var key in dict.Keys)
      {
          Console.WriteLine($"Key {key} = {dict.GetValue(key)}");
      }
      
      // Create a mutable copy
      var mutDict = dict.ToMutable();
      Example 3. Mutable
      // NOTE: No error handling, for brevity (see getting started)
      
      // Create a new mutable dictionary and populate some keys/values
      var mutable_dict = new MutableDictionaryObject();
      mutable_dict.SetString("street", "1 Main st.");
      mutable_dict.SetString("city", "San Francisco");
      
      // Add the dictionary to a document's properties and save the document
      var doc = new MutableDocument("doc1");
      doc.SetDictionary("address", mutable_dict);
      database.Save(doc);

      Using Arrays

      Example 4. Read Only
      // NOTE: No error handling, for brevity (see getting started)
      
      var document = database.GetDocument("doc1");
      
      // Getting a phones array from the document's properties
      var array = document.GetArray("phones");
      
      // Get element count
      var count = array.Count();
      
      // Access an array element by index
      if (count >= 0) { var phone = array[1]; }
      
      // Iterate dictionary
      for (int i = 0; i < count; i++)
      {
          Console.WriteLine($"Item {i.ToString()} = {array[i]}");
      }
      
      // Create a mutable copy
      var mutable_array = array.ToMutable();
      Example 5. Mutable
      // NOTE: No error handling, for brevity (see getting started)
      
      // Create a new mutable array and populate data into the array
      var mutable_array = new MutableArrayObject();
      mutable_array.AddString("650-000-0000");
      mutable_array.AddString("650-000-0001");
      
      // Set the array to document's properties and save the document
      var doc = new MutableDocument("doc1");
      doc.SetArray("phones", mutable_array);
      database.Save(doc);

      Using Blobs

      For more on working with blobs — see Blobs

      Document Initializers

      The following methods/initializers can be used:

      • The MutableDocument() initializer can be used to create a new document where the document ID is randomly generated by the database.

      • The MutableDocument(String id) initializer can be used to create a new document with a specific ID.

      • The Database.GetDocument() method can be used to get a document. If it doesn’t exist in the database, it will return null. This method can be used to check if a document with a given ID already exists in the database.

      Example 6. Persist a document

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

      using (var newTask = new MutableDocument("xyz")) {
          newTask.SetString("type", "task")
              .SetString("owner", "todo")
              .SetDate("createdAt", DateTimeOffset.UtcNow);
      
          db.Save(newTask);
      }

      Mutability

      By default, when a document is read from the database it is immutable. The Document.ToMutable() method should be used to create an instance of the document which can be updated.

      Example 7. Make a mutable document

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

      using(var document = db.GetDocument("xyz"))
      using (var mutableDocument = document.ToMutable()) {
          mutableDocument.SetString("name", "apples");
          db.Save(mutableDocument);
      }
      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
      db.InBatch) => {     for (var i = 0; i < 10; i++) {         using (var doc = new MutableDocument( {
                  doc.SetString("type", "user");
                  doc.SetString("name", $"user {i}");
                  doc.SetBoolean("admin", false);
                  db.Save(doc);
                  Console.WriteLine($"Saved user document {doc.GetString("name")}");
              }
          }
      });

      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

      It is possible to 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
      db.AddDocumentChangeListener("user.john", (sender, args) =>
      {
          using (var doc = Db.GetDocument(args.DocumentID)) {
              Console.WriteLine($"Status :: {doc.GetString("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 5 minutes from the current time.

      // Purge the document one day from now
      var ttl = DateTimeOffset.UtcNow.AddDays(1);
      db.SetDocumentExpiration("doc123", ttl);
      
      // Reset expiration
      db.SetDocumentExpiration("doc1", null);
      
      // Query documents that will be expired in less than five minutes
      var fiveMinutesFromNow = DateTimeOffset.UtcNow.AddMinutes(5).ToUnixTimeMilliseconds();
      var query = QueryBuilder
          .Select(SelectResult.Expression(Meta.ID))
          .From(DataSource.Database(db))
          .Where(Meta.Expiration.LessThan(Expression.Double(fiveMinutesFromNow)));

      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
      
      Database dbNew = new Database(ourdbname);
      
      // JSON String -- an Array (3 elements. including embedded arrays)
      var thisJSONstring = "[{'id':'1000','type':'hotel','name':'Hotel Ted','city':'Paris','country':'France','description':'Undefined description for Hotel Ted'},{'id':'1001','type':'hotel','name':'Hotel Fred','city':'London','country':'England','description':'Undefined description for Hotel Fred'},                        {'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}]".Replace("'", "\"");
      
      // Get JSON Array from JSON String
      JArray myJsonObj = JArray.Parse(thisJSONstring);
      
      // Create mutable array using JSON String Array
      var myArray = new MutableArrayObject();
      myArray.SetJSON(thisJSONstring);  (1)
      
      
      // Create a new documenty for each array element
      for (int i = 0; i < myArray.Count; i++)
      {
          var dict = myArray.GetDictionary(i);
          var docid = myArray[i].Dictionary.GetString("id");
          var newdoc = new MutableDocument(docid, dict.ToDictionary()); (2)
          dbNew.Save(newdoc);
      }
      
      // Get one of the created docs and iterate through one of the embedded arrays
      var extendedDoc = dbNew.GetDocument("1002");
      var features = extendedDoc.GetArray("features");
      (3)
      foreach (string feature in features) {
          System.Console.Write(feature);
          //process array item as required
      }
      var featuresJSON = extendedDoc.GetArray("features").ToJSON(); (4)
      1 Initialize array with JSON string
      2 Create and save new document using the array
      3 Get native array object from new doc and print its elements
      4 Get an array from the document as a JSON string

      Blobs

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

      You can also check whether a given dictionary object is a blob, or not, using isBlob() — again, see Example 13.

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

      Example 13. Blobs as JSON strings
      
      // Initialize base document for blob from a JSON string
      var docId = "1002";
      var aJSONstring = "{'ref':'hotel_1002','type':'hotel','name':'Hotel Ned'," +
          "'city':'Balmain','country':'Australia'," +
          "'description':'Undefined description for Hotel Ned'," +
          "'features':['Cable TV','Toaster','Microwave']}".Replace("'", "\"");
      var myDoc = new MutableDocument(docId, aJSONstring); (1)
      
      
      // Get the content (an image), create blob and add to doc)
      var defaultDirectory =
          Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>()
                      .DefaultDirectory(),
                          userName);
      var myImagePath = Path.Combine(defaultDirectory, "avatarimage.jpg");
      var myImageUri = new Uri(myImagePath.ToString());
      var myBlob = new Blob("image/jpg", myImageUri); (2)
      myDoc.SetBlob("avatar", myBlob); (3)
      
      
      // This example generates a 'blob not saved' exception
      try { Console.WriteLine("myBlob (unsaved) as JSON = {0}", myBlob.ToJSON());}
          catch (Exception e)
              {Console.WriteLine("Exception = {0}", e.Message);}
      
      dbNew.Save(myDoc);
      
      // Alternatively -- depending on use case
      dbNew.SaveBlob(new Blob("image/jpg", myImageUri)); (4)
      
      
      // Retrieve saved doc, get blob as JSON andheck its still a 'blob'
      var sameDoc = dbNew.GetDocument(docId);
      var reconstitutedBlob = new MutableDictionaryObject().
          SetDictionary("blobCOPY", new MutableDictionaryObject(sameDoc.GetBlob("avatar").ToJSON())); (5)
      
      if (Blob.IsBlob(
              reconstitutedBlob.GetDictionary("blobCOPY").ToDictionary()))  (6)
      {
         //... process accordingly
         Console.WriteLine("Its a Blob!!");
      }

      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
      
      // Get dictionary from JSONstring
      var aJSONstring = "{'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}".Replace("'", "\"");
      var myDict = new MutableDictionaryObject(json: aJSONstring); (1)
      
      // use dictionary to get name value
      var name = myDict.GetString("name");
      
      
      // Iterate through keys
      foreach (string key in myDict.Keys)
      {
          System.Console.WriteLine("Data -- {0} = {1}", key, myDict.GetValue(key).ToString());
      
      }
      1 Set the dictionary using a JSON string

      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) and-or init(id: json:) constructor-- see: Example 15

      • Set data with a JSON string using setJSON()

      Example 15. Documents as JSON strings
      Database this_DB = new Database("travel-sample");
      Database newDb = new Database("ournewdb");
      
      // Get a document
      var thisDoc = this_Db.GetDocument("hotel_10025");
      
      // Get document data as JSON String
      var thisDocAsJsonString = thisDoc?.ToJSON(); (1)
      
      // Get Json Object from the Json String
      JObject myJsonObj = JObject.Parse(thisDocAsJsonString);
      
      // Get Native Object (anhotel) from JSON String
      List<Hotel> thehotels = new List<Hotel>();
      
      Hotel anhotel = new Hotel();
      anhotel = JsonConvert.DeserializeObject<Hotel>(thisDocAsJsonString);
      thehotels.Add(anhotel);
      
      // Update the retrieved native object
      anhotel.Name = "A Copy of " + anhotel.Name;
      anhotel.Id = "2001";
      
      // Convert the updated object back to a JSON string
      var newJsonString = JsonConvert.SerializeObject(anhotel);
      
      // Update new document with JSOn String
      MutableDocument newhotel =
          new MutableDocument(anhotel.Id, newJsonString); (2)
      
      foreach (string key in newhotel.ToDictionary().Keys)
      {
          System.Console.WriteLine("Data -- {0} = {1}",
              key, newhotel.GetValue(key));
      }
      
      newDb.Save(newhotel);
      
      var thatDoc = newDb.GetDocument("2001").ToJSON(); (3)
      System.Console.Write(thatDoc);
      1 Get a document as a JSON string
      2 Initialize a MutableDocument using the JSON string and save to a separate database
      3 Retrieve the document created from JSON and print values

      Query Results as JSON

      Convert a Query Result to JSON using its ToJSON() accessor method.

      Example 16. Using JSON Results

      Use Result.ToJson() to transform your result string into a JSON string, which can easily be serialized or used as required in your application. See <> for a working example.

      
      foreach (var result in query.Execute().AsEnumerable()) {
      
          // get the result into a JSON String
                  var thisDocsJSONString = result.ToJSON();(1)
      
          // Get a native dictionary object using the JSON string
          var dictFromJSONstring =
                JsonConvert.
                  DeserializeObject<Dictionary<string, object>>
                    (thisDocsJSONString); (2)
      
          // use the created dictionary
          if (dictFromJSONstring != null)
          {
              thisDocsId = dictFromJSONstring["id"].ToString();
              thisDocsName = dictFromJSONstring["name"].ToString();
              thisDocsCity = dictFromJSONstring["city"].ToString();
              thisDocsType = dictFromJSONstring["type"].ToString();
          }
      
          //Get a custom object using the JSON string
          Hotel this_hotel =
              JsonConvert.DeserializeObject<Hotel>(thisDocsJSONString); (3)
      
          // Store this hotel object in a list of hotels
          hotels.Add(
              this_hotel.Id.ToString(),
                  this_hotel);
      
      } // end foreach result
      1 Get the Query result as a JSON string — see JSON String Format
      2 Get a native object from the JSON string
      3 Populate your custom object from the dictionary created from JSON data
      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`)