From 965176a7fa4438d74e805f7ef2c09a830e72a9f2 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 12 Nov 2024 13:40:51 +0000 Subject: [PATCH] deploy: a6e14bbb39dbb05b1cd88c378739367504477fd2 --- 404.html | 2 +- CNAME | 1 - DataSync/capella.html | 2 +- DataSync/remote-sync-gateway.html | 12 +++-- ProductNotes/compatibility.html | 2 +- ProductNotes/release-notes.html | 2 +- ProductNotes/supported-platforms.html | 2 +- Queries/live-queries.html | 2 +- Queries/query-result-set.html | 2 +- Queries/query-troubleshooting.html | 2 +- ...lusplus-mobile-and-server-differences.html | 2 +- Queries/sqlplusplus.html | 2 +- StartHere/build-run.html | 2 +- StartHere/install.html | 2 +- StartHere/prerequisites.html | 8 +-- Troubleshooting/troubleshoot-crashes.html | 2 +- Troubleshooting/troubleshoot-queries.html | 2 +- Troubleshooting/using-logs.html | 2 +- api-reference.html | 2 +- assets/js/295b567d.69cf5257.js | 1 - assets/js/295b567d.ef0dc828.js | 1 + assets/js/98e2200e.82609965.js | 1 - assets/js/98e2200e.c11532a6.js | 1 + assets/js/a7cb794f.30c99c14.js | 1 - assets/js/a7cb794f.993fd822.js | 1 + ...7b2ec.6d0d5ce2.js => a7e7b2ec.f29bd64f.js} | 2 +- assets/js/d211126e.6deec1a1.js | 1 + assets/js/d211126e.80a47ad6.js | 1 - assets/js/d22a0e6a.4570b277.js | 1 - assets/js/d22a0e6a.a3bfa71d.js | 1 + assets/js/f9585397.0323e3b0.js | 1 - assets/js/f9585397.e160bfd7.js | 1 + assets/js/runtime~main.5f78187e.js | 1 + assets/js/runtime~main.6e9e134b.js | 1 - blobs.html | 6 +-- blog.html | 2 +- blog/archive.html | 2 +- blog/intro-blog-post.html | 2 +- blog/tags.html | 2 +- blog/tags/couchbase-lite.html | 2 +- blog/tags/hello.html | 2 +- blog/tags/ionic.html | 2 +- blog/tags/mobile.html | 2 +- category/data-sync.html | 2 +- category/product-notes.html | 2 +- category/queries.html | 2 +- category/start-here.html | 2 +- category/troubleshooting.html | 2 +- database-prebuilt.html | 2 +- databases.html | 49 +++++++++++++++---- documents.html | 31 +++++++----- full-text-search.html | 2 +- index.html | 2 +- indexes.html | 2 +- learning-path.html | 2 +- migration.html | 25 +++++++--- scopes-collections.html | 9 ++-- typed-data.html | 2 +- 58 files changed, 142 insertions(+), 85 deletions(-) delete mode 100644 CNAME delete mode 100644 assets/js/295b567d.69cf5257.js create mode 100644 assets/js/295b567d.ef0dc828.js delete mode 100644 assets/js/98e2200e.82609965.js create mode 100644 assets/js/98e2200e.c11532a6.js delete mode 100644 assets/js/a7cb794f.30c99c14.js create mode 100644 assets/js/a7cb794f.993fd822.js rename assets/js/{a7e7b2ec.6d0d5ce2.js => a7e7b2ec.f29bd64f.js} (59%) create mode 100644 assets/js/d211126e.6deec1a1.js delete mode 100644 assets/js/d211126e.80a47ad6.js delete mode 100644 assets/js/d22a0e6a.4570b277.js create mode 100644 assets/js/d22a0e6a.a3bfa71d.js delete mode 100644 assets/js/f9585397.0323e3b0.js create mode 100644 assets/js/f9585397.e160bfd7.js create mode 100644 assets/js/runtime~main.5f78187e.js delete mode 100644 assets/js/runtime~main.6e9e134b.js diff --git a/404.html b/404.html index 39880db..64653f7 100644 --- a/404.html +++ b/404.html @@ -5,7 +5,7 @@ Couchbase Lite Ionic Capacitor Plugin - + diff --git a/CNAME b/CNAME deleted file mode 100644 index 448fc2f..0000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -cbl-ionic.dev \ No newline at end of file diff --git a/DataSync/capella.html b/DataSync/capella.html index 601e5f1..963d5dd 100644 --- a/DataSync/capella.html +++ b/DataSync/capella.html @@ -5,7 +5,7 @@ Couchbase Capella App Services | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/DataSync/remote-sync-gateway.html b/DataSync/remote-sync-gateway.html index 9ac22e5..22371dd 100644 --- a/DataSync/remote-sync-gateway.html +++ b/DataSync/remote-sync-gateway.html @@ -5,7 +5,7 @@ Remote Sync Gateway | Couchbase Lite Ionic Capacitor Plugin - + @@ -159,7 +159,13 @@

Channels

By default, Couchbase Lite gets all the channels to which the configured user account has access.

This behavior is suitable for most apps that rely on user authentication and the sync function to specify which data to pull for each user.

-

Optionally, it’s also possible to specify a string array of channel names on Couchbase Lite’s replicator configuration object. In this case, the replication from Sync Gateway will only pull documents tagged with those channels.

+

Optionally, it’s also possible to specify a string array of channel names on Couchbase Lite’s replicator configuration object by passing in a CollectionConfiguration object when adding in a collection.

+
const config = new ReplicatorConfiguration(target);
const collectionConfig = new CollectionConfiguration();
collectionConfig.setChannels(['channel1', 'channel2']);
config.addCollection(collection, collectionConfig);
+

In this case, the replication from Sync Gateway will only pull documents tagged with those channels.

+
caution

Push replicator will ignore this filter.

+

Documents

+

By default, Couchbase Lite will replicate all documents that belong to the channels specified in the collection configuration. However, you can override this behavior by filtering the documents using the CollectionConfiguration object.

+
const config = new ReplicatorConfiguration(target);
const collectionConfig = new CollectionConfiguration();
collectionConfig.setDocumentIDs(['doc1', 'doc2', 'doc3']);
config.addCollection(collection, collectionConfig);

Auto-purge on Channel Access Revocation

caution

This is a Breaking Change at 3.0

New outcome

@@ -306,6 +312,6 @@

CouchbaseLite Replicator ERROR: {Repl#2} Got LiteCore error: WebSocket error 1006 "connection closed abnormally"

If Sync Gateway is configured with a self signed certificate, and your app points to a wss scheme but the replicator configuration isn’t using the certificate you will encounter an error with status code 5011 — see: Example 23

Example 23. Certificate Mismatch or Not Found

-
CouchbaseLite Replicator ERROR: {Repl#2} Got LiteCore error: Network error 11 "server TLS certificate is self-signed or has unknown root cert"
+
CouchbaseLite Replicator ERROR: {Repl#2} Got LiteCore error: Network error 11 "server TLS certificate is self-signed or has unknown root cert"
\ No newline at end of file diff --git a/ProductNotes/compatibility.html b/ProductNotes/compatibility.html index aadfc09..9920ee6 100644 --- a/ProductNotes/compatibility.html +++ b/ProductNotes/compatibility.html @@ -5,7 +5,7 @@ Compatibility | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/ProductNotes/release-notes.html b/ProductNotes/release-notes.html index a4f48e8..6789909 100644 --- a/ProductNotes/release-notes.html +++ b/ProductNotes/release-notes.html @@ -5,7 +5,7 @@ Release Notes | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/ProductNotes/supported-platforms.html b/ProductNotes/supported-platforms.html index 0f7bb69..ac2731f 100644 --- a/ProductNotes/supported-platforms.html +++ b/ProductNotes/supported-platforms.html @@ -5,7 +5,7 @@ Supported Platforms | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/Queries/live-queries.html b/Queries/live-queries.html index a846fcd..c096f7b 100644 --- a/Queries/live-queries.html +++ b/Queries/live-queries.html @@ -5,7 +5,7 @@ Live Queries | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/Queries/query-result-set.html b/Queries/query-result-set.html index 610e7bf..d4b595f 100644 --- a/Queries/query-result-set.html +++ b/Queries/query-result-set.html @@ -5,7 +5,7 @@ Query Result Sets | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/Queries/query-troubleshooting.html b/Queries/query-troubleshooting.html index 29da3e5..a334dd7 100644 --- a/Queries/query-troubleshooting.html +++ b/Queries/query-troubleshooting.html @@ -5,7 +5,7 @@ Query Troubleshoooting | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/Queries/sqlplusplus-mobile-and-server-differences.html b/Queries/sqlplusplus-mobile-and-server-differences.html index 6bd396f..06e347d 100644 --- a/Queries/sqlplusplus-mobile-and-server-differences.html +++ b/Queries/sqlplusplus-mobile-and-server-differences.html @@ -5,7 +5,7 @@ SQL++ for Mobile and Server Differences | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/Queries/sqlplusplus.html b/Queries/sqlplusplus.html index d6a0f7d..55fb132 100644 --- a/Queries/sqlplusplus.html +++ b/Queries/sqlplusplus.html @@ -5,7 +5,7 @@ SQL++ for Mobile | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/StartHere/build-run.html b/StartHere/build-run.html index bf26d80..43ccbad 100644 --- a/StartHere/build-run.html +++ b/StartHere/build-run.html @@ -5,7 +5,7 @@ Build and Run | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/StartHere/install.html b/StartHere/install.html index 2e83c67..37a5c70 100644 --- a/StartHere/install.html +++ b/StartHere/install.html @@ -5,7 +5,7 @@ Install | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/StartHere/prerequisites.html b/StartHere/prerequisites.html index 5fbe7e4..91d58ed 100644 --- a/StartHere/prerequisites.html +++ b/StartHere/prerequisites.html @@ -5,7 +5,7 @@ Prerequisites | Couchbase Lite Ionic Capacitor Plugin - + @@ -24,7 +24,7 @@

Su

Capacitor Version

Development Environment

@@ -52,7 +52,7 @@

XCode 14 or higher installed and working (XCode 15 installed is preferred) +
  • XCode 15 or higher installed and working (XCode 15 installed is preferred)
  • [iOS 13 or higher]. Any apps using the plugin must be upgraded to iOS 13 or higher.
  • XCode Command Line Tools installed
  • Simulators downloaded and working
  • diff --git a/Troubleshooting/troubleshoot-crashes.html b/Troubleshooting/troubleshoot-crashes.html index 68091db..58e8c88 100644 --- a/Troubleshooting/troubleshoot-crashes.html +++ b/Troubleshooting/troubleshoot-crashes.html @@ -5,7 +5,7 @@ Troubleshooting Crashes | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/Troubleshooting/troubleshoot-queries.html b/Troubleshooting/troubleshoot-queries.html index 250eb95..6a5dc43 100644 --- a/Troubleshooting/troubleshoot-queries.html +++ b/Troubleshooting/troubleshoot-queries.html @@ -5,7 +5,7 @@ Troubleshooting Queries | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/Troubleshooting/using-logs.html b/Troubleshooting/using-logs.html index 9993925..4014e83 100644 --- a/Troubleshooting/using-logs.html +++ b/Troubleshooting/using-logs.html @@ -5,7 +5,7 @@ Using Logs for Troubleshooting | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/api-reference.html b/api-reference.html index 4fd05da..66a23b9 100644 --- a/api-reference.html +++ b/api-reference.html @@ -5,7 +5,7 @@ API References | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/assets/js/295b567d.69cf5257.js b/assets/js/295b567d.69cf5257.js deleted file mode 100644 index 78b9a48..0000000 --- a/assets/js/295b567d.69cf5257.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[3549],{6982:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var a=t(4848),i=t(8453);const o={id:"migration",sidebar_position:15},s="Migration",c={id:"migration",title:"Migration",description:"Description \u2014 Migrating from @ionic-enterprise/couchbase-lite package",source:"@site/docs/migration.md",sourceDirName:".",slug:"/migration",permalink:"/migration",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/migration.md",tags:[],version:"current",sidebarPosition:15,frontMatter:{id:"migration",sidebar_position:15},sidebar:"tutorialSidebar",previous:{title:"API References",permalink:"/api-reference"},next:{title:"Troubleshooting",permalink:"/category/troubleshooting"}},r={},l=[{value:"Information",id:"information",level:2},{value:"Database Compact / Database Maintenance",id:"database-compact--database-maintenance",level:2},{value:"Scopes/Collections",id:"scopescollections",level:2},{value:"Save Example",id:"save-example",level:3},{value:"Retrieving a Document",id:"retrieving-a-document",level:3},{value:"Delete a document",id:"delete-a-document",level:3},{value:"Javascript Docs support",id:"javascript-docs-support",level:4},{value:"Query Builder API Replaced with SQL++",id:"query-builder-api-replaced-with-sql",level:2},{value:"Replication Configuration",id:"replication-configuration",level:2},{value:"New Features",id:"new-features",level:3},{value:"Conclusion",id:"conclusion",level:2}];function d(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{id:"migration",children:"Migration"}),"\n",(0,a.jsxs)(n.blockquote,{children:["\n",(0,a.jsxs)(n.p,{children:["Description \u2014 Migrating from @ionic-enterprise/couchbase-lite package\nRelated Content \u2014 ",(0,a.jsx)(n.a,{href:"/databases",children:"Database"})," | ",(0,a.jsx)(n.a,{href:"/scopes-collections",children:"Scopes/Collections"})]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"information",children:"Information"}),"\n",(0,a.jsxs)(n.p,{children:["In the past, Ionic offered a plugin for Couchbase Lite, known as @ionic-enterprise/couchbase-lite. However, this plugin has since been deprecated by Ionic. The Couchbase Developer Experience and Ecosystem team took the original plugin as a foundation and transformed it into the cbl-ionic package. This migration documentation aims to address common challenges you may encounter when transitioning from the Ionic version of the plugin to the new open-source ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin."]}),"\n",(0,a.jsxs)(n.p,{children:["From an architectural standpoint, the ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin bears a strong resemblance to the original plugin. The key difference is that the ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," package is open-source and maintained by the Couchbase Developer Experience and Ecosystem team. The iOS implementation, originally written in Objective-C, has been rewritten in Swift. Similarly, the original Android implementation, which was written in Java, has been replaced with Kotlin. The primary objective of the ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin is to facilitate access to the latest versions of Couchbase Lite 3.x, while also supporting some of the major new features that the 3.x release offers:"]}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Scopes and Collections"}),"\n",(0,a.jsx)(n.li,{children:"SQL++ Queries"}),"\n",(0,a.jsx)(n.li,{children:"Database Maintenance"}),"\n",(0,a.jsx)(n.li,{}),"\n"]}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"By updating your app to use this plugin, your database will be instantly upgraded to a 3.x database, enabling support for Scopes and Collections. This behavior mirrors that of the native SDKs for each platform."})}),"\n",(0,a.jsx)(n.h2,{id:"database-compact--database-maintenance",children:"Database Compact / Database Maintenance"}),"\n",(0,a.jsx)(n.p,{children:"Originally the old plugin provided the ability to compact a database using the following API:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const db = new Database('myDatabaseName');\nawait db.open();\nawait db.compact();\n"})}),"\n",(0,a.jsx)(n.p,{children:"This has been changed out with the Database Maintenance API. Now you can call the performMaintenance function and pass it in one of the values from the MaintenanceType enum:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const db = new Database('myDatabaseName');\nawait db.open();\nawait db.performMaintenance(MaintenanceType.COMPACT);\n"})}),"\n",(0,a.jsx)(n.p,{children:"The MaintenanceType enum includes:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"export enum MaintenanceType {\n COMPACT = 0,\n REINDEX = 1,\n INTEGRITY_CHECK = 2,\n OPTIMIZE = 3,\n FULL_OPTIMIZE = 4,\n}\n"})}),"\n",(0,a.jsx)(n.p,{children:"These bring the Ionic package in line with the native SDK's for each platform."}),"\n",(0,a.jsx)(n.h2,{id:"scopescollections",children:"Scopes/Collections"}),"\n",(0,a.jsx)(n.p,{children:"With the introduction of Couchbase Mobile 3.1, the support for Scopes and Collections was added. This feature enhances performance by allowing documents to be stored in different collections based on their type, eliminating the need for indexes to locate documents of a specific type. While indexes can boost query performance, they can also hinder write performance. Therefore, Scopes and Collections offer a way to enhance query performance without relying on indexes."}),"\n",(0,a.jsx)(n.p,{children:'For teams that have not yet transitioned to using custom scopes and collections, the "_default" scope and "_default" collection can be utilized. The Database class provides methods to access both the default scope and collection:'}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"\nconst db = new Database('myDatabaseName');\nawait db.open();\n\nconst collection = await db.defaultCollection();\n"})}),"\n",(0,a.jsx)(n.p,{children:"Most operations for documents that were done on the Database API must be done using the Collection API. Basic CRUD examples are shown below:"}),"\n",(0,a.jsx)(n.h3,{id:"save-example",children:"Save Example"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Before"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = new MutableDocument(\'doc1\');\ndoc.setString("name", "test");\nawait db.save(doc); \n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"After"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = new MutableDocument(\'doc1\');\ndoc.setString("name", "test");\nawait collection.save(doc); \n'})}),"\n",(0,a.jsx)(n.h3,{id:"retrieving-a-document",children:"Retrieving a Document"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Before"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await db.getDocument("doc1")\n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"After"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await collection.document("doc1"); \n'})}),"\n",(0,a.jsx)(n.h3,{id:"delete-a-document",children:"Delete a document"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Before"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await db.deleteDocument("doc1")\n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"After"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await collection.deleteDocument("doc1"); \n'})}),"\n",(0,a.jsx)(n.p,{children:"Other APIs have also been moved from the Database class to the Collections class including:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Purge Document"}),"\n",(0,a.jsx)(n.li,{children:"Document Expiration"}),"\n",(0,a.jsx)(n.li,{children:"Document Change Listener"}),"\n",(0,a.jsx)(n.li,{children:"Create Index"}),"\n",(0,a.jsx)(n.li,{children:"Delete Index"}),"\n",(0,a.jsx)(n.li,{children:"Get Indexes"}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"For these APIs use the navigation menu on the left to see the new APIs for Database, Collection, and Indexes."}),"\n",(0,a.jsx)(n.h4,{id:"javascript-docs-support",children:"Javascript Docs support"}),"\n",(0,a.jsx)(n.p,{children:"Each function that was deprecated in the Database class has documentation stating that the API has been deprecated and using IDE's like Visual Studio Code should provide you with a warning along with information which API you should use instead."}),"\n",(0,a.jsx)(n.p,{children:"It is highly recommended that you call the collection version of these functions as the database version of the functions will be removed in a future release."}),"\n",(0,a.jsx)(n.h2,{id:"query-builder-api-replaced-with-sql",children:"Query Builder API Replaced with SQL++"}),"\n",(0,a.jsx)(n.p,{children:"The Query Builder API was removed due to it calling internal - non-public APIs that caused various issues along with hard to manage code. The Couchbase Mobile SDK team provided a more robust API with SQL++ and the cbl-ionic SDK uses it exclusively for querying the database."}),"\n",(0,a.jsx)(n.p,{children:"SQL++ provides a much simpler approach to querying documents:"}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Before"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const query = QueryBuilder.select(\n SelectResult.expression(Meta.id),\n SelectResult.property('name'),\n SelectResult.property('type'),\n)\n .from(DataSource.database(database))\n .where(Expression.property('type').equalTo(Expression.string('hotel')))\n .orderBy(Ordering.expression(Meta.id));\n\ntry {\n const resultSet = await(await query.execute()).allResults();\n for (let result of resultSet) {\n console.log(\n 'Sample',\n String.format('hotel id -> %s', result.getString('id')),\n );\n console.log(\n 'Sample',\n String.format('hotel name -> %s', result.getString('name')),\n );\n }\n} catch (e) {\n Log.e('Sample', e.getLocalizedMessage());\n}\n"})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"After"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const queryString = \"SELECT META().id, name, type FROM _default._default WHERE type = 'hotel' ORDER BY META().id\";\ntry {\n const query = await db.createQuery(queryString);\n const results = await query.execute();\n for (const result of results) {\n console.log('Sample', `hotel id -> ${result.getString('id')}`);\n console.log('Sample', `hotel name -> ${result.getString('name')}`);\n }\n} catch (e) {\n Log.e('Sample', e.getLocalizedMessage());\n} \n"})}),"\n",(0,a.jsx)(n.h2,{id:"replication-configuration",children:"Replication Configuration"}),"\n",(0,a.jsx)(n.p,{children:"With the release of Scopes and Collections comes the change to Replication. In the past you would set the Replicator to point to a database that you wanted to replicate to. Now you must set the Replicator to point to a collection or group of collections."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Before"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"// Create replicators to push and pull changes to and from the cloud.\nconst targetEndpoint = new URLEndpoint(\n new URI('ws://localhost:4984/projects'),\n);\nconst replConfig = new ReplicatorConfiguration(database, targetEndpoint);\nreplConfig.setReplicatorType(\n ReplicatorConfiguration.ReplicatorType.PUSH_AND_PULL,\n);\n\n// Add authentication.\nreplConfig.setAuthenticator(new BasicAuthenticator('demo@example.com', 'P@ssw0rd12'));\n\n// Create replicator.\nlet replicator = new Replicator(replConfig);\n\n// Listen to replicator change events.\nreplicator.addChangeListener(status => {});\n\n// Start replication.\nreplicator.start();\n"})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"After"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"//setup database and collection\nconst fileSystem = new FileSystem();\nconst directoryPath = await fileSystem.getDefaultPath();\n\nconst dc = new DatabaseConfiguration();\ndc.setDirectory(directoryPath);\nconst database = new Database('inventory', dc);\n\nawait database.open();\nconst collection = database.getDefaultCollection();\n\nconst target = new URLEndpoint('ws://localhost:4984/projects');\nconst auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12');\nconst config = new ReplicatorConfiguration(target);\nconfig.addCollection(this.collection);\nconfig.setAuthenticator(auth);\n\nconst replicator = await Replicator.create(config);\n\n //listen to the replicator change events\nconst token = await replicator.addChangeListener((change) => {\n\t//check to see if there was an error\n \tconst error = change.status.getError();\n \tif (error !== undefined) {\n\t\t//do something with the error\n \t}\n \t//get the status of the replicator using ReplicatorActivityLevel enum\n \tif (change.status.getActivityLevel() === ReplicatorActivityLevel.IDLE) {\n \t\t//do something because the replicator is now IDLE\n \t}\n });\n\n // start the replicator without making a new checkpoint\n await replicator.start(false);\n"})}),"\n",(0,a.jsx)(n.h3,{id:"new-features",children:"New Features"}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin, in addition to supporting Scopes/Collections and SQL++ from the 3.x release, introduces several new features:"]}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Database Change Encryption Key"}),": This new functionality in the Database API allows for the modification of a database's encryption key."]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Document Expiration"}),": A new method in the Collection API enables the setting and retrieval of a document's expiration date."]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Example App"}),": The ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," repository includes a ",(0,a.jsx)(n.code,{children:"example"})," folder, which contains a comprehensive application for testing all APIs and features of the cbl-ionic plugin. It also includes a custom test runner for executing end-to-end tests written for the plugin"]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Documentation"})," - Undeniably, one of the most challenging aspects of developing open source software is creating comprehensive documentation. This site addresses this by providing a complete set of documentation, allowing developers to explore the various APIs the plugin has to offers."]}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,a.jsxs)(n.p,{children:["An application can be migrated over from the Ionic version of the Couchbase Lite plugin to the new ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," package. The new package provides a path to the latest version of Couchbase Lite 3.x and provides support for the new features that the 3.x release provides."]}),"\n",(0,a.jsxs)(n.p,{children:["The new ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin is open source and maintained by the Couchbase Developer Experience and Ecosystem team. The new plugin is architecturally similar to the original plugin, but there are some differences in the API that will need to be addressed when migrating an application over to the new package. The new plugin provides support for Scopes and Collections, SQL++ Queries, and Database Maintenance. The new package also provides a more robust API for querying the database and for setting up replication. The new package is recommended for all new applications that are using Couchbase Lite and for applications that are looking to migrate over from the Ionic version of the Couchbase Lite plugin."]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>c});var a=t(6540);const i={},o=a.createContext(i);function s(e){const n=a.useContext(o);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:s(e.components),a.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/295b567d.ef0dc828.js b/assets/js/295b567d.ef0dc828.js new file mode 100644 index 0000000..8859f4e --- /dev/null +++ b/assets/js/295b567d.ef0dc828.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[3549],{6982:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>s,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var a=t(4848),i=t(8453);const o={id:"migration",sidebar_position:15},s="Migration",c={id:"migration",title:"Migration",description:"Description \u2014 Migrating from @ionic-enterprise/couchbase-lite package",source:"@site/docs/migration.md",sourceDirName:".",slug:"/migration",permalink:"/migration",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/migration.md",tags:[],version:"current",sidebarPosition:15,frontMatter:{id:"migration",sidebar_position:15},sidebar:"tutorialSidebar",previous:{title:"API References",permalink:"/api-reference"},next:{title:"Troubleshooting",permalink:"/category/troubleshooting"}},r={},l=[{value:"Information",id:"information",level:2},{value:"Database Compact / Database Maintenance",id:"database-compact--database-maintenance",level:2},{value:"Scopes/Collections",id:"scopescollections",level:2},{value:"Save Example",id:"save-example",level:3},{value:"Retrieving a Document",id:"retrieving-a-document",level:3},{value:"Delete a document",id:"delete-a-document",level:3},{value:"Javascript Docs support",id:"javascript-docs-support",level:4},{value:"Query Builder API Replaced with SQL++",id:"query-builder-api-replaced-with-sql",level:2},{value:"Replication Configuration",id:"replication-configuration",level:2},{value:"Blob Retrieval",id:"blob-retrieval",level:2},{value:"New Features",id:"new-features",level:3},{value:"Conclusion",id:"conclusion",level:2}];function d(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,i.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{id:"migration",children:"Migration"}),"\n",(0,a.jsxs)(n.blockquote,{children:["\n",(0,a.jsxs)(n.p,{children:["Description \u2014 Migrating from @ionic-enterprise/couchbase-lite package\nRelated Content \u2014 ",(0,a.jsx)(n.a,{href:"/databases",children:"Database"})," | ",(0,a.jsx)(n.a,{href:"/scopes-collections",children:"Scopes/Collections"})]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"information",children:"Information"}),"\n",(0,a.jsxs)(n.p,{children:["In the past, Ionic offered a plugin for Couchbase Lite, known as @ionic-enterprise/couchbase-lite. However, this plugin has since been deprecated by Ionic. The Couchbase Developer Experience and Ecosystem team took the original plugin as a foundation and transformed it into the cbl-ionic package. This migration documentation aims to address common challenges you may encounter when transitioning from the Ionic version of the plugin to the new open-source ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin."]}),"\n",(0,a.jsxs)(n.p,{children:["From an architectural standpoint, the ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin bears a strong resemblance to the original plugin. The key difference is that the ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," package is open-source and maintained by the Couchbase Developer Experience and Ecosystem team. The iOS implementation, originally written in Objective-C, has been rewritten in Swift. Similarly, the original Android implementation, which was written in Java, has been replaced with Kotlin. The primary objective of the ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin is to facilitate access to the latest versions of Couchbase Lite 3.2 >= , while also supporting some of the major new features that the >= 3.2 release offers:"]}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Scopes and Collections"}),"\n",(0,a.jsx)(n.li,{children:"SQL++ Queries"}),"\n",(0,a.jsx)(n.li,{children:"Database Maintenance"}),"\n"]}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"By updating your app to use this plugin, your database will be instantly upgraded to a 3.2 >= database, enabling support for Scopes and Collections. This behavior mirrors that of the native SDKs for each platform."})}),"\n",(0,a.jsx)(n.h2,{id:"database-compact--database-maintenance",children:"Database Compact / Database Maintenance"}),"\n",(0,a.jsx)(n.p,{children:"Originally the old plugin provided the ability to compact a database using the following API:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const db = new Database('myDatabaseName');\nawait db.open();\nawait db.compact();\n"})}),"\n",(0,a.jsx)(n.p,{children:"This has been changed out with the Database Maintenance API. Now you can call the performMaintenance function and pass it in one of the values from the MaintenanceType enum:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const db = new Database('myDatabaseName');\nawait db.open();\nawait db.performMaintenance(MaintenanceType.COMPACT);\n"})}),"\n",(0,a.jsx)(n.p,{children:"The MaintenanceType enum includes:"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"export enum MaintenanceType {\n COMPACT = 0,\n REINDEX = 1,\n INTEGRITY_CHECK = 2,\n OPTIMIZE = 3,\n FULL_OPTIMIZE = 4,\n}\n"})}),"\n",(0,a.jsxs)(n.p,{children:["These bring the Ionic package in line with the native SDK's for each platform. Documentation for the Database Maintenance API can be found ",(0,a.jsx)(n.a,{href:"/databases",children:"here"}),"."]}),"\n",(0,a.jsx)(n.h2,{id:"scopescollections",children:"Scopes/Collections"}),"\n",(0,a.jsx)(n.p,{children:"With the introduction of Couchbase Mobile 3.1, the support for Scopes and Collections was added. This feature enhances performance by allowing documents to be stored in different collections based on their type, eliminating the need for indexes to locate documents of a specific type. While indexes can boost query performance, they can also hinder write performance. Therefore, Scopes and Collections offer a way to enhance query performance without relying on indexes."}),"\n",(0,a.jsx)(n.p,{children:'For teams that have not yet transitioned to using custom scopes and collections, the "_default" scope and "_default" collection can be utilized. The Database class provides methods to access both the default scope and collection:'}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"\nconst db = new Database('myDatabaseName');\nawait db.open();\n\nconst collection = await db.defaultCollection();\n"})}),"\n",(0,a.jsx)(n.p,{children:"Most operations for documents that were done on the Database API must be done using the Collection API. Basic CRUD examples are shown below:"}),"\n",(0,a.jsx)(n.h3,{id:"save-example",children:"Save Example"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Before"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = new MutableDocument(\'doc1\');\ndoc.setString("name", "test");\nawait db.save(doc); \n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"After"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = new MutableDocument(\'doc1\');\ndoc.setString("name", "test");\nawait collection.save(doc); \n'})}),"\n",(0,a.jsx)(n.h3,{id:"retrieving-a-document",children:"Retrieving a Document"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Before"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await db.getDocument("doc1")\n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"After"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await collection.document("doc1"); \n'})}),"\n",(0,a.jsx)(n.h3,{id:"delete-a-document",children:"Delete a document"}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Before"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await db.deleteDocument("doc1")\n'})}),"\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"After"}),":"]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:'const doc = await collection.deleteDocument("doc1"); \n'})}),"\n",(0,a.jsx)(n.p,{children:"Other APIs have also been moved from the Database class to the Collections class including:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:"Purge Document"}),"\n",(0,a.jsx)(n.li,{children:"Document Expiration"}),"\n",(0,a.jsx)(n.li,{children:"Document Change Listener"}),"\n",(0,a.jsx)(n.li,{children:"Create Index"}),"\n",(0,a.jsx)(n.li,{children:"Delete Index"}),"\n",(0,a.jsx)(n.li,{children:"Get Indexes"}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"For these APIs use the navigation menu on the left to see the new APIs for Database, Collection, and Indexes."}),"\n",(0,a.jsx)(n.h4,{id:"javascript-docs-support",children:"Javascript Docs support"}),"\n",(0,a.jsx)(n.p,{children:"Each function that was deprecated in the Database class has documentation stating that the API has been deprecated and using IDE's like Visual Studio Code should provide you with a warning along with information which API you should use instead."}),"\n",(0,a.jsx)(n.p,{children:"It is highly recommended that you call the collection version of these functions as the database version of the functions will be removed in a future release."}),"\n",(0,a.jsx)(n.h2,{id:"query-builder-api-replaced-with-sql",children:"Query Builder API Replaced with SQL++"}),"\n",(0,a.jsx)(n.p,{children:"The Query Builder API was removed due to it calling internal - non-public APIs that caused various issues along with hard to manage code. The Couchbase Mobile SDK team provided a more robust API with SQL++ and the cbl-ionic SDK uses it exclusively for querying the database."}),"\n",(0,a.jsx)(n.p,{children:"SQL++ provides a much simpler approach to querying documents:"}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Before"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const query = QueryBuilder.select(\n SelectResult.expression(Meta.id),\n SelectResult.property('name'),\n SelectResult.property('type'),\n)\n .from(DataSource.database(database))\n .where(Expression.property('type').equalTo(Expression.string('hotel')))\n .orderBy(Ordering.expression(Meta.id));\n\ntry {\n const resultSet = await(await query.execute()).allResults();\n for (let result of resultSet) {\n console.log(\n 'Sample',\n String.format('hotel id -> %s', result.getString('id')),\n );\n console.log(\n 'Sample',\n String.format('hotel name -> %s', result.getString('name')),\n );\n }\n} catch (e) {\n Log.e('Sample', e.getLocalizedMessage());\n}\n"})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"After"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const queryString = \"SELECT META().id, name, type FROM _default._default WHERE type = 'hotel' ORDER BY META().id\";\ntry {\n const query = await db.createQuery(queryString);\n const results = await query.execute();\n for (const result of results) {\n console.log('Sample', `hotel id -> ${result.getString('id')}`);\n console.log('Sample', `hotel name -> ${result.getString('name')}`);\n }\n} catch (e) {\n Log.e('Sample', e.getLocalizedMessage());\n} \n"})}),"\n",(0,a.jsx)(n.h2,{id:"replication-configuration",children:"Replication Configuration"}),"\n",(0,a.jsx)(n.p,{children:"With the release of Scopes and Collections comes the change to Replication. In the past you would set the Replicator to point to a database that you wanted to replicate to. Now you must set the Replicator to point to a collection or group of collections."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Before"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"// Create replicators to push and pull changes to and from the cloud.\nconst targetEndpoint = new URLEndpoint(\n new URI('ws://localhost:4984/projects'),\n);\nconst replConfig = new ReplicatorConfiguration(database, targetEndpoint);\nreplConfig.setReplicatorType(\n ReplicatorConfiguration.ReplicatorType.PUSH_AND_PULL,\n);\n\n// Add authentication.\nreplConfig.setAuthenticator(new BasicAuthenticator('demo@example.com', 'P@ssw0rd12'));\n\n// Create replicator.\nlet replicator = new Replicator(replConfig);\n\n// Listen to replicator change events.\nreplicator.addChangeListener(status => {});\n\n// Start replication.\nreplicator.start();\n"})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"After"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"//setup database and collection\nconst fileSystem = new FileSystem();\nconst directoryPath = await fileSystem.getDefaultPath();\n\nconst dc = new DatabaseConfiguration();\ndc.setDirectory(directoryPath);\nconst database = new Database('inventory', dc);\n\nawait database.open();\nconst collection = database.getDefaultCollection();\n\nconst target = new URLEndpoint('ws://localhost:4984/projects');\nconst auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12');\nconst config = new ReplicatorConfiguration(target);\nconst collectionConfig = new CollectionConfiguration();\nconfig.addCollection(collection, collectionConfig);\nconfig.setAuthenticator(auth);\n\nconst replicator = await Replicator.create(config);\n\n //listen to the replicator change events\nconst token = await replicator.addChangeListener((change) => {\n\t//check to see if there was an error\n \tconst error = change.status.getError();\n \tif (error !== undefined) {\n\t\t//do something with the error\n \t}\n \t//get the status of the replicator using ReplicatorActivityLevel enum\n \tif (change.status.getActivityLevel() === ReplicatorActivityLevel.IDLE) {\n \t\t//do something because the replicator is now IDLE\n \t}\n });\n\n // start the replicator without making a new checkpoint\n await replicator.start(false);\n"})}),"\n",(0,a.jsx)(n.h2,{id:"blob-retrieval",children:"Blob Retrieval"}),"\n",(0,a.jsx)(n.p,{children:"The Blob API has been updated to pull a blob content when the document is retrieved."}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"Before"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const doc = await database.getDocument('doc1');\nconst blobArrayBuffer = await doc.getBlobContent('textBlob', database);\n"})}),"\n",(0,a.jsx)(n.p,{children:(0,a.jsx)(n.strong,{children:"After"})}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-javascript",children:"const doc = await collection.document('doc1');\nconst blob = doc.getBlob('textBlob');\nconst blobArrayBuffer = blob.getBytes();\n"})}),"\n",(0,a.jsx)(n.h3,{id:"new-features",children:"New Features"}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin, in addition to supporting Scopes/Collections and SQL++ from the 3.x release, introduces several new features:"]}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Database Delete Function"})," - a new function in the Database API in addtion to the existing delete function that allows for the deletion of a database by passing the name and path instead of the database object."]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Database Change Encryption Key"}),": This new functionality in the Database API allows for the modification of a database's encryption key."]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Document Expiration"}),": A new method in the Collection API enables the setting and retrieval of a document's expiration date."]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Document and MutableDocument"}),": The Document and MutableDocument APIs have been updated to follow the native SDK's business rules for returning values based on the data type."]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Example App"}),": The ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," repository includes a ",(0,a.jsx)(n.code,{children:"example"})," folder, which contains a comprehensive application for testing all APIs and features of the cbl-ionic plugin. It also includes a custom test runner for executing end-to-end tests written for the plugin"]}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.strong,{children:"Documentation"})," - Undeniably, one of the most challenging aspects of developing open source software is creating comprehensive documentation. This site addresses this by providing a complete set of documentation, allowing developers to explore the various APIs the plugin has to offers."]}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"conclusion",children:"Conclusion"}),"\n",(0,a.jsxs)(n.p,{children:["An application can be migrated over from the Ionic version of the Couchbase Lite plugin to the new ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," package. The new package provides a path to the latest version of Couchbase Lite 3.x and provides support for the new features that the 3.x release provides."]}),"\n",(0,a.jsxs)(n.p,{children:["The new ",(0,a.jsx)(n.code,{children:"cbl-ionic"})," plugin is open source and maintained by the Couchbase Developer Experience and Ecosystem team. The new plugin is architecturally similar to the original plugin, but there are some differences in the API that will need to be addressed when migrating an application over to the new package. The new plugin provides support for Scopes and Collections, SQL++ Queries, and Database Maintenance. The new package also provides a more robust API for querying the database and for setting up replication. The new package is recommended for all new applications that are using Couchbase Lite and for applications that are looking to migrate over from the Ionic version of the Couchbase Lite plugin."]})]})}function h(e={}){const{wrapper:n}={...(0,i.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>s,x:()=>c});var a=t(6540);const i={},o=a.createContext(i);function s(e){const n=a.useContext(o);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:s(e.components),a.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/98e2200e.82609965.js b/assets/js/98e2200e.82609965.js deleted file mode 100644 index e86dcfa..0000000 --- a/assets/js/98e2200e.82609965.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[5438],{9902:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>d,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var r=n(4848),s=n(8453);const o={id:"prerequisites",sidebar_position:1},t="Prerequisites",l={id:"StartHere/prerequisites",title:"Prerequisites",description:"Couchbase Lite for Ionic Capacitor is provided as a Capacitor Plugin.",source:"@site/docs/StartHere/prerequisties.md",sourceDirName:"StartHere",slug:"/StartHere/prerequisites",permalink:"/StartHere/prerequisites",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/StartHere/prerequisties.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{id:"prerequisites",sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Start Here!",permalink:"/category/start-here"},next:{title:"Install",permalink:"/StartHere/install"}},d={},c=[{value:"Supported Platforms",id:"supported-platforms",level:2},{value:"Capacitor Version",id:"capacitor-version",level:2},{value:"Development Environment",id:"development-environment",level:2}];function a(e){const i={a:"a",h1:"h1",h2:"h2",li:"li",p:"p",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(i.h1,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsxs)(i.p,{children:["Couchbase Lite for Ionic Capacitor is provided as a ",(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/plugins/creating-plugins",children:"Capacitor Plugin"}),"."]}),"\n",(0,r.jsxs)(i.p,{children:["The plugin can be found at the following repository ",(0,r.jsx)(i.a,{href:"https://github.com/Couchbase-Ecosystem/cbl-ionic",children:"Couchbase Lite for Ionic Capacitor"}),". This plugin is actively developed and maintained by the community. It is not an official Couchbase product."]}),"\n",(0,r.jsx)(i.p,{children:"A developer using this plugin should have a basic understanding of the following technologies:"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs",children:"Capacitor"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://ionicframework.com/docs",children:"Ionic Framework"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://docs.couchbase.com/couchbase-lite/current/index.html",children:"Couchbase Lite"})}),"\n"]}),"\n",(0,r.jsx)(i.h2,{id:"supported-platforms",children:"Supported Platforms"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"The capacitor plugin is supported on iOS and Android platforms. Web support is not available."}),"\n"]}),"\n",(0,r.jsx)(i.h2,{id:"capacitor-version",children:"Capacitor Version"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"The plugin is built using Capacitor 5.7.5. Support for 6.0 is planned. The plugin doesn't use any Ionic Framework specific code, so it should work with any version of Ionic that supports Capacitor 5.7.5."}),"\n"]}),"\n",(0,r.jsx)(i.h2,{id:"development-environment",children:"Development Environment"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsxs)(i.li,{children:["Javascript","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://formulae.brew.sh/formula/node@18",children:"Node >= 18"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["Capacitor","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/getting-started",children:"Capacitor v5 cli"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/plugins/creating-plugins",children:"Understanding on Capacitor Plugins Development"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["IDEs","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://code.visualstudio.com/download",children:"Visual Studio Code"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/vscode/getting-started",children:"Visual Studio Code Ionic Extension"})}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://www.jetbrains.com/idea/download/",children:"IntelliJ IDEA"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["iOS Development","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"A modern Mac"}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.apple.com/xcode/",children:"XCode 14"})," or higher installed and working (XCode 15 installed is preferred)"]}),"\n",(0,r.jsx)(i.li,{children:"[iOS 13 or higher]. Any apps using the plugin must be upgraded to iOS 13 or higher."}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.apple.com/download/more/",children:"XCode Command Line Tools"})," installed"]}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.apple.com/documentation/safari-developer-tools/installing-xcode-and-simulators",children:"Simulators"})," downloaded and working"]}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://brew.sh/",children:"Homebrew"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://formulae.brew.sh/formula/cocoapods",children:"Cocopods"})}),"\n",(0,r.jsx)(i.li,{children:"A valid Apple Developer account and certificates installed and working"}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["Android Development","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"[API 23 (Android 6)] or higher. Any apps using the plugin must be upgraded to API 23 or higher. Any older versions of Android are not supported."}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.android.com/studio?gad_source=1&gclid=CjwKCAjwzN-vBhAkEiwAYiO7oALYfxbMYW_zkuYoacS9TX16aItdvLYe6GB7_j1QwvXBjFDRkawfUBoComcQAvD_BwE&gclsrc=aw.ds",children:"Android Studio"})," installed and working"]}),"\n",(0,r.jsx)(i.li,{children:"Android SDK 34 >= installed and working (with command line tools)"}),"\n",(0,r.jsx)(i.li,{children:"Java SDK v17 installed and configured to work with Android Studio"}),"\n",(0,r.jsx)(i.li,{children:"An Android Emulator downloaded and working"}),"\n"]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:i}={...(0,s.R)(),...e.components};return i?(0,r.jsx)(i,{...e,children:(0,r.jsx)(a,{...e})}):a(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>t,x:()=>l});var r=n(6540);const s={},o=r.createContext(s);function t(e){const i=r.useContext(o);return r.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function l(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:t(e.components),r.createElement(o.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/98e2200e.c11532a6.js b/assets/js/98e2200e.c11532a6.js new file mode 100644 index 0000000..535ac7c --- /dev/null +++ b/assets/js/98e2200e.c11532a6.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[5438],{9902:(e,i,n)=>{n.r(i),n.d(i,{assets:()=>d,contentTitle:()=>t,default:()=>h,frontMatter:()=>o,metadata:()=>l,toc:()=>c});var r=n(4848),s=n(8453);const o={id:"prerequisites",sidebar_position:1},t="Prerequisites",l={id:"StartHere/prerequisites",title:"Prerequisites",description:"Couchbase Lite for Ionic Capacitor is provided as a Capacitor Plugin.",source:"@site/docs/StartHere/prerequisties.md",sourceDirName:"StartHere",slug:"/StartHere/prerequisites",permalink:"/StartHere/prerequisites",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/StartHere/prerequisties.md",tags:[],version:"current",sidebarPosition:1,frontMatter:{id:"prerequisites",sidebar_position:1},sidebar:"tutorialSidebar",previous:{title:"Start Here!",permalink:"/category/start-here"},next:{title:"Install",permalink:"/StartHere/install"}},d={},c=[{value:"Supported Platforms",id:"supported-platforms",level:2},{value:"Capacitor Version",id:"capacitor-version",level:2},{value:"Development Environment",id:"development-environment",level:2}];function a(e){const i={a:"a",h1:"h1",h2:"h2",li:"li",p:"p",ul:"ul",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(i.h1,{id:"prerequisites",children:"Prerequisites"}),"\n",(0,r.jsxs)(i.p,{children:["Couchbase Lite for Ionic Capacitor is provided as a ",(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/plugins/creating-plugins",children:"Capacitor Plugin"}),"."]}),"\n",(0,r.jsxs)(i.p,{children:["The plugin can be found at the following repository ",(0,r.jsx)(i.a,{href:"https://github.com/Couchbase-Ecosystem/cbl-ionic",children:"Couchbase Lite for Ionic Capacitor"}),". This plugin is actively developed and maintained by the community. It is not an official Couchbase product."]}),"\n",(0,r.jsx)(i.p,{children:"A developer using this plugin should have a basic understanding of the following technologies:"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs",children:"Capacitor"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://ionicframework.com/docs",children:"Ionic Framework"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://docs.couchbase.com/couchbase-lite/current/index.html",children:"Couchbase Lite"})}),"\n"]}),"\n",(0,r.jsx)(i.h2,{id:"supported-platforms",children:"Supported Platforms"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"The capacitor plugin is supported on iOS and Android platforms. Web support is not available."}),"\n"]}),"\n",(0,r.jsx)(i.h2,{id:"capacitor-version",children:"Capacitor Version"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"The plugin is built using Capacitor 6.1.2. The plugin doesn't use any Ionic Framework specific code, so it should work with any version of Ionic that supports Capacitor 6.1.2."}),"\n"]}),"\n",(0,r.jsx)(i.h2,{id:"development-environment",children:"Development Environment"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsxs)(i.li,{children:["Javascript","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://formulae.brew.sh/formula/node@18",children:"Node >= 18"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["Capacitor","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/getting-started",children:"Capacitor v6 cli"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/plugins/creating-plugins",children:"Understanding on Capacitor Plugins Development"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["IDEs","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://code.visualstudio.com/download",children:"Visual Studio Code"}),"\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://capacitorjs.com/docs/vscode/getting-started",children:"Visual Studio Code Ionic Extension"})}),"\n"]}),"\n"]}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://www.jetbrains.com/idea/download/",children:"IntelliJ IDEA"})}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["iOS Development","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"A modern Mac"}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.apple.com/xcode/",children:"XCode 15"})," or higher installed and working (XCode 15 installed is preferred)"]}),"\n",(0,r.jsx)(i.li,{children:"[iOS 13 or higher]. Any apps using the plugin must be upgraded to iOS 13 or higher."}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.apple.com/download/more/",children:"XCode Command Line Tools"})," installed"]}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.apple.com/documentation/safari-developer-tools/installing-xcode-and-simulators",children:"Simulators"})," downloaded and working"]}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://brew.sh/",children:"Homebrew"})}),"\n",(0,r.jsx)(i.li,{children:(0,r.jsx)(i.a,{href:"https://formulae.brew.sh/formula/cocoapods",children:"Cocopods"})}),"\n",(0,r.jsx)(i.li,{children:"A valid Apple Developer account and certificates installed and working"}),"\n"]}),"\n"]}),"\n",(0,r.jsxs)(i.li,{children:["Android Development","\n",(0,r.jsxs)(i.ul,{children:["\n",(0,r.jsx)(i.li,{children:"[API 23 (Android 6)] or higher. Any apps using the plugin must be upgraded to API 23 or higher. Any older versions of Android are not supported."}),"\n",(0,r.jsxs)(i.li,{children:[(0,r.jsx)(i.a,{href:"https://developer.android.com/studio?gad_source=1&gclid=CjwKCAjwzN-vBhAkEiwAYiO7oALYfxbMYW_zkuYoacS9TX16aItdvLYe6GB7_j1QwvXBjFDRkawfUBoComcQAvD_BwE&gclsrc=aw.ds",children:"Android Studio"})," installed and working"]}),"\n",(0,r.jsx)(i.li,{children:"Android SDK 34 >= installed and working (with command line tools)"}),"\n",(0,r.jsx)(i.li,{children:"Java SDK v17 installed and configured to work with Android Studio"}),"\n",(0,r.jsx)(i.li,{children:"An Android Emulator downloaded and working"}),"\n"]}),"\n"]}),"\n"]})]})}function h(e={}){const{wrapper:i}={...(0,s.R)(),...e.components};return i?(0,r.jsx)(i,{...e,children:(0,r.jsx)(a,{...e})}):a(e)}},8453:(e,i,n)=>{n.d(i,{R:()=>t,x:()=>l});var r=n(6540);const s={},o=r.createContext(s);function t(e){const i=r.useContext(o);return r.useMemo((function(){return"function"==typeof e?e(i):{...i,...e}}),[i,e])}function l(e){let i;return i=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:t(e.components),r.createElement(o.Provider,{value:i},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/a7cb794f.30c99c14.js b/assets/js/a7cb794f.30c99c14.js deleted file mode 100644 index b354c49..0000000 --- a/assets/js/a7cb794f.30c99c14.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[8226],{7993:(e,n,o)=>{o.r(n),o.d(n,{assets:()=>a,contentTitle:()=>l,default:()=>p,frontMatter:()=>s,metadata:()=>i,toc:()=>d});var c=o(4848),t=o(8453);const s={id:"scopes-collections",sidebar_position:6},l="Scopes and Collections",i={id:"scopes-collections",title:"Scopes and Collections",description:"Description \u2014 Scopes and collections allow you to organize your documents within a database.",source:"@site/docs/scopes-collections.md",sourceDirName:".",slug:"/scopes-collections",permalink:"/scopes-collections",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/scopes-collections.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{id:"scopes-collections",sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Pre-built Database",permalink:"/database-prebuilt"},next:{title:"Documents",permalink:"/documents"}},a={},d=[{value:"Default Scopes and Collections",id:"default-scopes-and-collections",level:2},{value:"Create a Scope and Collection",id:"create-a-scope-and-collection",level:2},{value:"Index a Collection",id:"index-a-collection",level:2},{value:"Drop a Collection",id:"drop-a-collection",level:2},{value:"List Scopes and Collections",id:"list-scopes-and-collections",level:2}];function r(e){const n={admonition:"admonition",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(n.h1,{id:"scopes-and-collections",children:"Scopes and Collections"}),"\n",(0,c.jsxs)(n.blockquote,{children:["\n",(0,c.jsxs)(n.p,{children:["Description \u2014 ",(0,c.jsx)(n.em,{children:"Scopes and collections allow you to organize your documents within a database."})]}),"\n"]}),"\n",(0,c.jsxs)(n.admonition,{title:"AT A GLANCE",type:"important",children:[(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Use collections to organize your content in a database"})}),(0,c.jsx)(n.p,{children:"For example, if your database contains travel information, airport documents can be assigned to an airports collection, hotel documents can be assigned to a hotels collection, and so on."}),(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"Document names must be unique within their collection."}),"\n"]}),(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Use scopes to group multiple collections"})}),(0,c.jsx)(n.p,{children:"Collections can be assigned to different scopes according to content-type or deployment-phase (for example, test versus production)."}),(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"Collection names must be unique within their scope."}),"\n"]}),(0,c.jsx)(n.p,{children:"Unlike Couchbase Server and Capella, a scope cannot exist without a collection, thus there is no API to create a scope. Instead, use the collection API to create a collection in a scope and the scope is created from that API."})]}),"\n",(0,c.jsx)(n.h2,{id:"default-scopes-and-collections",children:"Default Scopes and Collections"}),"\n",(0,c.jsxs)(n.p,{children:["Every database you create contains a default scope and a default collection named ",(0,c.jsx)(n.code,{children:"_default"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"If you create a document in the database and don't specify a specific scope or collection, it is saved in the default collection, in the default scope."}),"\n",(0,c.jsx)(n.p,{children:"If you upgrade from a version of Couchbase Lite prior to support for scops and collections, all existing data is automatically placed in the default scope and default collection."}),"\n",(0,c.jsx)(n.p,{children:"The default scope and collection cannot be dropped."}),"\n",(0,c.jsx)(n.h2,{id:"create-a-scope-and-collection",children:"Create a Scope and Collection"}),"\n",(0,c.jsx)(n.p,{children:"In addition to the default scope and collection, you can create your own scope and collection when you create a document."}),"\n",(0,c.jsx)(n.p,{children:"Naming conventions for collections and scopes:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"Must be between 1 and 251 characters in length."}),"\n",(0,c.jsxs)(n.li,{children:["Can only contain the characters ",(0,c.jsx)(n.code,{children:"A-Z"}),", ",(0,c.jsx)(n.code,{children:"a-z"}),", ",(0,c.jsx)(n.code,{children:"0-9"}),", and the symbols ",(0,c.jsx)(n.code,{children:"_"}),", ",(0,c.jsx)(n.code,{children:"-"}),", and ",(0,c.jsx)(n.code,{children:"%"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["Cannot start with ",(0,c.jsx)(n.code,{children:"_"})," or ",(0,c.jsx)(n.code,{children:"%"}),"."]}),"\n",(0,c.jsx)(n.li,{children:"Scope names must be unique in databases."}),"\n",(0,c.jsx)(n.li,{children:"Collection names must be unique within a scope."}),"\n"]}),"\n",(0,c.jsx)(n.admonition,{type:"note",children:(0,c.jsx)(n.p,{children:"Scope and collection names are case sensitive."})}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 1. Create a scope and collection"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:'const scopeName = "myScopeName"; \nconst collectionName = "myCollectionName"; \nconst collection = await database.createCollection(collectionName, scopeName);\n'})}),"\n",(0,c.jsxs)(n.p,{children:["In the example above, you can see that ",(0,c.jsx)(n.code,{children:"Database.createCollection"})," can take two parameters. The second is the scope assigned to the created collection, if this parameter is omitted then a collection of the given name will be assigned to the ",(0,c.jsx)(n.code,{children:"_default"})," scope."]}),"\n",(0,c.jsxs)(n.p,{children:["The first parameter is the name of the collection you want to create, in this case ",(0,c.jsx)(n.code,{children:"myCollectionName"}),"."]}),"\n",(0,c.jsxs)(n.p,{children:["If a collection with the specified name already exists in the specified scope, ",(0,c.jsx)(n.code,{children:"Database.createCollection"})," returns the existing collection."]}),"\n",(0,c.jsx)(n.admonition,{type:"note",children:(0,c.jsxs)(n.p,{children:["You cannot create an empty user-defined scope. A scope is implicitly created and removed by the ",(0,c.jsx)(n.code,{children:"Database.createCollection"})," and ",(0,c.jsx)(n.code,{children:"Database.deleteCollection"})," methods."]})}),"\n",(0,c.jsx)(n.h2,{id:"index-a-collection",children:"Index a Collection"}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 2. Index a Collection"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:" // Define a value index on 'name' and 'documentType'\nconst valueIndex = IndexBuilder.valueIndex(\n ValueIndexItem.property('name'),\n ValueIndexItem.property('documentType')\n);\n\n// Create the value index\nconst valueIndexName = 'nameTypeIndex';\nawait collection.createIndex(valueIndexName, valueIndex);\n"})}),"\n",(0,c.jsx)(n.h2,{id:"drop-a-collection",children:"Drop a Collection"}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 3. Drop a Collection"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:"await database.deleteCollection(collectionName, scopeName);\n"})}),"\n",(0,c.jsx)(n.h2,{id:"list-scopes-and-collections",children:"List Scopes and Collections"}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 4. List Scopes and Collections"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:"// Get Scopes\nconst scopes = await database.scopes();\n\n// Get Collections of a particular Scope\nconst collections = await database.collections(scopeName);\n"})})]})}function p(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,c.jsx)(n,{...e,children:(0,c.jsx)(r,{...e})}):r(e)}},8453:(e,n,o)=>{o.d(n,{R:()=>l,x:()=>i});var c=o(6540);const t={},s=c.createContext(t);function l(e){const n=c.useContext(s);return c.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:l(e.components),c.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/a7cb794f.993fd822.js b/assets/js/a7cb794f.993fd822.js new file mode 100644 index 0000000..e354487 --- /dev/null +++ b/assets/js/a7cb794f.993fd822.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[8226],{7993:(e,n,o)=>{o.r(n),o.d(n,{assets:()=>a,contentTitle:()=>l,default:()=>p,frontMatter:()=>s,metadata:()=>i,toc:()=>d});var c=o(4848),t=o(8453);const s={id:"scopes-collections",sidebar_position:6},l="Scopes and Collections",i={id:"scopes-collections",title:"Scopes and Collections",description:"Description \u2014 Scopes and collections allow you to organize your documents within a database.",source:"@site/docs/scopes-collections.md",sourceDirName:".",slug:"/scopes-collections",permalink:"/scopes-collections",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/scopes-collections.md",tags:[],version:"current",sidebarPosition:6,frontMatter:{id:"scopes-collections",sidebar_position:6},sidebar:"tutorialSidebar",previous:{title:"Pre-built Database",permalink:"/database-prebuilt"},next:{title:"Documents",permalink:"/documents"}},a={},d=[{value:"Default Scopes and Collections",id:"default-scopes-and-collections",level:2},{value:"Create a Scope and Collection",id:"create-a-scope-and-collection",level:2},{value:"Index a Collection",id:"index-a-collection",level:2},{value:"Drop a Collection",id:"drop-a-collection",level:2},{value:"List Scopes and Collections",id:"list-scopes-and-collections",level:2},{value:"Count Documents in a Collection",id:"count-documents-in-a-collection",level:2}];function r(e){const n={admonition:"admonition",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,t.R)(),...e.components};return(0,c.jsxs)(c.Fragment,{children:[(0,c.jsx)(n.h1,{id:"scopes-and-collections",children:"Scopes and Collections"}),"\n",(0,c.jsxs)(n.blockquote,{children:["\n",(0,c.jsxs)(n.p,{children:["Description \u2014 ",(0,c.jsx)(n.em,{children:"Scopes and collections allow you to organize your documents within a database."})]}),"\n"]}),"\n",(0,c.jsxs)(n.admonition,{title:"AT A GLANCE",type:"important",children:[(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Use collections to organize your content in a database"})}),(0,c.jsx)(n.p,{children:"For example, if your database contains travel information, airport documents can be assigned to an airports collection, hotel documents can be assigned to a hotels collection, and so on."}),(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"Document names must be unique within their collection."}),"\n"]}),(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Use scopes to group multiple collections"})}),(0,c.jsx)(n.p,{children:"Collections can be assigned to different scopes according to content-type or deployment-phase (for example, test versus production)."}),(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"Collection names must be unique within their scope."}),"\n"]}),(0,c.jsx)(n.p,{children:"Unlike Couchbase Server and Capella, a scope cannot exist without a collection, thus there is no API to create a scope. Instead, use the collection API to create a collection in a scope and the scope is created from that API."})]}),"\n",(0,c.jsx)(n.h2,{id:"default-scopes-and-collections",children:"Default Scopes and Collections"}),"\n",(0,c.jsxs)(n.p,{children:["Every database you create contains a default scope and a default collection named ",(0,c.jsx)(n.code,{children:"_default"}),"."]}),"\n",(0,c.jsx)(n.p,{children:"If you create a document in the database and don't specify a specific scope or collection, it is saved in the default collection, in the default scope."}),"\n",(0,c.jsx)(n.p,{children:"If you upgrade from a version of Couchbase Lite prior to support for scops and collections, all existing data is automatically placed in the default scope and default collection."}),"\n",(0,c.jsx)(n.p,{children:"The default scope and collection cannot be dropped."}),"\n",(0,c.jsx)(n.h2,{id:"create-a-scope-and-collection",children:"Create a Scope and Collection"}),"\n",(0,c.jsx)(n.p,{children:"In addition to the default scope and collection, you can create your own scope and collection when you create a document."}),"\n",(0,c.jsx)(n.p,{children:"Naming conventions for collections and scopes:"}),"\n",(0,c.jsxs)(n.ul,{children:["\n",(0,c.jsx)(n.li,{children:"Must be between 1 and 251 characters in length."}),"\n",(0,c.jsxs)(n.li,{children:["Can only contain the characters ",(0,c.jsx)(n.code,{children:"A-Z"}),", ",(0,c.jsx)(n.code,{children:"a-z"}),", ",(0,c.jsx)(n.code,{children:"0-9"}),", and the symbols ",(0,c.jsx)(n.code,{children:"_"}),", ",(0,c.jsx)(n.code,{children:"-"}),", and ",(0,c.jsx)(n.code,{children:"%"}),"."]}),"\n",(0,c.jsxs)(n.li,{children:["Cannot start with ",(0,c.jsx)(n.code,{children:"_"})," or ",(0,c.jsx)(n.code,{children:"%"}),"."]}),"\n",(0,c.jsx)(n.li,{children:"Scope names must be unique in databases."}),"\n",(0,c.jsx)(n.li,{children:"Collection names must be unique within a scope."}),"\n"]}),"\n",(0,c.jsx)(n.admonition,{type:"note",children:(0,c.jsx)(n.p,{children:"Scope and collection names are case sensitive."})}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 1. Create a scope and collection"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:'const scopeName = "myScopeName"; \nconst collectionName = "myCollectionName"; \nconst collection = await database.createCollection(collectionName, scopeName);\n'})}),"\n",(0,c.jsxs)(n.p,{children:["In the example above, you can see that Database ",(0,c.jsx)(n.code,{children:".createCollection"})," function can take two parameters. The second is the scope assigned to the created collection, if this parameter is omitted then a collection of the given name will be assigned to the ",(0,c.jsx)(n.code,{children:"_default"})," scope."]}),"\n",(0,c.jsxs)(n.p,{children:["The first parameter is the name of the collection you want to create, in this case ",(0,c.jsx)(n.code,{children:"myCollectionName"}),"."]}),"\n",(0,c.jsxs)(n.p,{children:["If a collection with the specified name already exists in the specified scope, ",(0,c.jsx)(n.code,{children:"Database.createCollection"})," returns the existing collection."]}),"\n",(0,c.jsx)(n.admonition,{type:"note",children:(0,c.jsxs)(n.p,{children:["You cannot create an empty user-defined scope. A scope is implicitly created and removed by the ",(0,c.jsx)(n.code,{children:"Database.createCollection"})," and ",(0,c.jsx)(n.code,{children:"Database.deleteCollection"})," methods."]})}),"\n",(0,c.jsx)(n.h2,{id:"index-a-collection",children:"Index a Collection"}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 2. Index a Collection"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:" // Define a value index on 'name' and 'documentType'\nconst valueIndex = IndexBuilder.valueIndex(\n ValueIndexItem.property('name'),\n ValueIndexItem.property('documentType')\n);\n\n// Create the value index\nconst valueIndexName = 'nameTypeIndex';\nawait collection.createIndex(valueIndexName, valueIndex);\n"})}),"\n",(0,c.jsx)(n.h2,{id:"drop-a-collection",children:"Drop a Collection"}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 3. Drop a Collection"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:"await database.deleteCollection(collectionName, scopeName);\n"})}),"\n",(0,c.jsx)(n.h2,{id:"list-scopes-and-collections",children:"List Scopes and Collections"}),"\n",(0,c.jsx)(n.p,{children:(0,c.jsx)(n.strong,{children:"Example 4. List Scopes and Collections"})}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:"// Get Scopes\nconst scopes = await database.scopes();\n\n// Get Collections of a particular Scope\nconst collections = await database.collections(scopeName);\n"})}),"\n",(0,c.jsx)(n.h2,{id:"count-documents-in-a-collection",children:"Count Documents in a Collection"}),"\n",(0,c.jsxs)(n.p,{children:["The ",(0,c.jsx)(n.code,{children:".count"})," method returns the number of documents in a given collection, a replacement for the Database ",(0,c.jsx)(n.code,{children:".getCount"})," method."]}),"\n",(0,c.jsx)(n.pre,{children:(0,c.jsx)(n.code,{className:"language-typescript",children:"const count = await collection.count();\n"})})]})}function p(e={}){const{wrapper:n}={...(0,t.R)(),...e.components};return n?(0,c.jsx)(n,{...e,children:(0,c.jsx)(r,{...e})}):r(e)}},8453:(e,n,o)=>{o.d(n,{R:()=>l,x:()=>i});var c=o(6540);const t={},s=c.createContext(t);function l(e){const n=c.useContext(s);return c.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(t):e.components||t:l(e.components),c.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/a7e7b2ec.6d0d5ce2.js b/assets/js/a7e7b2ec.f29bd64f.js similarity index 59% rename from assets/js/a7e7b2ec.6d0d5ce2.js rename to assets/js/a7e7b2ec.f29bd64f.js index 4100107..5e0f206 100644 --- a/assets/js/a7e7b2ec.6d0d5ce2.js +++ b/assets/js/a7e7b2ec.f29bd64f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[5815],{5726:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>i,default:()=>b,frontMatter:()=>a,metadata:()=>l,toc:()=>r});var o=t(4848),s=t(8453);const a={id:"blobs",sidebar_position:8},i="Blobs",l={id:"blobs",title:"Blobs",description:"Description \u2014 Couchbase Lite database data model concepts - blobs",source:"@site/docs/blobs.md",sourceDirName:".",slug:"/blobs",permalink:"/blobs",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/blobs.md",tags:[],version:"current",sidebarPosition:8,frontMatter:{id:"blobs",sidebar_position:8},sidebar:"tutorialSidebar",previous:{title:"Documents",permalink:"/documents"},next:{title:"Queries",permalink:"/category/queries"}},c={},r=[{value:"Introduction",id:"introduction",level:2},{value:"Blob Objects",id:"blob-objects",level:2},{value:"Constraints",id:"constraints",level:3},{value:"Using Blobs",id:"using-blobs",level:2},{value:"Example 1. Working with Blobs",id:"example-1-working-with-blobs",level:4},{value:"Example 2. Get Blob's content",id:"example-2-get-blobs-content",level:4},{value:"Syncing",id:"syncing",level:2},{value:"Figure 1. Sample Blob Document",id:"figure-1-sample-blob-document",level:4}];function d(e){const n={a:"a",blockquote:"blockquote",br:"br",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"blobs",children:"Blobs"}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:["Description \u2014 ",(0,o.jsx)(n.em,{children:"Couchbase Lite database data model concepts - blobs"}),(0,o.jsx)(n.br,{}),"\n","Related Content\u2009\u2014 ",(0,o.jsx)(n.a,{href:"/databases",children:"Databases"})," | ",(0,o.jsx)(n.a,{href:"/documents",children:"Documents"})," | ",(0,o.jsx)(n.a,{href:"indexing.md",children:"Indexing"})]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,o.jsx)(n.p,{children:"Couchbase Lite for Ionic uses blobs to store the contents of images, other media files and similar format files as binary objects."}),"\n",(0,o.jsx)(n.p,{children:"The blob itself is not stored in the document. It is held in a separate content-addressable store indexed from the document and retrieved only on-demand."}),"\n",(0,o.jsxs)(n.p,{children:["When a document is synchronized, the Couchbase Lite replicator adds an ",(0,o.jsx)(n.code,{children:"_attachments"})," dictionary to the document's properties if it contains a blob \u2014\u2009see ",(0,o.jsx)(n.a,{href:"#figure-1-sample-blob-document",children:"Figure 1"}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"blob-objects",children:"Blob Objects"}),"\n",(0,o.jsxs)(n.p,{children:["The blob as an object appears in a document as dictionary property \u2014\u2009see, for example avatar in ",(0,o.jsx)(n.a,{href:"#figure-1-sample-blob-document",children:"Figure 1"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Other properties include ",(0,o.jsx)(n.code,{children:"length"})," (the length in bytes), and optionally ",(0,o.jsx)(n.code,{children:"content_type"})," (typically, its MIME type)."]}),"\n",(0,o.jsxs)(n.p,{children:["The blob's data (an image, audio or video content) is not stored in the document, but in a separate content-addressable store, indexed by the ",(0,o.jsx)(n.code,{children:"digest"})," property \u2014\u2009see ",(0,o.jsx)(n.a,{href:"#using-blobs",children:"Using Blobs"}),"."]}),"\n",(0,o.jsx)(n.h3,{id:"constraints",children:"Constraints"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Couchbase Lite"}),"\n",(0,o.jsx)(n.p,{children:"Blobs can be arbitrarily large. They are only read on demand, not when you load a the document."}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Capella App Services/Sync Gateway"}),"\n",(0,o.jsx)(n.p,{children:"The maximum content size is 20 MB per blob. If a document's blob is over 20 MB, the document will be replicated but not the blob."}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"using-blobs",children:"Using Blobs"}),"\n",(0,o.jsx)(n.p,{children:"The Blob API lets you access the blob's data content as in-memory data or as a Stream of Uint8Array."}),"\n",(0,o.jsxs)(n.p,{children:["The code in ",(0,o.jsx)(n.a,{href:"#example-1-working-with-blobs",children:"Example 1"})," shows how you might add a blob to a document and save it to the database. Here we use avatar as the property key and a jpeg file as the blob data."]}),"\n",(0,o.jsx)(n.h4,{id:"example-1-working-with-blobs",children:"Example 1. Working with Blobs"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-typescript",children:"// Example function to simulate fetching binary data for an avatar image\nconst imageData = await fetchAvatarImageData();\n\n// Create a Blob instance with the image data and content type\nconst avatarBlob = new Blob('image/jpeg', imageData);\n\n// Retrieve an existing document\nconst document = await collection.document(documentId);\n\n// Assign the Blob to the document under the 'avatar' key\ndocument.setBlob('avatar', avatarBlob);\n\n// Save the updated document back to the database\nawait collection.save(document);\n"})}),"\n",(0,o.jsx)(n.h4,{id:"example-2-get-blobs-content",children:"Example 2. Get Blob's content"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-typescript",children:"// code for setting blob\nconst encoder = new TextEncoder();\nconst blobEncoded = new Blob(\"text/plain\", encoder.encode(\"Hello World\"));\ndoc.setBlob('textBlob', blobEncoded);\n\n// code for getting blob's content\nconst textDecoder = new TextDecoder();\nconst blobArrayBuffer = await doc.getBlobContent('textBlob', collection);\nconst textBlobResults = textDecoder.decode(blobArrayBuffer);\n"})}),"\n",(0,o.jsx)(n.h2,{id:"syncing",children:"Syncing"}),"\n",(0,o.jsxs)(n.p,{children:["When a document containing a blob object is synchronized, the Couchbase Lite replicator generates an ",(0,o.jsx)(n.code,{children:"_attachments"})," dictionary with an auto-generated name for each blob attachment. This is different to the ",(0,o.jsx)(n.code,{children:"avatar"})," key and is used internally to access the blob content."]}),"\n",(0,o.jsxs)(n.p,{children:["If you view a sync'd blob document in either Capella's Admin Interface or Couchbase Server's Admin Console, you will see something similar to ",(0,o.jsx)(n.a,{href:"#figure-1-sample-blob-document",children:"Figure 1"}),", which shows the document with its generated ",(0,o.jsx)(n.code,{children:"_attachments"})," dictionary, including the digest."]}),"\n",(0,o.jsx)(n.h4,{id:"figure-1-sample-blob-document",children:"Figure 1. Sample Blob Document"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-typescript",children:'{\n "_attachments": {\n "blob_1": {\n "content_type": "image/jpeg",\n "digest": "sha1-F1Tfe61RZP4zC9UYT6JFmLTh2s8=",\n "length": 8112955,\n "revpos": 2,\n "stub": true\n }\n },\n "avatar": {\n "@type": "blob",\n "content_type": "image/jpeg",\n "digest": "sha1-F1Tfe61RZP4zC9UYT6JFmLTh2s8=",\n "length": 8112955\n }\n}\n'})})]})}function b(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>l});var o=t(6540);const s={},a=o.createContext(s);function i(e){const n=o.useContext(a);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function l(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),o.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file +"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[5815],{5726:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>l,contentTitle:()=>i,default:()=>b,frontMatter:()=>a,metadata:()=>c,toc:()=>r});var o=t(4848),s=t(8453);const a={id:"blobs",sidebar_position:8},i="Blobs",c={id:"blobs",title:"Blobs",description:"Description \u2014 Couchbase Lite database data model concepts - blobs",source:"@site/docs/blobs.md",sourceDirName:".",slug:"/blobs",permalink:"/blobs",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/blobs.md",tags:[],version:"current",sidebarPosition:8,frontMatter:{id:"blobs",sidebar_position:8},sidebar:"tutorialSidebar",previous:{title:"Documents",permalink:"/documents"},next:{title:"Queries",permalink:"/category/queries"}},l={},r=[{value:"Introduction",id:"introduction",level:2},{value:"Blob Objects",id:"blob-objects",level:2},{value:"Constraints",id:"constraints",level:3},{value:"Using Blobs",id:"using-blobs",level:2},{value:"Example 1. Working with Blobs",id:"example-1-working-with-blobs",level:4},{value:"Example 2. Get Blob's content",id:"example-2-get-blobs-content",level:4},{value:"Syncing",id:"syncing",level:2},{value:"Figure 1. Sample Blob Document",id:"figure-1-sample-blob-document",level:4}];function d(e){const n={a:"a",blockquote:"blockquote",br:"br",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",ul:"ul",...(0,s.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(n.h1,{id:"blobs",children:"Blobs"}),"\n",(0,o.jsxs)(n.blockquote,{children:["\n",(0,o.jsxs)(n.p,{children:["Description \u2014 ",(0,o.jsx)(n.em,{children:"Couchbase Lite database data model concepts - blobs"}),(0,o.jsx)(n.br,{}),"\n","Related Content\u2009\u2014 ",(0,o.jsx)(n.a,{href:"/databases",children:"Databases"})," | ",(0,o.jsx)(n.a,{href:"/documents",children:"Documents"})," | ",(0,o.jsx)(n.a,{href:"indexing.md",children:"Indexing"})]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,o.jsx)(n.p,{children:"Couchbase Lite for Ionic uses blobs to store the contents of images, other media files and similar format files as binary objects."}),"\n",(0,o.jsx)(n.p,{children:"The blob itself is not stored in the document. It is held in a separate content-addressable store indexed from the document and retrieved only on-demand."}),"\n",(0,o.jsxs)(n.p,{children:["When a document is synchronized, the Couchbase Lite replicator adds an ",(0,o.jsx)(n.code,{children:"_attachments"})," dictionary to the document's properties if it contains a blob \u2014\u2009see ",(0,o.jsx)(n.a,{href:"#figure-1-sample-blob-document",children:"Figure 1"}),"."]}),"\n",(0,o.jsx)(n.h2,{id:"blob-objects",children:"Blob Objects"}),"\n",(0,o.jsxs)(n.p,{children:["The blob as an object appears in a document as dictionary property \u2014\u2009see, for example avatar in ",(0,o.jsx)(n.a,{href:"#figure-1-sample-blob-document",children:"Figure 1"}),"."]}),"\n",(0,o.jsxs)(n.p,{children:["Other properties include ",(0,o.jsx)(n.code,{children:"length"})," (the length in bytes), and optionally ",(0,o.jsx)(n.code,{children:"content_type"})," (typically, its MIME type)."]}),"\n",(0,o.jsxs)(n.p,{children:["The blob's data (an image, audio or video content) is not stored in the document, but in a separate content-addressable store, indexed by the ",(0,o.jsx)(n.code,{children:"digest"})," property \u2014\u2009see ",(0,o.jsx)(n.a,{href:"#using-blobs",children:"Using Blobs"}),"."]}),"\n",(0,o.jsx)(n.h3,{id:"constraints",children:"Constraints"}),"\n",(0,o.jsxs)(n.ul,{children:["\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Couchbase Lite"}),"\n",(0,o.jsx)(n.p,{children:"Blobs can be arbitrarily large. They are only read on demand, not when you load a the document."}),"\n"]}),"\n",(0,o.jsxs)(n.li,{children:["\n",(0,o.jsx)(n.p,{children:"Capella App Services/Sync Gateway"}),"\n",(0,o.jsx)(n.p,{children:"The maximum content size is 20 MB per blob. If a document's blob is over 20 MB, the document will be replicated but not the blob."}),"\n"]}),"\n"]}),"\n",(0,o.jsx)(n.h2,{id:"using-blobs",children:"Using Blobs"}),"\n",(0,o.jsx)(n.p,{children:"The Blob API lets you access the blob's data content as in-memory data or as a Stream of Uint8Array."}),"\n",(0,o.jsxs)(n.p,{children:["The code in ",(0,o.jsx)(n.a,{href:"#example-1-working-with-blobs",children:"Example 1"})," shows how you might add a blob to a document and save it to the database. Here we use avatar as the property key and a jpeg file as the blob data."]}),"\n",(0,o.jsx)(n.h4,{id:"example-1-working-with-blobs",children:"Example 1. Working with Blobs"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-typescript",children:"// Example function to simulate fetching binary data for an avatar image\nconst imageData = await fetchAvatarImageData();\n\n// Create a Blob instance with the image data and content type\nconst avatarBlob = new Blob('image/jpeg', imageData);\n\n// Retrieve an existing document\nconst document = await collection.document(documentId);\n\n//conver the document to a mutable document\nconst mutDoc = MutableDocument.fromDocument(document);\n\n// Assign the Blob to the document under the 'avatar' key\nmutDoc.setBlob('avatar', avatarBlob);\n\n// Save the updated document back to the database\nawait collection.save(document);\n"})}),"\n",(0,o.jsx)(n.h4,{id:"example-2-get-blobs-content",children:"Example 2. Get Blob's content"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-typescript",children:"// code for setting blob\nconst mutDoc = new MutableDocument('doc1');\nconst encoder = new TextEncoder();\nconst blobEncoded = new Blob(\"text/plain\", encoder.encode(\"Hello World\"));\nmutDoc.setBlob('textBlob', blobEncoded);\nawait collection.save(mutDoc);\n\n// code for getting blob's content\nconst doc = await collection.document('doc1');\nconst textDecoder = new TextDecoder();\nconst blob = doc.getBlob('textBlob');\nconst blobArrayBuffer = blob.getBytes();\nconst textBlobResults = textDecoder.decode(blobArrayBuffer);\n"})}),"\n",(0,o.jsx)(n.h2,{id:"syncing",children:"Syncing"}),"\n",(0,o.jsxs)(n.p,{children:["When a document containing a blob object is synchronized, the Couchbase Lite replicator generates an ",(0,o.jsx)(n.code,{children:"_attachments"})," dictionary with an auto-generated name for each blob attachment. This is different to the ",(0,o.jsx)(n.code,{children:"avatar"})," key and is used internally to access the blob content."]}),"\n",(0,o.jsxs)(n.p,{children:["If you view a sync'd blob document in either Capella's Admin Interface or Couchbase Server's Admin Console, you will see something similar to ",(0,o.jsx)(n.a,{href:"#figure-1-sample-blob-document",children:"Figure 1"}),", which shows the document with its generated ",(0,o.jsx)(n.code,{children:"_attachments"})," dictionary, including the digest."]}),"\n",(0,o.jsx)(n.h4,{id:"figure-1-sample-blob-document",children:"Figure 1. Sample Blob Document"}),"\n",(0,o.jsx)(n.pre,{children:(0,o.jsx)(n.code,{className:"language-typescript",children:'{\n "_attachments": {\n "blob_1": {\n "content_type": "image/jpeg",\n "digest": "sha1-F1Tfe61RZP4zC9UYT6JFmLTh2s8=",\n "length": 8112955,\n "revpos": 2,\n "stub": true\n }\n },\n "avatar": {\n "@type": "blob",\n "content_type": "image/jpeg",\n "digest": "sha1-F1Tfe61RZP4zC9UYT6JFmLTh2s8=",\n "length": 8112955\n }\n}\n'})})]})}function b(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,o.jsx)(n,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>i,x:()=>c});var o=t(6540);const s={},a=o.createContext(s);function i(e){const n=o.useContext(a);return o.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:i(e.components),o.createElement(a.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/d211126e.6deec1a1.js b/assets/js/d211126e.6deec1a1.js new file mode 100644 index 0000000..faebd2f --- /dev/null +++ b/assets/js/d211126e.6deec1a1.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[473],{8276:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>c,default:()=>u,frontMatter:()=>s,metadata:()=>i,toc:()=>l});var a=t(4848),o=t(8453);const s={id:"documents",sidebar_position:7},c="Documents",i={id:"documents",title:"Documents",description:"Description \u2014 Couchbase Lite Concepts - Data Model - Documents",source:"@site/docs/documents.md",sourceDirName:".",slug:"/documents",permalink:"/documents",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/documents.md",tags:[],version:"current",sidebarPosition:7,frontMatter:{id:"documents",sidebar_position:7},sidebar:"tutorialSidebar",previous:{title:"Scopes and Collections",permalink:"/scopes-collections"},next:{title:"Blobs",permalink:"/blobs"}},r={},l=[{value:"Overview",id:"overview",level:2},{value:"Document Structure",id:"document-structure",level:3},{value:"Data Encoding",id:"data-encoding",level:3},{value:"Data Types",id:"data-types",level:3},{value:"JSON",id:"json",level:3},{value:"Constructing a Document",id:"constructing-a-document",level:2},{value:"Data Model",id:"data-model",level:3},{value:"Create a Document",id:"create-a-document",level:3},{value:"Create a Dictionary",id:"create-a-dictionary",level:3},{value:"Create an Array",id:"create-an-array",level:3},{value:"Populate a Document",id:"populate-a-document",level:3},{value:"Save a Document",id:"save-a-document",level:3},{value:"Working with Data",id:"working-with-data",level:2},{value:"Date accessors",id:"date-accessors",level:3},{value:"Example 1. Date Getter",id:"example-1-date-getter",level:4},{value:"Using Dictionaries",id:"using-dictionaries",level:3},{value:"Example 2. Read Only",id:"example-2-read-only",level:4},{value:"Example 3. Mutable",id:"example-3-mutable",level:4},{value:"Using Arrays",id:"using-arrays",level:3},{value:"Example 4. Read Only",id:"example-4-read-only",level:4},{value:"Example 5. Mutable",id:"example-5-mutable",level:4},{value:"Using Blobs",id:"using-blobs",level:3},{value:"Document Initializers",id:"document-initializers",level:2},{value:"Convert Mutable Document from Document",id:"convert-mutable-document-from-document",level:3},{value:"Example 6. Persist a document",id:"example-6-persist-a-document",level:4},{value:"Document change events",id:"document-change-events",level:2},{value:"Document Expiration",id:"document-expiration",level:2},{value:"Purge a Document",id:"purge-a-document",level:2},{value:"Delete a Document",id:"delete-a-document",level:2},{value:"Document Constraints",id:"document-constraints",level:2},{value:"Example 13. Reserved Keys List",id:"example-13-reserved-keys-list",level:4}];function d(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",br:"br",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,o.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{id:"documents",children:"Documents"}),"\n",(0,a.jsxs)(n.blockquote,{children:["\n",(0,a.jsxs)(n.p,{children:["Description \u2014 ",(0,a.jsx)(n.em,{children:"Couchbase Lite Concepts - Data Model - Documents"}),(0,a.jsx)(n.br,{}),"\n","Related Content \u2014 ",(0,a.jsx)(n.a,{href:"/databases",children:"Databases"})," | ",(0,a.jsx)(n.a,{href:"/blobs",children:"Blobs"})," | ",(0,a.jsx)(n.a,{href:"indexing.md",children:"Indexing"})]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,a.jsx)(n.h3,{id:"document-structure",children:"Document Structure"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"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."})}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.h3,{id:"data-encoding",children:"Data Encoding"}),"\n",(0,a.jsxs)(n.p,{children:["The document body is stored in an internal, efficient, binary form called ",(0,a.jsx)(n.a,{href:"https://github.com/couchbase/fleece#readme",children:"Fleece"}),". This internal form can be easily converted into a manageable native dictionary format for manipulation in applications."]}),"\n",(0,a.jsx)(n.p,{children:"Fleece data is stored in the smallest format that will hold the value whilst maintaining the integrity of the value."}),"\n",(0,a.jsx)(n.h3,{id:"data-types",children:"Data Types"}),"\n",(0,a.jsx)(n.p,{children:"The Document class offers a set of property accessors for various scalar types, such as:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Boolean"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Date"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Double"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Float"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Int"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Long"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"String"}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"These accessors take care of converting to/from JSON encoding, and make sure you get the type you expect."}),"\n",(0,a.jsx)(n.p,{children:"In addition to these basic data types Couchbase Lite provides for the following:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Dictionary"})," - Represents a read-only key-value pair collection"]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"MutableDictionary"})," - Represents a writeable key-value pair collection."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Array"})," - Represents a writeable collection of objects."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"MutableArray"})," - Represents a writeable collection of objects."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Blob"})," - Represents an arbitrary piece of binary data."]}),"\n"]}),"\n",(0,a.jsx)(n.h3,{id:"json",children:"JSON"}),"\n",(0,a.jsx)(n.p,{children:"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)\u2009\u2014\u2009see Working with JSON Data."}),"\n",(0,a.jsx)(n.h2,{id:"constructing-a-document",children:"Constructing a Document"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.p,{children:"In this section we provide an example of how you might create a hotel document, which provides basic contact details and price data."}),"\n",(0,a.jsx)(n.h3,{id:"data-model",children:"Data Model"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"hotel: {\n type: string (value = `hotel`)\n name: string\n address: dictionary {\n street: string\n city: string\n state: string\n country: string\n code: string\n }\n phones: array\n rate: float\n}\n"})}),"\n",(0,a.jsx)(n.h3,{id:"create-a-document",children:"Create a Document"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"let document = new MutableDocument(hotel.id);\n"})}),"\n",(0,a.jsx)(n.p,{children:"For more on using Documents, see Document Initializers and Mutability."}),"\n",(0,a.jsx)(n.h3,{id:"create-a-dictionary",children:"Create a Dictionary"}),"\n",(0,a.jsx)(n.p,{children:"Now create a mutable dictionary (address)."}),"\n",(0,a.jsx)(n.p,{children:"Each element of the dictionary value will be directly accessible via its own key."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Create a new MutableDocument instance\nconst document = new MutableDocument();\n\n// Now, populate the document as if it's a dictionary named 'address'\ndocument.setString('address.street', '1 Main st.');\ndocument.setString('address.city', 'San Francisco');\ndocument.setString('address.state', 'CA');\ndocument.setString('address.country', 'USA');\ndocument.setString('address.code', '90210');\n"})}),"\n",(0,a.jsx)(n.h3,{id:"create-an-array",children:"Create an Array"}),"\n",(0,a.jsx)(n.p,{children:"Since the hotel may have multiple contact numbers, provide a field (phones) as a mutable array."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create an instance of MutableDocument\nconst hotelInfo = new MutableDocument();\n\n// Since `setArray` method accepts an array, directly pass the contact numbers as an array\nhotelInfo.setArray("phones", ["650-000-0000", "650-000-0001"]);\n'})}),"\n",(0,a.jsx)(n.h3,{id:"populate-a-document",children:"Populate a Document"}),"\n",(0,a.jsx)(n.p,{children:"Now add your data to the mutable document created earlier. Each data item is stored as a key-value pair."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Assuming address and phones are already defined as shown in previous examples\nconst address = {\n street: "1 Main st.",\n city: "San Francisco",\n state: "CA",\n country: "USA",\n code: "90210"\n};\n\nconst phones = ["650-000-0000", "650-000-0001"];\n\n// Create an instance of MutableDocument\nlet hotelInfo = new MutableDocument();\n\n// Add document type and hotel name as string\nhotelInfo.setString("type", "hotel");\nhotelInfo.setString("name", "Hotel Java Mo");\n\n// Add average room rate (float)\nhotelInfo.setFloat("room_rate", 121.75);\n\n// Add address (dictionary)\nhotelInfo.setDictionary("address", address);\n\n// Add phone numbers (array)\nhotelInfo.setArray("phones", phones);\n'})}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"Couchbase recommend using a type attribute to define each logical document type."})}),"\n",(0,a.jsx)(n.h3,{id:"save-a-document",children:"Save a Document"}),"\n",(0,a.jsx)(n.p,{children:"With the document now populated, we can persist to our Couchbase Lite database."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"await collection.save(document);\n"})}),"\n",(0,a.jsx)(n.h2,{id:"working-with-data",children:"Working with Data"}),"\n",(0,a.jsx)(n.h3,{id:"date-accessors",children:"Date accessors"}),"\n",(0,a.jsx)(n.p,{children:"Couchbase Lite offers Date accessors as a convenience. Dates are a common data type, but JSON doesn\u2019t natively support them, so the convention is to store them as strings in ISO-8601 format."}),"\n",(0,a.jsx)(n.h4,{id:"example-1-date-getter",children:"Example 1. Date Getter"}),"\n",(0,a.jsx)(n.p,{children:"This example sets the date on the createdAt property and reads it back using the Document.getDate() accessor method."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create an instance of MutableDocument\nlet document = new MutableDocument();\n\n// Set the current date on the "createdAt" property\ndocument.setDate("createdAt", new Date());\n\n// Get the Date \nconst date = document.getDate("createdAt");\n'})}),"\n",(0,a.jsx)(n.h3,{id:"using-dictionaries",children:"Using Dictionaries"}),"\n",(0,a.jsx)(n.h4,{id:"example-2-read-only",children:"Example 2. Read Only"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Get a document by ID\nconst doc: Document = collection.document('doc1');\n\n// Getting a dictionary from the document's properties\nconst addressDict = doc.getDictionary('address');\n\n// Access a value with a key from the dictionary\nconst street = addressDict?.getString('street');\n\n// Iterate over the dictionary\nObject.keys(addressDict || {}).forEach(key => {\n console.log(`Key ${key} = ${addressDict[key]}`);\n});\n\n// Create a mutable copy\nconst mutableDict = { ...addressDict };\n"})}),"\n",(0,a.jsx)(n.h4,{id:"example-3-mutable",children:"Example 3. Mutable"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create a new "dictionary" as a simple JavaScript object\nconst addressDict = {\n street: "1 Main st.",\n city: "San Francisco",\n};\n\n// Create a new mutable document and add the dictionary to its properties\nconst mutableDoc = new MutableDocument("doc1");\nmutableDoc.setDictionary("address", addressDict);\n\n// Simulate saving the document\nawait collection.save(mutableDoc);\n'})}),"\n",(0,a.jsx)(n.h3,{id:"using-arrays",children:"Using Arrays"}),"\n",(0,a.jsx)(n.h4,{id:"example-4-read-only",children:"Example 4. Read Only"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Getting a phones array from the document's properties\nconst phonesArray = document.data.phones;\n\n// Get element count\nconst count = phonesArray.length;\n\n// Access an array element by index\nif (count >= 1) { \n const phone = phonesArray[1]; \n console.log(`Second phone: ${phone}`);\n}\n\n// Iterate over the array\nphonesArray.forEach((item, index) => {\n console.log(`Item ${index} = ${item}`);\n});\n\n// Create a mutable copy of the array\nconst mutableArray = [...phonesArray];\n"})}),"\n",(0,a.jsx)(n.h4,{id:"example-5-mutable",children:"Example 5. Mutable"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create a new mutable document\nconst document = new MutableDocument();\n\n// Prepare the data for the array\nconst phones = ["650-000-0000", "650-000-0001"];\n\n// Assign the array to a key in the document\ndoc.setArray("phones", phones);\n\n// Save the document with the new array to the database\nawait collection.save(doc);\n'})}),"\n",(0,a.jsx)(n.h3,{id:"using-blobs",children:"Using Blobs"}),"\n",(0,a.jsxs)(n.p,{children:["For more on working with blobs, see ",(0,a.jsx)(n.a,{href:"/blobs",children:"Blobs"})]}),"\n",(0,a.jsx)(n.h2,{id:"document-initializers",children:"Document Initializers"}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"MutableDocument"})," constructor can be used to create a new document where the document ID is randomly generated by the database."]}),"\n",(0,a.jsxs)(n.p,{children:["Use the ",(0,a.jsx)(n.code,{children:"MutableDocument('specific_id')"})," initializer to create a new document with a specific ID."]}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"Collection.document"})," method can be used to get a document. If it doesn't exist in the collection, it will return null. This method can be used to check if a document with a given ID already exists in the collection."]}),"\n",(0,a.jsx)(n.h3,{id:"convert-mutable-document-from-document",children:"Convert Mutable Document from Document"}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"MutableDocument"})," class has a static function ",(0,a.jsx)(n.code,{children:"fromDocument"})," that takes a ",(0,a.jsx)(n.code,{children:"Document"})," as a parameter. This allows you to create a mutable copy of an existing document so you can modify it and then save it to a collection."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const doc: Document = collection.document('doc1');\nconst mutableDoc = MutableDocument.fromDocument(doc);\nmutableDoc.setString('name', 'New Name');\nawait collection.save(mutableDoc);\n"})}),"\n",(0,a.jsx)(n.h4,{id:"example-6-persist-a-document",children:"Example 6. Persist a document"}),"\n",(0,a.jsx)(n.p,{children:"The following code example creates a document and persists it to the database."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Create a new MutableDocument instance\nconst document = new MutableDocument();\n\n// Set various fields on the document\ndocument.setString('type', 'task');\ndocument.setString('owner', 'todo');\ndocument.setDate('createdAt', new Date());\n\n// Persist the document to the database\nawait collection.save(document);\n"})}),"\n",(0,a.jsx)(n.h2,{id:"document-change-events",children:"Document change events"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const token = collection.addDocumentChangeListener('user.john', async (change) => {\n const document = await collection.document(change.documentID);\n if (document !== null) {\n console.log(`Status: ${document.getString('verified_account')}`);\n }\n});\n\n// Remove the change listener when it is no longer needed\nawait collection.removeDocumentChangeListener(token);\n"})}),"\n",(0,a.jsx)(n.h2,{id:"document-expiration",children:"Document Expiration"}),"\n",(0,a.jsx)(n.p,{children:"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 or Capella App Services."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const expirationDate = new Date('2024-12-31T23:59:59');\nawait collection.setDocumentExpiration('doc123', expirationDate);\n"})}),"\n",(0,a.jsx)(n.h2,{id:"purge-a-document",children:"Purge a Document"}),"\n",(0,a.jsxs)(n.p,{children:["Documents can be purged from the local database using the ",(0,a.jsx)(n.code,{children:"purge"})," method on the collection they are stored in."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const doc = collection.document('doc1');\nawait collection.purge(doc);\n"})}),"\n",(0,a.jsx)(n.p,{children:"You can also purge a document by it's ID."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"await collection.purgeById('doc1');\n"})}),"\n",(0,a.jsx)(n.p,{children:"Collection purges are not replicated."}),"\n",(0,a.jsx)(n.h2,{id:"delete-a-document",children:"Delete a Document"}),"\n",(0,a.jsxs)(n.p,{children:["Documents can be deleted using the ",(0,a.jsx)(n.code,{children:"delete"})," method on the collection they are stored in."]}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const doc = collection.document('doc1');\nawait collection.delete(doc);\n"})}),"\n",(0,a.jsx)(n.p,{children:"Note that document deletion are replicated to Sync Gateway or Capella App Services."}),"\n",(0,a.jsx)(n.h2,{id:"document-constraints",children:"Document Constraints"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.code,{children:"_id"}),", ",(0,a.jsx)(n.code,{children:"_rev"})," and ",(0,a.jsx)(n.code,{children:"_sequence"})," are reserved keywords and must not be used as top-level attributes \u2014 see ",(0,a.jsx)(n.a,{href:"#example-13-reserved-keys-list",children:"Example 13"}),"."]})}),"\n",(0,a.jsx)(n.h4,{id:"example-13-reserved-keys-list",children:"Example 13. Reserved Keys List"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_attachments"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_deleted"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_id"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_removed"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_rev"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_sequence"})}),"\n"]})]})}function u(e={}){const{wrapper:n}={...(0,o.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>c,x:()=>i});var a=t(6540);const o={},s=a.createContext(o);function c(e){const n=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function i(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:c(e.components),a.createElement(s.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/d211126e.80a47ad6.js b/assets/js/d211126e.80a47ad6.js deleted file mode 100644 index 38cc6a6..0000000 --- a/assets/js/d211126e.80a47ad6.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[473],{8276:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>o,default:()=>h,frontMatter:()=>i,metadata:()=>c,toc:()=>l});var a=t(4848),s=t(8453);const i={id:"documents",sidebar_position:7},o="Documents",c={id:"documents",title:"Documents",description:"Description \u2014 Couchbase Lite Concepts - Data Model - Documents",source:"@site/docs/documents.md",sourceDirName:".",slug:"/documents",permalink:"/documents",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/documents.md",tags:[],version:"current",sidebarPosition:7,frontMatter:{id:"documents",sidebar_position:7},sidebar:"tutorialSidebar",previous:{title:"Scopes and Collections",permalink:"/scopes-collections"},next:{title:"Blobs",permalink:"/blobs"}},r={},l=[{value:"Overview",id:"overview",level:2},{value:"Document Structure",id:"document-structure",level:3},{value:"Data Encoding",id:"data-encoding",level:3},{value:"Data Types",id:"data-types",level:3},{value:"JSON",id:"json",level:3},{value:"Constructing a Document",id:"constructing-a-document",level:2},{value:"Data Model",id:"data-model",level:3},{value:"Open a Database",id:"open-a-database",level:3},{value:"Create a Document",id:"create-a-document",level:3},{value:"Create a Dictionary",id:"create-a-dictionary",level:3},{value:"Create an Array",id:"create-an-array",level:3},{value:"Populate a Document",id:"populate-a-document",level:3},{value:"Save a Document",id:"save-a-document",level:3},{value:"Close the Database",id:"close-the-database",level:3},{value:"Working with Data",id:"working-with-data",level:2},{value:"Date accessors",id:"date-accessors",level:3},{value:"Example 1. Date Getter",id:"example-1-date-getter",level:4},{value:"Using Dictionaries",id:"using-dictionaries",level:3},{value:"Example 2. Read Only",id:"example-2-read-only",level:4},{value:"Example 3. Mutable",id:"example-3-mutable",level:4},{value:"Using Arrays",id:"using-arrays",level:3},{value:"Example 4. Read Only",id:"example-4-read-only",level:4},{value:"Example 5. Mutable",id:"example-5-mutable",level:4},{value:"Using Blobs",id:"using-blobs",level:3},{value:"Document Initializers",id:"document-initializers",level:2},{value:"Example 6. Persist a document",id:"example-6-persist-a-document",level:4},{value:"Document change events",id:"document-change-events",level:2},{value:"Document Constraints",id:"document-constraints",level:2},{value:"Example 13. Reserved Keys List",id:"example-13-reserved-keys-list",level:4}];function d(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",br:"br",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",li:"li",p:"p",pre:"pre",strong:"strong",ul:"ul",...(0,s.R)(),...e.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(n.h1,{id:"documents",children:"Documents"}),"\n",(0,a.jsxs)(n.blockquote,{children:["\n",(0,a.jsxs)(n.p,{children:["Description \u2014 ",(0,a.jsx)(n.em,{children:"Couchbase Lite Concepts - Data Model - Documents"}),(0,a.jsx)(n.br,{}),"\n","Related Content \u2014 ",(0,a.jsx)(n.a,{href:"/databases",children:"Databases"})," | ",(0,a.jsx)(n.a,{href:"/blobs",children:"Blobs"})," | ",(0,a.jsx)(n.a,{href:"indexing.md",children:"Indexing"})]}),"\n"]}),"\n",(0,a.jsx)(n.h2,{id:"overview",children:"Overview"}),"\n",(0,a.jsx)(n.h3,{id:"document-structure",children:"Document Structure"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.p,{children:"Each document has an ID or unique identifier. This ID is similar to a primary key in other databases."}),"\n",(0,a.jsx)(n.p,{children:"You can specify the ID programmatically. If you omit it, it will be automatically generated as a UUID."}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"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."})}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.h3,{id:"data-encoding",children:"Data Encoding"}),"\n",(0,a.jsxs)(n.p,{children:["The document body is stored in an internal, efficient, binary form called ",(0,a.jsx)(n.a,{href:"https://github.com/couchbase/fleece#readme",children:"Fleece"}),". This internal form can be easily converted into a manageable native dictionary format for manipulation in applications."]}),"\n",(0,a.jsx)(n.p,{children:"Fleece data is stored in the smallest format that will hold the value whilst maintaining the integrity of the value."}),"\n",(0,a.jsx)(n.h3,{id:"data-types",children:"Data Types"}),"\n",(0,a.jsx)(n.p,{children:"The Document class offers a set of property accessors for various scalar types, such as:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Boolean"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Date"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Double"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Float"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Int"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"Long"}),"\n"]}),"\n",(0,a.jsxs)(n.li,{children:["\n",(0,a.jsx)(n.p,{children:"String"}),"\n"]}),"\n"]}),"\n",(0,a.jsx)(n.p,{children:"These accessors take care of converting to/from JSON encoding, and make sure you get the type you expect."}),"\n",(0,a.jsx)(n.p,{children:"In addition to these basic data types Couchbase Lite provides for the following:"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Dictionary"})," - Represents a read-only key-value pair collection"]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"MutableDictionary"})," - Represents a writeable key-value pair collection."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Array"})," - Represents a writeable collection of objects."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"MutableArray"})," - Represents a writeable collection of objects."]}),"\n",(0,a.jsxs)(n.li,{children:[(0,a.jsx)(n.strong,{children:"Blob"})," - Represents an arbitrary piece of binary data."]}),"\n"]}),"\n",(0,a.jsx)(n.h3,{id:"json",children:"JSON"}),"\n",(0,a.jsx)(n.p,{children:"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)\u2009\u2014\u2009see Working with JSON Data."}),"\n",(0,a.jsx)(n.h2,{id:"constructing-a-document",children:"Constructing a Document"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.p,{children:"In this section we provide an example of how you might create a hotel document, which provides basic contact details and price data."}),"\n",(0,a.jsx)(n.h3,{id:"data-model",children:"Data Model"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"hotel: {\n type: string (value = `hotel`)\n name: string\n address: dictionary {\n street: string\n city: string\n state: string\n country: string\n code: string\n }\n phones: array\n rate: float\n}\n"})}),"\n",(0,a.jsx)(n.h3,{id:"open-a-database",children:"Open a Database"}),"\n",(0,a.jsx)(n.p,{children:"First open your database. If the database does not already exist, Couchbase Lite will create it for you."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const myDatabase = new Database('myDatabaseName', config);\n"})}),"\n",(0,a.jsxs)(n.p,{children:["See ",(0,a.jsx)(n.a,{href:"https://cbl-ionic.dev/databases",children:"Databases"})," for more information."]}),"\n",(0,a.jsx)(n.h3,{id:"create-a-document",children:"Create a Document"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"let document = new MutableDocument(hotel.id);\n"})}),"\n",(0,a.jsx)(n.p,{children:"For more on using Documents, see Document Initializers and Mutability."}),"\n",(0,a.jsx)(n.h3,{id:"create-a-dictionary",children:"Create a Dictionary"}),"\n",(0,a.jsx)(n.p,{children:"Now create a mutable dictionary (address)."}),"\n",(0,a.jsx)(n.p,{children:"Each element of the dictionary value will be directly accessible via its own key."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Create a new MutableDocument instance\nconst document = new MutableDocument();\n\n// Now, populate the document as if it's a dictionary named 'address'\ndocument.setString('address.street', '1 Main st.');\ndocument.setString('address.city', 'San Francisco');\ndocument.setString('address.state', 'CA');\ndocument.setString('address.country', 'USA');\ndocument.setString('address.code', '90210');\n"})}),"\n",(0,a.jsx)(n.h3,{id:"create-an-array",children:"Create an Array"}),"\n",(0,a.jsx)(n.p,{children:"Since the hotel may have multiple contact numbers, provide a field (phones) as a mutable array."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create an instance of MutableDocument\nconst hotelInfo = new MutableDocument();\n\n// Since `setArray` method accepts an array, directly pass the contact numbers as an array\nhotelInfo.setArray("phones", ["650-000-0000", "650-000-0001"]);\n'})}),"\n",(0,a.jsx)(n.h3,{id:"populate-a-document",children:"Populate a Document"}),"\n",(0,a.jsx)(n.p,{children:"Now add your data to the mutable document created earlier. Each data item is stored as a key-value pair."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Assuming address and phones are already defined as shown in previous examples\nconst address = {\n street: "1 Main st.",\n city: "San Francisco",\n state: "CA",\n country: "USA",\n code: "90210"\n};\n\nconst phones = ["650-000-0000", "650-000-0001"];\n\n// Create an instance of MutableDocument\nlet hotelInfo = new MutableDocument();\n\n// Add document type and hotel name as string\nhotelInfo.setString("type", "hotel");\nhotelInfo.setString("name", "Hotel Java Mo");\n\n// Add average room rate (float)\nhotelInfo.setFloat("room_rate", 121.75);\n\n// Add address (dictionary)\nhotelInfo.setDictionary("address", address);\n\n// Add phone numbers (array)\nhotelInfo.setArray("phones", phones);\n'})}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsx)(n.p,{children:"Couchbase recommend using a type attribute to define each logical document type."})}),"\n",(0,a.jsx)(n.h3,{id:"save-a-document",children:"Save a Document"}),"\n",(0,a.jsx)(n.p,{children:"With the document now populated, we can persist to our Couchbase Lite database."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"await collection.save(document);\n"})}),"\n",(0,a.jsx)(n.h3,{id:"close-the-database",children:"Close the Database"}),"\n",(0,a.jsx)(n.p,{children:"With your document saved, you can now close our Couchbase Lite database."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"database.close();\n"})}),"\n",(0,a.jsx)(n.h2,{id:"working-with-data",children:"Working with Data"}),"\n",(0,a.jsx)(n.h3,{id:"date-accessors",children:"Date accessors"}),"\n",(0,a.jsx)(n.p,{children:"Couchbase Lite offers Date accessors as a convenience. Dates are a common data type, but JSON doesn\u2019t natively support them, so the convention is to store them as strings in ISO-8601 format."}),"\n",(0,a.jsx)(n.h4,{id:"example-1-date-getter",children:"Example 1. Date Getter"}),"\n",(0,a.jsx)(n.p,{children:"This example sets the date on the createdAt property and reads it back using the Document.getDate() accessor method."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create an instance of MutableDocument\nlet document = new MutableDocument();\n\n// Set the current date on the "createdAt" property\ndocument.setDate("createdAt", new Date());\n\n// Get the Date \nconst date = document.getDate("createdAt");\n'})}),"\n",(0,a.jsx)(n.h3,{id:"using-dictionaries",children:"Using Dictionaries"}),"\n",(0,a.jsx)(n.h4,{id:"example-2-read-only",children:"Example 2. Read Only"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Get a document by ID\nconst doc: Document = collection.document('doc1');\n\n// Getting a dictionary from the document's properties\nconst addressDict = doc.getDictionary('address');\n\n// Access a value with a key from the dictionary\nconst street = addressDict?.getString('street');\n\n// Iterate over the dictionary\nObject.keys(addressDict || {}).forEach(key => {\n console.log(`Key ${key} = ${addressDict[key]}`);\n});\n\n// Create a mutable copy\nconst mutableDict = { ...addressDict };\n"})}),"\n",(0,a.jsx)(n.h4,{id:"example-3-mutable",children:"Example 3. Mutable"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create a new "dictionary" as a simple JavaScript object\nconst addressDict = {\n street: "1 Main st.",\n city: "San Francisco",\n};\n\n// Create a new mutable document and add the dictionary to its properties\nconst mutableDoc = new MutableDocument("doc1");\nmutableDoc.setDictionary("address", addressDict);\n\n// Simulate saving the document\nawait collection.save(mutableDoc);\n'})}),"\n",(0,a.jsx)(n.h3,{id:"using-arrays",children:"Using Arrays"}),"\n",(0,a.jsx)(n.h4,{id:"example-4-read-only",children:"Example 4. Read Only"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Getting a phones array from the document's properties\nconst phonesArray = document.data.phones;\n\n// Get element count\nconst count = phonesArray.length;\n\n// Access an array element by index\nif (count >= 1) { \n const phone = phonesArray[1]; \n console.log(`Second phone: ${phone}`);\n}\n\n// Iterate over the array\nphonesArray.forEach((item, index) => {\n console.log(`Item ${index} = ${item}`);\n});\n\n// Create a mutable copy of the array\nconst mutableArray = [...phonesArray];\n"})}),"\n",(0,a.jsx)(n.h4,{id:"example-5-mutable",children:"Example 5. Mutable"}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:'// Create a new mutable document\nconst document = new MutableDocument();\n\n// Prepare the data for the array\nconst phones = ["650-000-0000", "650-000-0001"];\n\n// Assign the array to a key in the document\ndoc.setArray("phones", phones);\n\n// Save the document with the new array to the database\nawait collection.save(doc);\n'})}),"\n",(0,a.jsx)(n.h3,{id:"using-blobs",children:"Using Blobs"}),"\n",(0,a.jsxs)(n.p,{children:["For more on working with blobs, see ",(0,a.jsx)(n.a,{href:"/blobs",children:"Blobs"})]}),"\n",(0,a.jsx)(n.h2,{id:"document-initializers",children:"Document Initializers"}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"MutableDocument"})," constructor can be used to create a new document where the document ID is randomly generated by the database."]}),"\n",(0,a.jsxs)(n.p,{children:["Use the ",(0,a.jsx)(n.code,{children:"MutableDocument('specific_id')"})," initializer to create a new document with a specific ID."]}),"\n",(0,a.jsxs)(n.p,{children:["The ",(0,a.jsx)(n.code,{children:"Collection.document"})," method can be used to get a document. If it doesn't exist in the collection, it will return null. This method can be used to check if a document with a given ID already exists in the collection."]}),"\n",(0,a.jsx)(n.h4,{id:"example-6-persist-a-document",children:"Example 6. Persist a document"}),"\n",(0,a.jsx)(n.p,{children:"The following code example creates a document and persists it to the database."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"// Create a new MutableDocument instance\nconst document = new MutableDocument();\n\n// Set various fields on the document\ndocument.setString('type', 'task');\ndocument.setString('owner', 'todo');\ndocument.setDate('createdAt', new Date());\n\n// Persist the document to the database\nawait collection.save(document);\n"})}),"\n",(0,a.jsx)(n.h2,{id:"document-change-events",children:"Document change events"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.pre,{children:(0,a.jsx)(n.code,{className:"language-typescript",children:"const token = collection.addDocumentChangeListener('user.john', async (change) => {\n const document = await collection.document(change.documentID);\n if (document !== null) {\n console.log(`Status: ${document.getString('verified_account')}`);\n }\n});\n\n// Remove the change listener when it is no longer needed\nawait collection.removeDocumentChangeListener(token);\n"})}),"\n",(0,a.jsx)(n.h2,{id:"document-constraints",children:"Document Constraints"}),"\n",(0,a.jsx)(n.p,{children:"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."}),"\n",(0,a.jsx)(n.admonition,{type:"note",children:(0,a.jsxs)(n.p,{children:[(0,a.jsx)(n.code,{children:"_id"}),", ",(0,a.jsx)(n.code,{children:"_rev"})," and ",(0,a.jsx)(n.code,{children:"_sequence"})," are reserved keywords and must not be used as top-level attributes \u2014 see ",(0,a.jsx)(n.a,{href:"#example-13-reserved-keys-list",children:"Example 13"}),"."]})}),"\n",(0,a.jsx)(n.h4,{id:"example-13-reserved-keys-list",children:"Example 13. Reserved Keys List"}),"\n",(0,a.jsxs)(n.ul,{children:["\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_attachments"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_deleted"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_id"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_removed"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_rev"})}),"\n",(0,a.jsx)(n.li,{children:(0,a.jsx)(n.code,{children:"_sequence"})}),"\n"]})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,a.jsx)(n,{...e,children:(0,a.jsx)(d,{...e})}):d(e)}},8453:(e,n,t)=>{t.d(n,{R:()=>o,x:()=>c});var a=t(6540);const s={},i=a.createContext(s);function o(e){const n=a.useContext(i);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:o(e.components),a.createElement(i.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/d22a0e6a.4570b277.js b/assets/js/d22a0e6a.4570b277.js deleted file mode 100644 index 6ccf1ac..0000000 --- a/assets/js/d22a0e6a.4570b277.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[2184],{1048:(i,I,e)=>{e.r(I),e.d(I,{assets:()=>M,contentTitle:()=>t,default:()=>b,frontMatter:()=>n,metadata:()=>c,toc:()=>l});var a=e(4848),s=e(8453);const n={id:"databases",sidebar_position:4},t="Databases",c={id:"databases",title:"Databases",description:"Description \u2014 Working with Couchbase Lite Databases",source:"@site/docs/databases.md",sourceDirName:".",slug:"/databases",permalink:"/databases",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/databases.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{id:"databases",sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"Build and Run",permalink:"/StartHere/build-run"},next:{title:"Pre-built Database",permalink:"/database-prebuilt"}},M={},l=[{value:"Database Concepts",id:"database-concepts",level:2},{value:"Storing local configuration",id:"storing-local-configuration",level:3},{value:"Managing Couchbase Lite Databases in Ionic",id:"managing-couchbase-lite-databases-in-ionic",level:2},{value:"Initializing the Environment",id:"initializing-the-environment",level:3},{value:"Create or Open a Database",id:"create-or-open-a-database",level:3},{value:"Closing a Database",id:"closing-a-database",level:3},{value:"Database Encryption",id:"database-encryption",level:2},{value:"Enabling",id:"enabling",level:3},{value:"Persisting",id:"persisting",level:3},{value:"Opening",id:"opening",level:3},{value:"Database Maintenance",id:"database-maintenance",level:2},{value:"Command Line Tool",id:"command-line-tool",level:2},{value:"Couchbase Lite for VSCode",id:"couchbase-lite-for-vscode",level:2},{value:"Couchbase Lite for JetBrains",id:"couchbase-lite-for-jetbrains",level:2},{value:"Troubleshooting",id:"troubleshooting",level:2}];function d(i){const I={a:"a",blockquote:"blockquote",br:"br",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,s.R)(),...i.components};return(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(I.h1,{id:"databases",children:"Databases"}),"\n",(0,a.jsxs)(I.blockquote,{children:["\n",(0,a.jsxs)(I.p,{children:["Description \u2014 ",(0,a.jsx)(I.em,{children:"Working with Couchbase Lite Databases"}),(0,a.jsx)(I.br,{}),"\n","Related Content \u2014 ",(0,a.jsx)(I.a,{href:"/blobs",children:"Blobs"})," | ",(0,a.jsx)(I.a,{href:"/documents",children:"Documents"})," | ",(0,a.jsx)(I.a,{href:"indexing.md",children:"Indexing"})]}),"\n"]}),"\n",(0,a.jsx)(I.h2,{id:"database-concepts",children:"Database Concepts"}),"\n",(0,a.jsx)(I.p,{children:"Databases created on Couchbase Lite can share the same hierarchical structure as Capella databases. This makes it easier to sync data between mobile applications and applications built using Capella."}),"\n",(0,a.jsxs)("div",{align:"center",children:[(0,a.jsx)(I.p,{children:(0,a.jsx)(I.img,{alt:"Couchbase Lite Database Hierarchy",src:e(7432).A+"",width:"444",height:"380"})}),(0,a.jsx)(I.p,{children:(0,a.jsx)(I.em,{children:"Figure 1. Couchbase Lite Database Hierarchy"})})]}),"\n",(0,a.jsx)(I.p,{children:"Although the terminology is different, the structure can be mapped to relational database terms:"}),"\n",(0,a.jsx)(I.p,{children:"Table 1. Relational Database \u2192 Couchbase"}),"\n",(0,a.jsxs)(I.table,{children:[(0,a.jsx)(I.thead,{children:(0,a.jsxs)(I.tr,{children:[(0,a.jsx)(I.th,{children:"Relational database"}),(0,a.jsx)(I.th,{children:"Couchbase"})]})}),(0,a.jsxs)(I.tbody,{children:[(0,a.jsxs)(I.tr,{children:[(0,a.jsx)(I.td,{children:"Database"}),(0,a.jsx)(I.td,{children:"Database"})]}),(0,a.jsxs)(I.tr,{children:[(0,a.jsx)(I.td,{children:"Schema"}),(0,a.jsx)(I.td,{children:"Scope"})]}),(0,a.jsxs)(I.tr,{children:[(0,a.jsx)(I.td,{children:"Table"}),(0,a.jsx)(I.td,{children:"Collection"})]})]})]}),"\n",(0,a.jsx)(I.p,{children:"This structure gives you plenty of choices when it comes to partitioning your data. The most basic structure is to use the single default scope with a single default collection; or you could opt for a structure that allow you to split your collections into logical scopes."}),"\n",(0,a.jsxs)("div",{align:"center",children:[(0,a.jsx)(I.p,{children:(0,a.jsx)(I.img,{alt:"Couchbase Lite Examples",src:e(8130).A+"",width:"647",height:"321"})}),(0,a.jsx)(I.p,{children:(0,a.jsx)(I.em,{children:"Figure 2. Couchbase Lite Examples"})})]}),"\n",(0,a.jsx)(I.h3,{id:"storing-local-configuration",children:"Storing local configuration"}),"\n",(0,a.jsx)(I.p,{children:"You may not need to sync all the data related to a particular application. You can set up a scope that syncs data, and a second scope that doesn\u2019t. One reason for doing this is to store local configuration data (such as the preferred screen orientation or keyboard layout). Since this information only relates to a particular device, there is no need to sync it:"}),"\n",(0,a.jsxs)(I.ul,{children:["\n",(0,a.jsxs)(I.li,{children:[(0,a.jsx)(I.strong,{children:"local data scope"})," \u2014 Contains information pertaining to the device."]}),"\n",(0,a.jsxs)(I.li,{children:[(0,a.jsx)(I.strong,{children:"syncing data scope"})," \u2014 Contains information pertaining to the user, which can be synced back to the cloud for use on the web or another device."]}),"\n"]}),"\n",(0,a.jsx)(I.h2,{id:"managing-couchbase-lite-databases-in-ionic",children:"Managing Couchbase Lite Databases in Ionic"}),"\n",(0,a.jsx)(I.h3,{id:"initializing-the-environment",children:"Initializing the Environment"}),"\n",(0,a.jsx)(I.p,{children:"In an Ionic application using Couchbase Lite, begin by initializing the Capacitor Engine. Subsequently, employ a design pattern such as Context/Provider or Service Locator to maintain and access your database instances throughout the application lifecycle."}),"\n",(0,a.jsx)(I.p,{children:(0,a.jsx)(I.strong,{children:"Example: Initializing Capacitor Engine and Database Context"})}),"\n",(0,a.jsx)(I.pre,{children:(0,a.jsx)(I.code,{className:"language-typescript",children:"import { CapacitorEngine } from 'cbl-ionic';\n\nconst engine = new CapacitorEngine(); // Initialize once, early in your app\n"})}),"\n",(0,a.jsx)(I.p,{children:"This configuration ensures seamless interaction between your Ionic app and the underlying native database functionalities, facilitating effective database management."}),"\n",(0,a.jsx)(I.h3,{id:"create-or-open-a-database",children:"Create or Open a Database"}),"\n",(0,a.jsx)(I.p,{children:"To create or open a database, use the Database class from the cbl-ionic package, specifying the database name and optionally, a DatabaseConfiguration for custom settings like the database directory or encryption."}),"\n",(0,a.jsx)(I.p,{children:(0,a.jsx)(I.strong,{children:"Example 1. Creating/Opening a Database"})}),"\n",(0,a.jsx)(I.pre,{children:(0,a.jsx)(I.code,{className:"language-javascript",children:"import { Database, DatabaseConfiguration } from 'cbl-ionic'; //import the package\n"})}),"\n",(0,a.jsx)(I.pre,{children:(0,a.jsx)(I.code,{className:"language-javascript",children:"const config = new DatabaseConfiguration();\nconfig.setDirectory('path/to/database'); // Optional\nconst myDatabase = new Database('myDatabaseName', config);\nawait myDatabase.open();\n"})}),"\n",(0,a.jsx)(I.h3,{id:"closing-a-database",children:"Closing a Database"}),"\n",(0,a.jsx)(I.p,{children:"You are advised to incorporate the closing of all open databases into your application workflow."}),"\n",(0,a.jsx)(I.p,{children:(0,a.jsx)(I.strong,{children:"Example 2. Closing a Database"})}),"\n",(0,a.jsx)(I.pre,{children:(0,a.jsx)(I.code,{className:"language-javascript",children:"await myDatabase.close();\n"})}),"\n",(0,a.jsx)(I.h2,{id:"database-encryption",children:"Database Encryption"}),"\n",(0,a.jsx)(I.p,{children:"Couchbase Lite includes the ability to encrypt Couchbase Lite databases. This allows mobile applications to secure data at rest, when it is being stored on the device. The algorithm used to encrypt the database is 256-bit AES."}),"\n",(0,a.jsx)(I.h3,{id:"enabling",children:"Enabling"}),"\n",(0,a.jsxs)(I.p,{children:["To enable database encryption in Ionic, use the ",(0,a.jsx)(I.code,{children:"DatabaseConfiguration"})," class to set an encryption key before opening or creating a database. This encryption key must be provided every time the database is accessed."]}),"\n",(0,a.jsx)(I.p,{children:(0,a.jsx)(I.strong,{children:"Example3. Configure Database Encryption"})}),"\n",(0,a.jsx)(I.pre,{children:(0,a.jsx)(I.code,{className:"language-typescript",children:"const dbName = 'my_secure_db';\nconst encryptionKey = 'my_secret_key';\n\nconst config = new DatabaseConfiguration();\nconfig.setEncryptionKey(encryptionKey);\n\nconst db = new Database(dbName, config);\n\nawait db.open();\n"})}),"\n",(0,a.jsx)(I.h3,{id:"persisting",children:"Persisting"}),"\n",(0,a.jsxs)(I.p,{children:["Couchbase Lite does not persist the key. It is the application\u2019s responsibility to manage the key and store it in a platform-specific secure store such as Apples's ",(0,a.jsx)(I.a,{href:"https://developer.apple.com/documentation/security/keychain_services",children:"Keystore"})," or Android\u2019s ",(0,a.jsx)(I.a,{href:"https://developer.android.com/privacy-and-security/keystore",children:"Keystore"}),"."]}),"\n",(0,a.jsx)(I.h3,{id:"opening",children:"Opening"}),"\n",(0,a.jsx)(I.p,{children:"An encrypted database can only be opened with the same language package that was used to encrypt it in the first place. So a database encrypted using the Ionic package, and then exported, is readable only by the Ionic package."}),"\n",(0,a.jsx)(I.h2,{id:"database-maintenance",children:"Database Maintenance"}),"\n",(0,a.jsx)(I.p,{children:"From time to time it may be necessary to perform certain maintenance activities on your database, for example to compact the database file, removing unused documents and blobs no longer referenced by any documents."}),"\n",(0,a.jsx)(I.p,{children:"Couchbase Lite's API provides the Database.performMaintenance method. The available maintenance operations, including compact are as shown in the enum MaintenanceType to accomplish this."}),"\n",(0,a.jsxs)(I.p,{children:["This is a resource intensive operation and is not performed automatically. It should be run on-demand using the API. For questions or issues, please visit the ",(0,a.jsx)(I.a,{href:"https://www.couchbase.com/forums/",children:"Couchbase Forums"})," where you can ask for help and discuss with the community."]}),"\n",(0,a.jsx)(I.h2,{id:"command-line-tool",children:"Command Line Tool"}),"\n",(0,a.jsx)(I.p,{children:"cblite is a command-line tool for inspecting and querying Couchbase Lite databases."}),"\n",(0,a.jsxs)(I.p,{children:["You can download and build it from the couchbaselabs ",(0,a.jsx)(I.a,{href:"https://github.com/couchbaselabs/couchbase-mobile-tools/blob/master/README.cblite.md",children:"GitHub repository"}),". Note that the cblite tool is not supported by the ",(0,a.jsx)(I.a,{href:"https://www.couchbase.com/support-policy/",children:"Couchbase Support Policy"}),"."]}),"\n",(0,a.jsx)(I.h2,{id:"couchbase-lite-for-vscode",children:"Couchbase Lite for VSCode"}),"\n",(0,a.jsxs)(I.p,{children:["Couchbase Lite for VSCode is a Visual Studio Code extension that provides a user interface for inspecting and querying Couchbase Lite databases. You can find more information about this extension from it's ",(0,a.jsx)(I.a,{href:"https://github.com/couchbaselabs/vscode-cblite",children:"GitHub repository"}),"."]}),"\n",(0,a.jsx)(I.h2,{id:"couchbase-lite-for-jetbrains",children:"Couchbase Lite for JetBrains"}),"\n",(0,a.jsxs)(I.p,{children:["Couchbase Lite for JetBrains is a JetBrains IDE plugin that provides a user interface for inspecting and querying Couchbase Lite databases. You can find more information about this plugin from its ",(0,a.jsx)(I.a,{href:"https://github.com/couchbaselabs/couchbase_jetbrains_plugin",children:"GitHub repository"}),"."]}),"\n",(0,a.jsx)(I.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,a.jsx)(I.p,{children:"You should use console logs as your first source of diagnostic information. If the information in the default logging level is insufficient you can focus it on database errors and generate more verbose messages."}),"\n",(0,a.jsx)(I.pre,{children:(0,a.jsx)(I.code,{className:"language-typescript",children:"try {\n await db.setLogLevel(LogDomain.DATABASE, LogLevel.VERBOSE);\n console.log('Database log level set to VERBOSE.');\n} catch (error) {\n console.error('Setting log level failed:', error);\n}\n"})})]})}function b(i={}){const{wrapper:I}={...(0,s.R)(),...i.components};return I?(0,a.jsx)(I,{...i,children:(0,a.jsx)(d,{...i})}):d(i)}},7432:(i,I,e)=>{e.d(I,{A:()=>a});const a="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U2NyaXB0VHlwZT0iYXBwbGljYXRpb24vZWNtYXNjcmlwdCIgY29udGVudFN0eWxlVHlwZT0idGV4dC9jc3MiIGhlaWdodD0iMzgwcHgiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiIHN0eWxlPSJ3aWR0aDo0NDRweDtoZWlnaHQ6MzgwcHg7YmFja2dyb3VuZDojRkZGRkZGOyIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgNDQ0IDM4MCIgd2lkdGg9IjQ0NHB4IiB6b29tQW5kUGFuPSJtYWduaWZ5Ij48ZGVmcz48ZmlsdGVyIGhlaWdodD0iMzAwJSIgaWQ9ImYxYWg1eXpyN2JiY2w5IiB3aWR0aD0iMzAwJSIgeD0iLTEiIHk9Ii0xIj48ZmVHYXVzc2lhbkJsdXIgcmVzdWx0PSJibHVyT3V0IiBzdGREZXZpYXRpb249IjIuMCIvPjxmZUNvbG9yTWF0cml4IGluPSJibHVyT3V0IiByZXN1bHQ9ImJsdXJPdXQyIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgLjQgMCIvPjxmZU9mZnNldCBkeD0iNC4wIiBkeT0iNC4wIiBpbj0iYmx1ck91dDIiIHJlc3VsdD0iYmx1ck91dDMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyT3V0MyIgbW9kZT0ibm9ybWFsIi8+PC9maWx0ZXI+PC9kZWZzPjxnPjwhLS1NRDU9Wzc0Njg0NjdmM2I2YjM5MmYxZDE3NmQ4MjhjNzUzYjc3XQplbnRpdHkgZGItLT48cGF0aCBkPSJNMTM0LDIxLjUgQzEzNCwxMS41IDE3NCwxMS41IDE3NCwxMS41IEMxNzQsMTEuNSAyMTQsMTEuNSAyMTQsMjEuNSBMMjE0LDQ3LjI5OTkgQzIxNCw1Ny4yOTk5IDE3NCw1Ny4yOTk5IDE3NCw1Ny4yOTk5IEMxNzQsNTcuMjk5OSAxMzQsNTcuMjk5OSAxMzQsNDcuMjk5OSBMMTM0LDIxLjUgIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48cGF0aCBkPSJNMTM0LDIxLjUgQzEzNCwzMS41IDE3NCwzMS41IDE3NCwzMS41IEMxNzQsMzEuNSAyMTQsMzEuNSAyMTQsMjEuNSAiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjYwIiB4PSIxNDQiIHk9IjQ5LjQ5OTkiPkRhdGFiYXNlPC90ZXh0PjwhLS1NRDU9W2M4YTFmMDU0ZDQwNGRmMGE2NWZjMjVjZjdmYmVkMzZiXQplbnRpdHkgc2NvcGUtLT48cmVjdCBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBoZWlnaHQ9IjM2Ljc5OTkiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgd2lkdGg9IjU4IiB4PSIxNDUiIHk9IjEyMyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM4IiB4PSIxNTUiIHk9IjE0Ni45OTk5Ij5TY29wZTwvdGV4dD48IS0tTUQ1PVtlODE0NDAwOTI1MGZlZmE4ZGU3YWIxN2U4ZmUyMDM4MF0KZW50aXR5IGNvbGxlY3Rpb24tLT48cG9seWdvbiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBwb2ludHM9IjEyOS41LDIyMSwxNzUuNSwyMjEsMTgyLjUsMjQyLDIxOC41LDI0MiwyMTguNSwyNzUuNzk5OSwxMjkuNSwyNzUuNzk5OSwxMjkuNSwyMjEiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjEyOS41IiB4Mj0iMTgyLjUiIHkxPSIyNDIiIHkyPSIyNDIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI1OSIgeD0iMTM5LjUiIHk9IjI2Mi45OTk5Ij5Db2xsZWN0aW9uPC90ZXh0PjwhLS1NRDU9W2FiMWU1YmFmODVlZDE0M2I3NmQzZTc2YTQ4MDc4YmVkXQplbnRpdHkgZG9jdW1lbnQxLS0+PHBvbHlnb24gZmlsbD0iI0ZFRkVDRSIgZmlsdGVyPSJ1cmwoI2YxYWg1eXpyN2JiY2w5KSIgcG9pbnRzPSIxNiwzMzcsMTYsMzczLjc5OTksOTgsMzczLjc5OTksOTgsMzQ3LDg4LDMzNywxNiwzMzciIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxwYXRoIGQ9Ik04OCwzMzcgTDg4LDM0NyBMOTgsMzQ3ICIgZmlsbD0iI0ZFRkVDRSIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNjIiIHg9IjI2IiB5PSIzNjAuOTk5OSI+RG9jdW1lbnQ8L3RleHQ+PCEtLU1ENT1bZGQxYmViZmE4NGUyNDg4YjMxMjc4ODVjNWI0MWUzMjhdCmVudGl0eSBkb2N1bWVudDItLT48cG9seWdvbiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBwb2ludHM9IjEzMywzMzcsMTMzLDM3My43OTk5LDIxNSwzNzMuNzk5OSwyMTUsMzQ3LDIwNSwzMzcsMTMzLDMzNyIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHBhdGggZD0iTTIwNSwzMzcgTDIwNSwzNDcgTDIxNSwzNDcgIiBmaWxsPSIjRkVGRUNFIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2MiIgeD0iMTQzIiB5PSIzNjAuOTk5OSI+RG9jdW1lbnQ8L3RleHQ+PCEtLU1ENT1bYTA1Yjc2ODE2YjZmYzNhYmQzOWU0MzYyNjFlNzViNGNdCmVudGl0eSBkb2N1bWVudDMtLT48cG9seWdvbiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBwb2ludHM9IjI1MCwzMzcsMjUwLDM3My43OTk5LDMzMiwzNzMuNzk5OSwzMzIsMzQ3LDMyMiwzMzcsMjUwLDMzNyIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHBhdGggZD0iTTMyMiwzMzcgTDMyMiwzNDcgTDMzMiwzNDcgIiBmaWxsPSIjRkVGRUNFIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2MiIgeD0iMjYwIiB5PSIzNjAuOTk5OSI+RG9jdW1lbnQ8L3RleHQ+PHBhdGggZD0iTTI1NCwyMjAgTDI1NCwyNDQuNSBMMjE4LjgyLDI0OC41IEwyNTQsMjUyLjUgTDI1NCwyNzYuOCBBMCwwIDAgMCAwIDI1NCwyNzYuOCBMNDM0LDI3Ni44IEEwLDAgMCAwIDAgNDM0LDI3Ni44IEw0MzQsMjMwIEw0MjQsMjIwIEwyNTQsMjIwIEEwLDAgMCAwIDAgMjU0LDIyMCAiIGZpbGw9IiNGQkZCNzciIGZpbHRlcj0idXJsKCNmMWFoNXl6cjdiYmNsOSkiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwYXRoIGQ9Ik00MjQsMjIwIEw0MjQsMjMwIEw0MzQsMjMwIEw0MjQsMjIwICIgZmlsbD0iI0ZCRkI3NyIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMCIgeD0iMjYzIiB5PSIyMzgiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNTkiIHg9IjI2MCIgeT0iMjUzLjYiPk1heGltdW0gb2YgMTAwMCBwZXIgc2NvcGU8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMCIgeD0iMjYzIiB5PSIyNjkuMiIvPjxwYXRoIGQ9Ik0yNDksNiBMMjQ5LDMwLjUgTDIxNC4wMywzNC41IEwyNDksMzguNSBMMjQ5LDYyLjggQTAsMCAwIDAgMCAyNDksNjIuOCBMNDM1LDYyLjggQTAsMCAwIDAgMCA0MzUsNjIuOCBMNDM1LDE2IEw0MjUsNiBMMjQ5LDYgQTAsMCAwIDAgMCAyNDksNiAiIGZpbGw9IiNGQkZCNzciIGZpbHRlcj0idXJsKCNmMWFoNXl6cjdiYmNsOSkiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwYXRoIGQ9Ik00MjUsNiBMNDI1LDE2IEw0MzUsMTYgTDQyNSw2ICIgZmlsbD0iI0ZCRkI3NyIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMCIgeD0iMjU4IiB5PSIyNCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE2NSIgeD0iMjU1IiB5PSIzOS42Ij5PbmUgZGF0YWJhc2UgcGVyIGFwcGxpY2F0aW9uPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjAiIHg9IjI1OCIgeT0iNTUuMiIvPjwhLS1NRDU9W2UyOTM4MDhjMDc4NGZjY2NlMDFkZDIzOGI2NjcxZjYyXQpsaW5rIGRiIHRvIHNjb3BlLS0+PHBhdGggZD0iTTE3NCw1Ny41MSBDMTc0LDc1LjA2IDE3NCw5OS41NiAxNzQsMTE3LjM5ICIgZmlsbD0ibm9uZSIgaWQ9ImRiLXRvLXNjb3BlIiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjE3NCwxMjIuNywxNzgsMTEzLjcsMTc0LDExNy43LDE3MCwxMTMuNywxNzQsMTIyLjciIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjwhLS1NRDU9W2JkOTViMmQ2MGI4M2IzOGQ3NjE4ZjJhYjBhYTkzOTNjXQpsaW5rIHNjb3BlIHRvIGNvbGxlY3Rpb24tLT48cGF0aCBkPSJNMTc0LDE2MC4zMSBDMTc0LDE3NS41IDE3NCwxOTcuNyAxNzQsMjE1LjkgIiBmaWxsPSJub25lIiBpZD0ic2NvcGUtdG8tY29sbGVjdGlvbiIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIxNzQsMjIwLjkyLDE3OC4wMjU3LDIxMS45MzE1LDE3NC4wMTQzLDIxNS45MiwxNzAuMDI1NywyMTEuOTA4NiwxNzQsMjIwLjkyIiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48IS0tTUQ1PVthYjQzYTFmNjAwYmZiODliOWM1MTIxYjM2MDJkYTUxNF0KbGluayBjb2xsZWN0aW9uIHRvIGRvY3VtZW50MS0tPjxwYXRoIGQ9Ik0xNDQuMTcsMjc2LjI3IEMxMjQuNTMsMjkzLjg5IDk5LjE3LDMxNi42NiA4MC44NywzMzMuMDggIiBmaWxsPSJub25lIiBpZD0iY29sbGVjdGlvbi10by1kb2N1bWVudDEiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNzYuODQsMzM2LjY5LDg2LjIxMiwzMzMuNjYyNCw4MC41NjM2LDMzMy4zNTMxLDgwLjg3MjksMzI3LjcwNDcsNzYuODQsMzM2LjY5IiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48IS0tTUQ1PVs5M2Y4YzljYjUwNDZlYmQ4N2QzMDU5NDk4YWY2MTU1OF0KbGluayBjb2xsZWN0aW9uIHRvIGRvY3VtZW50Mi0tPjxwYXRoIGQ9Ik0xNzQsMjc2LjI3IEMxNzQsMjkzLjMgMTc0LDMxNS4xMyAxNzQsMzMxLjQgIiBmaWxsPSJub25lIiBpZD0iY29sbGVjdGlvbi10by1kb2N1bWVudDIiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMTc0LDMzNi42OSwxNzgsMzI3LjY5LDE3NCwzMzEuNjksMTcwLDMyNy42OSwxNzQsMzM2LjY5IiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48IS0tTUQ1PVtmYjE4MDU2OWJkM2VmZGNkNDM1MTNkNTQwNjYwZTEzMF0KbGluayBjb2xsZWN0aW9uIHRvIGRvY3VtZW50My0tPjxwYXRoIGQ9Ik0yMDMuODMsMjc2LjI3IEMyMjMuNDcsMjkzLjg5IDI0OC44MywzMTYuNjYgMjY3LjEzLDMzMy4wOCAiIGZpbGw9Im5vbmUiIGlkPSJjb2xsZWN0aW9uLXRvLWRvY3VtZW50MyIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIyNzEuMTYsMzM2LjY5LDI2Ny4xMjcxLDMyNy43MDQ3LDI2Ny40MzY0LDMzMy4zNTMxLDI2MS43ODgsMzMzLjY2MjQsMjcxLjE2LDMzNi42OSIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PCEtLU1ENT1bMjBmMzA3N2I2Yzc3MjczODBjZWRmNDgyMDQ3YTY2ZjFdCkBzdGFydHVtbA0KZGF0YWJhc2UgIkRhdGFiYXNlIiBhcyBkYg0KDQpyZWN0YW5nbGUgIlNjb3BlIiBhcyBzY29wZQ0KDQpmb2xkZXIgIkNvbGxlY3Rpb24iIGFzIGNvbGxlY3Rpb24NCg0KDQoNCmZpbGUgIkRvY3VtZW50IiBhcyBkb2N1bWVudDENCmZpbGUgIkRvY3VtZW50IiBhcyBkb2N1bWVudDINCmZpbGUgIkRvY3VtZW50IiBhcyBkb2N1bWVudDMNCg0KZGIgLSAtPiBzY29wZQ0Kc2NvcGUgLSAtPiBjb2xsZWN0aW9uDQpjb2xsZWN0aW9uIC0gLT4gZG9jdW1lbnQxDQpjb2xsZWN0aW9uIC0gLT4gZG9jdW1lbnQyDQpjb2xsZWN0aW9uIC0gLT4gZG9jdW1lbnQzDQoNCg0KDQoNCm5vdGUgcmlnaHQgb2YgY29sbGVjdGlvbg0KDQogIE1heGltdW0gb2YgMTAwMCBwZXIgc2NvcGUNCg0KZW5kIG5vdGUNCg0Kbm90ZSByaWdodCBvZiBkYg0KDQogIE9uZSBkYXRhYmFzZSBwZXIgYXBwbGljYXRpb24NCg0KZW5kIG5vdGUNCkBlbmR1bWwNCgpQbGFudFVNTCB2ZXJzaW9uIDEuMjAyMS4xNihXZWQgRGVjIDA4IDE3OjI1OjIyIEdNVCAyMDIxKQooR1BMIHNvdXJjZSBkaXN0cmlidXRpb24pCkphdmEgUnVudGltZTogT3BlbkpESyBSdW50aW1lIEVudmlyb25tZW50CkpWTTogT3BlbkpESyA2NC1CaXQgU2VydmVyIFZNCkRlZmF1bHQgRW5jb2Rpbmc6IFVURi04Ckxhbmd1YWdlOiBlbgpDb3VudHJ5OiBVUwotLT48L2c+PC9zdmc+"},8130:(i,I,e)=>{e.d(I,{A:()=>a});const a="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U2NyaXB0VHlwZT0iYXBwbGljYXRpb24vZWNtYXNjcmlwdCIgY29udGVudFN0eWxlVHlwZT0idGV4dC9jc3MiIGhlaWdodD0iMzIxcHgiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiIHN0eWxlPSJ3aWR0aDo2NDdweDtoZWlnaHQ6MzIxcHg7YmFja2dyb3VuZDojRkZGRkZGOyIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgNjQ3IDMyMSIgd2lkdGg9IjY0N3B4IiB6b29tQW5kUGFuPSJtYWduaWZ5Ij48ZGVmcz48ZmlsdGVyIGhlaWdodD0iMzAwJSIgaWQ9ImYxcnlvd2xqdmdzdTlzIiB3aWR0aD0iMzAwJSIgeD0iLTEiIHk9Ii0xIj48ZmVHYXVzc2lhbkJsdXIgcmVzdWx0PSJibHVyT3V0IiBzdGREZXZpYXRpb249IjIuMCIvPjxmZUNvbG9yTWF0cml4IGluPSJibHVyT3V0IiByZXN1bHQ9ImJsdXJPdXQyIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgLjQgMCIvPjxmZU9mZnNldCBkeD0iNC4wIiBkeT0iNC4wIiBpbj0iYmx1ck91dDIiIHJlc3VsdD0iYmx1ck91dDMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyT3V0MyIgbW9kZT0ibm9ybWFsIi8+PC9maWx0ZXI+PC9kZWZzPjxnPjwhLS1NRDU9WzUyMjFiYTI1YTNkYzU5YTc1MDdkZGU5OThiZDZiZmMyXQpjbHVzdGVyIGRiMS0tPjxwYXRoIGQ9Ik02LDE2IEM2LDYgMTE5LDYgMTE5LDYgQzExOSw2IDIzMiw2IDIzMiwxNiBMMjMyLDE4OSBDMjMyLDE5OSAxMTksMTk5IDExOSwxOTkgQzExOSwxOTkgNiwxOTkgNiwxODkgTDYsMTYgIiBmaWxsPSIjRkZGRkZGIiBmaWx0ZXI9InVybCgjZjFyeW93bGp2Z3N1OXMpIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48cGF0aCBkPSJNNiwxNiBDNiwyNiAxMTksMjYgMTE5LDI2IEMxMTksMjYgMjMyLDI2IDIzMiwxNiAiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNzQiIHg9IjMyIiB5PSI0MS45OTk5Ij5Db3VjaGJhc2UgTGl0ZSBkYXRhYmFzZTwvdGV4dD48IS0tTUQ1PVtmNDNmY2EwMGFhNDM3ODlhOThiN2MyMTQ1MmEwOWFhOV0KY2x1c3RlciBkZnMxLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSIzMCw3NCw0MCw2NCwyMDgsNjQsMjA4LDE2NSwxOTgsMTc1LDMwLDE3NSwzMCw3NCIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iMTk4IiB4Mj0iMjA4IiB5MT0iNzQiIHkyPSI2NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjMwIiB4Mj0iMTk4IiB5MT0iNzQiIHkyPSI3NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjE5OCIgeDI9IjE5OCIgeTE9Ijc0IiB5Mj0iMTc1Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjEwMiIgeD0iNjQiIHk9IjkwLjk5OTkiPl9kZWZhdWx0IHNjb3BlPC90ZXh0PjwhLS1NRDU9WzI4MmEzODc4Y2MyOTZmMTllNmM2NDE3YjAyNmU4OWY2XQpjbHVzdGVyIGRiMi0tPjxwYXRoIGQ9Ik0yNTYsMTYgQzI1Niw2IDQ0OCw2IDQ0OCw2IEM0NDgsNiA2NDAsNiA2NDAsMTYgTDY0MCwzMDQgQzY0MCwzMTQgNDQ4LDMxNCA0NDgsMzE0IEM0NDgsMzE0IDI1NiwzMTQgMjU2LDMwNCBMMjU2LDE2ICIgZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHBhdGggZD0iTTI1NiwxNiBDMjU2LDI2IDQ0OCwyNiA0NDgsMjYgQzQ0OCwyNiA2NDAsMjYgNjQwLDE2ICIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE3NCIgeD0iMzYxIiB5PSI0MS45OTk5Ij5Db3VjaGJhc2UgTGl0ZSBkYXRhYmFzZTwvdGV4dD48IS0tTUQ1PVs4ODcwYTZiZmQ1YTJjOTQxNDI2ZTdkMDQxMDIxYTk2Yl0KY2x1c3RlciBkZnMyLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSI0MzgsNzQsNDQ4LDY0LDYxNiw2NCw2MTYsMjgwLDYwNiwyOTAsNDM4LDI5MCw0MzgsNzQiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjYwNiIgeDI9IjYxNiIgeTE9Ijc0IiB5Mj0iNjQiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHgxPSI0MzgiIHgyPSI2MDYiIHkxPSI3NCIgeTI9Ijc0Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iNjA2IiB4Mj0iNjA2IiB5MT0iNzQiIHkyPSIyOTAiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTAyIiB4PSI0NzIiIHk9IjkwLjk5OTkiPl9kZWZhdWx0IHNjb3BlPC90ZXh0PjwhLS1NRDU9WzljM2I1NWJmZGIzMWVkNzkwM2Y1Yzg4NzEzMDU5NzYxXQpjbHVzdGVyIHNjb3BlQS0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iMjgwLDc0LDI5MCw2NCw0MTQsNjQsNDE0LDI4MCw0MDQsMjkwLDI4MCwyOTAsMjgwLDc0IiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHgxPSI0MDQiIHgyPSI0MTQiIHkxPSI3NCIgeTI9IjY0Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iMjgwIiB4Mj0iNDA0IiB5MT0iNzQiIHkyPSI3NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjQwNCIgeDI9IjQwNCIgeTE9Ijc0IiB5Mj0iMjkwIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjU4IiB4PSIzMTQiIHk9IjkwLjk5OTkiPlNjb3BlIEE8L3RleHQ+PCEtLU1ENT1bYWUxZDk5ODJjZTU0OWI0MWVmZjRkZDQ5MmQwOTMwMzJdCmVudGl0eSBkZmMxLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSI0OSwxMDQsOTUsMTA0LDEwMiwxMjUsMTg5LDEyNSwxODksMTU4Ljc5OTksNDksMTU4Ljc5OTksNDksMTA0IiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHgxPSI0OSIgeDI9IjEwMiIgeTE9IjEyNSIgeTI9IjEyNSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjExMCIgeD0iNTkiIHk9IjE0NS45OTk5Ij5fZGVmYXVsdCBjb2xsZWN0aW9uPC90ZXh0PjwhLS1NRDU9WzljMmJiZGY3MDY0MzBjODliZTNjZmRiNDUwMzlmZDlmXQplbnRpdHkgZGZjMi0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iNDU3LDIxOSw1MDMsMjE5LDUxMCwyNDAsNTk3LDI0MCw1OTcsMjczLjc5OTksNDU3LDI3My43OTk5LDQ1NywyMTkiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjQ1NyIgeDI9IjUxMCIgeTE9IjI0MCIgeTI9IjI0MCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjExMCIgeD0iNDY3IiB5PSIyNjAuOTk5OSI+X2RlZmF1bHQgY29sbGVjdGlvbjwvdGV4dD48IS0tTUQ1PVtjN2Q4YWU4ZTIzMjBiOTc1NmEyNzZiZDFhNzc3ZDM5YV0KZW50aXR5IGNvbGxlY3Rpb25BLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSI0NzYsMTA0LDUyMiwxMDQsNTI5LDEyNSw1NzgsMTI1LDU3OCwxNTguNzk5OSw0NzYsMTU4Ljc5OTksNDc2LDEwNCIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iNDc2IiB4Mj0iNTI5IiB5MT0iMTI1IiB5Mj0iMTI1Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNzIiIHg9IjQ4NiIgeT0iMTQ1Ljk5OTkiPkNvbGxlY3Rpb24gQTwvdGV4dD48IS0tTUQ1PVs3OWEwNTU4ZTZmYzYwMzFjNzExNjYxZTA5OGFjY2Y5Zl0KZW50aXR5IGNvbGxlY3Rpb25BMi0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iMjk2LDEwNCwzNDIsMTA0LDM0OSwxMjUsMzk4LDEyNSwzOTgsMTU4Ljc5OTksMjk2LDE1OC43OTk5LDI5NiwxMDQiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjI5NiIgeDI9IjM0OSIgeTE9IjEyNSIgeTI9IjEyNSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjcyIiB4PSIzMDYiIHk9IjE0NS45OTk5Ij5Db2xsZWN0aW9uIEE8L3RleHQ+PCEtLU1ENT1bZTAwNzJlNTMzYWIyZTkzODNkOTRmNDgzOTUwZTMyNDhdCmVudGl0eSBjb2xsZWN0aW9uQi0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iMjk2LDIxOSwzNDIsMjE5LDM0OSwyNDAsMzk4LDI0MCwzOTgsMjczLjc5OTksMjk2LDI3My43OTk5LDI5NiwyMTkiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjI5NiIgeDI9IjM0OSIgeTE9IjI0MCIgeTI9IjI0MCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjcyIiB4PSIzMDYiIHk9IjI2MC45OTk5Ij5Db2xsZWN0aW9uIEI8L3RleHQ+PCEtLU1ENT1bMDRlOTc5Y2EwNmYzZjZmYWI5ZDBjZjA5MDE1MTBlNjhdCmxpbmsgY29sbGVjdGlvbkEyIHRvIGNvbGxlY3Rpb25CLS0+PCEtLU1ENT1bN2YwMzZhMmRlNmE0YzEyZjY3ZDA5ZTYyNzdkZjllMzddCmxpbmsgY29sbGVjdGlvbkEgdG8gZGZjMi0tPjwhLS1NRDU9W2I2MWI4YjRlZjhhOTJjNzMzOWU2YTJkYTU5ZjI2YjMwXQpAc3RhcnR1bWwNCmRhdGFiYXNlICJDb3VjaGJhc2UgTGl0ZSBkYXRhYmFzZSIgYXMgZGIxIHsNCg0KICAgICAgbm9kZSAiX2RlZmF1bHQgc2NvcGUiIGFzIGRmczEgew0KDQogICAgICAgIGZvbGRlciAiX2RlZmF1bHQgY29sbGVjdGlvbiIgYXMgZGZjMSB7DQoNCiAgICAgICAgfQ0KICAgICAgfQ0KDQp9DQoNCmRhdGFiYXNlICJDb3VjaGJhc2UgTGl0ZSBkYXRhYmFzZSIgYXMgZGIyIHsNCg0KDQogICAgbm9kZSAiX2RlZmF1bHQgc2NvcGUiIGFzIGRmczIgew0KDQogICAgICBmb2xkZXIgIl9kZWZhdWx0IGNvbGxlY3Rpb24iIGFzIGRmYzIgew0KDQogICAgICB9DQoNCiAgICAgICAgZm9sZGVyICJDb2xsZWN0aW9uIEEiIGFzIGNvbGxlY3Rpb25BIHsNCg0KICAgICAgICB9DQogICAgfQ0KDQogICAgbm9kZSAiU2NvcGUgQSIgYXMgc2NvcGVBIHsNCg0KICAgICAgZm9sZGVyICJDb2xsZWN0aW9uIEEiIGFzIGNvbGxlY3Rpb25BMiB7DQoNCiAgICAgIH0NCg0KICAgICAgICBmb2xkZXIgIkNvbGxlY3Rpb24gQiIgYXMgY29sbGVjdGlvbkIgew0KDQogICAgICAgIH0NCiAgICB9DQoNCn0NCg0KY29sbGVjdGlvbkEyIC1EW2hpZGRlbl0tPiBjb2xsZWN0aW9uQg0KY29sbGVjdGlvbkEgLURbaGlkZGVuXS0+IGRmYzINCkBlbmR1bWwNCgpQbGFudFVNTCB2ZXJzaW9uIDEuMjAyMS4xNihXZWQgRGVjIDA4IDE3OjI1OjIyIEdNVCAyMDIxKQooR1BMIHNvdXJjZSBkaXN0cmlidXRpb24pCkphdmEgUnVudGltZTogT3BlbkpESyBSdW50aW1lIEVudmlyb25tZW50CkpWTTogT3BlbkpESyA2NC1CaXQgU2VydmVyIFZNCkRlZmF1bHQgRW5jb2Rpbmc6IFVURi04Ckxhbmd1YWdlOiBlbgpDb3VudHJ5OiBVUwotLT48L2c+PC9zdmc+"},8453:(i,I,e)=>{e.d(I,{R:()=>t,x:()=>c});var a=e(6540);const s={},n=a.createContext(s);function t(i){const I=a.useContext(n);return a.useMemo((function(){return"function"==typeof i?i(I):{...I,...i}}),[I,i])}function c(i){let I;return I=i.disableParentContext?"function"==typeof i.components?i.components(s):i.components||s:t(i.components),a.createElement(n.Provider,{value:I},i.children)}}}]); \ No newline at end of file diff --git a/assets/js/d22a0e6a.a3bfa71d.js b/assets/js/d22a0e6a.a3bfa71d.js new file mode 100644 index 0000000..e6732c6 --- /dev/null +++ b/assets/js/d22a0e6a.a3bfa71d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[2184],{1048:(i,e,a)=>{a.r(e),a.d(e,{assets:()=>l,contentTitle:()=>t,default:()=>o,frontMatter:()=>s,metadata:()=>c,toc:()=>d});var I=a(4848),n=a(8453);const s={id:"databases",sidebar_position:4},t="Databases",c={id:"databases",title:"Databases",description:"Description \u2014 Working with Couchbase Lite Databases",source:"@site/docs/databases.md",sourceDirName:".",slug:"/databases",permalink:"/databases",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/databases.md",tags:[],version:"current",sidebarPosition:4,frontMatter:{id:"databases",sidebar_position:4},sidebar:"tutorialSidebar",previous:{title:"Build and Run",permalink:"/StartHere/build-run"},next:{title:"Pre-built Database",permalink:"/database-prebuilt"}},l={},d=[{value:"Database Concepts",id:"database-concepts",level:2},{value:"Storing local configuration",id:"storing-local-configuration",level:3},{value:"Managing Couchbase Lite Databases in Ionic",id:"managing-couchbase-lite-databases-in-ionic",level:2},{value:"Initializing the Environment",id:"initializing-the-environment",level:3},{value:"Create or Open a Database",id:"create-or-open-a-database",level:3},{value:"Closing a Database",id:"closing-a-database",level:3},{value:"Deleting a Database",id:"deleting-a-database",level:3},{value:"Database Encryption",id:"database-encryption",level:2},{value:"Enabling",id:"enabling",level:3},{value:"Persisting",id:"persisting",level:3},{value:"Opening",id:"opening",level:3},{value:"Database Maintenance",id:"database-maintenance",level:2},{value:"Deprecated API",id:"deprecated-api",level:2},{value:"Document APIs",id:"document-apis",level:3},{value:"Index APIs",id:"index-apis",level:3},{value:"Couchbase Lite for VSCode",id:"couchbase-lite-for-vscode",level:2},{value:"Couchbase Lite for JetBrains",id:"couchbase-lite-for-jetbrains",level:2},{value:"Command Line Tool",id:"command-line-tool",level:2},{value:"Troubleshooting",id:"troubleshooting",level:2}];function M(i){const e={a:"a",blockquote:"blockquote",br:"br",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",img:"img",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,n.R)(),...i.components};return(0,I.jsxs)(I.Fragment,{children:[(0,I.jsx)(e.h1,{id:"databases",children:"Databases"}),"\n",(0,I.jsxs)(e.blockquote,{children:["\n",(0,I.jsxs)(e.p,{children:["Description \u2014 ",(0,I.jsx)(e.em,{children:"Working with Couchbase Lite Databases"}),(0,I.jsx)(e.br,{}),"\n","Related Content \u2014 ",(0,I.jsx)(e.a,{href:"/blobs",children:"Blobs"})," | ",(0,I.jsx)(e.a,{href:"/documents",children:"Documents"})," | ",(0,I.jsx)(e.a,{href:"indexing.md",children:"Indexing"})]}),"\n"]}),"\n",(0,I.jsx)(e.h2,{id:"database-concepts",children:"Database Concepts"}),"\n",(0,I.jsx)(e.p,{children:"Databases created on Couchbase Lite can share the same hierarchical structure as Capella databases. This makes it easier to sync data between mobile applications and applications built using Capella."}),"\n",(0,I.jsxs)("div",{align:"center",children:[(0,I.jsx)(e.p,{children:(0,I.jsx)(e.img,{alt:"Couchbase Lite Database Hierarchy",src:a(7432).A+"",width:"444",height:"380"})}),(0,I.jsx)(e.p,{children:(0,I.jsx)(e.em,{children:"Figure 1. Couchbase Lite Database Hierarchy"})})]}),"\n",(0,I.jsx)(e.p,{children:"Although the terminology is different, the structure can be mapped to relational database terms:"}),"\n",(0,I.jsx)(e.p,{children:"Table 1. Relational Database \u2192 Couchbase"}),"\n",(0,I.jsxs)(e.table,{children:[(0,I.jsx)(e.thead,{children:(0,I.jsxs)(e.tr,{children:[(0,I.jsx)(e.th,{children:"Relational database"}),(0,I.jsx)(e.th,{children:"Couchbase"})]})}),(0,I.jsxs)(e.tbody,{children:[(0,I.jsxs)(e.tr,{children:[(0,I.jsx)(e.td,{children:"Database"}),(0,I.jsx)(e.td,{children:"Database"})]}),(0,I.jsxs)(e.tr,{children:[(0,I.jsx)(e.td,{children:"Schema"}),(0,I.jsx)(e.td,{children:"Scope"})]}),(0,I.jsxs)(e.tr,{children:[(0,I.jsx)(e.td,{children:"Table"}),(0,I.jsx)(e.td,{children:"Collection"})]})]})]}),"\n",(0,I.jsx)(e.p,{children:"This structure gives you plenty of choices when it comes to partitioning your data. The most basic structure is to use the single default scope with a single default collection; or you could opt for a structure that allow you to split your collections into logical scopes."}),"\n",(0,I.jsxs)("div",{align:"center",children:[(0,I.jsx)(e.p,{children:(0,I.jsx)(e.img,{alt:"Couchbase Lite Examples",src:a(8130).A+"",width:"647",height:"321"})}),(0,I.jsx)(e.p,{children:(0,I.jsx)(e.em,{children:"Figure 2. Couchbase Lite Examples"})})]}),"\n",(0,I.jsx)(e.h3,{id:"storing-local-configuration",children:"Storing local configuration"}),"\n",(0,I.jsx)(e.p,{children:"You may not need to sync all the data related to a particular application. You can set up a scope that syncs data, and a second scope that doesn\u2019t. One reason for doing this is to store local configuration data (such as the preferred screen orientation or keyboard layout). Since this information only relates to a particular device, there is no need to sync it:"}),"\n",(0,I.jsxs)(e.ul,{children:["\n",(0,I.jsxs)(e.li,{children:[(0,I.jsx)(e.strong,{children:"local data scope"})," \u2014 Contains information pertaining to the device."]}),"\n",(0,I.jsxs)(e.li,{children:[(0,I.jsx)(e.strong,{children:"syncing data scope"})," \u2014 Contains information pertaining to the user, which can be synced back to the cloud for use on the web or another device."]}),"\n"]}),"\n",(0,I.jsx)(e.h2,{id:"managing-couchbase-lite-databases-in-ionic",children:"Managing Couchbase Lite Databases in Ionic"}),"\n",(0,I.jsx)(e.h3,{id:"initializing-the-environment",children:"Initializing the Environment"}),"\n",(0,I.jsx)(e.p,{children:"In an Ionic application using Couchbase Lite, begin by initializing the Capacitor Engine. Subsequently, employ a design pattern such as Context/Provider or Service Locator to maintain and access your database instances throughout the application lifecycle."}),"\n",(0,I.jsx)(e.p,{children:(0,I.jsx)(e.strong,{children:"Example: Initializing Capacitor Engine and Database Context"})}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-typescript",children:"import { CapacitorEngine } from 'cbl-ionic';\n\nconst engine = new CapacitorEngine(); // Initialize once, early in your app\n"})}),"\n",(0,I.jsx)(e.p,{children:"This configuration ensures seamless interaction between your Ionic app and the underlying native database functionalities, facilitating effective database management. This only needs to be done once in your application and must be done before any other Couchbase Lite operations are performed."}),"\n",(0,I.jsx)(e.h3,{id:"create-or-open-a-database",children:"Create or Open a Database"}),"\n",(0,I.jsx)(e.p,{children:"To create or open a database, use the Database class from the cbl-ionic package, specifying the database name and optionally, a DatabaseConfiguration for custom settings like the database directory or encryption."}),"\n",(0,I.jsx)(e.p,{children:(0,I.jsx)(e.strong,{children:"Example 1. Creating/Opening a Database"})}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-javascript",children:"import { Database, DatabaseConfiguration } from 'cbl-ionic'; //import the package\n"})}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-javascript",children:"const config = new DatabaseConfiguration();\nconfig.setDirectory('path/to/database'); // Optional\nconst myDatabase = new Database('myDatabaseName', config);\nawait myDatabase.open();\n"})}),"\n",(0,I.jsx)(e.h3,{id:"closing-a-database",children:"Closing a Database"}),"\n",(0,I.jsx)(e.p,{children:"You are advised to incorporate the closing of all open databases into your application workflow."}),"\n",(0,I.jsx)(e.p,{children:(0,I.jsx)(e.strong,{children:"Example 2. Closing a Database"})}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-javascript",children:"await myDatabase.close();\n"})}),"\n",(0,I.jsx)(e.h3,{id:"deleting-a-database",children:"Deleting a Database"}),"\n",(0,I.jsx)(e.p,{children:"The delete method is used to delete a database."}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-typescript",children:" await database.deleteDatabase();\n"})}),"\n",(0,I.jsx)(e.p,{children:"Alternatively, you can delete a database by calling the delete method on the Database class."}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-typescript",children:"Database.deleteDatabase('myDatabaseName', 'path/to/database');\n"})}),"\n",(0,I.jsx)(e.h2,{id:"database-encryption",children:"Database Encryption"}),"\n",(0,I.jsx)(e.p,{children:"Couchbase Lite Enterprise Edition includes the ability to encrypt Couchbase Lite databases. This allows mobile applications to secure data at rest, when it is being stored on the device. The algorithm used to encrypt the database is 256-bit AES."}),"\n",(0,I.jsx)(e.h3,{id:"enabling",children:"Enabling"}),"\n",(0,I.jsxs)(e.p,{children:["To enable database encryption in Ionic, use the ",(0,I.jsx)(e.code,{children:"DatabaseConfiguration"})," class to set an encryption key before opening or creating a database. This encryption key must be provided every time the database is accessed."]}),"\n",(0,I.jsx)(e.p,{children:(0,I.jsx)(e.strong,{children:"Example3. Configure Database Encryption"})}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-typescript",children:"const dbName = 'my_secure_db';\nconst encryptionKey = 'my_secret_key';\n\nconst config = new DatabaseConfiguration();\nconfig.setEncryptionKey(encryptionKey);\n\nconst db = new Database(dbName, config);\n\nawait db.open();\n"})}),"\n",(0,I.jsx)(e.h3,{id:"persisting",children:"Persisting"}),"\n",(0,I.jsxs)(e.p,{children:["Couchbase Lite does not persist the key. It is the application\u2019s responsibility to manage the key and store it in a platform-specific secure store such as Apples's ",(0,I.jsx)(e.a,{href:"https://developer.apple.com/documentation/security/keychain_services",children:"Keystore"})," or Android\u2019s ",(0,I.jsx)(e.a,{href:"https://developer.android.com/privacy-and-security/keystore",children:"Keystore"}),"."]}),"\n",(0,I.jsx)(e.h3,{id:"opening",children:"Opening"}),"\n",(0,I.jsx)(e.p,{children:"An encrypted database can only be opened with the same language package that was used to encrypt it in the first place. So a database encrypted using the Ionic package on iOS, and then exported, is readable only by the iOS SDK since cbl-ionic is a wrapper around the native SDKs."}),"\n",(0,I.jsx)(e.h2,{id:"database-maintenance",children:"Database Maintenance"}),"\n",(0,I.jsx)(e.p,{children:"From time to time it may be necessary to perform certain maintenance activities on your database, for example to compact the database file, removing unused documents and blobs no longer referenced by any documents."}),"\n",(0,I.jsx)(e.p,{children:"Couchbase Lite's API provides the Database.performMaintenance method. The available maintenance operations, including compact are as shown in the enum MaintenanceType to accomplish this."}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-typescript",children:"const dbName = 'my_secure_db';\nconst config = new DatabaseConfiguration();\nconst db = new Database(dbName, config);\nawait db.open();\nawait db.performMaintenance(MaintenanceType.compact);\n"})}),"\n",(0,I.jsx)(e.p,{children:"This is a resource intensive operation and is not performed automatically. It should be run on-demand using the API. A full listing of the available maintenance operations is shown below:"}),"\n",(0,I.jsxs)(e.ul,{children:["\n",(0,I.jsxs)(e.li,{children:[(0,I.jsx)(e.strong,{children:"MaintenanceType.compact"}),": Compact the database file and delete unused attachments."]}),"\n",(0,I.jsxs)(e.li,{children:[(0,I.jsx)(e.strong,{children:"MaintenanceType.reindex"}),": (Volatile API) Rebuild the entire database\u2019s indexes."]}),"\n",(0,I.jsxs)(e.li,{children:[(0,I.jsx)(e.strong,{children:"MaintenanceType.integrityCheck"}),": (Volatile API) Check for the database\u2019s corruption. If found, an error will be returned."]}),"\n",(0,I.jsxs)(e.li,{children:[(0,I.jsx)(e.strong,{children:"MaintenanceType.optimize"}),": Quickly updates database statistics that may help optimize queries that have been run by this Database since it was opened"]}),"\n",(0,I.jsxs)(e.li,{children:[(0,I.jsx)(e.strong,{children:"MaintenanceType.fullOptimize"}),": Fully scans all indexes to gather database statistics that help optimize queries."]}),"\n"]}),"\n",(0,I.jsxs)(e.p,{children:["For questions or issues, please visit the ",(0,I.jsx)(e.a,{href:"https://www.couchbase.com/forums/",children:"Couchbase Forums"})," where you can ask for help and discuss with the community."]}),"\n",(0,I.jsx)(e.h2,{id:"deprecated-api",children:"Deprecated API"}),"\n",(0,I.jsx)(e.p,{children:"With the introduction of Scopes and Collections, several Database APIs in the Ionic package are now deprecated. These features will be removed in a future release, and developers are encouraged to use the Scopes/Collections APIs instead for these operations."}),"\n",(0,I.jsx)(e.h3,{id:"document-apis",children:"Document APIs"}),"\n",(0,I.jsxs)(e.ul,{children:["\n",(0,I.jsx)(e.li,{children:"deleteDocument"}),"\n",(0,I.jsx)(e.li,{children:"getCount"}),"\n",(0,I.jsx)(e.li,{children:"getDocument"}),"\n",(0,I.jsx)(e.li,{children:"purgeDocument"}),"\n",(0,I.jsx)(e.li,{children:"save"}),"\n"]}),"\n",(0,I.jsx)(e.h3,{id:"index-apis",children:"Index APIs"}),"\n",(0,I.jsxs)(e.ul,{children:["\n",(0,I.jsx)(e.li,{children:"createIndex"}),"\n",(0,I.jsx)(e.li,{children:"getIndexes"}),"\n",(0,I.jsx)(e.li,{children:"deleteIndex"}),"\n"]}),"\n",(0,I.jsxs)(e.p,{children:["For developers not working with custom Scopes/Collections, you can use the ",(0,I.jsx)(e.code,{children:"_default"})," (default) scope and collection to achieve the same functionality with the new Scopes/Collections APIs."]}),"\n",(0,I.jsx)(e.h2,{id:"couchbase-lite-for-vscode",children:"Couchbase Lite for VSCode"}),"\n",(0,I.jsxs)(e.p,{children:["Couchbase Lite for VSCode is a Visual Studio Code extension that provides a user interface for inspecting and querying Couchbase Lite databases. You can find more information about this extension from it's ",(0,I.jsx)(e.a,{href:"https://github.com/couchbaselabs/vscode-cblite",children:"GitHub repository"}),"."]}),"\n",(0,I.jsx)(e.h2,{id:"couchbase-lite-for-jetbrains",children:"Couchbase Lite for JetBrains"}),"\n",(0,I.jsxs)(e.p,{children:["Couchbase Lite for JetBrains is a JetBrains IDE plugin that provides a user interface for inspecting and querying Couchbase Lite databases. You can find more information about this plugin from its ",(0,I.jsx)(e.a,{href:"https://github.com/couchbaselabs/couchbase_jetbrains_plugin",children:"GitHub repository"}),"."]}),"\n",(0,I.jsx)(e.h2,{id:"command-line-tool",children:"Command Line Tool"}),"\n",(0,I.jsx)(e.p,{children:"cblite is a command-line tool for inspecting and querying Couchbase Lite databases."}),"\n",(0,I.jsxs)(e.p,{children:["You can download and build it from the couchbaselabs ",(0,I.jsx)(e.a,{href:"https://github.com/couchbaselabs/couchbase-mobile-tools/blob/master/README.cblite.md",children:"GitHub repository"}),". Note that the cblite tool is not supported by the ",(0,I.jsx)(e.a,{href:"https://www.couchbase.com/support-policy/",children:"Couchbase Support Policy"}),"."]}),"\n",(0,I.jsx)(e.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,I.jsx)(e.p,{children:"You should use console logs as your first source of diagnostic information. If the information in the default logging level is insufficient you can focus it on database errors and generate more verbose messages."}),"\n",(0,I.jsx)(e.pre,{children:(0,I.jsx)(e.code,{className:"language-typescript",children:"try {\n await db.setLogLevel(LogDomain.DATABASE, LogLevel.VERBOSE);\n console.log('Database log level set to VERBOSE.');\n} catch (error) {\n console.error('Setting log level failed:', error);\n}\n"})})]})}function o(i={}){const{wrapper:e}={...(0,n.R)(),...i.components};return e?(0,I.jsx)(e,{...i,children:(0,I.jsx)(M,{...i})}):M(i)}},7432:(i,e,a)=>{a.d(e,{A:()=>I});const I="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U2NyaXB0VHlwZT0iYXBwbGljYXRpb24vZWNtYXNjcmlwdCIgY29udGVudFN0eWxlVHlwZT0idGV4dC9jc3MiIGhlaWdodD0iMzgwcHgiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiIHN0eWxlPSJ3aWR0aDo0NDRweDtoZWlnaHQ6MzgwcHg7YmFja2dyb3VuZDojRkZGRkZGOyIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgNDQ0IDM4MCIgd2lkdGg9IjQ0NHB4IiB6b29tQW5kUGFuPSJtYWduaWZ5Ij48ZGVmcz48ZmlsdGVyIGhlaWdodD0iMzAwJSIgaWQ9ImYxYWg1eXpyN2JiY2w5IiB3aWR0aD0iMzAwJSIgeD0iLTEiIHk9Ii0xIj48ZmVHYXVzc2lhbkJsdXIgcmVzdWx0PSJibHVyT3V0IiBzdGREZXZpYXRpb249IjIuMCIvPjxmZUNvbG9yTWF0cml4IGluPSJibHVyT3V0IiByZXN1bHQ9ImJsdXJPdXQyIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgLjQgMCIvPjxmZU9mZnNldCBkeD0iNC4wIiBkeT0iNC4wIiBpbj0iYmx1ck91dDIiIHJlc3VsdD0iYmx1ck91dDMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyT3V0MyIgbW9kZT0ibm9ybWFsIi8+PC9maWx0ZXI+PC9kZWZzPjxnPjwhLS1NRDU9Wzc0Njg0NjdmM2I2YjM5MmYxZDE3NmQ4MjhjNzUzYjc3XQplbnRpdHkgZGItLT48cGF0aCBkPSJNMTM0LDIxLjUgQzEzNCwxMS41IDE3NCwxMS41IDE3NCwxMS41IEMxNzQsMTEuNSAyMTQsMTEuNSAyMTQsMjEuNSBMMjE0LDQ3LjI5OTkgQzIxNCw1Ny4yOTk5IDE3NCw1Ny4yOTk5IDE3NCw1Ny4yOTk5IEMxNzQsNTcuMjk5OSAxMzQsNTcuMjk5OSAxMzQsNDcuMjk5OSBMMTM0LDIxLjUgIiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48cGF0aCBkPSJNMTM0LDIxLjUgQzEzNCwzMS41IDE3NCwzMS41IDE3NCwzMS41IEMxNzQsMzEuNSAyMTQsMzEuNSAyMTQsMjEuNSAiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjYwIiB4PSIxNDQiIHk9IjQ5LjQ5OTkiPkRhdGFiYXNlPC90ZXh0PjwhLS1NRDU9W2M4YTFmMDU0ZDQwNGRmMGE2NWZjMjVjZjdmYmVkMzZiXQplbnRpdHkgc2NvcGUtLT48cmVjdCBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBoZWlnaHQ9IjM2Ljc5OTkiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgd2lkdGg9IjU4IiB4PSIxNDUiIHk9IjEyMyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjM4IiB4PSIxNTUiIHk9IjE0Ni45OTk5Ij5TY29wZTwvdGV4dD48IS0tTUQ1PVtlODE0NDAwOTI1MGZlZmE4ZGU3YWIxN2U4ZmUyMDM4MF0KZW50aXR5IGNvbGxlY3Rpb24tLT48cG9seWdvbiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBwb2ludHM9IjEyOS41LDIyMSwxNzUuNSwyMjEsMTgyLjUsMjQyLDIxOC41LDI0MiwyMTguNSwyNzUuNzk5OSwxMjkuNSwyNzUuNzk5OSwxMjkuNSwyMjEiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjEyOS41IiB4Mj0iMTgyLjUiIHkxPSIyNDIiIHkyPSIyNDIiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI1OSIgeD0iMTM5LjUiIHk9IjI2Mi45OTk5Ij5Db2xsZWN0aW9uPC90ZXh0PjwhLS1NRDU9W2FiMWU1YmFmODVlZDE0M2I3NmQzZTc2YTQ4MDc4YmVkXQplbnRpdHkgZG9jdW1lbnQxLS0+PHBvbHlnb24gZmlsbD0iI0ZFRkVDRSIgZmlsdGVyPSJ1cmwoI2YxYWg1eXpyN2JiY2w5KSIgcG9pbnRzPSIxNiwzMzcsMTYsMzczLjc5OTksOTgsMzczLjc5OTksOTgsMzQ3LDg4LDMzNywxNiwzMzciIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxwYXRoIGQ9Ik04OCwzMzcgTDg4LDM0NyBMOTgsMzQ3ICIgZmlsbD0iI0ZFRkVDRSIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNjIiIHg9IjI2IiB5PSIzNjAuOTk5OSI+RG9jdW1lbnQ8L3RleHQ+PCEtLU1ENT1bZGQxYmViZmE4NGUyNDg4YjMxMjc4ODVjNWI0MWUzMjhdCmVudGl0eSBkb2N1bWVudDItLT48cG9seWdvbiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBwb2ludHM9IjEzMywzMzcsMTMzLDM3My43OTk5LDIxNSwzNzMuNzk5OSwyMTUsMzQ3LDIwNSwzMzcsMTMzLDMzNyIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHBhdGggZD0iTTIwNSwzMzcgTDIwNSwzNDcgTDIxNSwzNDcgIiBmaWxsPSIjRkVGRUNFIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2MiIgeD0iMTQzIiB5PSIzNjAuOTk5OSI+RG9jdW1lbnQ8L3RleHQ+PCEtLU1ENT1bYTA1Yjc2ODE2YjZmYzNhYmQzOWU0MzYyNjFlNzViNGNdCmVudGl0eSBkb2N1bWVudDMtLT48cG9seWdvbiBmaWxsPSIjRkVGRUNFIiBmaWx0ZXI9InVybCgjZjFhaDV5enI3YmJjbDkpIiBwb2ludHM9IjI1MCwzMzcsMjUwLDM3My43OTk5LDMzMiwzNzMuNzk5OSwzMzIsMzQ3LDMyMiwzMzcsMjUwLDMzNyIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHBhdGggZD0iTTMyMiwzMzcgTDMyMiwzNDcgTDMzMiwzNDcgIiBmaWxsPSIjRkVGRUNFIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSI2MiIgeD0iMjYwIiB5PSIzNjAuOTk5OSI+RG9jdW1lbnQ8L3RleHQ+PHBhdGggZD0iTTI1NCwyMjAgTDI1NCwyNDQuNSBMMjE4LjgyLDI0OC41IEwyNTQsMjUyLjUgTDI1NCwyNzYuOCBBMCwwIDAgMCAwIDI1NCwyNzYuOCBMNDM0LDI3Ni44IEEwLDAgMCAwIDAgNDM0LDI3Ni44IEw0MzQsMjMwIEw0MjQsMjIwIEwyNTQsMjIwIEEwLDAgMCAwIDAgMjU0LDIyMCAiIGZpbGw9IiNGQkZCNzciIGZpbHRlcj0idXJsKCNmMWFoNXl6cjdiYmNsOSkiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwYXRoIGQ9Ik00MjQsMjIwIEw0MjQsMjMwIEw0MzQsMjMwIEw0MjQsMjIwICIgZmlsbD0iI0ZCRkI3NyIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMCIgeD0iMjYzIiB5PSIyMzgiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxMyIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNTkiIHg9IjI2MCIgeT0iMjUzLjYiPk1heGltdW0gb2YgMTAwMCBwZXIgc2NvcGU8L3RleHQ+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMCIgeD0iMjYzIiB5PSIyNjkuMiIvPjxwYXRoIGQ9Ik0yNDksNiBMMjQ5LDMwLjUgTDIxNC4wMywzNC41IEwyNDksMzguNSBMMjQ5LDYyLjggQTAsMCAwIDAgMCAyNDksNjIuOCBMNDM1LDYyLjggQTAsMCAwIDAgMCA0MzUsNjIuOCBMNDM1LDE2IEw0MjUsNiBMMjQ5LDYgQTAsMCAwIDAgMCAyNDksNiAiIGZpbGw9IiNGQkZCNzciIGZpbHRlcj0idXJsKCNmMWFoNXl6cjdiYmNsOSkiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwYXRoIGQ9Ik00MjUsNiBMNDI1LDE2IEw0MzUsMTYgTDQyNSw2ICIgZmlsbD0iI0ZCRkI3NyIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTMiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMCIgeD0iMjU4IiB5PSIyNCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE2NSIgeD0iMjU1IiB5PSIzOS42Ij5PbmUgZGF0YWJhc2UgcGVyIGFwcGxpY2F0aW9uPC90ZXh0Pjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjEzIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjAiIHg9IjI1OCIgeT0iNTUuMiIvPjwhLS1NRDU9W2UyOTM4MDhjMDc4NGZjY2NlMDFkZDIzOGI2NjcxZjYyXQpsaW5rIGRiIHRvIHNjb3BlLS0+PHBhdGggZD0iTTE3NCw1Ny41MSBDMTc0LDc1LjA2IDE3NCw5OS41NiAxNzQsMTE3LjM5ICIgZmlsbD0ibm9uZSIgaWQ9ImRiLXRvLXNjb3BlIiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48cG9seWdvbiBmaWxsPSIjQTgwMDM2IiBwb2ludHM9IjE3NCwxMjIuNywxNzgsMTEzLjcsMTc0LDExNy43LDE3MCwxMTMuNywxNzQsMTIyLjciIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjwhLS1NRDU9W2JkOTViMmQ2MGI4M2IzOGQ3NjE4ZjJhYjBhYTkzOTNjXQpsaW5rIHNjb3BlIHRvIGNvbGxlY3Rpb24tLT48cGF0aCBkPSJNMTc0LDE2MC4zMSBDMTc0LDE3NS41IDE3NCwxOTcuNyAxNzQsMjE1LjkgIiBmaWxsPSJub25lIiBpZD0ic2NvcGUtdG8tY29sbGVjdGlvbiIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIxNzQsMjIwLjkyLDE3OC4wMjU3LDIxMS45MzE1LDE3NC4wMTQzLDIxNS45MiwxNzAuMDI1NywyMTEuOTA4NiwxNzQsMjIwLjkyIiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48IS0tTUQ1PVthYjQzYTFmNjAwYmZiODliOWM1MTIxYjM2MDJkYTUxNF0KbGluayBjb2xsZWN0aW9uIHRvIGRvY3VtZW50MS0tPjxwYXRoIGQ9Ik0xNDQuMTcsMjc2LjI3IEMxMjQuNTMsMjkzLjg5IDk5LjE3LDMxNi42NiA4MC44NywzMzMuMDggIiBmaWxsPSJub25lIiBpZD0iY29sbGVjdGlvbi10by1kb2N1bWVudDEiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iNzYuODQsMzM2LjY5LDg2LjIxMiwzMzMuNjYyNCw4MC41NjM2LDMzMy4zNTMxLDgwLjg3MjksMzI3LjcwNDcsNzYuODQsMzM2LjY5IiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48IS0tTUQ1PVs5M2Y4YzljYjUwNDZlYmQ4N2QzMDU5NDk4YWY2MTU1OF0KbGluayBjb2xsZWN0aW9uIHRvIGRvY3VtZW50Mi0tPjxwYXRoIGQ9Ik0xNzQsMjc2LjI3IEMxNzQsMjkzLjMgMTc0LDMxNS4xMyAxNzQsMzMxLjQgIiBmaWxsPSJub25lIiBpZD0iY29sbGVjdGlvbi10by1kb2N1bWVudDIiIHN0eWxlPSJzdHJva2U6I0E4MDAzNjtzdHJva2Utd2lkdGg6MS4wOyIvPjxwb2x5Z29uIGZpbGw9IiNBODAwMzYiIHBvaW50cz0iMTc0LDMzNi42OSwxNzgsMzI3LjY5LDE3NCwzMzEuNjksMTcwLDMyNy42OSwxNzQsMzM2LjY5IiBzdHlsZT0ic3Ryb2tlOiNBODAwMzY7c3Ryb2tlLXdpZHRoOjEuMDsiLz48IS0tTUQ1PVtmYjE4MDU2OWJkM2VmZGNkNDM1MTNkNTQwNjYwZTEzMF0KbGluayBjb2xsZWN0aW9uIHRvIGRvY3VtZW50My0tPjxwYXRoIGQ9Ik0yMDMuODMsMjc2LjI3IEMyMjMuNDcsMjkzLjg5IDI0OC44MywzMTYuNjYgMjY3LjEzLDMzMy4wOCAiIGZpbGw9Im5vbmUiIGlkPSJjb2xsZWN0aW9uLXRvLWRvY3VtZW50MyIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PHBvbHlnb24gZmlsbD0iI0E4MDAzNiIgcG9pbnRzPSIyNzEuMTYsMzM2LjY5LDI2Ny4xMjcxLDMyNy43MDQ3LDI2Ny40MzY0LDMzMy4zNTMxLDI2MS43ODgsMzMzLjY2MjQsMjcxLjE2LDMzNi42OSIgc3R5bGU9InN0cm9rZTojQTgwMDM2O3N0cm9rZS13aWR0aDoxLjA7Ii8+PCEtLU1ENT1bMjBmMzA3N2I2Yzc3MjczODBjZWRmNDgyMDQ3YTY2ZjFdCkBzdGFydHVtbA0KZGF0YWJhc2UgIkRhdGFiYXNlIiBhcyBkYg0KDQpyZWN0YW5nbGUgIlNjb3BlIiBhcyBzY29wZQ0KDQpmb2xkZXIgIkNvbGxlY3Rpb24iIGFzIGNvbGxlY3Rpb24NCg0KDQoNCmZpbGUgIkRvY3VtZW50IiBhcyBkb2N1bWVudDENCmZpbGUgIkRvY3VtZW50IiBhcyBkb2N1bWVudDINCmZpbGUgIkRvY3VtZW50IiBhcyBkb2N1bWVudDMNCg0KZGIgLSAtPiBzY29wZQ0Kc2NvcGUgLSAtPiBjb2xsZWN0aW9uDQpjb2xsZWN0aW9uIC0gLT4gZG9jdW1lbnQxDQpjb2xsZWN0aW9uIC0gLT4gZG9jdW1lbnQyDQpjb2xsZWN0aW9uIC0gLT4gZG9jdW1lbnQzDQoNCg0KDQoNCm5vdGUgcmlnaHQgb2YgY29sbGVjdGlvbg0KDQogIE1heGltdW0gb2YgMTAwMCBwZXIgc2NvcGUNCg0KZW5kIG5vdGUNCg0Kbm90ZSByaWdodCBvZiBkYg0KDQogIE9uZSBkYXRhYmFzZSBwZXIgYXBwbGljYXRpb24NCg0KZW5kIG5vdGUNCkBlbmR1bWwNCgpQbGFudFVNTCB2ZXJzaW9uIDEuMjAyMS4xNihXZWQgRGVjIDA4IDE3OjI1OjIyIEdNVCAyMDIxKQooR1BMIHNvdXJjZSBkaXN0cmlidXRpb24pCkphdmEgUnVudGltZTogT3BlbkpESyBSdW50aW1lIEVudmlyb25tZW50CkpWTTogT3BlbkpESyA2NC1CaXQgU2VydmVyIFZNCkRlZmF1bHQgRW5jb2Rpbmc6IFVURi04Ckxhbmd1YWdlOiBlbgpDb3VudHJ5OiBVUwotLT48L2c+PC9zdmc+"},8130:(i,e,a)=>{a.d(e,{A:()=>I});const I="data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiBjb250ZW50U2NyaXB0VHlwZT0iYXBwbGljYXRpb24vZWNtYXNjcmlwdCIgY29udGVudFN0eWxlVHlwZT0idGV4dC9jc3MiIGhlaWdodD0iMzIxcHgiIHByZXNlcnZlQXNwZWN0UmF0aW89Im5vbmUiIHN0eWxlPSJ3aWR0aDo2NDdweDtoZWlnaHQ6MzIxcHg7YmFja2dyb3VuZDojRkZGRkZGOyIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgNjQ3IDMyMSIgd2lkdGg9IjY0N3B4IiB6b29tQW5kUGFuPSJtYWduaWZ5Ij48ZGVmcz48ZmlsdGVyIGhlaWdodD0iMzAwJSIgaWQ9ImYxcnlvd2xqdmdzdTlzIiB3aWR0aD0iMzAwJSIgeD0iLTEiIHk9Ii0xIj48ZmVHYXVzc2lhbkJsdXIgcmVzdWx0PSJibHVyT3V0IiBzdGREZXZpYXRpb249IjIuMCIvPjxmZUNvbG9yTWF0cml4IGluPSJibHVyT3V0IiByZXN1bHQ9ImJsdXJPdXQyIiB0eXBlPSJtYXRyaXgiIHZhbHVlcz0iMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgMCAwIDAgLjQgMCIvPjxmZU9mZnNldCBkeD0iNC4wIiBkeT0iNC4wIiBpbj0iYmx1ck91dDIiIHJlc3VsdD0iYmx1ck91dDMiLz48ZmVCbGVuZCBpbj0iU291cmNlR3JhcGhpYyIgaW4yPSJibHVyT3V0MyIgbW9kZT0ibm9ybWFsIi8+PC9maWx0ZXI+PC9kZWZzPjxnPjwhLS1NRDU9WzUyMjFiYTI1YTNkYzU5YTc1MDdkZGU5OThiZDZiZmMyXQpjbHVzdGVyIGRiMS0tPjxwYXRoIGQ9Ik02LDE2IEM2LDYgMTE5LDYgMTE5LDYgQzExOSw2IDIzMiw2IDIzMiwxNiBMMjMyLDE4OSBDMjMyLDE5OSAxMTksMTk5IDExOSwxOTkgQzExOSwxOTkgNiwxOTkgNiwxODkgTDYsMTYgIiBmaWxsPSIjRkZGRkZGIiBmaWx0ZXI9InVybCgjZjFyeW93bGp2Z3N1OXMpIiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48cGF0aCBkPSJNNiwxNiBDNiwyNiAxMTksMjYgMTE5LDI2IEMxMTksMjYgMjMyLDI2IDIzMiwxNiAiIGZpbGw9Im5vbmUiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBmb250LXdlaWdodD0iYm9sZCIgbGVuZ3RoQWRqdXN0PSJzcGFjaW5nIiB0ZXh0TGVuZ3RoPSIxNzQiIHg9IjMyIiB5PSI0MS45OTk5Ij5Db3VjaGJhc2UgTGl0ZSBkYXRhYmFzZTwvdGV4dD48IS0tTUQ1PVtmNDNmY2EwMGFhNDM3ODlhOThiN2MyMTQ1MmEwOWFhOV0KY2x1c3RlciBkZnMxLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSIzMCw3NCw0MCw2NCwyMDgsNjQsMjA4LDE2NSwxOTgsMTc1LDMwLDE3NSwzMCw3NCIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iMTk4IiB4Mj0iMjA4IiB5MT0iNzQiIHkyPSI2NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjMwIiB4Mj0iMTk4IiB5MT0iNzQiIHkyPSI3NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjE5OCIgeDI9IjE5OCIgeTE9Ijc0IiB5Mj0iMTc1Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjEwMiIgeD0iNjQiIHk9IjkwLjk5OTkiPl9kZWZhdWx0IHNjb3BlPC90ZXh0PjwhLS1NRDU9WzI4MmEzODc4Y2MyOTZmMTllNmM2NDE3YjAyNmU4OWY2XQpjbHVzdGVyIGRiMi0tPjxwYXRoIGQ9Ik0yNTYsMTYgQzI1Niw2IDQ0OCw2IDQ0OCw2IEM0NDgsNiA2NDAsNiA2NDAsMTYgTDY0MCwzMDQgQzY0MCwzMTQgNDQ4LDMxNCA0NDgsMzE0IEM0NDgsMzE0IDI1NiwzMTQgMjU2LDMwNCBMMjU2LDE2ICIgZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHBhdGggZD0iTTI1NiwxNiBDMjU2LDI2IDQ0OCwyNiA0NDgsMjYgQzQ0OCwyNiA2NDAsMjYgNjQwLDE2ICIgZmlsbD0ibm9uZSIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjE3NCIgeD0iMzYxIiB5PSI0MS45OTk5Ij5Db3VjaGJhc2UgTGl0ZSBkYXRhYmFzZTwvdGV4dD48IS0tTUQ1PVs4ODcwYTZiZmQ1YTJjOTQxNDI2ZTdkMDQxMDIxYTk2Yl0KY2x1c3RlciBkZnMyLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSI0MzgsNzQsNDQ4LDY0LDYxNiw2NCw2MTYsMjgwLDYwNiwyOTAsNDM4LDI5MCw0MzgsNzQiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjYwNiIgeDI9IjYxNiIgeTE9Ijc0IiB5Mj0iNjQiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHgxPSI0MzgiIHgyPSI2MDYiIHkxPSI3NCIgeTI9Ijc0Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iNjA2IiB4Mj0iNjA2IiB5MT0iNzQiIHkyPSIyOTAiLz48dGV4dCBmaWxsPSIjMDAwMDAwIiBmb250LWZhbWlseT0ic2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZm9udC13ZWlnaHQ9ImJvbGQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iMTAyIiB4PSI0NzIiIHk9IjkwLjk5OTkiPl9kZWZhdWx0IHNjb3BlPC90ZXh0PjwhLS1NRDU9WzljM2I1NWJmZGIzMWVkNzkwM2Y1Yzg4NzEzMDU5NzYxXQpjbHVzdGVyIHNjb3BlQS0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iMjgwLDc0LDI5MCw2NCw0MTQsNjQsNDE0LDI4MCw0MDQsMjkwLDI4MCwyOTAsMjgwLDc0IiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHgxPSI0MDQiIHgyPSI0MTQiIHkxPSI3NCIgeTI9IjY0Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iMjgwIiB4Mj0iNDA0IiB5MT0iNzQiIHkyPSI3NCIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjQwNCIgeDI9IjQwNCIgeTE9Ijc0IiB5Mj0iMjkwIi8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGZvbnQtd2VpZ2h0PSJib2xkIiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjU4IiB4PSIzMTQiIHk9IjkwLjk5OTkiPlNjb3BlIEE8L3RleHQ+PCEtLU1ENT1bYWUxZDk5ODJjZTU0OWI0MWVmZjRkZDQ5MmQwOTMwMzJdCmVudGl0eSBkZmMxLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSI0OSwxMDQsOTUsMTA0LDEwMiwxMjUsMTg5LDEyNSwxODksMTU4Ljc5OTksNDksMTU4Ljc5OTksNDksMTA0IiBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiLz48bGluZSBzdHlsZT0ic3Ryb2tlOiMwMDAwMDA7c3Ryb2tlLXdpZHRoOjEuNTsiIHgxPSI0OSIgeDI9IjEwMiIgeTE9IjEyNSIgeTI9IjEyNSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjExMCIgeD0iNTkiIHk9IjE0NS45OTk5Ij5fZGVmYXVsdCBjb2xsZWN0aW9uPC90ZXh0PjwhLS1NRDU9WzljMmJiZGY3MDY0MzBjODliZTNjZmRiNDUwMzlmZDlmXQplbnRpdHkgZGZjMi0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iNDU3LDIxOSw1MDMsMjE5LDUxMCwyNDAsNTk3LDI0MCw1OTcsMjczLjc5OTksNDU3LDI3My43OTk5LDQ1NywyMTkiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjQ1NyIgeDI9IjUxMCIgeTE9IjI0MCIgeTI9IjI0MCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjExMCIgeD0iNDY3IiB5PSIyNjAuOTk5OSI+X2RlZmF1bHQgY29sbGVjdGlvbjwvdGV4dD48IS0tTUQ1PVtjN2Q4YWU4ZTIzMjBiOTc1NmEyNzZiZDFhNzc3ZDM5YV0KZW50aXR5IGNvbGxlY3Rpb25BLS0+PHBvbHlnb24gZmlsbD0iI0ZGRkZGRiIgZmlsdGVyPSJ1cmwoI2Yxcnlvd2xqdmdzdTlzKSIgcG9pbnRzPSI0NzYsMTA0LDUyMiwxMDQsNTI5LDEyNSw1NzgsMTI1LDU3OCwxNTguNzk5OSw0NzYsMTU4Ljc5OTksNDc2LDEwNCIgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7Ii8+PGxpbmUgc3R5bGU9InN0cm9rZTojMDAwMDAwO3N0cm9rZS13aWR0aDoxLjU7IiB4MT0iNDc2IiB4Mj0iNTI5IiB5MT0iMTI1IiB5Mj0iMTI1Ii8+PHRleHQgZmlsbD0iIzAwMDAwMCIgZm9udC1mYW1pbHk9InNhbnMtc2VyaWYiIGZvbnQtc2l6ZT0iMTQiIGxlbmd0aEFkanVzdD0ic3BhY2luZyIgdGV4dExlbmd0aD0iNzIiIHg9IjQ4NiIgeT0iMTQ1Ljk5OTkiPkNvbGxlY3Rpb24gQTwvdGV4dD48IS0tTUQ1PVs3OWEwNTU4ZTZmYzYwMzFjNzExNjYxZTA5OGFjY2Y5Zl0KZW50aXR5IGNvbGxlY3Rpb25BMi0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iMjk2LDEwNCwzNDIsMTA0LDM0OSwxMjUsMzk4LDEyNSwzOTgsMTU4Ljc5OTksMjk2LDE1OC43OTk5LDI5NiwxMDQiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjI5NiIgeDI9IjM0OSIgeTE9IjEyNSIgeTI9IjEyNSIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjcyIiB4PSIzMDYiIHk9IjE0NS45OTk5Ij5Db2xsZWN0aW9uIEE8L3RleHQ+PCEtLU1ENT1bZTAwNzJlNTMzYWIyZTkzODNkOTRmNDgzOTUwZTMyNDhdCmVudGl0eSBjb2xsZWN0aW9uQi0tPjxwb2x5Z29uIGZpbGw9IiNGRkZGRkYiIGZpbHRlcj0idXJsKCNmMXJ5b3dsanZnc3U5cykiIHBvaW50cz0iMjk2LDIxOSwzNDIsMjE5LDM0OSwyNDAsMzk4LDI0MCwzOTgsMjczLjc5OTksMjk2LDI3My43OTk5LDI5NiwyMTkiIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIvPjxsaW5lIHN0eWxlPSJzdHJva2U6IzAwMDAwMDtzdHJva2Utd2lkdGg6MS41OyIgeDE9IjI5NiIgeDI9IjM0OSIgeTE9IjI0MCIgeTI9IjI0MCIvPjx0ZXh0IGZpbGw9IiMwMDAwMDAiIGZvbnQtZmFtaWx5PSJzYW5zLXNlcmlmIiBmb250LXNpemU9IjE0IiBsZW5ndGhBZGp1c3Q9InNwYWNpbmciIHRleHRMZW5ndGg9IjcyIiB4PSIzMDYiIHk9IjI2MC45OTk5Ij5Db2xsZWN0aW9uIEI8L3RleHQ+PCEtLU1ENT1bMDRlOTc5Y2EwNmYzZjZmYWI5ZDBjZjA5MDE1MTBlNjhdCmxpbmsgY29sbGVjdGlvbkEyIHRvIGNvbGxlY3Rpb25CLS0+PCEtLU1ENT1bN2YwMzZhMmRlNmE0YzEyZjY3ZDA5ZTYyNzdkZjllMzddCmxpbmsgY29sbGVjdGlvbkEgdG8gZGZjMi0tPjwhLS1NRDU9W2I2MWI4YjRlZjhhOTJjNzMzOWU2YTJkYTU5ZjI2YjMwXQpAc3RhcnR1bWwNCmRhdGFiYXNlICJDb3VjaGJhc2UgTGl0ZSBkYXRhYmFzZSIgYXMgZGIxIHsNCg0KICAgICAgbm9kZSAiX2RlZmF1bHQgc2NvcGUiIGFzIGRmczEgew0KDQogICAgICAgIGZvbGRlciAiX2RlZmF1bHQgY29sbGVjdGlvbiIgYXMgZGZjMSB7DQoNCiAgICAgICAgfQ0KICAgICAgfQ0KDQp9DQoNCmRhdGFiYXNlICJDb3VjaGJhc2UgTGl0ZSBkYXRhYmFzZSIgYXMgZGIyIHsNCg0KDQogICAgbm9kZSAiX2RlZmF1bHQgc2NvcGUiIGFzIGRmczIgew0KDQogICAgICBmb2xkZXIgIl9kZWZhdWx0IGNvbGxlY3Rpb24iIGFzIGRmYzIgew0KDQogICAgICB9DQoNCiAgICAgICAgZm9sZGVyICJDb2xsZWN0aW9uIEEiIGFzIGNvbGxlY3Rpb25BIHsNCg0KICAgICAgICB9DQogICAgfQ0KDQogICAgbm9kZSAiU2NvcGUgQSIgYXMgc2NvcGVBIHsNCg0KICAgICAgZm9sZGVyICJDb2xsZWN0aW9uIEEiIGFzIGNvbGxlY3Rpb25BMiB7DQoNCiAgICAgIH0NCg0KICAgICAgICBmb2xkZXIgIkNvbGxlY3Rpb24gQiIgYXMgY29sbGVjdGlvbkIgew0KDQogICAgICAgIH0NCiAgICB9DQoNCn0NCg0KY29sbGVjdGlvbkEyIC1EW2hpZGRlbl0tPiBjb2xsZWN0aW9uQg0KY29sbGVjdGlvbkEgLURbaGlkZGVuXS0+IGRmYzINCkBlbmR1bWwNCgpQbGFudFVNTCB2ZXJzaW9uIDEuMjAyMS4xNihXZWQgRGVjIDA4IDE3OjI1OjIyIEdNVCAyMDIxKQooR1BMIHNvdXJjZSBkaXN0cmlidXRpb24pCkphdmEgUnVudGltZTogT3BlbkpESyBSdW50aW1lIEVudmlyb25tZW50CkpWTTogT3BlbkpESyA2NC1CaXQgU2VydmVyIFZNCkRlZmF1bHQgRW5jb2Rpbmc6IFVURi04Ckxhbmd1YWdlOiBlbgpDb3VudHJ5OiBVUwotLT48L2c+PC9zdmc+"},8453:(i,e,a)=>{a.d(e,{R:()=>t,x:()=>c});var I=a(6540);const n={},s=I.createContext(n);function t(i){const e=I.useContext(s);return I.useMemo((function(){return"function"==typeof i?i(e):{...e,...i}}),[e,i])}function c(i){let e;return e=i.disableParentContext?"function"==typeof i.components?i.components(n):i.components||n:t(i.components),I.createElement(s.Provider,{value:e},i.children)}}}]); \ No newline at end of file diff --git a/assets/js/f9585397.0323e3b0.js b/assets/js/f9585397.0323e3b0.js deleted file mode 100644 index 3bfe2bb..0000000 --- a/assets/js/f9585397.0323e3b0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[5967],{673:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>c,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>r,toc:()=>l});var i=t(4848),s=t(8453);const o={id:"remote-sync-gateway",sidebar_position:2},a="Remote Sync Gateway",r={id:"DataSync/remote-sync-gateway",title:"Remote Sync Gateway",description:"Description - Couchbase Lite for Ionic\u2009\u2014\u2009Synchronizing data changes between local and remote databases using Sync Gateway",source:"@site/docs/DataSync/remote-sync-gateway.md",sourceDirName:"DataSync",slug:"/DataSync/remote-sync-gateway",permalink:"/DataSync/remote-sync-gateway",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/DataSync/remote-sync-gateway.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{id:"remote-sync-gateway",sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Couchbase Capella App Services",permalink:"/DataSync/capella"},next:{title:"Typed Data",permalink:"/typed-data"}},c={},l=[{value:"Introduction",id:"introduction",level:2},{value:"Replication Concepts",id:"replication-concepts",level:2},{value:"Database",id:"database",level:4},{value:"Collection",id:"collection",level:4},{value:"Replication Protocol",id:"replication-protocol",level:2},{value:"Scheme",id:"scheme",level:3},{value:"Incompatibilities",id:"incompatibilities",level:4},{value:"Ordering",id:"ordering",level:3},{value:"Scopes and Collections",id:"scopes-and-collections",level:2},{value:"Default Collection",id:"default-collection",level:3},{value:"Sync Couchbase Lite database with the default collection on Sync Gateway",id:"sync-couchbase-lite-database-with-the-default-collection-on-sync-gateway",level:4},{value:"Sync Couchbase Lite default collection with default collection on Sync Gateway",id:"sync-couchbase-lite-default-collection-with-default-collection-on-sync-gateway",level:4},{value:"User-Defined Collections",id:"user-defined-collections",level:3},{value:"Syncing scope with user-defined collections.",id:"syncing-scope-with-user-defined-collections",level:4},{value:"Syncing scope with user-defined collections. Couchbase Lite has more collections than the Sync Gateway configuration (with collection filters)",id:"syncing-scope-with-user-defined-collections-couchbase-lite-has-more-collections-than-the-sync-gateway-configuration-with-collection-filters",level:4},{value:"Configuration Summary",id:"configuration-summary",level:2},{value:"Example 1. Replication configuration and initialization",id:"example-1-replication-configuration-and-initialization",level:4},{value:"Configure",id:"configure",level:2},{value:"In this section",id:"in-this-section",level:5},{value:"Configure Target",id:"configure-target",level:3},{value:"Example 2. Add Target to Configuration",id:"example-2-add-target-to-configuration",level:4},{value:"Sync Mode",id:"sync-mode",level:3},{value:"Example 4. Configure replicator type and mode",id:"example-4-configure-replicator-type-and-mode",level:4},{value:"Retry Configuration",id:"retry-configuration",level:3},{value:"Table 1. Replication Retry Configuration Properties",id:"table-1-replication-retry-configuration-properties",level:4},{value:"Example 5. Configuring Replication Retries",id:"example-5-configuring-replication-retries",level:4},{value:"User Authorization",id:"user-authorization",level:3},{value:"Example 6. Enable Authorization",id:"example-6-enable-authorization",level:4},{value:"Server Authentication",id:"server-authentication",level:3},{value:"Example 7. Set Server TLS security",id:"example-7-set-server-tls-security",level:4},{value:"CA Cert",id:"ca-cert",level:5},{value:"Self Signed Cert",id:"self-signed-cert",level:5},{value:"Client Authentication",id:"client-authentication",level:3},{value:"Basic Authentication",id:"basic-authentication",level:4},{value:"Example 8. Basic Authentication",id:"example-8-basic-authentication",level:4},{value:"Session Authentication",id:"session-authentication",level:4},{value:"Example 9. Session Authentication",id:"example-9-session-authentication",level:4},{value:"Custom Headers",id:"custom-headers",level:3},{value:"Example 10. Setting custom headers",id:"example-10-setting-custom-headers",level:4},{value:"Channels",id:"channels",level:3},{value:"Auto-purge on Channel Access Revocation",id:"auto-purge-on-channel-access-revocation",level:3},{value:"New outcome",id:"new-outcome",level:4},{value:"Prior outcome",id:"prior-outcome",level:4},{value:"Behaviour",id:"behaviour",level:4},{value:"Table 2. Behavior following access revocation",id:"table-2-behavior-following-access-revocation",level:4},{value:"Table 3. Behavior if access is regained",id:"table-3-behavior-if-access-is-regained",level:4},{value:"Config",id:"config",level:4},{value:"Example 11. Setting auto-purge",id:"example-11-setting-auto-purge",level:4},{value:"Delta Sync",id:"delta-sync",level:3},{value:"Initialize",id:"initialize",level:2},{value:"In this section",id:"in-this-section-1",level:5},{value:"Start Replicator",id:"start-replicator",level:3},{value:"Example 12. Initialize and run replicator",id:"example-12-initialize-and-run-replicator",level:4},{value:"Checkpoint Starts",id:"checkpoint-starts",level:3},{value:"Example 13. Resetting checkpoints",id:"example-13-resetting-checkpoints",level:4},{value:"Monitor",id:"monitor",level:2},{value:"In this section",id:"in-this-section-2",level:5},{value:"Change Listeners",id:"change-listeners",level:3},{value:"Replicator Status",id:"replicator-status",level:3},{value:"Example 14. Monitor replication",id:"example-14-monitor-replication",level:4},{value:"Replication States",id:"replication-states",level:3},{value:"Table 5. Replicator activity levels",id:"table-5-replicator-activity-levels",level:4},{value:"Replication Status and App Life Cycle",id:"replication-status-and-app-life-cycle",level:3},{value:"Monitor Document Changes",id:"monitor-document-changes",level:3},{value:"Example 15. Register a document listener",id:"example-15-register-a-document-listener",level:4},{value:"Example 16. Stop document listener",id:"example-16-stop-document-listener",level:4},{value:"Document Access Removal Behavior",id:"document-access-removal-behavior",level:3},{value:"Documents Pending Push",id:"documents-pending-push",level:2},{value:"Example 17. Use Pending Document ID API",id:"example-17-use-pending-document-id-api",level:4},{value:"Stop",id:"stop",level:2},{value:"Example 18. Stop replicator",id:"example-18-stop-replicator",level:4},{value:"Error Handling",id:"error-handling",level:2},{value:"Example 19. Monitoring for network errors",id:"example-19-monitoring-for-network-errors",level:4},{value:"Load Balancers",id:"load-balancers",level:2},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Logs",id:"logs",level:3},{value:"Example 21. Set logging verbosity",id:"example-21-set-logging-verbosity",level:4},{value:"Authentication Errors",id:"authentication-errors",level:3},{value:"Example 22. Protocol Mismatch",id:"example-22-protocol-mismatch",level:4},{value:"Example 23. Certificate Mismatch or Not Found",id:"example-23-certificate-mismatch-or-not-found",level:4}];function d(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"remote-sync-gateway",children:"Remote Sync Gateway"}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["Description - ",(0,i.jsx)(n.em,{children:"Couchbase Lite for Ionic\u2009\u2014\u2009Synchronizing data changes between local and remote databases using Sync Gateway"})]}),"\n"]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:"All code examples are indicative only. They demonstrate the basic concepts and approaches to using a feature. Use them as inspiration and adapt these examples to best practice when developing applications for your platform."})}),"\n",(0,i.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Lite for Ionic provides API support for secure, bi-directional, synchronization of data changes between mobile applications and a central server database. It does so by using a ",(0,i.jsx)(n.em,{children:"replicator"})," to interact with Sync Gateway."]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.em,{children:"replicator"})," is designed to manage replication of documents and-or document changes between a source and a target database. For example, between a local Couchbase Lite database and remote Sync Gateway database, which is ultimately mapped to a bucket in a Couchbase Server instance in the cloud or on a server."]}),"\n",(0,i.jsx)(n.p,{children:"This page shows sample code and configuration examples covering the implementation of a replication using Sync Gateway."}),"\n",(0,i.jsx)(n.p,{children:"Your application runs a replicator (also referred to here as a client), which will initiate connection with a Sync Gateway (also referred to here as a server) and participate in the replication of database changes to bring both local and remote databases into sync."}),"\n",(0,i.jsx)(n.p,{children:"Subsequent sections provide additional details and examples for the main configuration options."}),"\n",(0,i.jsx)(n.h2,{id:"replication-concepts",children:"Replication Concepts"}),"\n",(0,i.jsx)(n.p,{children:"Couchbase Lite allows for one database for each application running on the mobile device. This database can contain one or more scopes. Each scope can contain one or more collections."}),"\n",(0,i.jsxs)(n.p,{children:["To learn about Scopes and Collections, see ",(0,i.jsx)(n.a,{href:"/databases",children:"Databases"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"You can set up a replication scheme across these data levels:"}),"\n",(0,i.jsx)(n.h4,{id:"database",children:"Database"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"_default"})," collection is synced."]}),"\n",(0,i.jsx)(n.h4,{id:"collection",children:"Collection"}),"\n",(0,i.jsx)(n.p,{children:"A specific collection or a set of collections is synced."}),"\n",(0,i.jsx)(n.p,{children:"As part of the syncing setup, the Gateway has to map the Couchbase Lite database to the database being synced on Capella."}),"\n",(0,i.jsx)(n.h2,{id:"replication-protocol",children:"Replication Protocol"}),"\n",(0,i.jsx)(n.h3,{id:"scheme",children:"Scheme"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Mobile uses a replication protocol based on WebSockets for replication. To use this protocol the replication URL should specify WebSockets as the URL scheme (see the ",(0,i.jsx)(n.a,{href:"#configure-target",children:"Configure Target"})," section below)."]}),"\n",(0,i.jsx)(n.h4,{id:"incompatibilities",children:"Incompatibilities"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Lite\u2019s replication protocol is incompatible with CouchDB-based databases. And since Couchbase Lite 2.x+ only supports the new protocol, you will need to run a version of Sync Gateway that supports it\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"/ProductNotes/compatibility",children:"Compatibility"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"ordering",children:"Ordering"}),"\n",(0,i.jsx)(n.p,{children:"To optimize for speed, the replication protocol doesn\u2019t guarantee that documents will be received in a particular order. So we don\u2019t recommend to rely on that when using the replication or database change listeners for example."}),"\n",(0,i.jsx)(n.h2,{id:"scopes-and-collections",children:"Scopes and Collections"}),"\n",(0,i.jsx)(n.p,{children:"Scopes and Collections allow you to organize your documents in Couchbase Lite."}),"\n",(0,i.jsx)(n.p,{children:"When syncing, you can configure the collections to be synced."}),"\n",(0,i.jsx)(n.p,{children:"The collections specified in the Couchbase Lite replicator setup must exist (both scope and collection name must be identical) on the Sync Gateway side, otherwise starting the Couchbase Lite replicator will result in an error."}),"\n",(0,i.jsx)(n.p,{children:"During replication:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"If Sync Gateway config (or server) is updated to remove a collection that is being synced, the client replicator will be offline and will be stopped after the first retry. An error will be reported."}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"If Sync Gateway config is updated to add a collection to a scope that is being synchronized, the replication will ignore the collection. The added collection will not automatically sync until the Couchbase Lite replicator\u2019s configuration is updated."}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"default-collection",children:"Default Collection"}),"\n",(0,i.jsx)(n.p,{children:"When upgrading Couchbase Lite to 3.1, the existing documents in the database will be automatically migrated to the default collection."}),"\n",(0,i.jsx)(n.p,{children:"For backward compatibility with the code prior to 3.1, when you set up the replicator with the database, the default collection will be set up to sync with the default collection on Sync Gateway."}),"\n",(0,i.jsx)(n.h4,{id:"sync-couchbase-lite-database-with-the-default-collection-on-sync-gateway",children:"Sync Couchbase Lite database with the default collection on Sync Gateway"}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 1",src:t(7943).A+"",width:"898",height:"308"})})}),"\n",(0,i.jsx)(n.h4,{id:"sync-couchbase-lite-default-collection-with-default-collection-on-sync-gateway",children:"Sync Couchbase Lite default collection with default collection on Sync Gateway"}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 2",src:t(7308).A+"",width:"870",height:"429"})})}),"\n",(0,i.jsx)(n.h3,{id:"user-defined-collections",children:"User-Defined Collections"}),"\n",(0,i.jsx)(n.p,{children:"The user-defined collections specified in the Couchbase Lite replicator setup must exist (and be identical) on the Sync Gateway side to sync."}),"\n",(0,i.jsx)(n.h4,{id:"syncing-scope-with-user-defined-collections",children:"Syncing scope with user-defined collections."}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 3",src:t(6725).A+"",width:"866",height:"421"})})}),"\n",(0,i.jsx)(n.h4,{id:"syncing-scope-with-user-defined-collections-couchbase-lite-has-more-collections-than-the-sync-gateway-configuration-with-collection-filters",children:"Syncing scope with user-defined collections. Couchbase Lite has more collections than the Sync Gateway configuration (with collection filters)"}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 4",src:t(2058).A+"",width:"902",height:"436"})})}),"\n",(0,i.jsx)(n.h2,{id:"configuration-summary",children:"Configuration Summary"}),"\n",(0,i.jsxs)(n.p,{children:["You should configure and initialize a replicator for each Couchbase Lite database instance you want to sync. ",(0,i.jsx)(n.a,{href:"#example-1-replication-configuration-and-initialization",children:"Example 1"})," shows the configuration and initialization process."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["You need Couchbase Lite 3.1+ and Sync Gateway 3.1+ to use ",(0,i.jsx)(n.code,{children:"custom"})," Scopes and Collections.\nIf you\u2019re using Capella App Services or Sync Gateway releases that are older than version 3.1, you won\u2019t be able to access ",(0,i.jsx)(n.code,{children:"custom"})," Scopes and Collections. To use Couchbase Lite 3.1+ with these older versions, you can use the ",(0,i.jsx)(n.code,{children:"default"})," Collection as a backup option."]})}),"\n",(0,i.jsx)(n.h4,{id:"example-1-replication-configuration-and-initialization",children:"Example 1. Replication configuration and initialization"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"//assumes you are running sync gateway locally, if you are \n //running app services, replace enpoint with proper url and creditentials\n const target = new URLEndpoint('ws://localhost:4984/projects');\n const auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12');\n const config = new ReplicatorConfiguration(target);\n config.addCollection(collectionName);\n config.setAuthenticator(auth);\n\n const replicator = await Replicator.create(config);\n\n //listen to the replicator change events\n const token = await replicator.addChangeListener((change) => {\n\t//check to see if there was an error\n \tconst error = change.status.getError();\n \tif (error !== undefined) {\n\t\t//do something with the error\n \t}\n \t//get the status of the replicator using ReplicatorActivityLevel enum\n \tif (change.status.getActivityLevel() === ReplicatorActivityLevel.IDLE) {\n \t\t//do something because the replicator is now IDLE\n \t}\n });\n\n // start the replicator without making a new checkpoint\n await replicator.start(false);\n\n //remember you must clean up the replicator when done with it by \n //doing the following lines\n\n //await replicator.removeChangeListener(token);\n //await replicator.stop();\n"})}),"\n",(0,i.jsx)(n.h2,{id:"configure",children:"Configure"}),"\n",(0,i.jsx)(n.h5,{id:"in-this-section",children:"In this section"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#configure-target",children:"Configure Target"})," | ",(0,i.jsx)(n.a,{href:"#sync-mode",children:"Sync Mode"})," | ",(0,i.jsx)(n.a,{href:"#retry-configuration",children:"Retry Configuration"})," | ",(0,i.jsx)(n.a,{href:"#user-authorization",children:"User Authorization"})," | ",(0,i.jsx)(n.a,{href:"#server-authentication",children:"Server Authentication"})," | ",(0,i.jsx)(n.a,{href:"#client-authentication",children:"Client Authentication"})," | ",(0,i.jsx)(n.a,{href:"#monitor-document-changes",children:"Monitor Document Changes"})," | ",(0,i.jsx)(n.a,{href:"#custom-headers",children:"Custom Headers"})," | ",(0,i.jsx)(n.a,{href:"#checkpoint-starts",children:"Checkpoint Starts"})," | ",(0,i.jsx)(n.a,{href:"#channels",children:"Channels"})," | ",(0,i.jsx)(n.a,{href:"#auto-purge-on-channel-access-revocation",children:"Auto-purge on Channel Access Revocation"})," | ",(0,i.jsx)(n.a,{href:"#delta-sync",children:"Delta Sync"})]}),"\n",(0,i.jsx)(n.h3,{id:"configure-target",children:"Configure Target"}),"\n",(0,i.jsx)(n.p,{children:"Use the Initialize and define the replication configuration with local and remote database locations using the ReplicatorConfiguration object."}),"\n",(0,i.jsx)(n.p,{children:"The constructor provides:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"the name of the local database to be sync\u2019d"}),"\n",(0,i.jsx)(n.li,{children:"the server\u2019s URL (including the port number and the name of the remote database to sync with)"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["It is expected that the app will identify the IP address and URL and append the remote database name to the URL endpoint, producing for example: ",(0,i.jsx)(n.code,{children:"wss://10.0.2.2:4984/travel-sample"}),"\nThe URL scheme for web socket URLs uses ",(0,i.jsx)(n.code,{children:"ws:"})," (non-TLS) or ",(0,i.jsx)(n.code,{children:"wss:"})," (SSL/TLS) prefixes."]}),"\n",(0,i.jsx)(n.h4,{id:"example-2-add-target-to-configuration",children:"Example 2. Add Target to Configuration"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Define the target URL for the replication endpoint\nconst targetURL = 'wss://10.1.1.12:4984/travel-sample';\n\n// Initialize the URLEndpoint with the target URL\nconst targetEndpoint = new URLEndpoint(targetURL);\n\n// Create the ReplicatorConfiguration with the target endpoint\nconst config = new ReplicatorConfiguration(targetEndpoint);\n\n// Add the already initialised collection to the replicator configuration\nconfig.addCollection(collectionName);\n"})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["Note use of the scheme prefix (",(0,i.jsx)(n.code,{children:"wss://"})," to ensure TLS encryption\u2009\u2014\u2009strongly recommended in production\u2009\u2014\u2009or ",(0,i.jsx)(n.code,{children:"ws://"}),")"]})}),"\n",(0,i.jsx)(n.h3,{id:"sync-mode",children:"Sync Mode"}),"\n",(0,i.jsx)(n.p,{children:"Here we define the direction and type of replication we want to initiate."}),"\n",(0,i.jsxs)(n.p,{children:["We use ",(0,i.jsx)(n.code,{children:"ReplicatorConfiguration"})," class\u2019s ",(0,i.jsx)(n.code,{children:"replicatorType"})," and ",(0,i.jsx)(n.code,{children:"continuous"})," parameters, to tell the replicator:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["The type (or direction) of the replication: ",(0,i.jsx)(n.code,{children:"PUSH_AND_PULL"}),"; ",(0,i.jsx)(n.code,{children:"PULL"}),"; ",(0,i.jsx)(n.code,{children:"PUSH"})]}),"\n",(0,i.jsxs)(n.li,{children:["The replication mode, that is either of:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Continuous - remaining active indefinitely to replicate changed documents (",(0,i.jsx)(n.code,{children:"continuous=true"}),")."]}),"\n",(0,i.jsxs)(n.li,{children:["Ad-hoc - a one-shot replication of changed documents (",(0,i.jsx)(n.code,{children:"continuous=false"}),")"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-4-configure-replicator-type-and-mode",children:"Example 4. Configure replicator type and mode"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"config.replicatorType = ReplicatorType.PUSH_AND_PULL;\n\n// Configure Sync Mode\nconfig.continuous = true\n"})}),"\n",(0,i.jsxs)(n.admonition,{type:"tip",children:[(0,i.jsxs)(n.p,{children:["Unless there is a solid use-case not to, always initiate a single ",(0,i.jsx)(n.code,{children:"PUSH_AND_PULL"})," replication rather than identical separate ",(0,i.jsx)(n.code,{children:"PUSH"})," and ",(0,i.jsx)(n.code,{children:"PULL"})," replications."]}),(0,i.jsxs)(n.p,{children:["This prevents the replications generating the same checkpoint ",(0,i.jsx)(n.code,{children:"docID"})," resulting in multiple conflicts."]})]}),"\n",(0,i.jsx)(n.h3,{id:"retry-configuration",children:"Retry Configuration"}),"\n",(0,i.jsx)(n.p,{children:"Couchbase Lite for Ionic's replication retry logic assures a resilient connection."}),"\n",(0,i.jsx)(n.p,{children:"Couchbase Lite for Swift\u2019s replication retry logic assures a resilient connection."}),"\n",(0,i.jsx)(n.p,{children:"In the event it detects a transient error, the replicator will attempt to reconnect, stopping only when the connection is re-established, or the number of retries exceeds the retry limit (9 times for a single-shot replication and unlimited for a continuous replication)."}),"\n",(0,i.jsx)(n.p,{children:"On each retry the interval between attempts is increased exponentially (exponential backoff) up to the maximum wait time limit (5 minutes)."}),"\n",(0,i.jsxs)(n.p,{children:["The REST API provides configurable control over this replication retry logic using a set of configiurable properties\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#table-1-replication-retry-configuration-properties",children:"Table 1"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"table-1-replication-retry-configuration-properties",children:"Table 1. Replication Retry Configuration Properties"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Property"}),(0,i.jsx)(n.th,{children:"Use cases"}),(0,i.jsx)(n.th,{children:"Description"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"heartbeat()"})}),(0,i.jsxs)(n.td,{children:["Reduce to detect connection errors sooner ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Align to load-balancer or proxy ",(0,i.jsx)(n.code,{children:"keep-alive"})," interval - see Sync Gateway\u2019s topic ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/load-balancer.html#websocket-connection",children:"Load Balancer - Keep Alive"})]}),(0,i.jsxs)(n.td,{children:["The interval (in seconds) between the heartbeat pulses. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Default: The replicator pings the Sync Gateway every 300 seconds."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"maxAttempts()"})}),(0,i.jsx)(n.td,{children:"Change this to limit or extend the number of retry attempts."}),(0,i.jsxs)(n.td,{children:["The maximum number of retry attempts. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Set to zero (0) to use default values. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Set to one (1) to prevent any retry attempt. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," The retry attempt count is reset when the replicator is able to connect and replicate. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Default values are: ",(0,i.jsx)("br",{})," - Single-shot replication = 9 ",(0,i.jsx)("br",{})," - Continuous replication = maximum integer value. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Negative values generate a Couchbase exception ",(0,i.jsx)(n.code,{children:"InvalidArgumentException"}),"."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"maxAttemptWaitTime()"})}),(0,i.jsx)(n.td,{children:"Change this to adjust the interval between retries."}),(0,i.jsxs)(n.td,{children:["The maximum interval between retry attempts. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," While you can configure the maximum permitted wait time, the replicator\u2019s exponential backoff algorithm calculates each individual interval which is not configurable. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Default value: 300 seconds (5 minutes). ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Zero sets the maximum interval between retries to the default of 300 seconds. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," 300 sets the maximum interval between retries to the default of 300 seconds. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," A negative value generates a Couchbase exception, ",(0,i.jsx)(n.code,{children:"InvalidArgumentException"}),"."]})]})]})]}),"\n",(0,i.jsxs)(n.p,{children:["When necessary you can adjust any or all of those configurable values\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-5-configuring-replication-retries",children:"Example 5"})," for how to do this."]}),"\n",(0,i.jsx)(n.h4,{id:"example-5-configuring-replication-retries",children:"Example 5. Configuring Replication Retries"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Create the target endpoint\nconst target = new URLEndpoint('ws://foo.couchbase.com/db');\n\n// Create the replicator configuration\nconst config = new ReplicatorConfiguration(target);\n\n// Add the collection to the replicator configuration\nconfig.addCollection(collection);\n\n// Set the replicator type\nconfig.setReplicatorType(ReplicatorType.PUSH_AND_PULL);\n\n// Set continuous replication\nconfig.setContinuous(true);\n\n// Set heartbeat interval (in seconds)\nconfig.setHeartbeat(150);\n\n// Set the maximum number of retry attempts\nconfig.setMaxAttempts(20);\n\n// Set the maximum wait time between retry attempts (in seconds)\nconfig.setMaxAttemptWaitTime(600);\n\n// Create the replicator with the configuration\nconst replicator = await Replicator.create(config);\n"})}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["Here we use ",(0,i.jsx)(n.code,{children:"setHeartbeat()"})," to set the required interval (in seconds) between the heartbeat pulses"]}),"\n",(0,i.jsxs)(n.li,{children:["Here we use ",(0,i.jsx)(n.code,{children:"setMaxAttempts()"})," to set the required number of retry attempts"]}),"\n",(0,i.jsxs)(n.li,{children:["Here we use ",(0,i.jsx)(n.code,{children:"setMaxAttemptWaitTime()"})," to set the required interval between retry attempts."]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"user-authorization",children:"User Authorization"}),"\n",(0,i.jsx)(n.p,{children:"By default, Sync Gateway does not enable user authorization. This makes it easier to get up and running with synchronization."}),"\n",(0,i.jsxs)(n.p,{children:["You can enable authorization in the sync gateway configuration file, as shown in ",(0,i.jsx)(n.a,{href:"#example-6-enable-authorization",children:"Example 6"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-6-enable-authorization",children:"Example 6. Enable Authorization"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "databases": {\n "mydatabase": {\n "users": {\n "GUEST": {"disabled": true}\n }\n }\n }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["To authorize with Sync Gateway, an associated user must first be created. Sync Gateway users can be created through the ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/rest-api-admin.html#/user/post__db___user_",children:(0,i.jsx)(n.code,{children:"POST /{tkn-db}/_user"})})," endpoint on the Admin REST API."]}),"\n",(0,i.jsx)(n.h3,{id:"server-authentication",children:"Server Authentication"}),"\n",(0,i.jsx)(n.p,{children:"Define the credentials your app (the client) is expecting to receive from the Sync Gateway (the server) in order to ensure it is prepared to continue with the sync."}),"\n",(0,i.jsxs)(n.p,{children:["Note that the client cannot authenticate the server if TLS is turned off. When TLS is enabled (Sync Gateway\u2019s default) the client ",(0,i.jsx)(n.em,{children:"must"})," authenticate the server. If the server cannot provide acceptable credentials then the connection will fail."]}),"\n",(0,i.jsxs)(n.p,{children:["Use ",(0,i.jsx)(n.code,{children:"ReplicatorConfiguration"})," property ",(0,i.jsx)(n.code,{children:"acceptOnlySelfSignedServerCertificate"})," to tell the replicator how to verify server-supplied TLS server certificates."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"acceptOnlySelfSignedServerCertificate"})," is ",(0,i.jsx)(n.code,{children:"true"})," then any self-signed certificate is accepted. Certificates that are not self signed are rejected, no matter who signed them."]}),"\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"acceptOnlySelfSignedServerCertificate"})," is ",(0,i.jsx)(n.code,{children:"false"})," (default), the client validates the server\u2019s certificates against the system CA certificates. The server must supply a chain of certificates whose root is signed by one of the certificates in the system CA bundle."]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-7-set-server-tls-security",children:"Example 7. Set Server TLS security"}),"\n",(0,i.jsx)(n.h5,{id:"ca-cert",children:"CA Cert"}),"\n",(0,i.jsx)(n.p,{children:"Set the client to expect and accept only CA attested certificates."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Configure Server Security -- only accept CA Certs\nconfig.acceptOnlySelfSignedServerCertificate = false\n"})}),"\n",(0,i.jsx)(n.p,{children:"This is the default. Only certificate chains with roots signed by a trusted CA are allowed. Self signed certificates are not allowed."}),"\n",(0,i.jsx)(n.h5,{id:"self-signed-cert",children:"Self Signed Cert"}),"\n",(0,i.jsx)(n.p,{children:"Set the client to expect and accept only self-signed certificates"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Configure Server Security -- only accept self-signed certs\nconfig.acceptOnlySelfSignedServerCertificate = true; \n"})}),"\n",(0,i.jsx)(n.p,{children:"Set this to true to accept any self signed cert. Any certificates that are not self-signed are rejected."}),"\n",(0,i.jsx)(n.p,{children:"This all assumes that you have configured the Sync Gateway to provide the appropriate SSL certificates, and have included the appropriate certificate in your app bundle."}),"\n",(0,i.jsx)(n.h3,{id:"client-authentication",children:"Client Authentication"}),"\n",(0,i.jsxs)(n.p,{children:["There are two ways to authenticate from a Couchbase Lite client: ",(0,i.jsx)(n.code,{children:"Basic Authentication"})," or ",(0,i.jsx)(n.code,{children:"Session Authentication"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"basic-authentication",children:"Basic Authentication"}),"\n",(0,i.jsxs)(n.p,{children:["You can provide a user name and password to the basic authenticator class method. Under the hood, the replicator will send the credentials in the first request to retrieve a ",(0,i.jsx)(n.code,{children:"SyncGatewaySession"})," cookie and use it for all subsequent requests during the replication. This is the recommended way of using basic authentication. ",(0,i.jsx)(n.a,{href:"#example-8-basic-authentication",children:"Example 8"})," shows how to initiate a one-shot replication as the user ",(0,i.jsx)(n.strong,{children:"username"})," with the password ",(0,i.jsx)(n.strong,{children:"password"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-8-basic-authentication",children:"Example 8. Basic Authentication"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:'const url = "ws://localhost:4984/mydatabase";\nconst target = new URLEndpoint(url);\nconst auth = new BasicAuthenticator("john", "pass"); \nconst config = new ReplicatorConfiguration(target);\n\n// Assuming collection is already defined and initialized\nconfig.addCollection(collectionName);\nconfig.setAuthenticator(auth);\n\nconst replicator = new Replicator(config);\nawait replicator.start();\n'})}),"\n",(0,i.jsx)(n.h4,{id:"session-authentication",children:"Session Authentication"}),"\n",(0,i.jsx)(n.p,{children:"Session authentication is another way to authenticate with Sync Gateway."}),"\n",(0,i.jsxs)(n.p,{children:["A user session must first be created through the ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/rest-api.html#/session/post__db___session",children:(0,i.jsx)(n.code,{children:"POST /{tkn-db}/_session"})})," endpoint on the Public REST API."]}),"\n",(0,i.jsx)(n.p,{children:"The HTTP response contains a session ID which can then be used to authenticate as the user it was created for."}),"\n",(0,i.jsxs)(n.p,{children:["See ",(0,i.jsx)(n.a,{href:"#example-9-session-authentication",children:"Example 9"}),", which shows how to initiate a one-shot replication with the session ID returned from the ",(0,i.jsx)(n.code,{children:"POST /{tkn-db}/_session"})," endpoint."]}),"\n",(0,i.jsx)(n.h4,{id:"example-9-session-authentication",children:"Example 9. Session Authentication"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"const url = new URL(\"ws://localhost:4984/mydatabase\");\nconst target = new URLEndpoint(url);\nconst config = new ReplicatorConfiguration(target);\nconst sessionID = 'your-session-id';\nconst cookieName = 'your-cookie-name';\n\n// Assuming collection is already defined and initialized\nconfig.addCollection(collectionName);\n\n// Here cookieName is optional, if not passed it will default to the default value\nconst sessionAuthenticator = new SessionAuthenticator(sessionID, cookieName);\nconfig.setAuthenticator(sessionAuthenticator);\n\nlet replicator = new Replicator(config);\nawait replicator.start();\n"})}),"\n",(0,i.jsx)(n.h3,{id:"custom-headers",children:"Custom Headers"}),"\n",(0,i.jsx)(n.p,{children:"Custom headers can be set on the configuration object. The replicator will then include those headers in every request."}),"\n",(0,i.jsxs)(n.p,{children:["This feature is useful in passing additional credentials, perhaps when an authentication or authorization step is being done by a proxy server (between Couchbase Lite and Sync Gateway)\u2009\u2014\u2009see ",(0,i.jsx)(n.a,{href:"#example-10-setting-custom-headers",children:"Example 10"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-10-setting-custom-headers",children:"Example 10. Setting custom headers"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:'const config = new ReplicatorConfiguration(target);\nconfig.addCollection(collection);\n\n// Setting headers\nconfig.setHeaders({ "CustomHeaderName": "Value" });\n'})}),"\n",(0,i.jsx)(n.h3,{id:"channels",children:"Channels"}),"\n",(0,i.jsx)(n.p,{children:"By default, Couchbase Lite gets all the channels to which the configured user account has access."}),"\n",(0,i.jsxs)(n.p,{children:["This behavior is suitable for most apps that rely on ",(0,i.jsx)(n.code,{children:"user authentication"})," and the ",(0,i.jsx)(n.code,{children:"sync function"})," to specify which data to pull for each user."]}),"\n",(0,i.jsx)(n.p,{children:"Optionally, it\u2019s also possible to specify a string array of channel names on Couchbase Lite\u2019s replicator configuration object. In this case, the replication from Sync Gateway will only pull documents tagged with those channels."}),"\n",(0,i.jsx)(n.h3,{id:"auto-purge-on-channel-access-revocation",children:"Auto-purge on Channel Access Revocation"}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsx)(n.p,{children:"This is a Breaking Change at 3.0"})}),"\n",(0,i.jsx)(n.h4,{id:"new-outcome",children:"New outcome"}),"\n",(0,i.jsx)(n.p,{children:"By default, when a user loses access to a channel all documents in the channel (that do not also belong to any of the user\u2019s other channels) are auto-purged from the local database (in devices belonging to the user)."}),"\n",(0,i.jsx)(n.h4,{id:"prior-outcome",children:"Prior outcome"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.em,{children:"Previously these documents remained in the local database"})}),"\n",(0,i.jsx)(n.p,{children:"Prior to this release, CBL auto-purged only in the case when the user loses access to a document by removing the doc from all of the channels belong to the user. Now, in addition to 2.x auto purge, Couchbase Lite will also auto-purges the docs when the user loses access to the doc via channel access revocation. This feature is enabled by default, but an opt-out is available."}),"\n",(0,i.jsx)(n.h4,{id:"behaviour",children:"Behaviour"}),"\n",(0,i.jsx)(n.p,{children:"Users may lose access to channels in a number of ways:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"User loses direct access to channel"}),"\n",(0,i.jsx)(n.li,{children:"User is removed from a role"}),"\n",(0,i.jsx)(n.li,{children:"A channel is removed from a role the user is assigned to"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["By default, when a user loses access to a channel, the next Couchbase Lite Pull replication auto-purges all documents in the channel from local Couchbase Lite databases (on devices belonging to the user) unless they belong to any of the user\u2019s other channels\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#table-2-behavior-following-access-revocation",children:"Table 2"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Documents that exist in multiple channels belonging to the user (even if they are not actively replicating that channel) are not auto-purged unless the user loses access to all channels."}),"\n",(0,i.jsxs)(n.p,{children:["Users will be receive an ",(0,i.jsx)(n.code,{children:"AccessRemoved"})," notification from the DocumentListener if they lose document access due to channel access revocation; this is sent regardless of the current auto-purge setting."]}),"\n",(0,i.jsx)(n.h4,{id:"table-2-behavior-following-access-revocation",children:"Table 2. Behavior following access revocation"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{}),(0,i.jsx)(n.th,{children:"System State"}),(0,i.jsx)(n.th,{children:"Impact on Sync"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Replication Type"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Access Control on Sync Gateway"})}),(0,i.jsxs)(n.td,{children:[(0,i.jsx)(n.strong,{children:"Expected behavior when"})," ",(0,i.jsx)(n.em,{children:"enable_auto_purge=true"})]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Pull only"})}),(0,i.jsx)(n.td,{children:"User revoked access to channel."}),(0,i.jsx)(n.td,{children:"Previously synced documents are auto purged on local"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(revokedChannel)"})]}),(0,i.jsx)(n.td,{})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push only"})}),(0,i.jsx)(n.td,{children:"User revoked access to channel."}),(0,i.jsx)(n.td,{children:"No impact of auto-purge"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(revokedChannel)"})]}),(0,i.jsx)(n.td,{children:"Documents get pushed but are rejected by Sync Gateway"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push-pull"})}),(0,i.jsx)(n.td,{children:"User revoked access to channel"}),(0,i.jsx)(n.td,{children:"Previously synced documents are auto purged on Couchbase Lite."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(revokedChannel)"})]}),(0,i.jsx)(n.td,{children:"Local changes continue to be pushed to remote but are rejected by Sync Gateway"})]})]})]}),"\n",(0,i.jsxs)(n.p,{children:["If a user subsequently regains access to a lost channel, then any previously auto-purged documents still assigned to any of their channels are automatically pulled down by the active Sync Gateway when they are next updated\u2009\u2014\u2009see behavior summary in ",(0,i.jsx)(n.a,{href:"#table-3-behavior-if-access-is-regained",children:"Table 3"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"table-3-behavior-if-access-is-regained",children:"Table 3. Behavior if access is regained"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{}),(0,i.jsx)(n.th,{children:"System State"}),(0,i.jsx)(n.th,{children:"Impact on Sync"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Replication Type"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Access Control on Sync Gateway"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Expected behavior when enable_auto_purge=true"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Pull only"})}),(0,i.jsx)(n.td,{children:"User REASSIGNED access to channel"}),(0,i.jsx)(n.td,{children:"Previously purged documents that are still in the channel are automatically pulled by Couchbase Lite when they are next updated"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push only"})}),(0,i.jsx)(n.td,{children:"User REASSIGNED access to channel"}),(0,i.jsx)(n.td,{children:"Local changes previously rejected by Sync Gateway will not be automatically pushed to remote unless resetCheckpoint is involved on CBL. Document changes subsequent to the channel reassignment will be pushed up as usual."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(reassignedChannel)"})," No impact of auto-purge"]}),(0,i.jsx)(n.td,{})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push-pull"})}),(0,i.jsx)(n.td,{children:"User REASSIGNED access to channel"}),(0,i.jsx)(n.td,{children:"Previously purged documents are automatically pulled by Couchbase Lite"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(reassignedChannel)"})]}),(0,i.jsx)(n.td,{children:"Local changes previously rejected by Sync Gateway will not be automatically pushed to remote unless resetCheckpoint is involved. Document changes subsequent to the channel reassignment will be pushed up as usual."})]})]})]}),"\n",(0,i.jsx)(n.h4,{id:"config",children:"Config"}),"\n",(0,i.jsxs)(n.p,{children:["Auto-purge behavior is controlled primarily by the ReplicationConfiguration option ",(0,i.jsx)(n.code,{children:"enableAutoPurge"}),". Changing the state of this will impact only future replications; the replicator will not attempt to sync revisions that were auto purged on channel access removal. Clients wishing to sync previously removed documents must use the resetCheckpoint API to resync from the start."]}),"\n",(0,i.jsx)(n.h4,{id:"example-11-setting-auto-purge",children:"Example 11. Setting auto-purge"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// set auto-purge behavior (here we override default)\nconfig.enableAutoPurge = false;\n"})}),"\n",(0,i.jsx)(n.p,{children:"Here we have opted to turn off the auto purge behavior. By default auto purge is enabled."}),"\n",(0,i.jsx)(n.h3,{id:"delta-sync",children:"Delta Sync"}),"\n",(0,i.jsx)(n.admonition,{type:"important",children:(0,i.jsxs)(n.p,{children:["This is an ",(0,i.jsx)(n.a,{href:"https://www.couchbase.com/products/editions/",children:"Enterprise Edition"})," feature."]})}),"\n",(0,i.jsx)(n.p,{children:"With Delta Sync, only the changed parts of a Couchbase document are replicated. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained."}),"\n",(0,i.jsxs)(n.p,{children:["Replications to a Server (for example, a Sync Gateway, or passive listener) automatically use delta sync if the property is enabled at database level by the server\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/configuration-properties-legacy.html#databases-foo_db-delta_sync",children:"databases.$db.delta_sync.enabled"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Intra-Device replications automatically disable delta sync, whilst Peer-to-Peer replications automatically enable delta sync."}),"\n",(0,i.jsx)(n.h2,{id:"initialize",children:"Initialize"}),"\n",(0,i.jsx)(n.h5,{id:"in-this-section-1",children:"In this section"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#start-replicator",children:"Start Replicator"})," | ",(0,i.jsx)(n.a,{href:"#checkpoint-starts",children:"Checkpoint Starts"})]}),"\n",(0,i.jsx)(n.h3,{id:"start-replicator",children:"Start Replicator"}),"\n",(0,i.jsxs)(n.p,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator.create()"})," method to initialize the replicator with the configuration you have defined. You can optionally add a change listener (see ",(0,i.jsx)(n.a,{href:"#monitor",children:"Monitor"}),") before starting the replicator using ",(0,i.jsx)(n.code,{children:"start()"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-12-initialize-and-run-replicator",children:"Example 12. Initialize and run replicator"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Apply configuration settings to the replicator\nconst replicator = await Replicator.create(config);\n\n// start the replicator without making a new checkpoint\nawait replicator.start(false);\n"})}),"\n",(0,i.jsx)(n.h3,{id:"checkpoint-starts",children:"Checkpoint Starts"}),"\n",(0,i.jsxs)(n.p,{children:["Replicators use ",(0,i.jsx)(n.code,{children:"checkpoints"})," to keep track of documents sent to the target database."]}),"\n",(0,i.jsxs)(n.p,{children:["Without ",(0,i.jsx)(n.code,{children:"checkpoints"}),", Couchbase Lite would replicate the entire database content to the target database on each connection, even though previous replications may already have replicated some or all of that content."]}),"\n",(0,i.jsxs)(n.p,{children:["This functionality is generally not a concern to application developers. However, if you do want to force the replication to start again from zero, use the ",(0,i.jsx)(n.code,{children:"checkpoint"})," reset argument when starting the replicator\u2009\u2014\u2009as shown in Example 13."]}),"\n",(0,i.jsx)(n.h4,{id:"example-13-resetting-checkpoints",children:"Example 13. Resetting checkpoints"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"if (doResetCheckpointRequired) {\n this.replicator.start(true);\n} else {\n this.replicator.start(false);\n}\n"})}),"\n",(0,i.jsxs)(n.p,{children:["The default ",(0,i.jsx)(n.code,{children:"false"})," is shown here for completeness only; it is unlikely you would explicitly use it in practice."]}),"\n",(0,i.jsx)(n.h2,{id:"monitor",children:"Monitor"}),"\n",(0,i.jsx)(n.h5,{id:"in-this-section-2",children:"In this section"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#change-listeners",children:"Change Listeners"})," | ",(0,i.jsx)(n.a,{href:"#replicator-status",children:"Replicator Status"})," | ",(0,i.jsx)(n.a,{href:"#monitor-document-changes",children:"Monitor Document Changes"})," | ",(0,i.jsx)(n.a,{href:"#documents-pending-push",children:"Documents Pending Push"})]}),"\n",(0,i.jsxs)(n.p,{children:["You can monitor a replication\u2019s status by using a combination of ",(0,i.jsx)(n.code,{children:"Change Listeners"})," and the ",(0,i.jsx)(n.code,{children:"Replicator.getStatus()"})," property. This enables you to know, for example, when the replication is actively transferring data and when it has stopped."]}),"\n",(0,i.jsxs)(n.p,{children:["You can also choose to monitor document changes\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#monitor-document-changes",children:"Monitor Document Changes"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"change-listeners",children:"Change Listeners"}),"\n",(0,i.jsx)(n.p,{children:"Use this to monitor changes and to inform on sync progress; this is an optional step. You can add and a replicator change listener at any point; it will report changes from the point it is registered."}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsx)(n.p,{children:"Don\u2019t forget to save the token so you can remove the listener later"})}),"\n",(0,i.jsxs)(n.p,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator"})," class to add a change listener as a callback to the Replicator (",(0,i.jsx)(n.code,{children:"addChangeListener()"}),")\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-14-monitor-replication",children:"Example 14"}),". You will then be asynchronously notified of state changes."]}),"\n",(0,i.jsxs)(n.p,{children:["You can remove a change listener with ",(0,i.jsx)(n.code,{children:"removeChangeListener(token)"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"replicator-status",children:"Replicator Status"}),"\n",(0,i.jsxs)(n.p,{children:["You can use the ",(0,i.jsx)(n.code,{children:"Replicator.getStatus()"})," property to check the replicator status. That is, whether it is actively transferring data or if it has stopped\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-14-monitor-replication",children:"Example 14"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["The returned ",(0,i.jsx)(n.em,{children:"ReplicationStatus"})," structure comprises:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"ActivityLevel"}),"\u2009\u2014\u2009stopped, offline, connecting, idle or busy\u2009\u2014\u2009see states described in: ",(0,i.jsx)(n.a,{href:"#table-5-replicator-activity-levels",children:"Table 5"})]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.code,{children:"Progress"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"completed\u2009\u2014\u2009the total number of changes completed"}),"\n",(0,i.jsx)(n.li,{children:"total\u2009\u2014\u2009the total number of changes to be processed"}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"Error"})," - the current error, if any."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-14-monitor-replication",children:"Example 14. Monitor replication"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Optionally add a change listener\n// Retain token for use in deletion\nconst token = replicator.addChangeListener((change) => {\n if (change.status.getActivityLevel() === 'STOPPED') {\n console.log(\"Replication stopped\");\n } else {\n console.log(`Replicator is currently : ${change.status.getActivityLevel()}`);\n }\n});\n"})}),"\n",(0,i.jsx)(n.h3,{id:"replication-states",children:"Replication States"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#table-5-replicator-activity-levels",children:"Table 5"})," shows the different states, or activity levels, reported in the API; and the meaning of each."]}),"\n",(0,i.jsx)(n.h4,{id:"table-5-replicator-activity-levels",children:"Table 5. Replicator activity levels"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:(0,i.jsx)(n.strong,{children:"State"})}),(0,i.jsx)(n.th,{children:(0,i.jsx)(n.strong,{children:"Meaning"})})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"STOPPED"})}),(0,i.jsx)(n.td,{children:"The replication is finished or hit a fatal error."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"OFFLINE"})}),(0,i.jsx)(n.td,{children:"The replicator is offline as the remote host is unreachable"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"CONNECTING"})}),(0,i.jsx)(n.td,{children:"The replicator is connecting to the remote host"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"IDLE"})}),(0,i.jsxs)(n.td,{children:["The replication caught up with all the changes available from the server. The ",(0,i.jsx)(n.code,{children:"IDLE"})," state is only used in continuous replications."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"BUSY"})}),(0,i.jsx)(n.td,{children:"The replication is actively transferring data."})]})]})]}),"\n",(0,i.jsx)(n.h3,{id:"replication-status-and-app-life-cycle",children:"Replication Status and App Life Cycle"}),"\n",(0,i.jsx)(n.p,{children:"The following diagram describes the status changes when the application starts a replication, and when the application is being backgrounded or foregrounded by the OS. It applies to iOS only."}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Replicator States",src:t(4697).A+"",width:"1934",height:"914"})}),"\n",(0,i.jsxs)(n.p,{children:["Additionally, on iOS, an app already in the background may be terminated. In this case, the ",(0,i.jsx)(n.code,{children:"Database"})," and ",(0,i.jsx)(n.code,{children:"Replicator"})," instances will be ",(0,i.jsx)(n.code,{children:"null"})," when the app returns to the foreground. Therefore, as preventive measure, it is recommended to do a ",(0,i.jsx)(n.code,{children:"null"})," check when the app enters the foreground, and to re-initialize the database and replicator if any of those is ",(0,i.jsx)(n.code,{children:"null"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"On other platforms, Couchbase Lite doesn\u2019t react to OS backgrounding or foregrounding events and replication(s) will continue running as long as the remote system does not terminate the connection and the app does not terminate. It is generally recommended to stop replications before going into the background otherwise socket connections may be closed by the OS and this may interfere with the replication process."}),"\n",(0,i.jsx)(n.h3,{id:"monitor-document-changes",children:"Monitor Document Changes"}),"\n",(0,i.jsx)(n.p,{children:"You can choose to register for document updates during a replication."}),"\n",(0,i.jsxs)(n.p,{children:["For example, the code snippet in ",(0,i.jsx)(n.a,{href:"#example-15-register-a-document-listener",children:"Example 15"})," registers a listener to monitor document replication performed by the replicator referenced by the variable ",(0,i.jsx)(n.code,{children:"replicator"}),". It prints the document ID of each document received and sent. Stop the listener as shown in ",(0,i.jsx)(n.a,{href:"#example-16-stop-document-listener",children:"Example 16"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-15-register-a-document-listener",children:"Example 15. Register a document listener"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:'const token = await replicator.addDocumentChangeListener((replication) => {\n console.log(`Replication type :: ${replication.isPush ? "Push" : "Pull"}`);\n for (const document of replication.documents) {\n if (document.error === undefined) {\n console.log(`Doc ID :: ${document.id}`);\n if (document.flags.includes(\'DELETED\')) {\n console.log("Successfully replicated a deleted document");\n }\n } else {\n console.error("Error replicating document:", document.error);\n }\n }\n});\n\n// Start the replicator without resetting the checkpoint\nawait replicator.start(false);\n'})}),"\n",(0,i.jsx)(n.h4,{id:"example-16-stop-document-listener",children:"Example 16. Stop document listener"}),"\n",(0,i.jsx)(n.p,{children:"This code snippet shows how to stop the document listener using the token from the previous example."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"await this.replicator.removeChangeListener(token);\n"})}),"\n",(0,i.jsx)(n.h3,{id:"document-access-removal-behavior",children:"Document Access Removal Behavior"}),"\n",(0,i.jsxs)(n.p,{children:["When access to a document is removed on Sync Gateway (see: Sync Gateway\u2019s ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/sync-function-api.html",children:"Sync Function"}),"), the document replication listener sends a notification with the ",(0,i.jsx)(n.code,{children:"AccessRemoved"})," flag set to ",(0,i.jsx)(n.code,{children:"true"})," and subsequently purges the document from the database."]}),"\n",(0,i.jsx)(n.h2,{id:"documents-pending-push",children:"Documents Pending Push"}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"Replicator.isDocumentPending()"})," is quicker and more efficient. Use it in preference to returning a list of pending document IDs, where possible."]})}),"\n",(0,i.jsx)(n.p,{children:"You can check whether documents are waiting to be pushed in any forthcoming sync by using either of the following API methods:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator.pendingDocumentIds()"})," method, which returns a list of document IDs that have local changes, but which have not yet been pushed to the server."]}),"\n",(0,i.jsxs)(n.li,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator.isDocumentPending()"})," method to quickly check whether an individual document is pending a push."]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-17-use-pending-document-id-api",children:"Example 17. Use Pending Document ID API"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Todo \n"})}),"\n",(0,i.jsx)(n.h2,{id:"stop",children:"Stop"}),"\n",(0,i.jsxs)(n.p,{children:["Stopping a replication is straightforward. It is done using ",(0,i.jsx)(n.code,{children:"stop()"}),". This initiates an asynchronous operation and so is not necessarily immediate. Your app should account for this potential delay before attempting any subsequent operations."]}),"\n",(0,i.jsxs)(n.p,{children:["You can find further information on database operations in ",(0,i.jsx)(n.a,{href:"/databases",children:"Databases"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-18-stop-replicator",children:"Example 18. Stop replicator"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Remove the change listener\nawait this.replicator.removeChangeListener(token)\n\n// Stop the replicator\nawait this.replicator.stop()\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Here we initiate the stopping of the replication using the ",(0,i.jsx)(n.code,{children:"stop()"})," method. It will stop any active ",(0,i.jsx)(n.code,{children:"change listener"})," once the replication is stopped."]}),"\n",(0,i.jsx)(n.h2,{id:"error-handling",children:"Error Handling"}),"\n",(0,i.jsxs)(n.p,{children:["When ",(0,i.jsx)(n.em,{children:"replicator"})," detects a network error it updates its status depending on the error type (permanent or temporary) and returns an appropriate HTTP error code."]}),"\n",(0,i.jsxs)(n.p,{children:["The following code snippet adds a ",(0,i.jsx)(n.code,{children:"Change Listener"}),", which monitors a replication for errors and logs the the returned error code."]}),"\n",(0,i.jsx)(n.h4,{id:"example-19-monitoring-for-network-errors",children:"Example 19. Monitoring for network errors"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"replicator.addChangeListener((change) => {\n const error = change.status.getError();\n if (error) {\n console.log(`Error code :: ${error.code}`);\n }\n});\n"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"For permanent network errors"})," (for example, ",(0,i.jsx)(n.code,{children:"404"})," not found, or ",(0,i.jsx)(n.code,{children:"401"})," unauthorized): ",(0,i.jsx)(n.em,{children:"Replicator"})," will stop permanently, whether ",(0,i.jsx)(n.code,{children:"setContinuous"})," is ",(0,i.jsx)(n.em,{children:"true"})," or ",(0,i.jsx)(n.em,{children:"false"}),". Of course, it sets its status to ",(0,i.jsx)(n.code,{children:"STOPPED"})]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"For recoverable or temporary errors:"})," ",(0,i.jsx)(n.em,{children:"Replicator"})," sets its status to ",(0,i.jsx)(n.code,{children:"OFFLINE"}),", then:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"setContinuous=true"})," it retries the connection indefinitely"]}),"\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"setContinuous=false"})," (one-shot) it retries the connection a limited number of times."]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"The following error codes are considered temporary by the Couchbase Lite replicator and thus will trigger a connection retry."}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"408"}),": Request Timeout"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"429"}),": Too Many Requests"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"500"}),": Internal Server Error"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"502"}),": Bad Gateway"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"503"}),": Service Unavailable"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"504"}),": Gateway Timeout"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"1001"}),": DNS resolution error"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"load-balancers",children:"Load Balancers"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Lite uses WebSockets as the communication protocol to transmit data. Some load balancers are not configured for WebSocket connections by default (NGINX for example); so it might be necessary to explicitly enable them in the load balancer\u2019s configuration (see ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/load-balancer.html",children:"Load Balancers"}),")."]}),"\n",(0,i.jsxs)(n.p,{children:["By default, the WebSocket protocol uses compression to optimize for speed and bandwidth utilization. The level of compression is set on Sync Gateway and can be tuned in the configuration file (",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/configuration-properties-legacy.html#replicator_compression",children:"replicator_compression"}),")."]}),"\n",(0,i.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,i.jsx)(n.h3,{id:"logs",children:"Logs"}),"\n",(0,i.jsxs)(n.p,{children:["As always, when there is a problem with replication, logging is your friend. You can increase the log output for activity related to replication with Sync Gateway\u2009\u2014\u2009see ",(0,i.jsx)(n.a,{href:"#example-21-set-logging-verbosity",children:"Example 21"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-21-set-logging-verbosity",children:"Example 21. Set logging verbosity"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Verbose / Replicator\ndatabase.setLogLevel(LogDomain.REPLICATOR, Loglevel.VERBOSE);\n\n// Verbose / Network\ndatabase.setLogLevel(LogDomain.NETWORK, Loglevel.VERBOSE);\n"})}),"\n",(0,i.jsxs)(n.p,{children:["For more on troubleshooting with logs, see: ",(0,i.jsx)(n.a,{href:"/Troubleshooting/using-logs",children:"Using Logs"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"authentication-errors",children:"Authentication Errors"}),"\n",(0,i.jsxs)(n.p,{children:["If Sync Gateway is configured with a self signed certificate but your app points to a ",(0,i.jsx)(n.code,{children:"ws"})," scheme instead of ",(0,i.jsx)(n.code,{children:"wss"})," you will encounter an error with status code 11006\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-22-protocol-mismatch",children:"Example 22"})]}),"\n",(0,i.jsx)(n.h4,{id:"example-22-protocol-mismatch",children:"Example 22. Protocol Mismatch"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-console",children:'CouchbaseLite Replicator ERROR: {Repl#2} Got LiteCore error: WebSocket error 1006 "connection closed abnormally"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["If Sync Gateway is configured with a self signed certificate, and your app points to a ",(0,i.jsx)(n.code,{children:"wss"})," scheme but the replicator configuration isn\u2019t using the certificate you will encounter an error with status code ",(0,i.jsx)(n.code,{children:"5011"}),"\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-23-certificate-mismatch-or-not-found",children:"Example 23"})]}),"\n",(0,i.jsx)(n.h4,{id:"example-23-certificate-mismatch-or-not-found",children:"Example 23. Certificate Mismatch or Not Found"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-console",children:'CouchbaseLite Replicator ERROR: {Repl#2} Got LiteCore error: Network error 11 "server TLS certificate is self-signed or has unknown root cert"\n'})})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},7943:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-1-0d0f78f1482260e9798165e302a25a79.png"},7308:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-2-0fe3c8d39a7e607ffc343de2337b5dbe.png"},6725:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-3-f7f78a091e8ffbd50c5bdbf57b70085b.png"},2058:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-4-1b030ec159be9f916e2efc746bea9ca1.png"},4697:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/replicator-states-131cd1d916435293b2ad2c7049ceb4d9.png"},8453:(e,n,t)=>{t.d(n,{R:()=>a,x:()=>r});var i=t(6540);const s={},o=i.createContext(s);function a(e){const n=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function r(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/f9585397.e160bfd7.js b/assets/js/f9585397.e160bfd7.js new file mode 100644 index 0000000..c83fa8c --- /dev/null +++ b/assets/js/f9585397.e160bfd7.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkcbl_ionic=self.webpackChunkcbl_ionic||[]).push([[5967],{673:(e,n,t)=>{t.r(n),t.d(n,{assets:()=>r,contentTitle:()=>a,default:()=>h,frontMatter:()=>o,metadata:()=>c,toc:()=>l});var i=t(4848),s=t(8453);const o={id:"remote-sync-gateway",sidebar_position:2},a="Remote Sync Gateway",c={id:"DataSync/remote-sync-gateway",title:"Remote Sync Gateway",description:"Description - Couchbase Lite for Ionic\u2009\u2014\u2009Synchronizing data changes between local and remote databases using Sync Gateway",source:"@site/docs/DataSync/remote-sync-gateway.md",sourceDirName:"DataSync",slug:"/DataSync/remote-sync-gateway",permalink:"/DataSync/remote-sync-gateway",draft:!1,unlisted:!1,editUrl:"https://github.com/Couchbase-Ecosystem/cbl-ionic-docs/docs/DataSync/remote-sync-gateway.md",tags:[],version:"current",sidebarPosition:2,frontMatter:{id:"remote-sync-gateway",sidebar_position:2},sidebar:"tutorialSidebar",previous:{title:"Couchbase Capella App Services",permalink:"/DataSync/capella"},next:{title:"Typed Data",permalink:"/typed-data"}},r={},l=[{value:"Introduction",id:"introduction",level:2},{value:"Replication Concepts",id:"replication-concepts",level:2},{value:"Database",id:"database",level:4},{value:"Collection",id:"collection",level:4},{value:"Replication Protocol",id:"replication-protocol",level:2},{value:"Scheme",id:"scheme",level:3},{value:"Incompatibilities",id:"incompatibilities",level:4},{value:"Ordering",id:"ordering",level:3},{value:"Scopes and Collections",id:"scopes-and-collections",level:2},{value:"Default Collection",id:"default-collection",level:3},{value:"Sync Couchbase Lite database with the default collection on Sync Gateway",id:"sync-couchbase-lite-database-with-the-default-collection-on-sync-gateway",level:4},{value:"Sync Couchbase Lite default collection with default collection on Sync Gateway",id:"sync-couchbase-lite-default-collection-with-default-collection-on-sync-gateway",level:4},{value:"User-Defined Collections",id:"user-defined-collections",level:3},{value:"Syncing scope with user-defined collections.",id:"syncing-scope-with-user-defined-collections",level:4},{value:"Syncing scope with user-defined collections. Couchbase Lite has more collections than the Sync Gateway configuration (with collection filters)",id:"syncing-scope-with-user-defined-collections-couchbase-lite-has-more-collections-than-the-sync-gateway-configuration-with-collection-filters",level:4},{value:"Configuration Summary",id:"configuration-summary",level:2},{value:"Example 1. Replication configuration and initialization",id:"example-1-replication-configuration-and-initialization",level:4},{value:"Configure",id:"configure",level:2},{value:"In this section",id:"in-this-section",level:5},{value:"Configure Target",id:"configure-target",level:3},{value:"Example 2. Add Target to Configuration",id:"example-2-add-target-to-configuration",level:4},{value:"Sync Mode",id:"sync-mode",level:3},{value:"Example 4. Configure replicator type and mode",id:"example-4-configure-replicator-type-and-mode",level:4},{value:"Retry Configuration",id:"retry-configuration",level:3},{value:"Table 1. Replication Retry Configuration Properties",id:"table-1-replication-retry-configuration-properties",level:4},{value:"Example 5. Configuring Replication Retries",id:"example-5-configuring-replication-retries",level:4},{value:"User Authorization",id:"user-authorization",level:3},{value:"Example 6. Enable Authorization",id:"example-6-enable-authorization",level:4},{value:"Server Authentication",id:"server-authentication",level:3},{value:"Example 7. Set Server TLS security",id:"example-7-set-server-tls-security",level:4},{value:"CA Cert",id:"ca-cert",level:5},{value:"Self Signed Cert",id:"self-signed-cert",level:5},{value:"Client Authentication",id:"client-authentication",level:3},{value:"Basic Authentication",id:"basic-authentication",level:4},{value:"Example 8. Basic Authentication",id:"example-8-basic-authentication",level:4},{value:"Session Authentication",id:"session-authentication",level:4},{value:"Example 9. Session Authentication",id:"example-9-session-authentication",level:4},{value:"Custom Headers",id:"custom-headers",level:3},{value:"Example 10. Setting custom headers",id:"example-10-setting-custom-headers",level:4},{value:"Channels",id:"channels",level:3},{value:"Documents",id:"documents",level:3},{value:"Auto-purge on Channel Access Revocation",id:"auto-purge-on-channel-access-revocation",level:3},{value:"New outcome",id:"new-outcome",level:4},{value:"Prior outcome",id:"prior-outcome",level:4},{value:"Behaviour",id:"behaviour",level:4},{value:"Table 2. Behavior following access revocation",id:"table-2-behavior-following-access-revocation",level:4},{value:"Table 3. Behavior if access is regained",id:"table-3-behavior-if-access-is-regained",level:4},{value:"Config",id:"config",level:4},{value:"Example 11. Setting auto-purge",id:"example-11-setting-auto-purge",level:4},{value:"Delta Sync",id:"delta-sync",level:3},{value:"Initialize",id:"initialize",level:2},{value:"In this section",id:"in-this-section-1",level:5},{value:"Start Replicator",id:"start-replicator",level:3},{value:"Example 12. Initialize and run replicator",id:"example-12-initialize-and-run-replicator",level:4},{value:"Checkpoint Starts",id:"checkpoint-starts",level:3},{value:"Example 13. Resetting checkpoints",id:"example-13-resetting-checkpoints",level:4},{value:"Monitor",id:"monitor",level:2},{value:"In this section",id:"in-this-section-2",level:5},{value:"Change Listeners",id:"change-listeners",level:3},{value:"Replicator Status",id:"replicator-status",level:3},{value:"Example 14. Monitor replication",id:"example-14-monitor-replication",level:4},{value:"Replication States",id:"replication-states",level:3},{value:"Table 5. Replicator activity levels",id:"table-5-replicator-activity-levels",level:4},{value:"Replication Status and App Life Cycle",id:"replication-status-and-app-life-cycle",level:3},{value:"Monitor Document Changes",id:"monitor-document-changes",level:3},{value:"Example 15. Register a document listener",id:"example-15-register-a-document-listener",level:4},{value:"Example 16. Stop document listener",id:"example-16-stop-document-listener",level:4},{value:"Document Access Removal Behavior",id:"document-access-removal-behavior",level:3},{value:"Documents Pending Push",id:"documents-pending-push",level:2},{value:"Example 17. Use Pending Document ID API",id:"example-17-use-pending-document-id-api",level:4},{value:"Stop",id:"stop",level:2},{value:"Example 18. Stop replicator",id:"example-18-stop-replicator",level:4},{value:"Error Handling",id:"error-handling",level:2},{value:"Example 19. Monitoring for network errors",id:"example-19-monitoring-for-network-errors",level:4},{value:"Load Balancers",id:"load-balancers",level:2},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Logs",id:"logs",level:3},{value:"Example 21. Set logging verbosity",id:"example-21-set-logging-verbosity",level:4},{value:"Authentication Errors",id:"authentication-errors",level:3},{value:"Example 22. Protocol Mismatch",id:"example-22-protocol-mismatch",level:4},{value:"Example 23. Certificate Mismatch or Not Found",id:"example-23-certificate-mismatch-or-not-found",level:4}];function d(e){const n={a:"a",admonition:"admonition",blockquote:"blockquote",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",h5:"h5",img:"img",li:"li",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,s.R)(),...e.components};return(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(n.h1,{id:"remote-sync-gateway",children:"Remote Sync Gateway"}),"\n",(0,i.jsxs)(n.blockquote,{children:["\n",(0,i.jsxs)(n.p,{children:["Description - ",(0,i.jsx)(n.em,{children:"Couchbase Lite for Ionic\u2009\u2014\u2009Synchronizing data changes between local and remote databases using Sync Gateway"})]}),"\n"]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsx)(n.p,{children:"All code examples are indicative only. They demonstrate the basic concepts and approaches to using a feature. Use them as inspiration and adapt these examples to best practice when developing applications for your platform."})}),"\n",(0,i.jsx)(n.h2,{id:"introduction",children:"Introduction"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Lite for Ionic provides API support for secure, bi-directional, synchronization of data changes between mobile applications and a central server database. It does so by using a ",(0,i.jsx)(n.em,{children:"replicator"})," to interact with Sync Gateway."]}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.em,{children:"replicator"})," is designed to manage replication of documents and-or document changes between a source and a target database. For example, between a local Couchbase Lite database and remote Sync Gateway database, which is ultimately mapped to a bucket in a Couchbase Server instance in the cloud or on a server."]}),"\n",(0,i.jsx)(n.p,{children:"This page shows sample code and configuration examples covering the implementation of a replication using Sync Gateway."}),"\n",(0,i.jsx)(n.p,{children:"Your application runs a replicator (also referred to here as a client), which will initiate connection with a Sync Gateway (also referred to here as a server) and participate in the replication of database changes to bring both local and remote databases into sync."}),"\n",(0,i.jsx)(n.p,{children:"Subsequent sections provide additional details and examples for the main configuration options."}),"\n",(0,i.jsx)(n.h2,{id:"replication-concepts",children:"Replication Concepts"}),"\n",(0,i.jsx)(n.p,{children:"Couchbase Lite allows for one database for each application running on the mobile device. This database can contain one or more scopes. Each scope can contain one or more collections."}),"\n",(0,i.jsxs)(n.p,{children:["To learn about Scopes and Collections, see ",(0,i.jsx)(n.a,{href:"/databases",children:"Databases"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"You can set up a replication scheme across these data levels:"}),"\n",(0,i.jsx)(n.h4,{id:"database",children:"Database"}),"\n",(0,i.jsxs)(n.p,{children:["The ",(0,i.jsx)(n.code,{children:"_default"})," collection is synced."]}),"\n",(0,i.jsx)(n.h4,{id:"collection",children:"Collection"}),"\n",(0,i.jsx)(n.p,{children:"A specific collection or a set of collections is synced."}),"\n",(0,i.jsx)(n.p,{children:"As part of the syncing setup, the Gateway has to map the Couchbase Lite database to the database being synced on Capella."}),"\n",(0,i.jsx)(n.h2,{id:"replication-protocol",children:"Replication Protocol"}),"\n",(0,i.jsx)(n.h3,{id:"scheme",children:"Scheme"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Mobile uses a replication protocol based on WebSockets for replication. To use this protocol the replication URL should specify WebSockets as the URL scheme (see the ",(0,i.jsx)(n.a,{href:"#configure-target",children:"Configure Target"})," section below)."]}),"\n",(0,i.jsx)(n.h4,{id:"incompatibilities",children:"Incompatibilities"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Lite\u2019s replication protocol is incompatible with CouchDB-based databases. And since Couchbase Lite 2.x+ only supports the new protocol, you will need to run a version of Sync Gateway that supports it\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"/ProductNotes/compatibility",children:"Compatibility"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"ordering",children:"Ordering"}),"\n",(0,i.jsx)(n.p,{children:"To optimize for speed, the replication protocol doesn\u2019t guarantee that documents will be received in a particular order. So we don\u2019t recommend to rely on that when using the replication or database change listeners for example."}),"\n",(0,i.jsx)(n.h2,{id:"scopes-and-collections",children:"Scopes and Collections"}),"\n",(0,i.jsx)(n.p,{children:"Scopes and Collections allow you to organize your documents in Couchbase Lite."}),"\n",(0,i.jsx)(n.p,{children:"When syncing, you can configure the collections to be synced."}),"\n",(0,i.jsx)(n.p,{children:"The collections specified in the Couchbase Lite replicator setup must exist (both scope and collection name must be identical) on the Sync Gateway side, otherwise starting the Couchbase Lite replicator will result in an error."}),"\n",(0,i.jsx)(n.p,{children:"During replication:"}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"If Sync Gateway config (or server) is updated to remove a collection that is being synced, the client replicator will be offline and will be stopped after the first retry. An error will be reported."}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:"If Sync Gateway config is updated to add a collection to a scope that is being synchronized, the replication will ignore the collection. The added collection will not automatically sync until the Couchbase Lite replicator\u2019s configuration is updated."}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"default-collection",children:"Default Collection"}),"\n",(0,i.jsx)(n.p,{children:"When upgrading Couchbase Lite to 3.1, the existing documents in the database will be automatically migrated to the default collection."}),"\n",(0,i.jsx)(n.p,{children:"For backward compatibility with the code prior to 3.1, when you set up the replicator with the database, the default collection will be set up to sync with the default collection on Sync Gateway."}),"\n",(0,i.jsx)(n.h4,{id:"sync-couchbase-lite-database-with-the-default-collection-on-sync-gateway",children:"Sync Couchbase Lite database with the default collection on Sync Gateway"}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 1",src:t(7943).A+"",width:"898",height:"308"})})}),"\n",(0,i.jsx)(n.h4,{id:"sync-couchbase-lite-default-collection-with-default-collection-on-sync-gateway",children:"Sync Couchbase Lite default collection with default collection on Sync Gateway"}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 2",src:t(7308).A+"",width:"870",height:"429"})})}),"\n",(0,i.jsx)(n.h3,{id:"user-defined-collections",children:"User-Defined Collections"}),"\n",(0,i.jsx)(n.p,{children:"The user-defined collections specified in the Couchbase Lite replicator setup must exist (and be identical) on the Sync Gateway side to sync."}),"\n",(0,i.jsx)(n.h4,{id:"syncing-scope-with-user-defined-collections",children:"Syncing scope with user-defined collections."}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 3",src:t(6725).A+"",width:"866",height:"421"})})}),"\n",(0,i.jsx)(n.h4,{id:"syncing-scope-with-user-defined-collections-couchbase-lite-has-more-collections-than-the-sync-gateway-configuration-with-collection-filters",children:"Syncing scope with user-defined collections. Couchbase Lite has more collections than the Sync Gateway configuration (with collection filters)"}),"\n",(0,i.jsx)("div",{align:"center",children:(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Cbl Replication Scopes Collections 4",src:t(2058).A+"",width:"902",height:"436"})})}),"\n",(0,i.jsx)(n.h2,{id:"configuration-summary",children:"Configuration Summary"}),"\n",(0,i.jsxs)(n.p,{children:["You should configure and initialize a replicator for each Couchbase Lite database instance you want to sync. ",(0,i.jsx)(n.a,{href:"#example-1-replication-configuration-and-initialization",children:"Example 1"})," shows the configuration and initialization process."]}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["You need Couchbase Lite 3.1+ and Sync Gateway 3.1+ to use ",(0,i.jsx)(n.code,{children:"custom"})," Scopes and Collections.\nIf you\u2019re using Capella App Services or Sync Gateway releases that are older than version 3.1, you won\u2019t be able to access ",(0,i.jsx)(n.code,{children:"custom"})," Scopes and Collections. To use Couchbase Lite 3.1+ with these older versions, you can use the ",(0,i.jsx)(n.code,{children:"default"})," Collection as a backup option."]})}),"\n",(0,i.jsx)(n.h4,{id:"example-1-replication-configuration-and-initialization",children:"Example 1. Replication configuration and initialization"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"//assumes you are running sync gateway locally, if you are \n //running app services, replace enpoint with proper url and creditentials\n const target = new URLEndpoint('ws://localhost:4984/projects');\n const auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12');\n const config = new ReplicatorConfiguration(target);\n config.addCollection(collectionName);\n config.setAuthenticator(auth);\n\n const replicator = await Replicator.create(config);\n\n //listen to the replicator change events\n const token = await replicator.addChangeListener((change) => {\n\t//check to see if there was an error\n \tconst error = change.status.getError();\n \tif (error !== undefined) {\n\t\t//do something with the error\n \t}\n \t//get the status of the replicator using ReplicatorActivityLevel enum\n \tif (change.status.getActivityLevel() === ReplicatorActivityLevel.IDLE) {\n \t\t//do something because the replicator is now IDLE\n \t}\n });\n\n // start the replicator without making a new checkpoint\n await replicator.start(false);\n\n //remember you must clean up the replicator when done with it by \n //doing the following lines\n\n //await replicator.removeChangeListener(token);\n //await replicator.stop();\n"})}),"\n",(0,i.jsx)(n.h2,{id:"configure",children:"Configure"}),"\n",(0,i.jsx)(n.h5,{id:"in-this-section",children:"In this section"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#configure-target",children:"Configure Target"})," | ",(0,i.jsx)(n.a,{href:"#sync-mode",children:"Sync Mode"})," | ",(0,i.jsx)(n.a,{href:"#retry-configuration",children:"Retry Configuration"})," | ",(0,i.jsx)(n.a,{href:"#user-authorization",children:"User Authorization"})," | ",(0,i.jsx)(n.a,{href:"#server-authentication",children:"Server Authentication"})," | ",(0,i.jsx)(n.a,{href:"#client-authentication",children:"Client Authentication"})," | ",(0,i.jsx)(n.a,{href:"#monitor-document-changes",children:"Monitor Document Changes"})," | ",(0,i.jsx)(n.a,{href:"#custom-headers",children:"Custom Headers"})," | ",(0,i.jsx)(n.a,{href:"#checkpoint-starts",children:"Checkpoint Starts"})," | ",(0,i.jsx)(n.a,{href:"#channels",children:"Channels"})," | ",(0,i.jsx)(n.a,{href:"#auto-purge-on-channel-access-revocation",children:"Auto-purge on Channel Access Revocation"})," | ",(0,i.jsx)(n.a,{href:"#delta-sync",children:"Delta Sync"})]}),"\n",(0,i.jsx)(n.h3,{id:"configure-target",children:"Configure Target"}),"\n",(0,i.jsx)(n.p,{children:"Use the Initialize and define the replication configuration with local and remote database locations using the ReplicatorConfiguration object."}),"\n",(0,i.jsx)(n.p,{children:"The constructor provides:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"the name of the local database to be sync\u2019d"}),"\n",(0,i.jsx)(n.li,{children:"the server\u2019s URL (including the port number and the name of the remote database to sync with)"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["It is expected that the app will identify the IP address and URL and append the remote database name to the URL endpoint, producing for example: ",(0,i.jsx)(n.code,{children:"wss://10.0.2.2:4984/travel-sample"}),"\nThe URL scheme for web socket URLs uses ",(0,i.jsx)(n.code,{children:"ws:"})," (non-TLS) or ",(0,i.jsx)(n.code,{children:"wss:"})," (SSL/TLS) prefixes."]}),"\n",(0,i.jsx)(n.h4,{id:"example-2-add-target-to-configuration",children:"Example 2. Add Target to Configuration"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Define the target URL for the replication endpoint\nconst targetURL = 'wss://10.1.1.12:4984/travel-sample';\n\n// Initialize the URLEndpoint with the target URL\nconst targetEndpoint = new URLEndpoint(targetURL);\n\n// Create the ReplicatorConfiguration with the target endpoint\nconst config = new ReplicatorConfiguration(targetEndpoint);\n\n// Add the already initialised collection to the replicator configuration\nconfig.addCollection(collectionName);\n"})}),"\n",(0,i.jsx)(n.admonition,{type:"note",children:(0,i.jsxs)(n.p,{children:["Note use of the scheme prefix (",(0,i.jsx)(n.code,{children:"wss://"})," to ensure TLS encryption\u2009\u2014\u2009strongly recommended in production\u2009\u2014\u2009or ",(0,i.jsx)(n.code,{children:"ws://"}),")"]})}),"\n",(0,i.jsx)(n.h3,{id:"sync-mode",children:"Sync Mode"}),"\n",(0,i.jsx)(n.p,{children:"Here we define the direction and type of replication we want to initiate."}),"\n",(0,i.jsxs)(n.p,{children:["We use ",(0,i.jsx)(n.code,{children:"ReplicatorConfiguration"})," class\u2019s ",(0,i.jsx)(n.code,{children:"replicatorType"})," and ",(0,i.jsx)(n.code,{children:"continuous"})," parameters, to tell the replicator:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["The type (or direction) of the replication: ",(0,i.jsx)(n.code,{children:"PUSH_AND_PULL"}),"; ",(0,i.jsx)(n.code,{children:"PULL"}),"; ",(0,i.jsx)(n.code,{children:"PUSH"})]}),"\n",(0,i.jsxs)(n.li,{children:["The replication mode, that is either of:","\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Continuous - remaining active indefinitely to replicate changed documents (",(0,i.jsx)(n.code,{children:"continuous=true"}),")."]}),"\n",(0,i.jsxs)(n.li,{children:["Ad-hoc - a one-shot replication of changed documents (",(0,i.jsx)(n.code,{children:"continuous=false"}),")"]}),"\n"]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-4-configure-replicator-type-and-mode",children:"Example 4. Configure replicator type and mode"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"config.replicatorType = ReplicatorType.PUSH_AND_PULL;\n\n// Configure Sync Mode\nconfig.continuous = true\n"})}),"\n",(0,i.jsxs)(n.admonition,{type:"tip",children:[(0,i.jsxs)(n.p,{children:["Unless there is a solid use-case not to, always initiate a single ",(0,i.jsx)(n.code,{children:"PUSH_AND_PULL"})," replication rather than identical separate ",(0,i.jsx)(n.code,{children:"PUSH"})," and ",(0,i.jsx)(n.code,{children:"PULL"})," replications."]}),(0,i.jsxs)(n.p,{children:["This prevents the replications generating the same checkpoint ",(0,i.jsx)(n.code,{children:"docID"})," resulting in multiple conflicts."]})]}),"\n",(0,i.jsx)(n.h3,{id:"retry-configuration",children:"Retry Configuration"}),"\n",(0,i.jsx)(n.p,{children:"Couchbase Lite for Ionic's replication retry logic assures a resilient connection."}),"\n",(0,i.jsx)(n.p,{children:"Couchbase Lite for Swift\u2019s replication retry logic assures a resilient connection."}),"\n",(0,i.jsx)(n.p,{children:"In the event it detects a transient error, the replicator will attempt to reconnect, stopping only when the connection is re-established, or the number of retries exceeds the retry limit (9 times for a single-shot replication and unlimited for a continuous replication)."}),"\n",(0,i.jsx)(n.p,{children:"On each retry the interval between attempts is increased exponentially (exponential backoff) up to the maximum wait time limit (5 minutes)."}),"\n",(0,i.jsxs)(n.p,{children:["The REST API provides configurable control over this replication retry logic using a set of configiurable properties\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#table-1-replication-retry-configuration-properties",children:"Table 1"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"table-1-replication-retry-configuration-properties",children:"Table 1. Replication Retry Configuration Properties"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:"Property"}),(0,i.jsx)(n.th,{children:"Use cases"}),(0,i.jsx)(n.th,{children:"Description"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"heartbeat()"})}),(0,i.jsxs)(n.td,{children:["Reduce to detect connection errors sooner ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Align to load-balancer or proxy ",(0,i.jsx)(n.code,{children:"keep-alive"})," interval - see Sync Gateway\u2019s topic ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/load-balancer.html#websocket-connection",children:"Load Balancer - Keep Alive"})]}),(0,i.jsxs)(n.td,{children:["The interval (in seconds) between the heartbeat pulses. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Default: The replicator pings the Sync Gateway every 300 seconds."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"maxAttempts()"})}),(0,i.jsx)(n.td,{children:"Change this to limit or extend the number of retry attempts."}),(0,i.jsxs)(n.td,{children:["The maximum number of retry attempts. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Set to zero (0) to use default values. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Set to one (1) to prevent any retry attempt. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," The retry attempt count is reset when the replicator is able to connect and replicate. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Default values are: ",(0,i.jsx)("br",{})," - Single-shot replication = 9 ",(0,i.jsx)("br",{})," - Continuous replication = maximum integer value. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Negative values generate a Couchbase exception ",(0,i.jsx)(n.code,{children:"InvalidArgumentException"}),"."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"maxAttemptWaitTime()"})}),(0,i.jsx)(n.td,{children:"Change this to adjust the interval between retries."}),(0,i.jsxs)(n.td,{children:["The maximum interval between retry attempts. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," While you can configure the maximum permitted wait time, the replicator\u2019s exponential backoff algorithm calculates each individual interval which is not configurable. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Default value: 300 seconds (5 minutes). ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," Zero sets the maximum interval between retries to the default of 300 seconds. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," 300 sets the maximum interval between retries to the default of 300 seconds. ",(0,i.jsx)("br",{})," ",(0,i.jsx)("br",{})," A negative value generates a Couchbase exception, ",(0,i.jsx)(n.code,{children:"InvalidArgumentException"}),"."]})]})]})]}),"\n",(0,i.jsxs)(n.p,{children:["When necessary you can adjust any or all of those configurable values\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-5-configuring-replication-retries",children:"Example 5"})," for how to do this."]}),"\n",(0,i.jsx)(n.h4,{id:"example-5-configuring-replication-retries",children:"Example 5. Configuring Replication Retries"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Create the target endpoint\nconst target = new URLEndpoint('ws://foo.couchbase.com/db');\n\n// Create the replicator configuration\nconst config = new ReplicatorConfiguration(target);\n\n// Add the collection to the replicator configuration\nconfig.addCollection(collection);\n\n// Set the replicator type\nconfig.setReplicatorType(ReplicatorType.PUSH_AND_PULL);\n\n// Set continuous replication\nconfig.setContinuous(true);\n\n// Set heartbeat interval (in seconds)\nconfig.setHeartbeat(150);\n\n// Set the maximum number of retry attempts\nconfig.setMaxAttempts(20);\n\n// Set the maximum wait time between retry attempts (in seconds)\nconfig.setMaxAttemptWaitTime(600);\n\n// Create the replicator with the configuration\nconst replicator = await Replicator.create(config);\n"})}),"\n",(0,i.jsxs)(n.ol,{children:["\n",(0,i.jsxs)(n.li,{children:["Here we use ",(0,i.jsx)(n.code,{children:"setHeartbeat()"})," to set the required interval (in seconds) between the heartbeat pulses"]}),"\n",(0,i.jsxs)(n.li,{children:["Here we use ",(0,i.jsx)(n.code,{children:"setMaxAttempts()"})," to set the required number of retry attempts"]}),"\n",(0,i.jsxs)(n.li,{children:["Here we use ",(0,i.jsx)(n.code,{children:"setMaxAttemptWaitTime()"})," to set the required interval between retry attempts."]}),"\n"]}),"\n",(0,i.jsx)(n.h3,{id:"user-authorization",children:"User Authorization"}),"\n",(0,i.jsx)(n.p,{children:"By default, Sync Gateway does not enable user authorization. This makes it easier to get up and running with synchronization."}),"\n",(0,i.jsxs)(n.p,{children:["You can enable authorization in the sync gateway configuration file, as shown in ",(0,i.jsx)(n.a,{href:"#example-6-enable-authorization",children:"Example 6"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-6-enable-authorization",children:"Example 6. Enable Authorization"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-json",children:'{\n "databases": {\n "mydatabase": {\n "users": {\n "GUEST": {"disabled": true}\n }\n }\n }\n}\n'})}),"\n",(0,i.jsxs)(n.p,{children:["To authorize with Sync Gateway, an associated user must first be created. Sync Gateway users can be created through the ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/rest-api-admin.html#/user/post__db___user_",children:(0,i.jsx)(n.code,{children:"POST /{tkn-db}/_user"})})," endpoint on the Admin REST API."]}),"\n",(0,i.jsx)(n.h3,{id:"server-authentication",children:"Server Authentication"}),"\n",(0,i.jsx)(n.p,{children:"Define the credentials your app (the client) is expecting to receive from the Sync Gateway (the server) in order to ensure it is prepared to continue with the sync."}),"\n",(0,i.jsxs)(n.p,{children:["Note that the client cannot authenticate the server if TLS is turned off. When TLS is enabled (Sync Gateway\u2019s default) the client ",(0,i.jsx)(n.em,{children:"must"})," authenticate the server. If the server cannot provide acceptable credentials then the connection will fail."]}),"\n",(0,i.jsxs)(n.p,{children:["Use ",(0,i.jsx)(n.code,{children:"ReplicatorConfiguration"})," property ",(0,i.jsx)(n.code,{children:"acceptOnlySelfSignedServerCertificate"})," to tell the replicator how to verify server-supplied TLS server certificates."]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"acceptOnlySelfSignedServerCertificate"})," is ",(0,i.jsx)(n.code,{children:"true"})," then any self-signed certificate is accepted. Certificates that are not self signed are rejected, no matter who signed them."]}),"\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"acceptOnlySelfSignedServerCertificate"})," is ",(0,i.jsx)(n.code,{children:"false"})," (default), the client validates the server\u2019s certificates against the system CA certificates. The server must supply a chain of certificates whose root is signed by one of the certificates in the system CA bundle."]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-7-set-server-tls-security",children:"Example 7. Set Server TLS security"}),"\n",(0,i.jsx)(n.h5,{id:"ca-cert",children:"CA Cert"}),"\n",(0,i.jsx)(n.p,{children:"Set the client to expect and accept only CA attested certificates."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Configure Server Security -- only accept CA Certs\nconfig.acceptOnlySelfSignedServerCertificate = false\n"})}),"\n",(0,i.jsx)(n.p,{children:"This is the default. Only certificate chains with roots signed by a trusted CA are allowed. Self signed certificates are not allowed."}),"\n",(0,i.jsx)(n.h5,{id:"self-signed-cert",children:"Self Signed Cert"}),"\n",(0,i.jsx)(n.p,{children:"Set the client to expect and accept only self-signed certificates"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Configure Server Security -- only accept self-signed certs\nconfig.acceptOnlySelfSignedServerCertificate = true; \n"})}),"\n",(0,i.jsx)(n.p,{children:"Set this to true to accept any self signed cert. Any certificates that are not self-signed are rejected."}),"\n",(0,i.jsx)(n.p,{children:"This all assumes that you have configured the Sync Gateway to provide the appropriate SSL certificates, and have included the appropriate certificate in your app bundle."}),"\n",(0,i.jsx)(n.h3,{id:"client-authentication",children:"Client Authentication"}),"\n",(0,i.jsxs)(n.p,{children:["There are two ways to authenticate from a Couchbase Lite client: ",(0,i.jsx)(n.code,{children:"Basic Authentication"})," or ",(0,i.jsx)(n.code,{children:"Session Authentication"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"basic-authentication",children:"Basic Authentication"}),"\n",(0,i.jsxs)(n.p,{children:["You can provide a user name and password to the basic authenticator class method. Under the hood, the replicator will send the credentials in the first request to retrieve a ",(0,i.jsx)(n.code,{children:"SyncGatewaySession"})," cookie and use it for all subsequent requests during the replication. This is the recommended way of using basic authentication. ",(0,i.jsx)(n.a,{href:"#example-8-basic-authentication",children:"Example 8"})," shows how to initiate a one-shot replication as the user ",(0,i.jsx)(n.strong,{children:"username"})," with the password ",(0,i.jsx)(n.strong,{children:"password"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-8-basic-authentication",children:"Example 8. Basic Authentication"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:'const url = "ws://localhost:4984/mydatabase";\nconst target = new URLEndpoint(url);\nconst auth = new BasicAuthenticator("john", "pass"); \nconst config = new ReplicatorConfiguration(target);\n\n// Assuming collection is already defined and initialized\nconfig.addCollection(collectionName);\nconfig.setAuthenticator(auth);\n\nconst replicator = new Replicator(config);\nawait replicator.start();\n'})}),"\n",(0,i.jsx)(n.h4,{id:"session-authentication",children:"Session Authentication"}),"\n",(0,i.jsx)(n.p,{children:"Session authentication is another way to authenticate with Sync Gateway."}),"\n",(0,i.jsxs)(n.p,{children:["A user session must first be created through the ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/rest-api.html#/session/post__db___session",children:(0,i.jsx)(n.code,{children:"POST /{tkn-db}/_session"})})," endpoint on the Public REST API."]}),"\n",(0,i.jsx)(n.p,{children:"The HTTP response contains a session ID which can then be used to authenticate as the user it was created for."}),"\n",(0,i.jsxs)(n.p,{children:["See ",(0,i.jsx)(n.a,{href:"#example-9-session-authentication",children:"Example 9"}),", which shows how to initiate a one-shot replication with the session ID returned from the ",(0,i.jsx)(n.code,{children:"POST /{tkn-db}/_session"})," endpoint."]}),"\n",(0,i.jsx)(n.h4,{id:"example-9-session-authentication",children:"Example 9. Session Authentication"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"const url = new URL(\"ws://localhost:4984/mydatabase\");\nconst target = new URLEndpoint(url);\nconst config = new ReplicatorConfiguration(target);\nconst sessionID = 'your-session-id';\nconst cookieName = 'your-cookie-name';\n\n// Assuming collection is already defined and initialized\nconfig.addCollection(collectionName);\n\n// Here cookieName is optional, if not passed it will default to the default value\nconst sessionAuthenticator = new SessionAuthenticator(sessionID, cookieName);\nconfig.setAuthenticator(sessionAuthenticator);\n\nlet replicator = new Replicator(config);\nawait replicator.start();\n"})}),"\n",(0,i.jsx)(n.h3,{id:"custom-headers",children:"Custom Headers"}),"\n",(0,i.jsx)(n.p,{children:"Custom headers can be set on the configuration object. The replicator will then include those headers in every request."}),"\n",(0,i.jsxs)(n.p,{children:["This feature is useful in passing additional credentials, perhaps when an authentication or authorization step is being done by a proxy server (between Couchbase Lite and Sync Gateway)\u2009\u2014\u2009see ",(0,i.jsx)(n.a,{href:"#example-10-setting-custom-headers",children:"Example 10"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-10-setting-custom-headers",children:"Example 10. Setting custom headers"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:'const config = new ReplicatorConfiguration(target);\nconfig.addCollection(collection);\n\n// Setting headers\nconfig.setHeaders({ "CustomHeaderName": "Value" });\n'})}),"\n",(0,i.jsx)(n.h3,{id:"channels",children:"Channels"}),"\n",(0,i.jsx)(n.p,{children:"By default, Couchbase Lite gets all the channels to which the configured user account has access."}),"\n",(0,i.jsxs)(n.p,{children:["This behavior is suitable for most apps that rely on ",(0,i.jsx)(n.code,{children:"user authentication"})," and the ",(0,i.jsx)(n.code,{children:"sync function"})," to specify which data to pull for each user."]}),"\n",(0,i.jsx)(n.p,{children:"Optionally, it\u2019s also possible to specify a string array of channel names on Couchbase Lite\u2019s replicator configuration object by passing in a CollectionConfiguration object when adding in a collection."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"const config = new ReplicatorConfiguration(target);\nconst collectionConfig = new CollectionConfiguration();\ncollectionConfig.setChannels(['channel1', 'channel2']);\nconfig.addCollection(collection, collectionConfig);\n"})}),"\n",(0,i.jsx)(n.p,{children:"In this case, the replication from Sync Gateway will only pull documents tagged with those channels."}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsx)(n.p,{children:"Push replicator will ignore this filter."})}),"\n",(0,i.jsx)(n.h3,{id:"documents",children:"Documents"}),"\n",(0,i.jsxs)(n.p,{children:["By default, Couchbase Lite will replicate all documents that belong to the channels specified in the collection configuration. However, you can override this behavior by ",(0,i.jsx)(n.code,{children:"filtering"})," the documents using the ",(0,i.jsx)(n.code,{children:"CollectionConfiguration"})," object."]}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"const config = new ReplicatorConfiguration(target);\nconst collectionConfig = new CollectionConfiguration();\ncollectionConfig.setDocumentIDs(['doc1', 'doc2', 'doc3']);\nconfig.addCollection(collection, collectionConfig);\n"})}),"\n",(0,i.jsx)(n.h3,{id:"auto-purge-on-channel-access-revocation",children:"Auto-purge on Channel Access Revocation"}),"\n",(0,i.jsx)(n.admonition,{type:"caution",children:(0,i.jsx)(n.p,{children:"This is a Breaking Change at 3.0"})}),"\n",(0,i.jsx)(n.h4,{id:"new-outcome",children:"New outcome"}),"\n",(0,i.jsx)(n.p,{children:"By default, when a user loses access to a channel all documents in the channel (that do not also belong to any of the user\u2019s other channels) are auto-purged from the local database (in devices belonging to the user)."}),"\n",(0,i.jsx)(n.h4,{id:"prior-outcome",children:"Prior outcome"}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.em,{children:"Previously these documents remained in the local database"})}),"\n",(0,i.jsx)(n.p,{children:"Prior to this release, CBL auto-purged only in the case when the user loses access to a document by removing the doc from all of the channels belong to the user. Now, in addition to 2.x auto purge, Couchbase Lite will also auto-purges the docs when the user loses access to the doc via channel access revocation. This feature is enabled by default, but an opt-out is available."}),"\n",(0,i.jsx)(n.h4,{id:"behaviour",children:"Behaviour"}),"\n",(0,i.jsx)(n.p,{children:"Users may lose access to channels in a number of ways:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"User loses direct access to channel"}),"\n",(0,i.jsx)(n.li,{children:"User is removed from a role"}),"\n",(0,i.jsx)(n.li,{children:"A channel is removed from a role the user is assigned to"}),"\n"]}),"\n",(0,i.jsxs)(n.p,{children:["By default, when a user loses access to a channel, the next Couchbase Lite Pull replication auto-purges all documents in the channel from local Couchbase Lite databases (on devices belonging to the user) unless they belong to any of the user\u2019s other channels\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#table-2-behavior-following-access-revocation",children:"Table 2"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Documents that exist in multiple channels belonging to the user (even if they are not actively replicating that channel) are not auto-purged unless the user loses access to all channels."}),"\n",(0,i.jsxs)(n.p,{children:["Users will be receive an ",(0,i.jsx)(n.code,{children:"AccessRemoved"})," notification from the DocumentListener if they lose document access due to channel access revocation; this is sent regardless of the current auto-purge setting."]}),"\n",(0,i.jsx)(n.h4,{id:"table-2-behavior-following-access-revocation",children:"Table 2. Behavior following access revocation"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{}),(0,i.jsx)(n.th,{children:"System State"}),(0,i.jsx)(n.th,{children:"Impact on Sync"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Replication Type"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Access Control on Sync Gateway"})}),(0,i.jsxs)(n.td,{children:[(0,i.jsx)(n.strong,{children:"Expected behavior when"})," ",(0,i.jsx)(n.em,{children:"enable_auto_purge=true"})]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Pull only"})}),(0,i.jsx)(n.td,{children:"User revoked access to channel."}),(0,i.jsx)(n.td,{children:"Previously synced documents are auto purged on local"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(revokedChannel)"})]}),(0,i.jsx)(n.td,{})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push only"})}),(0,i.jsx)(n.td,{children:"User revoked access to channel."}),(0,i.jsx)(n.td,{children:"No impact of auto-purge"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(revokedChannel)"})]}),(0,i.jsx)(n.td,{children:"Documents get pushed but are rejected by Sync Gateway"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push-pull"})}),(0,i.jsx)(n.td,{children:"User revoked access to channel"}),(0,i.jsx)(n.td,{children:"Previously synced documents are auto purged on Couchbase Lite."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(revokedChannel)"})]}),(0,i.jsx)(n.td,{children:"Local changes continue to be pushed to remote but are rejected by Sync Gateway"})]})]})]}),"\n",(0,i.jsxs)(n.p,{children:["If a user subsequently regains access to a lost channel, then any previously auto-purged documents still assigned to any of their channels are automatically pulled down by the active Sync Gateway when they are next updated\u2009\u2014\u2009see behavior summary in ",(0,i.jsx)(n.a,{href:"#table-3-behavior-if-access-is-regained",children:"Table 3"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"table-3-behavior-if-access-is-regained",children:"Table 3. Behavior if access is regained"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{}),(0,i.jsx)(n.th,{children:"System State"}),(0,i.jsx)(n.th,{children:"Impact on Sync"})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Replication Type"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Access Control on Sync Gateway"})}),(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Expected behavior when enable_auto_purge=true"})})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Pull only"})}),(0,i.jsx)(n.td,{children:"User REASSIGNED access to channel"}),(0,i.jsx)(n.td,{children:"Previously purged documents that are still in the channel are automatically pulled by Couchbase Lite when they are next updated"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push only"})}),(0,i.jsx)(n.td,{children:"User REASSIGNED access to channel"}),(0,i.jsx)(n.td,{children:"Local changes previously rejected by Sync Gateway will not be automatically pushed to remote unless resetCheckpoint is involved on CBL. Document changes subsequent to the channel reassignment will be pushed up as usual."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(reassignedChannel)"})," No impact of auto-purge"]}),(0,i.jsx)(n.td,{})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.strong,{children:"Push-pull"})}),(0,i.jsx)(n.td,{children:"User REASSIGNED access to channel"}),(0,i.jsx)(n.td,{children:"Previously purged documents are automatically pulled by Couchbase Lite"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{}),(0,i.jsxs)(n.td,{children:["Sync Function includes ",(0,i.jsx)(n.code,{children:"requireAccess(reassignedChannel)"})]}),(0,i.jsx)(n.td,{children:"Local changes previously rejected by Sync Gateway will not be automatically pushed to remote unless resetCheckpoint is involved. Document changes subsequent to the channel reassignment will be pushed up as usual."})]})]})]}),"\n",(0,i.jsx)(n.h4,{id:"config",children:"Config"}),"\n",(0,i.jsxs)(n.p,{children:["Auto-purge behavior is controlled primarily by the ReplicationConfiguration option ",(0,i.jsx)(n.code,{children:"enableAutoPurge"}),". Changing the state of this will impact only future replications; the replicator will not attempt to sync revisions that were auto purged on channel access removal. Clients wishing to sync previously removed documents must use the resetCheckpoint API to resync from the start."]}),"\n",(0,i.jsx)(n.h4,{id:"example-11-setting-auto-purge",children:"Example 11. Setting auto-purge"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// set auto-purge behavior (here we override default)\nconfig.enableAutoPurge = false;\n"})}),"\n",(0,i.jsx)(n.p,{children:"Here we have opted to turn off the auto purge behavior. By default auto purge is enabled."}),"\n",(0,i.jsx)(n.h3,{id:"delta-sync",children:"Delta Sync"}),"\n",(0,i.jsx)(n.admonition,{type:"important",children:(0,i.jsxs)(n.p,{children:["This is an ",(0,i.jsx)(n.a,{href:"https://www.couchbase.com/products/editions/",children:"Enterprise Edition"})," feature."]})}),"\n",(0,i.jsx)(n.p,{children:"With Delta Sync, only the changed parts of a Couchbase document are replicated. This can result in significant savings in bandwidth consumption as well as throughput improvements, especially when network bandwidth is typically constrained."}),"\n",(0,i.jsxs)(n.p,{children:["Replications to a Server (for example, a Sync Gateway, or passive listener) automatically use delta sync if the property is enabled at database level by the server\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/configuration-properties-legacy.html#databases-foo_db-delta_sync",children:"databases.$db.delta_sync.enabled"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"Intra-Device replications automatically disable delta sync, whilst Peer-to-Peer replications automatically enable delta sync."}),"\n",(0,i.jsx)(n.h2,{id:"initialize",children:"Initialize"}),"\n",(0,i.jsx)(n.h5,{id:"in-this-section-1",children:"In this section"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#start-replicator",children:"Start Replicator"})," | ",(0,i.jsx)(n.a,{href:"#checkpoint-starts",children:"Checkpoint Starts"})]}),"\n",(0,i.jsx)(n.h3,{id:"start-replicator",children:"Start Replicator"}),"\n",(0,i.jsxs)(n.p,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator.create()"})," method to initialize the replicator with the configuration you have defined. You can optionally add a change listener (see ",(0,i.jsx)(n.a,{href:"#monitor",children:"Monitor"}),") before starting the replicator using ",(0,i.jsx)(n.code,{children:"start()"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-12-initialize-and-run-replicator",children:"Example 12. Initialize and run replicator"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Apply configuration settings to the replicator\nconst replicator = await Replicator.create(config);\n\n// start the replicator without making a new checkpoint\nawait replicator.start(false);\n"})}),"\n",(0,i.jsx)(n.h3,{id:"checkpoint-starts",children:"Checkpoint Starts"}),"\n",(0,i.jsxs)(n.p,{children:["Replicators use ",(0,i.jsx)(n.code,{children:"checkpoints"})," to keep track of documents sent to the target database."]}),"\n",(0,i.jsxs)(n.p,{children:["Without ",(0,i.jsx)(n.code,{children:"checkpoints"}),", Couchbase Lite would replicate the entire database content to the target database on each connection, even though previous replications may already have replicated some or all of that content."]}),"\n",(0,i.jsxs)(n.p,{children:["This functionality is generally not a concern to application developers. However, if you do want to force the replication to start again from zero, use the ",(0,i.jsx)(n.code,{children:"checkpoint"})," reset argument when starting the replicator\u2009\u2014\u2009as shown in Example 13."]}),"\n",(0,i.jsx)(n.h4,{id:"example-13-resetting-checkpoints",children:"Example 13. Resetting checkpoints"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"if (doResetCheckpointRequired) {\n this.replicator.start(true);\n} else {\n this.replicator.start(false);\n}\n"})}),"\n",(0,i.jsxs)(n.p,{children:["The default ",(0,i.jsx)(n.code,{children:"false"})," is shown here for completeness only; it is unlikely you would explicitly use it in practice."]}),"\n",(0,i.jsx)(n.h2,{id:"monitor",children:"Monitor"}),"\n",(0,i.jsx)(n.h5,{id:"in-this-section-2",children:"In this section"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#change-listeners",children:"Change Listeners"})," | ",(0,i.jsx)(n.a,{href:"#replicator-status",children:"Replicator Status"})," | ",(0,i.jsx)(n.a,{href:"#monitor-document-changes",children:"Monitor Document Changes"})," | ",(0,i.jsx)(n.a,{href:"#documents-pending-push",children:"Documents Pending Push"})]}),"\n",(0,i.jsxs)(n.p,{children:["You can monitor a replication\u2019s status by using a combination of ",(0,i.jsx)(n.code,{children:"Change Listeners"})," and the ",(0,i.jsx)(n.code,{children:"Replicator.getStatus()"})," property. This enables you to know, for example, when the replication is actively transferring data and when it has stopped."]}),"\n",(0,i.jsxs)(n.p,{children:["You can also choose to monitor document changes\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#monitor-document-changes",children:"Monitor Document Changes"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"change-listeners",children:"Change Listeners"}),"\n",(0,i.jsx)(n.p,{children:"Use this to monitor changes and to inform on sync progress; this is an optional step. You can add and a replicator change listener at any point; it will report changes from the point it is registered."}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsx)(n.p,{children:"Don\u2019t forget to save the token so you can remove the listener later"})}),"\n",(0,i.jsxs)(n.p,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator"})," class to add a change listener as a callback to the Replicator (",(0,i.jsx)(n.code,{children:"addChangeListener()"}),")\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-14-monitor-replication",children:"Example 14"}),". You will then be asynchronously notified of state changes."]}),"\n",(0,i.jsxs)(n.p,{children:["You can remove a change listener with ",(0,i.jsx)(n.code,{children:"removeChangeListener(token)"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"replicator-status",children:"Replicator Status"}),"\n",(0,i.jsxs)(n.p,{children:["You can use the ",(0,i.jsx)(n.code,{children:"Replicator.getStatus()"})," property to check the replicator status. That is, whether it is actively transferring data or if it has stopped\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-14-monitor-replication",children:"Example 14"}),"."]}),"\n",(0,i.jsxs)(n.p,{children:["The returned ",(0,i.jsx)(n.em,{children:"ReplicationStatus"})," structure comprises:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"ActivityLevel"}),"\u2009\u2014\u2009stopped, offline, connecting, idle or busy\u2009\u2014\u2009see states described in: ",(0,i.jsx)(n.a,{href:"#table-5-replicator-activity-levels",children:"Table 5"})]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.code,{children:"Progress"})}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsx)(n.li,{children:"completed\u2009\u2014\u2009the total number of changes completed"}),"\n",(0,i.jsx)(n.li,{children:"total\u2009\u2014\u2009the total number of changes to be processed"}),"\n"]}),"\n"]}),"\n",(0,i.jsxs)(n.li,{children:["\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"Error"})," - the current error, if any."]}),"\n"]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-14-monitor-replication",children:"Example 14. Monitor replication"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Optionally add a change listener\n// Retain token for use in deletion\nconst token = replicator.addChangeListener((change) => {\n if (change.status.getActivityLevel() === 'STOPPED') {\n console.log(\"Replication stopped\");\n } else {\n console.log(`Replicator is currently : ${change.status.getActivityLevel()}`);\n }\n});\n"})}),"\n",(0,i.jsx)(n.h3,{id:"replication-states",children:"Replication States"}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.a,{href:"#table-5-replicator-activity-levels",children:"Table 5"})," shows the different states, or activity levels, reported in the API; and the meaning of each."]}),"\n",(0,i.jsx)(n.h4,{id:"table-5-replicator-activity-levels",children:"Table 5. Replicator activity levels"}),"\n",(0,i.jsxs)(n.table,{children:[(0,i.jsx)(n.thead,{children:(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.th,{children:(0,i.jsx)(n.strong,{children:"State"})}),(0,i.jsx)(n.th,{children:(0,i.jsx)(n.strong,{children:"Meaning"})})]})}),(0,i.jsxs)(n.tbody,{children:[(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"STOPPED"})}),(0,i.jsx)(n.td,{children:"The replication is finished or hit a fatal error."})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"OFFLINE"})}),(0,i.jsx)(n.td,{children:"The replicator is offline as the remote host is unreachable"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"CONNECTING"})}),(0,i.jsx)(n.td,{children:"The replicator is connecting to the remote host"})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"IDLE"})}),(0,i.jsxs)(n.td,{children:["The replication caught up with all the changes available from the server. The ",(0,i.jsx)(n.code,{children:"IDLE"})," state is only used in continuous replications."]})]}),(0,i.jsxs)(n.tr,{children:[(0,i.jsx)(n.td,{children:(0,i.jsx)(n.code,{children:"BUSY"})}),(0,i.jsx)(n.td,{children:"The replication is actively transferring data."})]})]})]}),"\n",(0,i.jsx)(n.h3,{id:"replication-status-and-app-life-cycle",children:"Replication Status and App Life Cycle"}),"\n",(0,i.jsx)(n.p,{children:"The following diagram describes the status changes when the application starts a replication, and when the application is being backgrounded or foregrounded by the OS. It applies to iOS only."}),"\n",(0,i.jsx)(n.p,{children:(0,i.jsx)(n.img,{alt:"Replicator States",src:t(4697).A+"",width:"1934",height:"914"})}),"\n",(0,i.jsxs)(n.p,{children:["Additionally, on iOS, an app already in the background may be terminated. In this case, the ",(0,i.jsx)(n.code,{children:"Database"})," and ",(0,i.jsx)(n.code,{children:"Replicator"})," instances will be ",(0,i.jsx)(n.code,{children:"null"})," when the app returns to the foreground. Therefore, as preventive measure, it is recommended to do a ",(0,i.jsx)(n.code,{children:"null"})," check when the app enters the foreground, and to re-initialize the database and replicator if any of those is ",(0,i.jsx)(n.code,{children:"null"}),"."]}),"\n",(0,i.jsx)(n.p,{children:"On other platforms, Couchbase Lite doesn\u2019t react to OS backgrounding or foregrounding events and replication(s) will continue running as long as the remote system does not terminate the connection and the app does not terminate. It is generally recommended to stop replications before going into the background otherwise socket connections may be closed by the OS and this may interfere with the replication process."}),"\n",(0,i.jsx)(n.h3,{id:"monitor-document-changes",children:"Monitor Document Changes"}),"\n",(0,i.jsx)(n.p,{children:"You can choose to register for document updates during a replication."}),"\n",(0,i.jsxs)(n.p,{children:["For example, the code snippet in ",(0,i.jsx)(n.a,{href:"#example-15-register-a-document-listener",children:"Example 15"})," registers a listener to monitor document replication performed by the replicator referenced by the variable ",(0,i.jsx)(n.code,{children:"replicator"}),". It prints the document ID of each document received and sent. Stop the listener as shown in ",(0,i.jsx)(n.a,{href:"#example-16-stop-document-listener",children:"Example 16"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-15-register-a-document-listener",children:"Example 15. Register a document listener"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:'const token = await replicator.addDocumentChangeListener((replication) => {\n console.log(`Replication type :: ${replication.isPush ? "Push" : "Pull"}`);\n for (const document of replication.documents) {\n if (document.error === undefined) {\n console.log(`Doc ID :: ${document.id}`);\n if (document.flags.includes(\'DELETED\')) {\n console.log("Successfully replicated a deleted document");\n }\n } else {\n console.error("Error replicating document:", document.error);\n }\n }\n});\n\n// Start the replicator without resetting the checkpoint\nawait replicator.start(false);\n'})}),"\n",(0,i.jsx)(n.h4,{id:"example-16-stop-document-listener",children:"Example 16. Stop document listener"}),"\n",(0,i.jsx)(n.p,{children:"This code snippet shows how to stop the document listener using the token from the previous example."}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"await this.replicator.removeChangeListener(token);\n"})}),"\n",(0,i.jsx)(n.h3,{id:"document-access-removal-behavior",children:"Document Access Removal Behavior"}),"\n",(0,i.jsxs)(n.p,{children:["When access to a document is removed on Sync Gateway (see: Sync Gateway\u2019s ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/sync-function-api.html",children:"Sync Function"}),"), the document replication listener sends a notification with the ",(0,i.jsx)(n.code,{children:"AccessRemoved"})," flag set to ",(0,i.jsx)(n.code,{children:"true"})," and subsequently purges the document from the database."]}),"\n",(0,i.jsx)(n.h2,{id:"documents-pending-push",children:"Documents Pending Push"}),"\n",(0,i.jsx)(n.admonition,{type:"tip",children:(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.code,{children:"Replicator.isDocumentPending()"})," is quicker and more efficient. Use it in preference to returning a list of pending document IDs, where possible."]})}),"\n",(0,i.jsx)(n.p,{children:"You can check whether documents are waiting to be pushed in any forthcoming sync by using either of the following API methods:"}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator.pendingDocumentIds()"})," method, which returns a list of document IDs that have local changes, but which have not yet been pushed to the server."]}),"\n",(0,i.jsxs)(n.li,{children:["Use the ",(0,i.jsx)(n.code,{children:"Replicator.isDocumentPending()"})," method to quickly check whether an individual document is pending a push."]}),"\n"]}),"\n",(0,i.jsx)(n.h4,{id:"example-17-use-pending-document-id-api",children:"Example 17. Use Pending Document ID API"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Todo \n"})}),"\n",(0,i.jsx)(n.h2,{id:"stop",children:"Stop"}),"\n",(0,i.jsxs)(n.p,{children:["Stopping a replication is straightforward. It is done using ",(0,i.jsx)(n.code,{children:"stop()"}),". This initiates an asynchronous operation and so is not necessarily immediate. Your app should account for this potential delay before attempting any subsequent operations."]}),"\n",(0,i.jsxs)(n.p,{children:["You can find further information on database operations in ",(0,i.jsx)(n.a,{href:"/databases",children:"Databases"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-18-stop-replicator",children:"Example 18. Stop replicator"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Remove the change listener\nawait this.replicator.removeChangeListener(token)\n\n// Stop the replicator\nawait this.replicator.stop()\n"})}),"\n",(0,i.jsxs)(n.p,{children:["Here we initiate the stopping of the replication using the ",(0,i.jsx)(n.code,{children:"stop()"})," method. It will stop any active ",(0,i.jsx)(n.code,{children:"change listener"})," once the replication is stopped."]}),"\n",(0,i.jsx)(n.h2,{id:"error-handling",children:"Error Handling"}),"\n",(0,i.jsxs)(n.p,{children:["When ",(0,i.jsx)(n.em,{children:"replicator"})," detects a network error it updates its status depending on the error type (permanent or temporary) and returns an appropriate HTTP error code."]}),"\n",(0,i.jsxs)(n.p,{children:["The following code snippet adds a ",(0,i.jsx)(n.code,{children:"Change Listener"}),", which monitors a replication for errors and logs the the returned error code."]}),"\n",(0,i.jsx)(n.h4,{id:"example-19-monitoring-for-network-errors",children:"Example 19. Monitoring for network errors"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"replicator.addChangeListener((change) => {\n const error = change.status.getError();\n if (error) {\n console.log(`Error code :: ${error.code}`);\n }\n});\n"})}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"For permanent network errors"})," (for example, ",(0,i.jsx)(n.code,{children:"404"})," not found, or ",(0,i.jsx)(n.code,{children:"401"})," unauthorized): ",(0,i.jsx)(n.em,{children:"Replicator"})," will stop permanently, whether ",(0,i.jsx)(n.code,{children:"setContinuous"})," is ",(0,i.jsx)(n.em,{children:"true"})," or ",(0,i.jsx)(n.em,{children:"false"}),". Of course, it sets its status to ",(0,i.jsx)(n.code,{children:"STOPPED"})]}),"\n",(0,i.jsxs)(n.p,{children:[(0,i.jsx)(n.strong,{children:"For recoverable or temporary errors:"})," ",(0,i.jsx)(n.em,{children:"Replicator"})," sets its status to ",(0,i.jsx)(n.code,{children:"OFFLINE"}),", then:"]}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"setContinuous=true"})," it retries the connection indefinitely"]}),"\n",(0,i.jsxs)(n.li,{children:["If ",(0,i.jsx)(n.code,{children:"setContinuous=false"})," (one-shot) it retries the connection a limited number of times."]}),"\n"]}),"\n",(0,i.jsx)(n.p,{children:"The following error codes are considered temporary by the Couchbase Lite replicator and thus will trigger a connection retry."}),"\n",(0,i.jsxs)(n.ul,{children:["\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"408"}),": Request Timeout"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"429"}),": Too Many Requests"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"500"}),": Internal Server Error"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"502"}),": Bad Gateway"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"503"}),": Service Unavailable"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"504"}),": Gateway Timeout"]}),"\n",(0,i.jsxs)(n.li,{children:[(0,i.jsx)(n.code,{children:"1001"}),": DNS resolution error"]}),"\n"]}),"\n",(0,i.jsx)(n.h2,{id:"load-balancers",children:"Load Balancers"}),"\n",(0,i.jsxs)(n.p,{children:["Couchbase Lite uses WebSockets as the communication protocol to transmit data. Some load balancers are not configured for WebSocket connections by default (NGINX for example); so it might be necessary to explicitly enable them in the load balancer\u2019s configuration (see ",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/load-balancer.html",children:"Load Balancers"}),")."]}),"\n",(0,i.jsxs)(n.p,{children:["By default, the WebSocket protocol uses compression to optimize for speed and bandwidth utilization. The level of compression is set on Sync Gateway and can be tuned in the configuration file (",(0,i.jsx)(n.a,{href:"https://docs.couchbase.com/sync-gateway/current/configuration-properties-legacy.html#replicator_compression",children:"replicator_compression"}),")."]}),"\n",(0,i.jsx)(n.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,i.jsx)(n.h3,{id:"logs",children:"Logs"}),"\n",(0,i.jsxs)(n.p,{children:["As always, when there is a problem with replication, logging is your friend. You can increase the log output for activity related to replication with Sync Gateway\u2009\u2014\u2009see ",(0,i.jsx)(n.a,{href:"#example-21-set-logging-verbosity",children:"Example 21"}),"."]}),"\n",(0,i.jsx)(n.h4,{id:"example-21-set-logging-verbosity",children:"Example 21. Set logging verbosity"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-typescript",children:"// Verbose / Replicator\ndatabase.setLogLevel(LogDomain.REPLICATOR, Loglevel.VERBOSE);\n\n// Verbose / Network\ndatabase.setLogLevel(LogDomain.NETWORK, Loglevel.VERBOSE);\n"})}),"\n",(0,i.jsxs)(n.p,{children:["For more on troubleshooting with logs, see: ",(0,i.jsx)(n.a,{href:"/Troubleshooting/using-logs",children:"Using Logs"}),"."]}),"\n",(0,i.jsx)(n.h3,{id:"authentication-errors",children:"Authentication Errors"}),"\n",(0,i.jsxs)(n.p,{children:["If Sync Gateway is configured with a self signed certificate but your app points to a ",(0,i.jsx)(n.code,{children:"ws"})," scheme instead of ",(0,i.jsx)(n.code,{children:"wss"})," you will encounter an error with status code 11006\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-22-protocol-mismatch",children:"Example 22"})]}),"\n",(0,i.jsx)(n.h4,{id:"example-22-protocol-mismatch",children:"Example 22. Protocol Mismatch"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-console",children:'CouchbaseLite Replicator ERROR: {Repl#2} Got LiteCore error: WebSocket error 1006 "connection closed abnormally"\n'})}),"\n",(0,i.jsxs)(n.p,{children:["If Sync Gateway is configured with a self signed certificate, and your app points to a ",(0,i.jsx)(n.code,{children:"wss"})," scheme but the replicator configuration isn\u2019t using the certificate you will encounter an error with status code ",(0,i.jsx)(n.code,{children:"5011"}),"\u2009\u2014\u2009see: ",(0,i.jsx)(n.a,{href:"#example-23-certificate-mismatch-or-not-found",children:"Example 23"})]}),"\n",(0,i.jsx)(n.h4,{id:"example-23-certificate-mismatch-or-not-found",children:"Example 23. Certificate Mismatch or Not Found"}),"\n",(0,i.jsx)(n.pre,{children:(0,i.jsx)(n.code,{className:"language-console",children:'CouchbaseLite Replicator ERROR: {Repl#2} Got LiteCore error: Network error 11 "server TLS certificate is self-signed or has unknown root cert"\n'})})]})}function h(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,i.jsx)(n,{...e,children:(0,i.jsx)(d,{...e})}):d(e)}},7943:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-1-0d0f78f1482260e9798165e302a25a79.png"},7308:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-2-0fe3c8d39a7e607ffc343de2337b5dbe.png"},6725:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-3-f7f78a091e8ffbd50c5bdbf57b70085b.png"},2058:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/cbl-replication-scopes-collections-4-1b030ec159be9f916e2efc746bea9ca1.png"},4697:(e,n,t)=>{t.d(n,{A:()=>i});const i=t.p+"assets/images/replicator-states-131cd1d916435293b2ad2c7049ceb4d9.png"},8453:(e,n,t)=>{t.d(n,{R:()=>a,x:()=>c});var i=t(6540);const s={},o=i.createContext(s);function a(e){const n=i.useContext(o);return i.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function c(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(s):e.components||s:a(e.components),i.createElement(o.Provider,{value:n},e.children)}}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.5f78187e.js b/assets/js/runtime~main.5f78187e.js new file mode 100644 index 0000000..d9f39e5 --- /dev/null +++ b/assets/js/runtime~main.5f78187e.js @@ -0,0 +1 @@ +(()=>{"use strict";var e,a,c,f,t,r={},d={};function b(e){var a=d[e];if(void 0!==a)return a.exports;var c=d[e]={id:e,loaded:!1,exports:{}};return r[e].call(c.exports,c,c.exports,b),c.loaded=!0,c.exports}b.m=r,b.c=d,e=[],b.O=(a,c,f,t)=>{if(!c){var r=1/0;for(i=0;i=t)&&Object.keys(b.O).every((e=>b.O[e](c[o])))?c.splice(o--,1):(d=!1,t0&&e[i-1][2]>t;i--)e[i]=e[i-1];e[i]=[c,f,t]},b.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return b.d(a,{a:a}),a},c=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,b.t=function(e,f){if(1&f&&(e=this(e)),8&f)return e;if("object"==typeof e&&e){if(4&f&&e.__esModule)return e;if(16&f&&"function"==typeof e.then)return e}var t=Object.create(null);b.r(t);var r={};a=a||[null,c({}),c([]),c(c)];for(var d=2&f&&e;"object"==typeof d&&!~a.indexOf(d);d=c(d))Object.getOwnPropertyNames(d).forEach((a=>r[a]=()=>e[a]));return r.default=()=>e,b.d(t,r),t},b.d=(e,a)=>{for(var c in a)b.o(a,c)&&!b.o(e,c)&&Object.defineProperty(e,c,{enumerable:!0,get:a[c]})},b.f={},b.e=e=>Promise.all(Object.keys(b.f).reduce(((a,c)=>(b.f[c](e,a),a)),[])),b.u=e=>"assets/js/"+({434:"b7b0e5b2",473:"d211126e",477:"76082c53",644:"4c8f02e7",1150:"fa8f80ec",1233:"a8031cc3",1655:"94ea5d99",1737:"392dc68c",1970:"63aa4882",1991:"b2b675dd",2131:"fa9d6f84",2178:"80e7f649",2184:"d22a0e6a",2612:"e61c5f8c",2711:"9e4087bc",3095:"9736982f",3249:"ccc49370",3549:"295b567d",3669:"30a24c52",3823:"103c9c1b",3837:"6e975239",3976:"0e384e19",4374:"66406991",4761:"754d5016",4813:"6875c492",4866:"2391a45b",5006:"e0bfb9bc",5031:"c043769f",5438:"98e2200e",5465:"ac7c142a",5468:"85a8c145",5495:"956701b4",5815:"a7e7b2ec",5894:"b2f554cd",5967:"f9585397",6375:"3770a9bc",6969:"14eb3368",7098:"a7bd4aaa",7128:"bf09d495",7443:"964ae018",7472:"814f3328",7643:"a6aa9e1f",7890:"a04f062d",7987:"f67021ea",8209:"01a85c17",8226:"a7cb794f",8288:"9b522e88",8401:"17896441",8581:"935f2afb",8726:"3d109541",8841:"075bfa50",8898:"56c12b48",9048:"a94703ab",9168:"edca5c13",9267:"a7023ddc",9284:"fb0aa980",9314:"5e54861e",9647:"5e95c892",9710:"f20c4297",9803:"fdace129"}[e]||e)+"."+{434:"38c7252d",473:"6deec1a1",477:"d0eb019d",644:"ce9a43aa",1150:"1c4b0120",1233:"5126df2c",1655:"26b708e5",1737:"bf32b067",1970:"6ed2fc4f",1991:"9f4d8934",2131:"dec635d9",2178:"45806014",2184:"a3bfa71d",2237:"0a438d9d",2612:"ba15e9f3",2711:"e5f6aeaf",2778:"29a16532",3095:"ed707a29",3249:"bb54c49f",3549:"ef0dc828",3669:"16cf3853",3823:"5ebda1e4",3837:"17ea1466",3976:"5856df15",4374:"c8c31d22",4761:"aba45d3a",4813:"b3d0735f",4866:"7a5f9496",5006:"9249ccb5",5031:"6fe87f66",5438:"c11532a6",5465:"8c955754",5468:"fde10e8a",5495:"fad961d2",5815:"f29bd64f",5894:"7169647d",5967:"e160bfd7",6375:"ee09bfa7",6969:"cb964633",7098:"e1807782",7128:"a7a9aa84",7443:"b7d14749",7472:"fa10d80f",7643:"04a394fa",7890:"9dcf6528",7987:"b659ebc5",8209:"c45f2475",8226:"993fd822",8288:"ab22d8e5",8401:"2787dc1e",8544:"35479697",8581:"1967f302",8726:"d4382058",8841:"0eb8e783",8898:"eef61cb3",9048:"ec0ec6f5",9168:"f5beb92f",9267:"9cfb408e",9284:"3f37929f",9314:"a23b7f13",9647:"ebd0b938",9710:"6897a68e",9803:"f19cbd4e"}[e]+".js",b.miniCssF=e=>{},b.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),b.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),f={},t="cbl-ionic:",b.l=(e,a,c,r)=>{if(f[e])f[e].push(a);else{var d,o;if(void 0!==c)for(var n=document.getElementsByTagName("script"),i=0;i{d.onerror=d.onload=null,clearTimeout(s);var t=f[e];if(delete f[e],d.parentNode&&d.parentNode.removeChild(d),t&&t.forEach((e=>e(c))),a)return a(c)},s=setTimeout(u.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=u.bind(null,d.onerror),d.onload=u.bind(null,d.onload),o&&document.head.appendChild(d)}},b.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},b.p="/",b.gca=function(e){return e={17896441:"8401",66406991:"4374",b7b0e5b2:"434",d211126e:"473","76082c53":"477","4c8f02e7":"644",fa8f80ec:"1150",a8031cc3:"1233","94ea5d99":"1655","392dc68c":"1737","63aa4882":"1970",b2b675dd:"1991",fa9d6f84:"2131","80e7f649":"2178",d22a0e6a:"2184",e61c5f8c:"2612","9e4087bc":"2711","9736982f":"3095",ccc49370:"3249","295b567d":"3549","30a24c52":"3669","103c9c1b":"3823","6e975239":"3837","0e384e19":"3976","754d5016":"4761","6875c492":"4813","2391a45b":"4866",e0bfb9bc:"5006",c043769f:"5031","98e2200e":"5438",ac7c142a:"5465","85a8c145":"5468","956701b4":"5495",a7e7b2ec:"5815",b2f554cd:"5894",f9585397:"5967","3770a9bc":"6375","14eb3368":"6969",a7bd4aaa:"7098",bf09d495:"7128","964ae018":"7443","814f3328":"7472",a6aa9e1f:"7643",a04f062d:"7890",f67021ea:"7987","01a85c17":"8209",a7cb794f:"8226","9b522e88":"8288","935f2afb":"8581","3d109541":"8726","075bfa50":"8841","56c12b48":"8898",a94703ab:"9048",edca5c13:"9168",a7023ddc:"9267",fb0aa980:"9284","5e54861e":"9314","5e95c892":"9647",f20c4297:"9710",fdace129:"9803"}[e]||e,b.p+b.u(e)},(()=>{var e={5354:0,1869:0};b.f.j=(a,c)=>{var f=b.o(e,a)?e[a]:void 0;if(0!==f)if(f)c.push(f[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var t=new Promise(((c,t)=>f=e[a]=[c,t]));c.push(f[2]=t);var r=b.p+b.u(a),d=new Error;b.l(r,(c=>{if(b.o(e,a)&&(0!==(f=e[a])&&(e[a]=void 0),f)){var t=c&&("load"===c.type?"missing":c.type),r=c&&c.target&&c.target.src;d.message="Loading chunk "+a+" failed.\n("+t+": "+r+")",d.name="ChunkLoadError",d.type=t,d.request=r,f[1](d)}}),"chunk-"+a,a)}},b.O.j=a=>0===e[a];var a=(a,c)=>{var f,t,r=c[0],d=c[1],o=c[2],n=0;if(r.some((a=>0!==e[a]))){for(f in d)b.o(d,f)&&(b.m[f]=d[f]);if(o)var i=o(b)}for(a&&a(c);n{"use strict";var e,a,c,f,t,r={},b={};function d(e){var a=b[e];if(void 0!==a)return a.exports;var c=b[e]={id:e,loaded:!1,exports:{}};return r[e].call(c.exports,c,c.exports,d),c.loaded=!0,c.exports}d.m=r,d.c=b,e=[],d.O=(a,c,f,t)=>{if(!c){var r=1/0;for(i=0;i=t)&&Object.keys(d.O).every((e=>d.O[e](c[o])))?c.splice(o--,1):(b=!1,t0&&e[i-1][2]>t;i--)e[i]=e[i-1];e[i]=[c,f,t]},d.n=e=>{var a=e&&e.__esModule?()=>e.default:()=>e;return d.d(a,{a:a}),a},c=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,d.t=function(e,f){if(1&f&&(e=this(e)),8&f)return e;if("object"==typeof e&&e){if(4&f&&e.__esModule)return e;if(16&f&&"function"==typeof e.then)return e}var t=Object.create(null);d.r(t);var r={};a=a||[null,c({}),c([]),c(c)];for(var b=2&f&&e;"object"==typeof b&&!~a.indexOf(b);b=c(b))Object.getOwnPropertyNames(b).forEach((a=>r[a]=()=>e[a]));return r.default=()=>e,d.d(t,r),t},d.d=(e,a)=>{for(var c in a)d.o(a,c)&&!d.o(e,c)&&Object.defineProperty(e,c,{enumerable:!0,get:a[c]})},d.f={},d.e=e=>Promise.all(Object.keys(d.f).reduce(((a,c)=>(d.f[c](e,a),a)),[])),d.u=e=>"assets/js/"+({434:"b7b0e5b2",473:"d211126e",477:"76082c53",644:"4c8f02e7",1150:"fa8f80ec",1233:"a8031cc3",1655:"94ea5d99",1737:"392dc68c",1970:"63aa4882",1991:"b2b675dd",2131:"fa9d6f84",2178:"80e7f649",2184:"d22a0e6a",2612:"e61c5f8c",2711:"9e4087bc",3095:"9736982f",3249:"ccc49370",3549:"295b567d",3669:"30a24c52",3823:"103c9c1b",3837:"6e975239",3976:"0e384e19",4374:"66406991",4761:"754d5016",4813:"6875c492",4866:"2391a45b",5006:"e0bfb9bc",5031:"c043769f",5438:"98e2200e",5465:"ac7c142a",5468:"85a8c145",5495:"956701b4",5815:"a7e7b2ec",5894:"b2f554cd",5967:"f9585397",6375:"3770a9bc",6969:"14eb3368",7098:"a7bd4aaa",7128:"bf09d495",7443:"964ae018",7472:"814f3328",7643:"a6aa9e1f",7890:"a04f062d",7987:"f67021ea",8209:"01a85c17",8226:"a7cb794f",8288:"9b522e88",8401:"17896441",8581:"935f2afb",8726:"3d109541",8841:"075bfa50",8898:"56c12b48",9048:"a94703ab",9168:"edca5c13",9267:"a7023ddc",9284:"fb0aa980",9314:"5e54861e",9647:"5e95c892",9710:"f20c4297",9803:"fdace129"}[e]||e)+"."+{434:"38c7252d",473:"80a47ad6",477:"d0eb019d",644:"ce9a43aa",1150:"1c4b0120",1233:"5126df2c",1655:"26b708e5",1737:"bf32b067",1970:"6ed2fc4f",1991:"9f4d8934",2131:"dec635d9",2178:"45806014",2184:"4570b277",2237:"0a438d9d",2612:"ba15e9f3",2711:"e5f6aeaf",2778:"29a16532",3095:"ed707a29",3249:"bb54c49f",3549:"69cf5257",3669:"16cf3853",3823:"5ebda1e4",3837:"17ea1466",3976:"5856df15",4374:"c8c31d22",4761:"aba45d3a",4813:"b3d0735f",4866:"7a5f9496",5006:"9249ccb5",5031:"6fe87f66",5438:"82609965",5465:"8c955754",5468:"fde10e8a",5495:"fad961d2",5815:"6d0d5ce2",5894:"7169647d",5967:"0323e3b0",6375:"ee09bfa7",6969:"cb964633",7098:"e1807782",7128:"a7a9aa84",7443:"b7d14749",7472:"fa10d80f",7643:"04a394fa",7890:"9dcf6528",7987:"b659ebc5",8209:"c45f2475",8226:"30c99c14",8288:"ab22d8e5",8401:"2787dc1e",8544:"35479697",8581:"1967f302",8726:"d4382058",8841:"0eb8e783",8898:"eef61cb3",9048:"ec0ec6f5",9168:"f5beb92f",9267:"9cfb408e",9284:"3f37929f",9314:"a23b7f13",9647:"ebd0b938",9710:"6897a68e",9803:"f19cbd4e"}[e]+".js",d.miniCssF=e=>{},d.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),d.o=(e,a)=>Object.prototype.hasOwnProperty.call(e,a),f={},t="cbl-ionic:",d.l=(e,a,c,r)=>{if(f[e])f[e].push(a);else{var b,o;if(void 0!==c)for(var n=document.getElementsByTagName("script"),i=0;i{b.onerror=b.onload=null,clearTimeout(s);var t=f[e];if(delete f[e],b.parentNode&&b.parentNode.removeChild(b),t&&t.forEach((e=>e(c))),a)return a(c)},s=setTimeout(u.bind(null,void 0,{type:"timeout",target:b}),12e4);b.onerror=u.bind(null,b.onerror),b.onload=u.bind(null,b.onload),o&&document.head.appendChild(b)}},d.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},d.p="/",d.gca=function(e){return e={17896441:"8401",66406991:"4374",b7b0e5b2:"434",d211126e:"473","76082c53":"477","4c8f02e7":"644",fa8f80ec:"1150",a8031cc3:"1233","94ea5d99":"1655","392dc68c":"1737","63aa4882":"1970",b2b675dd:"1991",fa9d6f84:"2131","80e7f649":"2178",d22a0e6a:"2184",e61c5f8c:"2612","9e4087bc":"2711","9736982f":"3095",ccc49370:"3249","295b567d":"3549","30a24c52":"3669","103c9c1b":"3823","6e975239":"3837","0e384e19":"3976","754d5016":"4761","6875c492":"4813","2391a45b":"4866",e0bfb9bc:"5006",c043769f:"5031","98e2200e":"5438",ac7c142a:"5465","85a8c145":"5468","956701b4":"5495",a7e7b2ec:"5815",b2f554cd:"5894",f9585397:"5967","3770a9bc":"6375","14eb3368":"6969",a7bd4aaa:"7098",bf09d495:"7128","964ae018":"7443","814f3328":"7472",a6aa9e1f:"7643",a04f062d:"7890",f67021ea:"7987","01a85c17":"8209",a7cb794f:"8226","9b522e88":"8288","935f2afb":"8581","3d109541":"8726","075bfa50":"8841","56c12b48":"8898",a94703ab:"9048",edca5c13:"9168",a7023ddc:"9267",fb0aa980:"9284","5e54861e":"9314","5e95c892":"9647",f20c4297:"9710",fdace129:"9803"}[e]||e,d.p+d.u(e)},(()=>{var e={5354:0,1869:0};d.f.j=(a,c)=>{var f=d.o(e,a)?e[a]:void 0;if(0!==f)if(f)c.push(f[2]);else if(/^(1869|5354)$/.test(a))e[a]=0;else{var t=new Promise(((c,t)=>f=e[a]=[c,t]));c.push(f[2]=t);var r=d.p+d.u(a),b=new Error;d.l(r,(c=>{if(d.o(e,a)&&(0!==(f=e[a])&&(e[a]=void 0),f)){var t=c&&("load"===c.type?"missing":c.type),r=c&&c.target&&c.target.src;b.message="Loading chunk "+a+" failed.\n("+t+": "+r+")",b.name="ChunkLoadError",b.type=t,b.request=r,f[1](b)}}),"chunk-"+a,a)}},d.O.j=a=>0===e[a];var a=(a,c)=>{var f,t,r=c[0],b=c[1],o=c[2],n=0;if(r.some((a=>0!==e[a]))){for(f in b)d.o(b,f)&&(d.m[f]=b[f]);if(o)var i=o(d)}for(a&&a(c);n Blobs | Couchbase Lite Ionic Capacitor Plugin - + @@ -37,9 +37,9 @@

    Using Blob

    The Blob API lets you access the blob's data content as in-memory data or as a Stream of Uint8Array.

    The code in Example 1 shows how you might add a blob to a document and save it to the database. Here we use avatar as the property key and a jpeg file as the blob data.

    Example 1. Working with Blobs

    -
    // Example function to simulate fetching binary data for an avatar image
    const imageData = await fetchAvatarImageData();

    // Create a Blob instance with the image data and content type
    const avatarBlob = new Blob('image/jpeg', imageData);

    // Retrieve an existing document
    const document = await collection.document(documentId);

    // Assign the Blob to the document under the 'avatar' key
    document.setBlob('avatar', avatarBlob);

    // Save the updated document back to the database
    await collection.save(document);
    +
    // Example function to simulate fetching binary data for an avatar image
    const imageData = await fetchAvatarImageData();

    // Create a Blob instance with the image data and content type
    const avatarBlob = new Blob('image/jpeg', imageData);

    // Retrieve an existing document
    const document = await collection.document(documentId);

    //conver the document to a mutable document
    const mutDoc = MutableDocument.fromDocument(document);

    // Assign the Blob to the document under the 'avatar' key
    mutDoc.setBlob('avatar', avatarBlob);

    // Save the updated document back to the database
    await collection.save(document);

    Example 2. Get Blob's content

    -
    // code for setting blob
    const encoder = new TextEncoder();
    const blobEncoded = new Blob("text/plain", encoder.encode("Hello World"));
    doc.setBlob('textBlob', blobEncoded);

    // code for getting blob's content
    const textDecoder = new TextDecoder();
    const blobArrayBuffer = await doc.getBlobContent('textBlob', collection);
    const textBlobResults = textDecoder.decode(blobArrayBuffer);
    +
    // code for setting blob
    const mutDoc = new MutableDocument('doc1');
    const encoder = new TextEncoder();
    const blobEncoded = new Blob("text/plain", encoder.encode("Hello World"));
    mutDoc.setBlob('textBlob', blobEncoded);
    await collection.save(mutDoc);

    // code for getting blob's content
    const doc = await collection.document('doc1');
    const textDecoder = new TextDecoder();
    const blob = doc.getBlob('textBlob');
    const blobArrayBuffer = blob.getBytes();
    const textBlobResults = textDecoder.decode(blobArrayBuffer);

    Syncing

    When a document containing a blob object is synchronized, the Couchbase Lite replicator generates an _attachments dictionary with an auto-generated name for each blob attachment. This is different to the avatar key and is used internally to access the blob content.

    If you view a sync'd blob document in either Capella's Admin Interface or Couchbase Server's Admin Console, you will see something similar to Figure 1, which shows the document with its generated _attachments dictionary, including the digest.

    diff --git a/blog.html b/blog.html index bd35419..9abd569 100644 --- a/blog.html +++ b/blog.html @@ -5,7 +5,7 @@ Blog | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/blog/archive.html b/blog/archive.html index decdd9c..4c4a6eb 100644 --- a/blog/archive.html +++ b/blog/archive.html @@ -5,7 +5,7 @@ Archive | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/blog/intro-blog-post.html b/blog/intro-blog-post.html index fd09be7..c03dd57 100644 --- a/blog/intro-blog-post.html +++ b/blog/intro-blog-post.html @@ -5,7 +5,7 @@ Introduction to Couchbase Lite for Ionic | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/blog/tags.html b/blog/tags.html index 8f4fb31..aebeb95 100644 --- a/blog/tags.html +++ b/blog/tags.html @@ -5,7 +5,7 @@ Tags | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/blog/tags/couchbase-lite.html b/blog/tags/couchbase-lite.html index 5832d71..04d86f9 100644 --- a/blog/tags/couchbase-lite.html +++ b/blog/tags/couchbase-lite.html @@ -5,7 +5,7 @@ One post tagged with "couchbase lite" | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/blog/tags/hello.html b/blog/tags/hello.html index 4d081e0..5f77db8 100644 --- a/blog/tags/hello.html +++ b/blog/tags/hello.html @@ -5,7 +5,7 @@ One post tagged with "hello" | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/blog/tags/ionic.html b/blog/tags/ionic.html index a82b3f7..0b4854c 100644 --- a/blog/tags/ionic.html +++ b/blog/tags/ionic.html @@ -5,7 +5,7 @@ One post tagged with "ionic" | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/blog/tags/mobile.html b/blog/tags/mobile.html index 516a57b..ac71e67 100644 --- a/blog/tags/mobile.html +++ b/blog/tags/mobile.html @@ -5,7 +5,7 @@ One post tagged with "mobile" | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/category/data-sync.html b/category/data-sync.html index 8c1facf..41a50a0 100644 --- a/category/data-sync.html +++ b/category/data-sync.html @@ -5,7 +5,7 @@ Data Sync | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/category/product-notes.html b/category/product-notes.html index dd2a1b0..a636ff2 100644 --- a/category/product-notes.html +++ b/category/product-notes.html @@ -5,7 +5,7 @@ Product Notes | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/category/queries.html b/category/queries.html index 6212d20..ad69fb3 100644 --- a/category/queries.html +++ b/category/queries.html @@ -5,7 +5,7 @@ Queries | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/category/start-here.html b/category/start-here.html index e278e2c..b9dc81c 100644 --- a/category/start-here.html +++ b/category/start-here.html @@ -5,7 +5,7 @@ Start Here! | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/category/troubleshooting.html b/category/troubleshooting.html index 6f9e40e..73f4e07 100644 --- a/category/troubleshooting.html +++ b/category/troubleshooting.html @@ -5,7 +5,7 @@ Troubleshooting | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/database-prebuilt.html b/database-prebuilt.html index 3b197b1..2ecdc07 100644 --- a/database-prebuilt.html +++ b/database-prebuilt.html @@ -5,7 +5,7 @@ Pre-built Database | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/databases.html b/databases.html index 60088dd..5cda985 100644 --- a/databases.html +++ b/databases.html @@ -5,7 +5,7 @@ Databases | Couchbase Lite Ionic Capacitor Plugin - + @@ -33,7 +33,7 @@

    import { CapacitorEngine } from 'cbl-ionic';

    const engine = new CapacitorEngine(); // Initialize once, early in your app
    -

    This configuration ensures seamless interaction between your Ionic app and the underlying native database functionalities, facilitating effective database management.

    +

    This configuration ensures seamless interaction between your Ionic app and the underlying native database functionalities, facilitating effective database management. This only needs to be done once in your application and must be done before any other Couchbase Lite operations are performed.

    Create or Open a Database

    To create or open a database, use the Database class from the cbl-ionic package, specifying the database name and optionally, a DatabaseConfiguration for custom settings like the database directory or encryption.

    Example 1. Creating/Opening a Database

    @@ -43,8 +43,13 @@

    Clo

    You are advised to incorporate the closing of all open databases into your application workflow.

    Example 2. Closing a Database

    await myDatabase.close();
    +

    Deleting a Database

    +

    The delete method is used to delete a database.

    +
     await database.deleteDatabase();
    +

    Alternatively, you can delete a database by calling the delete method on the Database class.

    +
    Database.deleteDatabase('myDatabaseName', 'path/to/database');

    Database Encryption

    -

    Couchbase Lite includes the ability to encrypt Couchbase Lite databases. This allows mobile applications to secure data at rest, when it is being stored on the device. The algorithm used to encrypt the database is 256-bit AES.

    +

    Couchbase Lite Enterprise Edition includes the ability to encrypt Couchbase Lite databases. This allows mobile applications to secure data at rest, when it is being stored on the device. The algorithm used to encrypt the database is 256-bit AES.

    Enabling

    To enable database encryption in Ionic, use the DatabaseConfiguration class to set an encryption key before opening or creating a database. This encryption key must be provided every time the database is accessed.

    Example3. Configure Database Encryption

    @@ -52,20 +57,46 @@

    EnablingPersisting

    Couchbase Lite does not persist the key. It is the application’s responsibility to manage the key and store it in a platform-specific secure store such as Apples's Keystore or Android’s Keystore.

    Opening

    -

    An encrypted database can only be opened with the same language package that was used to encrypt it in the first place. So a database encrypted using the Ionic package, and then exported, is readable only by the Ionic package.

    +

    An encrypted database can only be opened with the same language package that was used to encrypt it in the first place. So a database encrypted using the Ionic package on iOS, and then exported, is readable only by the iOS SDK since cbl-ionic is a wrapper around the native SDKs.

    Database Maintenance

    From time to time it may be necessary to perform certain maintenance activities on your database, for example to compact the database file, removing unused documents and blobs no longer referenced by any documents.

    Couchbase Lite's API provides the Database.performMaintenance method. The available maintenance operations, including compact are as shown in the enum MaintenanceType to accomplish this.

    -

    This is a resource intensive operation and is not performed automatically. It should be run on-demand using the API. For questions or issues, please visit the Couchbase Forums where you can ask for help and discuss with the community.

    -

    Command Line Tool

    -

    cblite is a command-line tool for inspecting and querying Couchbase Lite databases.

    -

    You can download and build it from the couchbaselabs GitHub repository. Note that the cblite tool is not supported by the Couchbase Support Policy.

    +
    const dbName = 'my_secure_db';
    const config = new DatabaseConfiguration();
    const db = new Database(dbName, config);
    await db.open();
    await db.performMaintenance(MaintenanceType.compact);
    +

    This is a resource intensive operation and is not performed automatically. It should be run on-demand using the API. A full listing of the available maintenance operations is shown below:

    +
      +
    • MaintenanceType.compact: Compact the database file and delete unused attachments.
    • +
    • MaintenanceType.reindex: (Volatile API) Rebuild the entire database’s indexes.
    • +
    • MaintenanceType.integrityCheck: (Volatile API) Check for the database’s corruption. If found, an error will be returned.
    • +
    • MaintenanceType.optimize: Quickly updates database statistics that may help optimize queries that have been run by this Database since it was opened
    • +
    • MaintenanceType.fullOptimize: Fully scans all indexes to gather database statistics that help optimize queries.
    • +
    +

    For questions or issues, please visit the Couchbase Forums where you can ask for help and discuss with the community.

    +

    Deprecated API

    +

    With the introduction of Scopes and Collections, several Database APIs in the Ionic package are now deprecated. These features will be removed in a future release, and developers are encouraged to use the Scopes/Collections APIs instead for these operations.

    +

    Document APIs

    +
      +
    • deleteDocument
    • +
    • getCount
    • +
    • getDocument
    • +
    • purgeDocument
    • +
    • save
    • +
    +

    Index APIs

    +
      +
    • createIndex
    • +
    • getIndexes
    • +
    • deleteIndex
    • +
    +

    For developers not working with custom Scopes/Collections, you can use the _default (default) scope and collection to achieve the same functionality with the new Scopes/Collections APIs.

    Couchbase Lite for VSCode

    Couchbase Lite for VSCode is a Visual Studio Code extension that provides a user interface for inspecting and querying Couchbase Lite databases. You can find more information about this extension from it's GitHub repository.

    Couchbase Lite for JetBrains

    Couchbase Lite for JetBrains is a JetBrains IDE plugin that provides a user interface for inspecting and querying Couchbase Lite databases. You can find more information about this plugin from its GitHub repository.

    +

    Command Line Tool

    +

    cblite is a command-line tool for inspecting and querying Couchbase Lite databases.

    +

    You can download and build it from the couchbaselabs GitHub repository. Note that the cblite tool is not supported by the Couchbase Support Policy.

    Troubleshooting

    You should use console logs as your first source of diagnostic information. If the information in the default logging level is insufficient you can focus it on database errors and generate more verbose messages.

    -
    try {
    await db.setLogLevel(LogDomain.DATABASE, LogLevel.VERBOSE);
    console.log('Database log level set to VERBOSE.');
    } catch (error) {
    console.error('Setting log level failed:', error);
    }
    +
    try {
    await db.setLogLevel(LogDomain.DATABASE, LogLevel.VERBOSE);
    console.log('Database log level set to VERBOSE.');
    } catch (error) {
    console.error('Setting log level failed:', error);
    }
    \ No newline at end of file diff --git a/documents.html b/documents.html index c2e88da..9f11b90 100644 --- a/documents.html +++ b/documents.html @@ -5,7 +5,7 @@ Documents | Couchbase Lite Ionic Capacitor Plugin - + @@ -16,9 +16,7 @@

    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.

    +

    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.

    note

    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

    @@ -67,10 +65,6 @@

    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.

    -
    const myDatabase = new Database('myDatabaseName', config);
    -

    See Databases for more information.

    Create a Document

    let document = new MutableDocument(hotel.id);

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

    @@ -88,9 +82,6 @@

    Po

    Save a Document

    With the document now populated, we can persist to our Couchbase Lite database.

    await collection.save(document);
    -

    Close the Database

    -

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

    -
    database.close();

    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.

    @@ -113,12 +104,28 @@

    The MutableDocument constructor can be used to create a new document where the document ID is randomly generated by the database.

    Use the MutableDocument('specific_id') initializer to create a new document with a specific ID.

    The Collection.document method can be used to get a document. If it doesn't exist in the collection, it will return null. This method can be used to check if a document with a given ID already exists in the collection.

    +

    Convert Mutable Document from Document

    +

    The MutableDocument class has a static function fromDocument that takes a Document as a parameter. This allows you to create a mutable copy of an existing document so you can modify it and then save it to a collection.

    +
    const doc: Document = collection.document('doc1');
    const mutableDoc = MutableDocument.fromDocument(doc);
    mutableDoc.setString('name', 'New Name');
    await collection.save(mutableDoc);

    Example 6. Persist a document

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

    // Create a new MutableDocument instance
    const document = new MutableDocument();

    // Set various fields on the document
    document.setString('type', 'task');
    document.setString('owner', 'todo');
    document.setDate('createdAt', new Date());

    // Persist the document to the database
    await collection.save(document);

    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.

    const token = collection.addDocumentChangeListener('user.john', async (change) => {
    const document = await collection.document(change.documentID);
    if (document !== null) {
    console.log(`Status: ${document.getString('verified_account')}`);
    }
    });

    // Remove the change listener when it is no longer needed
    await collection.removeDocumentChangeListener(token);
    +

    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 or Capella App Services.

    +
    const expirationDate = new Date('2024-12-31T23:59:59');
    await collection.setDocumentExpiration('doc123', expirationDate);
    +

    Purge a Document

    +

    Documents can be purged from the local database using the purge method on the collection they are stored in.

    +
    const doc = collection.document('doc1');
    await collection.purge(doc);
    +

    You can also purge a document by it's ID.

    +
    await collection.purgeById('doc1');
    +

    Collection purges are not replicated.

    +

    Delete a Document

    +

    Documents can be deleted using the delete method on the collection they are stored in.

    +
    const doc = collection.document('doc1');
    await collection.delete(doc);
    +

    Note that document deletion are replicated to Sync Gateway or Capella App Services.

    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.

    note

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

    @@ -130,6 +137,6 @@

    + \ No newline at end of file diff --git a/full-text-search.html b/full-text-search.html index a646bfb..d12f021 100644 --- a/full-text-search.html +++ b/full-text-search.html @@ -5,7 +5,7 @@ Using Full Text Search | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/index.html b/index.html index 76f75be..eb61712 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ Overview | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/indexes.html b/indexes.html index c0f6b6e..fcd5b0d 100644 --- a/indexes.html +++ b/indexes.html @@ -5,7 +5,7 @@ Indexing | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/learning-path.html b/learning-path.html index c710c08..cedacf0 100644 --- a/learning-path.html +++ b/learning-path.html @@ -5,7 +5,7 @@ Learning Path | Couchbase Lite Ionic Capacitor Plugin - + diff --git a/migration.html b/migration.html index eb25e72..90678c8 100644 --- a/migration.html +++ b/migration.html @@ -5,7 +5,7 @@ Migration | Couchbase Lite Ionic Capacitor Plugin - + @@ -16,14 +16,13 @@

    Information

    In the past, Ionic offered a plugin for Couchbase Lite, known as @ionic-enterprise/couchbase-lite. However, this plugin has since been deprecated by Ionic. The Couchbase Developer Experience and Ecosystem team took the original plugin as a foundation and transformed it into the cbl-ionic package. This migration documentation aims to address common challenges you may encounter when transitioning from the Ionic version of the plugin to the new open-source cbl-ionic plugin.

    -

    From an architectural standpoint, the cbl-ionic plugin bears a strong resemblance to the original plugin. The key difference is that the cbl-ionic package is open-source and maintained by the Couchbase Developer Experience and Ecosystem team. The iOS implementation, originally written in Objective-C, has been rewritten in Swift. Similarly, the original Android implementation, which was written in Java, has been replaced with Kotlin. The primary objective of the cbl-ionic plugin is to facilitate access to the latest versions of Couchbase Lite 3.x, while also supporting some of the major new features that the 3.x release offers:

    +

    From an architectural standpoint, the cbl-ionic plugin bears a strong resemblance to the original plugin. The key difference is that the cbl-ionic package is open-source and maintained by the Couchbase Developer Experience and Ecosystem team. The iOS implementation, originally written in Objective-C, has been rewritten in Swift. Similarly, the original Android implementation, which was written in Java, has been replaced with Kotlin. The primary objective of the cbl-ionic plugin is to facilitate access to the latest versions of Couchbase Lite 3.2 >= , while also supporting some of the major new features that the >= 3.2 release offers:

    • Scopes and Collections
    • SQL++ Queries
    • Database Maintenance
    • -
    -
    note

    By updating your app to use this plugin, your database will be instantly upgraded to a 3.x database, enabling support for Scopes and Collections. This behavior mirrors that of the native SDKs for each platform.

    +
    note

    By updating your app to use this plugin, your database will be instantly upgraded to a 3.2 >= database, enabling support for Scopes and Collections. This behavior mirrors that of the native SDKs for each platform.

    Database Compact / Database Maintenance

    Originally the old plugin provided the ability to compact a database using the following API:

    const db = new Database('myDatabaseName');
    await db.open();
    await db.compact();
    @@ -31,7 +30,7 @@

    const db = new Database('myDatabaseName');
    await db.open();
    await db.performMaintenance(MaintenanceType.COMPACT);

    The MaintenanceType enum includes:

    export enum MaintenanceType {
    COMPACT = 0,
    REINDEX = 1,
    INTEGRITY_CHECK = 2,
    OPTIMIZE = 3,
    FULL_OPTIMIZE = 4,
    }
    -

    These bring the Ionic package in line with the native SDK's for each platform.

    +

    These bring the Ionic package in line with the native SDK's for each platform. Documentation for the Database Maintenance API can be found here.

    Scopes/Collections

    With the introduction of Couchbase Mobile 3.1, the support for Scopes and Collections was added. This feature enhances performance by allowing documents to be stored in different collections based on their type, eliminating the need for indexes to locate documents of a specific type. While indexes can boost query performance, they can also hinder write performance. Therefore, Scopes and Collections offer a way to enhance query performance without relying on indexes.

    For teams that have not yet transitioned to using custom scopes and collections, the "_default" scope and "_default" collection can be utilized. The Database class provides methods to access both the default scope and collection:

    @@ -77,17 +76,29 @@

    // Create replicators to push and pull changes to and from the cloud.
    const targetEndpoint = new URLEndpoint(
    new URI('ws://localhost:4984/projects'),
    );
    const replConfig = new ReplicatorConfiguration(database, targetEndpoint);
    replConfig.setReplicatorType(
    ReplicatorConfiguration.ReplicatorType.PUSH_AND_PULL,
    );

    // Add authentication.
    replConfig.setAuthenticator(new BasicAuthenticator('demo@example.com', 'P@ssw0rd12'));

    // Create replicator.
    let replicator = new Replicator(replConfig);

    // Listen to replicator change events.
    replicator.addChangeListener(status => {});

    // Start replication.
    replicator.start();

    After

    -
    //setup database and collection
    const fileSystem = new FileSystem();
    const directoryPath = await fileSystem.getDefaultPath();

    const dc = new DatabaseConfiguration();
    dc.setDirectory(directoryPath);
    const database = new Database('inventory', dc);

    await database.open();
    const collection = database.getDefaultCollection();

    const target = new URLEndpoint('ws://localhost:4984/projects');
    const auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12');
    const config = new ReplicatorConfiguration(target);
    config.addCollection(this.collection);
    config.setAuthenticator(auth);

    const replicator = await Replicator.create(config);

    //listen to the replicator change events
    const token = await replicator.addChangeListener((change) => {
    //check to see if there was an error
    const error = change.status.getError();
    if (error !== undefined) {
    //do something with the error
    }
    //get the status of the replicator using ReplicatorActivityLevel enum
    if (change.status.getActivityLevel() === ReplicatorActivityLevel.IDLE) {
    //do something because the replicator is now IDLE
    }
    });

    // start the replicator without making a new checkpoint
    await replicator.start(false);
    +
    //setup database and collection
    const fileSystem = new FileSystem();
    const directoryPath = await fileSystem.getDefaultPath();

    const dc = new DatabaseConfiguration();
    dc.setDirectory(directoryPath);
    const database = new Database('inventory', dc);

    await database.open();
    const collection = database.getDefaultCollection();

    const target = new URLEndpoint('ws://localhost:4984/projects');
    const auth = new BasicAuthenticator('demo@example.com', 'P@ssw0rd12');
    const config = new ReplicatorConfiguration(target);
    const collectionConfig = new CollectionConfiguration();
    config.addCollection(collection, collectionConfig);
    config.setAuthenticator(auth);

    const replicator = await Replicator.create(config);

    //listen to the replicator change events
    const token = await replicator.addChangeListener((change) => {
    //check to see if there was an error
    const error = change.status.getError();
    if (error !== undefined) {
    //do something with the error
    }
    //get the status of the replicator using ReplicatorActivityLevel enum
    if (change.status.getActivityLevel() === ReplicatorActivityLevel.IDLE) {
    //do something because the replicator is now IDLE
    }
    });

    // start the replicator without making a new checkpoint
    await replicator.start(false);
    +

    Blob Retrieval

    +

    The Blob API has been updated to pull a blob content when the document is retrieved.

    +

    Before

    +
    const doc = await database.getDocument('doc1');
    const blobArrayBuffer = await doc.getBlobContent('textBlob', database);
    +

    After

    +
    const doc = await collection.document('doc1');
    const blob = doc.getBlob('textBlob');
    const blobArrayBuffer = blob.getBytes();

    New Features

    The cbl-ionic plugin, in addition to supporting Scopes/Collections and SQL++ from the 3.x release, introduces several new features:

    • +

      Database Delete Function - a new function in the Database API in addtion to the existing delete function that allows for the deletion of a database by passing the name and path instead of the database object.

      +
    • +
    • Database Change Encryption Key: This new functionality in the Database API allows for the modification of a database's encryption key.

    • Document Expiration: A new method in the Collection API enables the setting and retrieval of a document's expiration date.

    • +

      Document and MutableDocument: The Document and MutableDocument APIs have been updated to follow the native SDK's business rules for returning values based on the data type.

      +
    • +
    • Example App: The cbl-ionic repository includes a example folder, which contains a comprehensive application for testing all APIs and features of the cbl-ionic plugin. It also includes a custom test runner for executing end-to-end tests written for the plugin

    • @@ -96,6 +107,6 @@

      New Featu

    Conclusion

    An application can be migrated over from the Ionic version of the Couchbase Lite plugin to the new cbl-ionic package. The new package provides a path to the latest version of Couchbase Lite 3.x and provides support for the new features that the 3.x release provides.

    -

    The new cbl-ionic plugin is open source and maintained by the Couchbase Developer Experience and Ecosystem team. The new plugin is architecturally similar to the original plugin, but there are some differences in the API that will need to be addressed when migrating an application over to the new package. The new plugin provides support for Scopes and Collections, SQL++ Queries, and Database Maintenance. The new package also provides a more robust API for querying the database and for setting up replication. The new package is recommended for all new applications that are using Couchbase Lite and for applications that are looking to migrate over from the Ionic version of the Couchbase Lite plugin.

    +

    The new cbl-ionic plugin is open source and maintained by the Couchbase Developer Experience and Ecosystem team. The new plugin is architecturally similar to the original plugin, but there are some differences in the API that will need to be addressed when migrating an application over to the new package. The new plugin provides support for Scopes and Collections, SQL++ Queries, and Database Maintenance. The new package also provides a more robust API for querying the database and for setting up replication. The new package is recommended for all new applications that are using Couchbase Lite and for applications that are looking to migrate over from the Ionic version of the Couchbase Lite plugin.

    \ No newline at end of file diff --git a/scopes-collections.html b/scopes-collections.html index 1ec2bba..bd3d5bf 100644 --- a/scopes-collections.html +++ b/scopes-collections.html @@ -5,7 +5,7 @@ Scopes and Collections | Couchbase Lite Ionic Capacitor Plugin - + @@ -36,7 +36,7 @@

    note

    Scope and collection names are case sensitive.

    Example 1. Create a scope and collection

    const scopeName = "myScopeName";  
    const collectionName = "myCollectionName";
    const collection = await database.createCollection(collectionName, scopeName);
    -

    In the example above, you can see that Database.createCollection can take two parameters. The second is the scope assigned to the created collection, if this parameter is omitted then a collection of the given name will be assigned to the _default scope.

    +

    In the example above, you can see that Database .createCollection function can take two parameters. The second is the scope assigned to the created collection, if this parameter is omitted then a collection of the given name will be assigned to the _default scope.

    The first parameter is the name of the collection you want to create, in this case myCollectionName.

    If a collection with the specified name already exists in the specified scope, Database.createCollection returns the existing collection.

    note

    You cannot create an empty user-defined scope. A scope is implicitly created and removed by the Database.createCollection and Database.deleteCollection methods.

    @@ -48,6 +48,9 @@

    Drop
    await database.deleteCollection(collectionName, scopeName);

    List Scopes and Collections

    Example 4. List Scopes and Collections

    -
    // Get Scopes
    const scopes = await database.scopes();

    // Get Collections of a particular Scope
    const collections = await database.collections(scopeName);
    +
    // Get Scopes
    const scopes = await database.scopes();

    // Get Collections of a particular Scope
    const collections = await database.collections(scopeName);
    +

    Count Documents in a Collection

    +

    The .count method returns the number of documents in a given collection, a replacement for the Database .getCount method.

    +
    const count = await collection.count();
    \ No newline at end of file diff --git a/typed-data.html b/typed-data.html index a536d1a..0928e9f 100644 --- a/typed-data.html +++ b/typed-data.html @@ -5,7 +5,7 @@ Typed Data | Couchbase Lite Ionic Capacitor Plugin - +