Managing TLS Identities
Description — Couchbase Lite - this content covers how to manage TLS identities using Couchbase Lite
Related Content — API Reference | Passive Peer | Active Peer
Description — Couchbase Lite - this content covers how to manage TLS identities using Couchbase Lite
Overview
-
This describes the configuration and management of TLS identities
-
API: TLSIdentity
API References
You can find C#.Net API References here.
Creating TLS Identity
There are couple of options by which TLS Identity is created:
-
You can use the anonymous self-signed cert auto-generated by Couchbase Lite
-
You can import a cert to be bundled with the app and-or stored in the keychain
Use Anonymous Cert
Anonymous certification uses the self signed certificate auto-generated by Couchbase Lite when TLS is enabled, but no TLSIdentity is provided.
listenerConfig.disableTLS = false // Use with anonymous self signed cert
listenerConfig.tlsIdentity = nil
Import a Cert
Use the TLSIdentity class’s ImportIdentity() method to import a certificate that can be bundled with the app and-or added to the keychain.
-
First check the keychain to see if the identity already exists
-
Check for an existing resource bundle
// // Program.cs // // Copyright (c) 2017 Couchbase, Inc All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using Couchbase.Lite; using Couchbase.Lite.DI; using Couchbase.Lite.Enterprise.Query; using Couchbase.Lite.Logging; using Couchbase.Lite.P2P; using Couchbase.Lite.Query; using Couchbase.Lite.Sync; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Net.NetworkInformation; using System.Security; using System.Security.Cryptography.X509Certificates; namespace api_walkthrough { class Hotel { public string Id { get; set; } public string Name { get; set; } } class Program { private static readonly Database _Database = null; private static readonly Replicator _Replicator = null; private static readonly URLEndpointListener _listener = null; public void GettingStarted() { // tag::getting-started[] // using System; // using Couchbase.Lite; // using Couchbase.Lite.Query; // using Couchbase.Lite.Sync; // Get the database (and create it if it doesn't exist) var database = new Database("mydb"); var collection = database.GetDefaultCollection(); // Create a new document (i.e. a record) in the database var id = default(string); using var createdDoc = new MutableDocument(); createdDoc.SetFloat("version", 2.0f) .SetString("type", "SDK"); // Save it to the database collection.Save(createdDoc); id = createdDoc.Id; // Update a document using var doc = collection.GetDocument(id); using var mutableDoc = doc.ToMutable(); createdDoc.SetString("language", "C#"); collection.Save(createdDoc); using var docAgain = collection.GetDocument(id); Console.WriteLine($"Document ID :: {docAgain.Id}"); Console.WriteLine($"Learning {docAgain.GetString("language")}"); // Create a query to fetch documents of type SDK // i.e. SELECT * FROM database WHERE type = "SDK" using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("SDK"))); // Run the query var result = query.Execute(); Console.WriteLine($"Number of rows :: {result.AllResults().Count}"); // Create replicator to push and pull changes to and from the cloud var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4984/getting-started-db")); var replConfig = new ReplicatorConfiguration(targetEndpoint); replConfig.AddCollection(database.GetDefaultCollection()); // Add authentication replConfig.Authenticator = new BasicAuthenticator("john", "pass"); // Create replicator (make sure to add an instance or static variable // named _Replicator) var replicator = new Replicator(replConfig); replicator.AddChangeListener((sender, args) => { if (args.Status.Error != null) { Console.WriteLine($"Error :: {args.Status.Error}"); } }); replicator.Start(); // Later, stop and dispose the replicator *before* closing/disposing the database // end::getting-started[] } private static void TestReplicatorConflictResolver() { var collection = _Database.GetDefaultCollection(); // tag::replication-conflict-resolver[] var target = new URLEndpoint(new Uri("ws://localhost:4984/mydatabase")); var replConfig = new ReplicatorConfiguration(target); replConfig.AddCollection(collection, new CollectionConfiguration() { ConflictResolver = new LocalWinConflictResolver() }); var replicator = new Replicator(replConfig); replicator.Start(); // end::replication-conflict-resolver[] } private static void TestSaveWithConflictHandler() { var collection = _Database.GetDefaultCollection(); // tag::update-document-with-conflict-handler[] using var doc = collection.GetDocument("xyz"); using var mutableDoc = doc.ToMutable(); mutableDoc.SetString("name", "apples"); collection.Save(mutableDoc, (updated, current) => { var currentDict = current.ToDictionary(); var newDict = updated.ToDictionary(); var result = newDict.Concat(currentDict) .GroupBy(kv => kv.Key) .ToDictionary(g => g.Key, g => g.First().Value); updated.SetData(result); return true; }); // end::update-document-with-conflict-handler[] } private static bool IsValidCredential(string name, SecureString password) { return true; } // helper private static void TestInitListener() { var database = new Database("other-database"); var collection = database.GetDefaultCollection(); #warning init-urllistener Unused? // tag::init-urllistener[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); endpointConfig.TlsIdentity = null; // Use with anonymous self-signed cert endpointConfig.Authenticator = new ListenerPasswordAuthenticator((sender, username, password) => { if (IsValidCredential(username, password)) { return true; } return false; }); var listener = new URLEndpointListener(endpointConfig); // end::init-urllistener[] } private static void TestListenerStart() { var listener = _listener; #warning start-urllistener Unused? // tag::start-urllistener[] // CouchbaseLiteException will be thrown when the listener cannot be started. The most common error // would be that the configured port has already been used. listener.Start(); // end::start-urllistener[] } private static void TestListenerStop() { var listener = _listener; #warning stop-urllistener Unused? // tag::stop-urllistener[] listener.Stop(); // end::stop-urllistener[] } private static void TestCreateSelfSignedCert() { X509Store store = new X509Store(StoreName.My); // The identity will be stored in the secure // storage using the given label. DateTimeOffset fiveMinToExpireCert = DateTimeOffset.UtcNow.AddMinutes(5); // tag::create-self-signed-cert[] // tag::listener-config-tls-id-SelfSigned[] var identity = TLSIdentity.CreateIdentity(true, /* isServer */ new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "Couchbase Inc" } }, // The common name attribute is required // when creating a CSR. If it is not presented // in the cert, an exception is thrown. fiveMinToExpireCert, // If the expiration date is not specified, // the certs expiration will be 365 days store, "CBL-Server-Cert", null); // The key label to get cert in certificate map. // If null, the same default directory // for a Couchbase Lite db is used for map. // end::listener-config-tls-id-SelfSigned[] // end::create-self-signed-cert[] } private static void TestImportTLSIdentity() { X509Store store = new X509Store(StoreName.My); // The identity will be stored in the secure storage using the given label byte[] data = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates // tag::import-tls-identity[] // tag::listener-config-tls-id-caCert[] var identity = TLSIdentity.ImportIdentity(store, data, "123", // The password that is needed to access the certificate data "CBL-Client-Cert", null); // The key label to get cert in certificate map. // If null, the same default directory // for a Couchbase Lite db is used for map. // end::listener-config-tls-id-caCert[] // end::import-tls-identity[] } private static void TestClientCertAuthenticatorRootCerts() { var otherDatabase = new Database("other-database"); var collection = otherDatabase.GetDefaultCollection(); X509Store store = new X509Store(StoreName.My); #warning client-cert-authenticator-root-certs unused? // tag::client-cert-authenticator-root-certs[] byte[] caData, clientData; clientData = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates caData = File.ReadAllBytes("C:\\client-ca.der"); // Root certs var rootCert = new X509Certificate2(caData); var auth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert)); // Create URL Endpoint Listener var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection }); listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default. listenerConfig.Authenticator = auth; var listener = new URLEndpointListener(listenerConfig); listener.Start(); // Client identity var identity = TLSIdentity.ImportIdentity(store, clientData, "123", "CBL-Client-Cert", null); // Replicator -- Client var database = new Database("client-database"); var builder = new UriBuilder( "wss", "localhost", listener.Port, $"/{listener.Config.Collections.First().Name}" ); var url = builder.Uri; var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.PushAndPull; config.Continuous = false; config.Authenticator = new ClientCertificateAuthenticator(identity); config.AcceptOnlySelfSignedServerCertificate = true; config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0]; var replicator = new Replicator(config); replicator.Start(); // Dispose after stop // Stop listener after replicator is stopped listener.Stop(); // end::client-cert-authenticator-root-certs[] } private static void TestClientCertAuthenticator() { var otherDatabase = new Database("other-database"); var collection = otherDatabase.GetDefaultCollection(); X509Store store = new X509Store(StoreName.My); #warning client-cert-authenticator unused? // tag::client-cert-authenticator[] // Create Listener Certificate Authenticator var auth = new ListenerCertificateAuthenticator((sender, cert) => { if (cert.Count != 1) { return false; } return cert[0].SubjectName.Name?.Replace("CN=", "") == "couchbase"; }); // Create URL Endpoint Listener var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection }); listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default. listenerConfig.Authenticator = auth; var listener = new URLEndpointListener(listenerConfig); listener.Start(); // User Identity var identity = TLSIdentity.CreateIdentity(false, new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "couchbase" } }, null, store, "ClientCertLabel", null); // Replicator -- Client var database = new Database("client-database"); var builder = new UriBuilder( "wss", "localhost", listener.Port, $"/{listener.Config.Collections.First().Name}" ); var url = builder.Uri; var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.PushAndPull; config.Continuous = false; config.Authenticator = new ClientCertificateAuthenticator(identity); config.AcceptOnlySelfSignedServerCertificate = false; config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0]; var replicator = new Replicator(config); replicator.Start(); // Dispose after stopped // Stop listener after replicator is stopped listener.Stop(); // end::client-cert-authenticator[] } public void IdentityWithLabel() { X509Store store = null; byte[] clientData = null; var replConfig = new ReplicatorConfiguration(null); // tag::p2p-tlsid-tlsidentity-with-label[] // Client identity var identity = TLSIdentity.ImportIdentity(store, clientData, "123", "CBL-Client-Cert", null); (1) replConfig.Authenticator = new ClientCertificateAuthenticator(identity); (2) // end::p2p-tlsid-tlsidentity-with-label[] } public void UseEncryption() { // Enterprise edition only // tag::database-encryption[] // Create a new, or open an existing database with encryption enabled var config = new DatabaseConfiguration { // Or, derive a key yourself and pass a byte array of the proper size EncryptionKey = new EncryptionKey("password") }; using var database = new Database("seekrit", config); // Change the encryption key (or add encryption if the DB is unencrypted) database.ChangeEncryptionKey(new EncryptionKey("betterpassw0rd")); // Remove encryption database.ChangeEncryptionKey(null); // end::database-encryption[] } private static void ResetReplicatorCheckpoint() { var url = new Uri("ws://localhost:4984/db"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); bool resetCheckpointRequired_Example = true; config.AddCollection(_Database.GetDefaultCollection()); var replicator = new Replicator(config); // tag::replication-reset-checkpoint[] // replicator is a Replicator instance if (resetCheckpointRequired_Example) { replicator.Start(true); (3) } else { replicator.Start(false); } // Stop and dispose replicator later // end::replication-reset-checkpoint[] } private static void Read1xAttachment() { using var doc = new MutableDocument(); // tag::1x-attachment[] var attachments = doc.GetDictionary("_attachments"); var avatar = attachments.GetBlob("avatar"); var content = avatar?.Content; // end::1x-attachment[] } private static void CreateNewDatabase() { // tag::new-database[] var database = new Database("my-database"); // end::new-database[] } private static void CloseDatabase() { var database = _Database; // tag::close-database[] database.Close(); // end::close-database[] } private static void DatabaseFullsync() { var config = new DatabaseConfiguration(); // tag::database-fullsync[] // this enables fullsync config.FullSync = true; // end::database-fullsync[] } private static void CreateCollection() { // tag::scopes-manage-create-collection[] var collectionWithDefaultScope = _Database.CreateCollection("colA"); var collection = _Database.CreateCollection("colA", "scopeA"); // Scope with named scopeA will be created if it's not existed. There is no public API to create a Scope. // end::scopes-manage-create-collection[] } private static void DeleteCollection() { // tag::scopes-manage-drop-collection[] _Database.DeleteCollection("colA", "scopeA"); // Scope with named scopeA will be deleted if there is no collections in the scope after the last collection is deleted via this API. There is no public API to remove a Scope. // end::scopes-manage-drop-collection[] } private static void ListCollectionsAndScopes() { // tag::scopes-manage-list[] // Get Scopes var scopes = _Database.GetScopes(); // Get Collections of a Scope named scopeA var scopeA = _Database.GetScope("scopeA"); var collectionsInScopeA = scopeA.GetCollections(); // end::scopes-manage-list[] } private static void ChangeLogging() { // tag::logging[] // This sets the overall level of console logging Database.Log.Console.Level = LogLevel.Verbose; // This flag can enable and disable specific domains Database.Log.Console.Domains = LogDomain.Couchbase | LogDomain.Database; // end::logging[] } private static void LoadPrebuilt() { // tag::prebuilt-database[] // Note: Getting the path to a database is platform-specific. For .NET Core / .NET Framework this // can be a simple filesystem path. For UWP, you will need to get the path from your assets. For // iOS you need to get the path from the main bundle. For Android you need to extract it from your // assets to a temporary directory and then pass that path. var path = Path.Combine(Environment.CurrentDirectory, "travel-sample.cblite2" + Path.DirectorySeparatorChar); if (!Database.Exists("travel-sample", null)) { Database.Copy(path, "travel-sample", null); } // end::prebuilt-database[] } private static void QueryDeletedDocuments() { var collection = _Database.GetDefaultCollection(); // tag::query-deleted-documents[] // Query documents that have been deleted var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(Meta.IsDeleted); // end::query-deleted-documents[] } private static void CreateDocument() { var collection = _Database.GetDefaultCollection(); // tag::initializer[] using var mutableDoc = new MutableDocument("xyz"); mutableDoc.SetString("type", "task") .SetString("owner", "todo") .SetDate("createdAt", DateTimeOffset.UtcNow); collection.Save(mutableDoc); // end::initializer[] } private static void UpdateDocument() { var collection = _Database.GetDefaultCollection(); // tag::update-document[] using var doc = collection.GetDocument("xyz"); using var mutableDoc = doc.ToMutable(); mutableDoc.SetString("name", "apples"); collection.Save(mutableDoc); // end::update-document[] } private static void UseTypedAccessors() { using var mutableDoc = new MutableDocument(); // tag::date-getter[] mutableDoc.SetValue("createdAt", DateTimeOffset.UtcNow); var date = mutableDoc.GetDate("createdAt"); // end::date-getter[] Console.WriteLine(date); } private static void DoBatchOperation() { var database = _Database; var collection = database.GetDefaultCollection(); // tag::batch[] database.InBatch(() => { for (var i = 0; i < 10; i++) { using var mutableDoc = new MutableDocument(); mutableDoc.SetString("type", "user"); mutableDoc.SetString("name", $"user {i}"); mutableDoc.SetBoolean("admin", false); collection.Save(mutableDoc); Console.WriteLine($"Saved user document {mutableDoc.GetString("name")}"); } }); // end::batch[] } private static void DatabaseChangeListener() { var collection = _Database.GetDefaultCollection(); // tag::document-listener[] collection.AddDocumentChangeListener("user.john", (sender, args) => { using var doc = collection.GetDocument(args.DocumentID); Console.WriteLine($"Status :: {doc.GetString("verified_account")}"); }); // end::document-listener[] } private static void DocumentExpiration() { var collection = _Database.GetDefaultCollection(); // tag::document-expiration[] // Purge the document one day from now var ttl = DateTimeOffset.UtcNow.AddDays(1); collection.SetDocumentExpiration("doc123", ttl); // Reset expiration collection.SetDocumentExpiration("doc1", null); // Query documents that will be expired in less than five minutes var fiveMinutesFromNow = DateTimeOffset.UtcNow.AddMinutes(5).ToUnixTimeMilliseconds(); using var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(Meta.Expiration.LessThan(Expression.Double(fiveMinutesFromNow))); // end::document-expiration[] } private static void UseBlob() { var collection = _Database.GetDefaultCollection(); using var newTask = new MutableDocument(); // tag::blob[] // Note: Reading the data is implementation dependent, as with prebuilt databases var image = File.ReadAllBytes("avatar.jpg"); (4) var blob = new Blob("image/jpeg", image); (5) newTask.SetBlob("avatar", blob); (6) collection.Save(newTask); // end::blob[] } public void CreateIndex() { var collection = _Database.GetDefaultCollection(); // tag::query-index[] // tag::scopes-manage-index-collection[] string[] indexProperties = new string[] { "type", "name" }; var config = new ValueIndexConfiguration(indexProperties); collection.CreateIndex("TypeNameIndex", config); // end::scopes-manage-index-collection[] // end::query-index[] } public void CreateIndex_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::query-index_Querybuilder[] // For value types, this is optional but provides performance enhancements var index = IndexBuilder.ValueIndex( ValueIndexItem.Expression(Expression.Property("type")), ValueIndexItem.Expression(Expression.Property("name"))); (7) collection.CreateIndex("TypeNameIndex", index); // end::query-index_Querybuilder[] } private static void SelectMeta() { var collection = _Database.GetDefaultCollection(); #warning query-select-meta unused? // tag::query-select-meta[] // tag::query-select-props[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("type"), SelectResult.Property("name")) .From(DataSource.Collection(collection)); foreach (var result in query.Execute()) { Console.WriteLine($"Document ID :: {result.GetString("id")}"); Console.WriteLine($"Document Name :: {result.GetString("name")}"); } // end::query-select-props[] // end::query-select-meta[] } private static void SelectAll() { var collection = _Database.GetDefaultCollection(); { // tag::query-select-all[] using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)); // end::query-select-all[] } { // tag::live-query[] var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)); (8) // Adds a query change listener. // Changes will be posted on the main queue. var token = query.AddChangeListener((sender, args) => (9) { var allResult = args.Results.AllResults(); foreach (var result in allResult) { Console.WriteLine(result.Keys); /* Update UI */ } }); // end::live-query[] // tag::stop-live-query[] query.RemoveChangeListener(token); query.Dispose(); // end::stop-live-query[] } } private static void SelectWhere() { var collection = _Database.GetDefaultCollection(); // tag::query-where[] using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { var dict = result.GetDictionary(collection.Name); Console.WriteLine($"Document Name :: {dict?.GetString("name")}"); } // end::query-where[] } private static void UseCollectionContains() { var collection = _Database.GetDefaultCollection(); // tag::query-collection-operator-contains[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name"), SelectResult.Property("public_likes")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel")) .And(ArrayFunction.Contains(Expression.Property("public_likes"), Expression.String("Armani Langworth")))); foreach (var result in query.Execute()) { var publicLikes = result.GetArray("public_likes"); var jsonString = JsonConvert.SerializeObject(publicLikes); Console.WriteLine($"Public Likes :: {jsonString}"); } // end::query-collection-operator-contains[] } private static void UseCollectionIn() { var collection = _Database.GetDefaultCollection(); // tag::query-collection-operator-in[] var values = new IExpression[] { Expression.Property("first"), Expression.Property("last"), Expression.Property("username") }; using var query = QueryBuilder.Select( SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.String("Armani").In(values)); foreach (var result in query.Execute()) { var body = result.GetDictionary(0); var jsonString = JsonConvert.SerializeObject(body); Console.WriteLine($"In results :: {jsonString}"); } // end::query-collection-operator-in[] } private static void SelectLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("Royal Engineers Museum")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator[] } private static void SelectWildcardLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator-wildcard-match[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("Eng%e%")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator-wildcard-match[] } private static void SelectWildcardCharacterLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator-wildcard-character-match[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Expression.Property("name").Like(Expression.String("Royal Eng____rs Museum")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator-wildcard-character-match[] } private static void SelectRegex() { var collection = _Database.GetDefaultCollection(); // tag::query-regex-operator[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Expression.Property("name").Regex(Expression.String("\\bEng.*e\\b")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-regex-operator[] } private static void SelectJoin() { var collection = _Database.GetDefaultCollection(); var collection2 = _Database.GetDefaultCollection(); // tag::query-join[] using var query = QueryBuilder.Select( SelectResult.Expression(Expression.Property("name").From("airline")), SelectResult.Expression(Expression.Property("callsign").From("airline")), SelectResult.Expression(Expression.Property("destinationairport").From("route")), SelectResult.Expression(Expression.Property("stops").From("route")), SelectResult.Expression(Expression.Property("airline").From("route"))) .From(DataSource.Collection(collection).As("airline")) .Join(Join.InnerJoin(DataSource.Collection(collection2).As("route")) .On(Meta.ID.From("airline").EqualTo(Expression.Property("airlineid").From("route")))) .Where(Expression.Property("type").From("route").EqualTo(Expression.String("route")) .And(Expression.Property("type").From("airline").EqualTo(Expression.String("airline"))) .And(Expression.Property("sourceairport").From("route").EqualTo(Expression.String("RIX")))); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-join[] } private static void GroupBy() { var collection = _Database.GetDefaultCollection(); // tag::query-groupby[] using var query = QueryBuilder.Select( SelectResult.Expression(Function.Count(Expression.All())), SelectResult.Property("country"), SelectResult.Property("tz")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("airport")) .And(Expression.Property("geo.alt").GreaterThanOrEqualTo(Expression.Int(300)))) .GroupBy(Expression.Property("country"), Expression.Property("tz")); foreach (var result in query.Execute()) { Console.WriteLine( $"There are {result.GetInt("$1")} airports in the {result.GetString("tz")} timezone located in {result.GetString("country")} and above 300 ft"); } // end::query-groupby[] } private static void OrderBy() { var collection = _Database.GetDefaultCollection(); // tag::query-orderby[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("title")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .OrderBy(Ordering.Property("title").Ascending()) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Title :: {result.GetString("title")}"); } // end::query-orderby[] } private static void TestExplainStatement() { var collection = _Database.GetDefaultCollection(); { // tag::query-explain-all[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .GroupBy(Expression.Property("country")) .OrderBy(Ordering.Property("title").Ascending()); (10) Console.WriteLine(query.Explain()); (11) // end::query-explain-all[] } { // tag::query-explain-like[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").Like(Expression.String("%hotel%")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (12) Console.WriteLine(query.Explain()); // end::query-explain-like[] } { // tag::query-explain-nopfx[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").Like(Expression.String("hotel%")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (13) Console.WriteLine(query.Explain()); // end::query-explain-nopfx[] } { // tag::query-explain-function[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Function.Lower(Expression.Property("type")).EqualTo(Expression.String("hotel"))); (14) Console.WriteLine(query.Explain()); // end::query-explain-function[] } { // tag::query-explain-nofunction[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))); (15) Console.WriteLine(query.Explain()); // end::query-explain-nofunction[] } } public void CreateFullTextIndex() { var collection = _Database.GetDefaultCollection(); // tag::fts-index[] string[] indexProperties = new string[] { "overview", "name" }; var config = new FullTextIndexConfiguration(indexProperties); collection.CreateIndex("overviewFTSIndex", config); // end::fts-index[] } public void FullTextSearch() { var collection = _Database.GetDefaultCollection(); // tag::fts-query[] var query = collection.CreateQuery("SELECT * FROM _ WHERE MATCH(overviewFTSIndex, 'Michigan') ORDER BY RANK(overviewFTSIndex)"); foreach (var result in query.Execute()) { Console.WriteLine($"Document id {result.GetString(0)}"); } // end::fts-query[] } private static void CreateFullTextIndex_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::fts-index_Querybuilder[] var index = IndexBuilder.FullTextIndex(FullTextIndexItem.Property("overview")).IgnoreAccents(false); collection.CreateIndex("overviewFTSIndex", index); // end::fts-index_Querybuilder[] } private static void FullTextSearch_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::fts-query_Querybuilder[] var whereClause = FullTextFunction.Match(Expression.FullTextIndex("overviewFTSIndex"), "'michigan'"); using var query = QueryBuilder.Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(whereClause); foreach (var result in query.Execute()) { Console.WriteLine($"Document id {result.GetString(0)}"); } // end::fts-query_Querybuilder[] } private static void StartReplication() { var collection = _Database.GetDefaultCollection(); #warning replication unused? // tag::replication[] // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) var url = new Uri("ws://localhost:4984/db"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target) { ReplicatorType = ReplicatorType.Pull }; config.AddCollection(collection); var replicator = new Replicator(config); replicator.Start(); // end::replication[] } private static void ConsoleLogging() { // tag::console-logging[] Database.Log.Console.Domains = LogDomain.All; (16) Database.Log.Console.Level = LogLevel.Verbose; (17) // end::console-logging[] // tag::console-logging-db[] Database.Log.Console.Domains = LogDomain.Database; // end::console-logging-db[] } private static void FileLogging() { // tag::file-logging[] var tempFolder = Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>().DefaultDirectory(), "cbllog"); var config = new LogFileConfiguration(tempFolder) (18) { MaxRotateCount = 5, (19) MaxSize = 10240, (20) UsePlaintext = false (21) }; Database.Log.File.Config = config; // Apply configuration Database.Log.File.Level = LogLevel.Info; (22) // end::file-logging[] } private static void EnableCustomLogging() { // tag::set-custom-logging[] Database.Log.Custom = new LogTestLogger(); (23) // You can also specify the level of logging the logger receives Database.Log.Custom = new LogTestLogger { Level = LogLevel.Warning }; // end::set-custom-logging[] } private static void WriteConsoleLog() { #warning write-console-logmsg unused? // tag::write-console-logmsg[] Database.Log.Console.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-console-logmsg[] } private static void WriteCustomLog() { #warning write-custom-logmsg unused? // tag::write-custom-logmsg[] Database.Log.Custom?.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-custom-logmsg[] } private static void WriteFileLog() { #warning write-file-logmsg unused? // tag::write-file-logmsg[] Database.Log.File.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-file-logmsg[] } private static void EnableBasicAuth() { var collection = _Database.GetDefaultCollection(); #warning basic-authentication unused? // tag::basic-authentication[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection); config.Authenticator = new BasicAuthenticator("john", "pass"); var replicator = new Replicator(config); replicator.Start(); // end::basic-authentication[] } private static void EnableSessionAuth() { var collection = _Database.GetDefaultCollection(); // tag::session-authentication[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection); config.Authenticator = new SessionAuthenticator("904ac010862f37c8dd99015a33ab5a3565fd8447"); var replicator = new Replicator(config); replicator.Start(); // end::session-authentication[] } private static void SetupReplicatorListener() { var replicator = _Replicator; #warning replication-status unused? // tag::replication-status[] replicator.AddChangeListener((sender, args) => { if (args.Status.Activity == ReplicatorActivityLevel.Stopped) { Console.WriteLine("Replication stopped"); } }); // end::replication-status[] } private static void ReplicatorPendingDocuments() { // tag::replication-pendingdocuments[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var database = new Database("myDB"); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.Push; // tag::replication-push-pendingdocumentids[] var replicator = new Replicator(config); var pendingDocIDs = new HashSet<string>(replicator.GetPendingDocumentIDs(database.GetDefaultCollection())); (24) // end::replication-push-pendingdocumentids[] if (pendingDocIDs.Count > 0) { Console.WriteLine($"There are {pendingDocIDs.Count} documents pending"); replicator.AddChangeListener((sender, change) => { Console.WriteLine($"Replicator activity level is " + change.Status.Activity.ToString()); // iterate and report-on previously // retrieved pending docids 'list' foreach (var docID in pendingDocIDs) #warning replication-push-isdocumentpending unused? // tag::replication-push-isdocumentpending[] if (!replicator.IsDocumentPending(docID, database.GetDefaultCollection())) (25) { Console.WriteLine($"Doc ID {docID} now pushed"); }; // end::replication-push-isdocumentpending[] }); replicator.Start(); } // end::replication-pendingdocuments[] } private static void ReplicatorDocumentEvent() { var replicator = _Replicator; // tag::add-document-replication-listener[] var token = replicator.AddDocumentReplicationListener((sender, args) => { var direction = args.IsPush ? "Push" : "Pull"; Console.WriteLine($"Replication type :: {direction}"); foreach (var doc in args.Documents) { if (doc.Error == null) { Console.WriteLine($"Doc ID :: {doc.Id}"); if (doc.Flags.HasFlag(DocumentFlags.Deleted)) { Console.WriteLine("Successfully replicated a deleted document"); } } else { // There was an error } } }); replicator.Start(); // end::add-document-replication-listener[] // tag::remove-document-replication-listener[] replicator.RemoveChangeListener(token); // end::remove-document-replication-listener[] } private static void SetupReplicatorErrorListener() { // This can be done in the SetupReplicatorListener method // But it is separate so that we can have two documentation entries var replicator = _Replicator; // tag::replication-error-handling[] replicator.AddChangeListener((sender, args) => { if (args.Status.Error != null) { Console.WriteLine($"Error :: {args.Status.Error}"); } }); // end::replication-error-handling[] } private static void DatabaseReplica() { var collection = _Database.GetDefaultCollection(); using (var database2 = new Database("backup")) { // EE feature: This code will not compile on the community edition // tag::database-replica[] var targetDatabase = new DatabaseEndpoint(database2); var config = new ReplicatorConfiguration(targetDatabase) { ReplicatorType = ReplicatorType.Push }; config.AddCollection(collection); var replicator = new Replicator(config); replicator.Start(); // end::database-replica[] } } private X509Certificate2 GetCertificate(string name) { return null; } public void PinCertificate() { var url = new Uri("wss://localhost:4984/db"); var target = new URLEndpoint(url); // tag::certificate-pinning[] // Note: `GetCertificate` is a placeholder method. This would be the platform-specific method // to find and load the certificate as an instance of `X509Certificate2`. // For .NET Core / .NET Framework this can be loaded from the filesystem path. // For WinUI, from the assets directory. // For iOS, from the main bundle. // For Android, from the assets directory. var certificate = GetCertificate("cert.cer"); var config = new ReplicatorConfiguration(target) { PinnedServerCertificate = certificate }; // end::certificate-pinning[] } public void ReplicationCustomHeaders() { var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); // tag::replication-custom-header[] var config = new ReplicatorConfiguration(target) { Headers = new Dictionary<string, string> { ["CustomHeaderName"] = "Value" } }; // end::replication-custom-header[] } private static void PushWithFilter(Database database) { var collection = _Database.GetDefaultCollection(); // tag::replication-push-filter[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection, new CollectionConfiguration() { PushFilter = (document, flags) => (1) { if (flags.HasFlag(DocumentFlags.Deleted)) { return false; } return true; } }); // Dispose() later var replicator = new Replicator(config); replicator.Start(); // end::replication-push-filter[] } private static void PullWithFilter(Database database) { var collection = _Database.GetDefaultCollection(); // tag::replication-pull-filter[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection, new CollectionConfiguration() { PullFilter = (document, flags) => (1) { if (document.GetString("type") == "draft") { return false; } return true; } }); // Dispose() later var replicator = new Replicator(config); replicator.Start(); // end::replication-pull-filter[] } public void TestCustomRetryConfig() { // tag::replication-retry-config[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); // other config as required . . . #warning replication-set-heartbeat unused? // tag::replication-set-heartbeat[] config.Heartbeat = TimeSpan.FromSeconds(120); // (26) // end::replication-set-heartbeat[] // tag::replication-set-maxattempts[] #warning replication-set-maxattempts unused? config.MaxAttempts = 20; // (27) // end::replication-set-maxattempts[] // tag::replication-set-maxattemptwaittime[] #warning replication-set-maxattemptwaittime unused? config.MaxAttemptsWaitTime = TimeSpan.FromSeconds(600); // (28) // end::replication-set-maxattemptwaittime[] // other config as required . . . var replicator = new Replicator(config); // end::replication-retry-config[] } private static void UsePredictiveModel() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::register-model[] var model = new ImageClassifierModel(); Database.Prediction.RegisterModel("ImageClassifier", model); // end::register-model[] // tag::predictive-query-value-index[] var index = IndexBuilder.ValueIndex(ValueIndexItem.Property("label")); collection.CreateIndex("value-index-image-classifier", index); // end::predictive-query-value-index[] // tag::unregister-model[] Database.Prediction.UnregisterModel("ImageClassifier"); // end::unregister-model[] } } private static void UsePredictiveIndex() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::predictive-query-predictive-index[] var input = Expression.Dictionary(new Dictionary<string, object> { ["photo"] = Expression.Property("photo") }); var index = IndexBuilder.PredictiveIndex("ImageClassifier", input); collection.CreateIndex("predictive-index-image-classifier", index); // end::predictive-query-predictive-index[] } } private static void DoPredictiveQuery() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::predictive-query[] var input = Expression.Dictionary(new Dictionary<string, object> { ["photo"] = Expression.Property("photo") }); var prediction = Function.Prediction("ImageClassifier", input); (1) using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(prediction.Property("label").EqualTo(Expression.String("car")) .And(prediction.Property("probability").GreaterThanOrEqualTo(Expression.Double(0.8)))); var result = query.Execute(); Console.WriteLine($"Number of rows: {result.Count()}"); // end::predictive-query[] } } public List<Result> docsonly_N1QLQueryString(Database argDB) { DatabaseConfiguration config = new DatabaseConfiguration(); Database database = new Database("dbName", config); // tag::query-syntax-n1ql[] using var query = database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = \"hotel\""); (29) return query.Execute().AllResults(); // end::query-syntax-n1ql[] } public void docsonly_N1QLQueryStringParams(Database argDB) { var database = _Database; // tag::query-syntax-n1ql-params[] using var query = database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = $type"); (30) var n1qlParams = new Parameters(); n1qlParams.SetString("type", "hotel"); (31) query.Parameters = n1qlParams; var results = query.Execute().AllResults(); // end::query-syntax-n1ql-params[] } public void testQuerySyntaxAll() { // tag::query-syntax-all[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-all[] // tag::query-access-all[] var results = query.Execute().AllResults(); var hotels = new List<Dictionary<string, object>>(); if (results?.Count > 0) { foreach (var result in results) { // get the result into our dictionary object var thisDocsProps = result.GetDictionary("hotels"); (32) if (thisDocsProps != null) { var docID = thisDocsProps.GetString("id"); (33) var docName = thisDocsProps.GetString("name"); var docCity = thisDocsProps.GetString("city"); var docType = thisDocsProps.GetString("type"); var hotel = thisDocsProps.ToDictionary(); hotels.Add(hotel); } } } // end::query-access-all[] // tag::query-access-json[] foreach (var result in query.Execute()) { // get the result into a JSON String var docJSONString = result.ToJSON(); // Get a native dictionary object using the JSON string var dictFromJSONstring = JsonConvert. DeserializeObject<Dictionary<string, object>> (docJSONString); // use the created dictionary if (dictFromJSONstring != null) { var docID = dictFromJSONstring["id"].ToString(); var docName = dictFromJSONstring["name"].ToString(); var docCity = dictFromJSONstring["city"].ToString(); var docType = dictFromJSONstring["type"].ToString(); } //Get a custom object using the JSON string Hotel hotel = JsonConvert.DeserializeObject<Hotel>(docJSONString); } // end::query-access-json[] } public void testQuerySyntaxProps() { // tag::query-syntax-props[] var database = new Database("hotels"); List<Dictionary<string, object>> hotels = new List<Dictionary<string, object>>(); var query = QueryBuilder.Select( SelectResult.Property("type"), SelectResult.Property("name"), SelectResult.Property("city")).From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-props[] // tag::query-access-props[] var results = query.Execute().AllResults(); foreach (var result in results) { // get the returned array of k-v pairs into a dictionary var hotel = result.ToDictionary(); // add hotel dictionary to list of hotel dictionaries hotels.Add(hotel); // use the properties of the returned array of k-v pairs directly var docType = result.GetString("type"); var docName = result.GetString("name"); var docCity = result.GetString("city"); } // end::query-access-props[] } public void testQuerySyntaxCount() { // tag::query-syntax-count-only[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount")) (34) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-count-only[] // tag::query-access-count-only[] var results = query.Execute().AllResults(); foreach (var result in results) { var numberOfDocs = result.GetInt("mycount"); (35) } // end::query-access-count-only[] } public void ibQueryForID() { // tag::query-syntax-id[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID).As("this_ID")) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-id[] // tag::query-access-id[] var results = query.Execute().AllResults(); foreach (var result in results) { var docID = result.GetString("this_ID"); (36) var doc = database.GetDefaultCollection().GetDocument(docID); } // end::query-access-id[] } #warning query-syntax-pagination-all unused (and out of place)? // tag::query-syntax-pagination-all[] public void testQueryPagination() { // tag::query-syntax-pagination[] var database = new Database("hotels"); var limit = 20; var offset = 0; // get a count of the number of docs matching the query var countQuery = QueryBuilder .Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount")) .From(DataSource.Collection(database.GetDefaultCollection())); var numberOfDocs = countQuery.Execute().First().GetInt("mycount"); if (numberOfDocs < limit) { limit = numberOfDocs; } while (offset < numberOfDocs) { var listQuery = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(database.GetDefaultCollection())) .Limit(Expression.Int(limit), Expression.Int(offset)); (37) foreach (var result in listQuery.Execute()) { // Display and or process query results batch } offset = offset + limit; } // end::query-syntax-pagination[] // end::query-syntax-pagination-all[] } public void JsonApiDocument() { var collection = _Database.GetDefaultCollection(); // tag::tojson-document[] // Get a document var doc = collection.GetDocument("hotel_10025"); // Get document data as JSON String var docJSONString = doc?.ToJSON(); // Get Json Object from the Json String JObject jsonObject = JObject.Parse(docJSONString); // Get Native Object (anhotel) from JSON String List<Hotel> hotels = new List<Hotel>(); var hotel = JsonConvert.DeserializeObject<Hotel>(docJSONString); hotels.Add(hotel); // Update the retrieved native object hotel.Name = "A Copy of " + hotel.Name; hotel.Id = "2001"; // Convert the updated object back to a JSON string var newJsonString = JsonConvert.SerializeObject(hotel); // Update new document with JSOn String MutableDocument newhotel = doc.ToMutable(); newhotel.SetJSON(newJsonString); foreach (string key in newhotel.ToDictionary().Keys) { Console.WriteLine("Data -- {0} = {1}", key, newhotel.GetValue(key)); } collection.Save(newhotel); var retrievedDoc = collection.GetDocument("2001").ToJSON(); Console.Write(retrievedDoc); // end::tojson-document[] } public void JsonApiArray() { var collection = _Database.GetDefaultCollection(); // tag::tojson-array[] // JSON String -- an Array (3 elements. including embedded arrays) var jsonString = "[{'id':'1000','type':'hotel','name':'Hotel Ted','city':'Paris','country':'France','description':'Undefined description for Hotel Ted'},{'id':'1001','type':'hotel','name':'Hotel Fred','city':'London','country':'England','description':'Undefined description for Hotel Fred'}, {'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}]".Replace("'", "\""); // Get JSON Array from JSON String var jsonArray = JArray.Parse(jsonString); // Create mutable array using JSON String Array var mutableArray = new MutableArrayObject(); mutableArray.SetJSON(jsonString); // Create a new document for each array element for (int i = 0; i < mutableArray.Count; i++) { var dict = mutableArray.GetDictionary(i); var docid = mutableArray[i].Dictionary.GetString("id"); var mutableDoc = new MutableDocument(docid, dict.ToDictionary()); collection.Save(mutableDoc); } // Get one of the created docs and iterate through one of the embedded arrays var extendedDoc = collection.GetDocument("1002"); var features = extendedDoc.GetArray("features"); // Print its elements foreach (string feature in features) { Console.Write($"{feature} "); //process array item as required } var featuresJSON = extendedDoc.GetArray("features").ToJSON(); // end::tojson-array[] } public void JsonApiDictionary() { var ourdbname = "ournewdb"; if (Database.Exists(ourdbname, "/")) { Database.Delete(ourdbname, "/"); } // tag::tojson-dictionary[] // Get dictionary from JSONstring var jsonString = "{'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}".Replace("'", "\""); var mutableDict = new MutableDictionaryObject(json: jsonString); // use dictionary to get name value var name = mutableDict.GetString("name"); // Iterate through keys foreach (string key in mutableDict.Keys) { Console.WriteLine("Data -- {0} = {1}", key, mutableDict.GetValue(key).ToString()); } // end::tojson-dictionary[] } public void JsonApiBlob() { var userName = "ian"; var collection = _Database.GetDefaultCollection(); var database = _Database; // tag::tojson-blob[] // Initialize base document for blob from a JSON string var docId = "1002"; var jsonString = "{'ref':'hotel_1002','type':'hotel','name':'Hotel Ned'," + "'city':'Balmain','country':'Australia'," + "'description':'Undefined description for Hotel Ned'," + "'features':['Cable TV','Toaster','Microwave']}".Replace("'", "\""); var mutableDoc = new MutableDocument(docId, jsonString); // Get the content (an image), create blob and add to doc) var defaultDirectory = Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>() .DefaultDirectory(), userName); var imagePath = Path.Combine(defaultDirectory, "avatarimage.jpg"); var imageUri = new Uri(imagePath.ToString()); var imageBlob = new Blob("image/jpg", imageUri); mutableDoc.SetBlob("avatar", imageBlob); // This example generates a 'blob not saved' exception try { Console.WriteLine("myBlob (unsaved) as JSON = {0}", imageBlob.ToJSON()); } catch (Exception e) { Console.WriteLine("Exception = {0}", e.Message); } collection.Save(mutableDoc); // Alternatively -- depending on use case database.SaveBlob(new Blob("image/jpg", imageUri)); // Retrieve saved doc, get blob as JSON andheck its still a 'blob' var sameDoc = collection.GetDocument(docId); var reconstitutedBlob = new MutableDictionaryObject(). SetDictionary("blobCOPY", new MutableDictionaryObject(sameDoc.GetBlob("avatar").ToJSON())); if (Blob.IsBlob( reconstitutedBlob.GetDictionary("blobCOPY").ToDictionary())) { //... process accordingly Console.WriteLine("Its a Blob!!"); } // end::tojson-blob[] } private bool ValidatePassword(SecureString password) => true; public void P2PListenerSimple() { var collection = _Database.GetDefaultCollection(); // tag::listener-simple[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (38) endpointConfig.Authenticator = new ListenerPasswordAuthenticator( (sender, username, password) => { // ValidatePassword can make use of the SecureString class // to the desired level of security (or just convert it to string // if no intense security is required) return username == "valid.user" && ValidatePassword(password); } ); (39) var listener = new URLEndpointListener(endpointConfig); (40) listener.Start(); (41) // end::listener-simple[] } public void P2PReplicatorSimple() { var collection = _Database.GetDefaultCollection(); // tag::replicator-simple[] var endpointConfig = new URLEndpoint(new Uri("wss://listener.com:4984/otherDB")); (42) var replConfig = new ReplicatorConfiguration(endpointConfig); (43) replConfig.AddCollection(collection); replConfig.AcceptOnlySelfSignedServerCertificate = true; (44) replConfig.Authenticator = new BasicAuthenticator("valid.user", "valid.password.string"); (45) var replicator = new Replicator(replConfig); (46) replicator.Start(); (47) // end::replicator-simple[] } public void GettingStarted1() { var collection = _Database.GetDefaultCollection(); // tag::listener-initialize[] // tag::listener-config-db[] // Initialize the listener config var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (48) // end::listener-config-db[] // tag::listener-config-port[] endpointConfig.Port = 55990; (49) // end::listener-config-port[] // tag::listener-config-netw-iface[] endpointConfig.NetworkInterface = "10.1.1.10"; (50) // end::listener-config-netw-iface[] // tag::listener-config-delta-sync[] endpointConfig.EnableDeltaSync = true; (51) // end::listener-config-delta-sync[] #warning listener-config-tls-full unused? // tag::listener-config-tls-full[] // tag::listener-config-tls-enable[] endpointConfig.DisableTLS = false; (52) // end::listener-config-tls-enable[] // tag::listener-config-tls-id-anon[] // Use an Anonymous Self-Signed Cert endpointConfig.TlsIdentity = null; (53) // end::listener-config-tls-id-anon[] // tag::listener-config-client-auth-pwd[] // Configure the client authenticator // Here we are using Basic Authentication) (54) SecureString validPassword = new SecureString(); /* example only */ // Get SecureString input for validPassword var validUser = "valid.username"; endpointConfig.Authenticator = new ListenerPasswordAuthenticator( (sender, username, password) => { // Implement your own ValidatePassword function return username == validUser && ValidatePassword(password); } ); // end::listener-config-client-auth-pwd[] // tag::listener-start[] // Initialize the listener var listener = new URLEndpointListener(endpointConfig); (55) // Start the listener listener.Start(); (56) // end::listener-start[] // end::listener-initialize[] #warning old-listener-config-tls-disable unused? // tag::old-listener-config-tls-disable[] endpointConfig.DisableTLS = true; // end::old-listener-config-tls-disable[] #warning listener-config-tls-id-nil-2 unused? // tag::listener-config-tls-id-nil-2[] // Use “anonymous” cert. These are self signed certs created by the system endpointConfig.TlsIdentity = null; // end::listener-config-tls-id-nil-2[] #warning old-listener-config-delta-sync unused? // tag::old-listener-config-delta-sync[] endpointConfig.EnableDeltaSync = true; // end::old-listener-config-delta-sync[] // tag::listener-status-check[] ulong connectionCount = listener.Status.ConnectionCount; (57) ulong activeConnectionCount = listener.Status.ActiveConnectionCount; (58) // end::listener-status-check[] // tag::listener-stop[] listener.Stop(); // end::listener-stop[] // tag::listener-get-network-interfaces[] foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 || ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet) { // do something with the interface(s) } } // end::listener-get-network-interfaces[] } public void GettingStarted2() { var collection = _Database.GetDefaultCollection(); { #warning listener-get-url-list unused? // tag::listener-get-url-list[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); var listener = new URLEndpointListener(endpointConfig); listener.Start(); // Note, converting to string omitted. Console.WriteLine("URLS are {0} ", listener.Urls); // end::listener-get-url-list[] #warning listener-config-tls-disable unused? // tag::listener-config-tls-disable[] endpointConfig.DisableTLS = true; (59) // end::listener-config-tls-disable[] #warning listener-local-db unused? // tag::listener-local-db[] // . . . preceding application logic . . . // Get the database (and create it if it doesn't exist) var database = new Database("mydb"); // end::listener-local-db[] // tag::listener-config-tls-id-full[] // tag::listener-config-tls-id-caCert[] // Use CA Cert // Create a TLSIdentity from an imported key-pair // . . . previously declared variables include ... X509Store store = new X509Store(StoreName.My); // create and label x509 store // Get keys and certificates from PKCS12 data byte[] certData = File.ReadAllBytes("c:client.p12"); (60) // . . . other user code . . . #warning import-tls-identity unused? // tag::import-tls-identity[] TLSIdentity identity = TLSIdentity.ImportIdentity( store, certData, (61) "123", // Password to access certificate data "couchbase-demo-cert", null); // Label to get cert in certificate map // NOTE: If a null label is supplied then the same // default directory for a Couchbase Lite database // is used for map. // end::import-tls-identity[] // end::listener-config-tls-id-caCert[] // tag::listener-config-tls-id-anon[] // Use an Anonymous Self-Signed Cert endpointConfig.TlsIdentity = null; (62) // end::listener-config-tls-id-anon[] #warning listener-config-tls-id-set unused? // tag::listener-config-tls-id-set[] // Set the TLS Identity endpointConfig.TlsIdentity = identity; (63) // end::listener-config-tls-id-set[] // end::listener-config-tls-id-full[] } { var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); // tag::listener-config-client-auth-root[] // Configure the client authenticator // to validate using ROOT CA // Get the valid cert chain, in this instance from // PKCS12 data containing private key, public key // and certificates (64) var clientData = File.ReadAllBytes("c:client.p12"); var ourCaData = File.ReadAllBytes("c:client-ca.der"); // Get the root certs from the data var rootCert = new X509Certificate2(ourCaData); (65) // Configure the authenticator to use the root certs var certAuth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert)); endpointConfig.Authenticator = certAuth; (66) // Initialize the listener using the config var listener = new URLEndpointListener(endpointConfig); // end::listener-config-client-auth-root[] // tag::listener-config-client-auth-lambda[] // Configure the client authenticator // to validate using application logic // Get the valid cert chain, in this instance from // PKCS12 data containing private key, public key // and certificates (67) clientData = File.ReadAllBytes("c:client.p12"); ourCaData = File.ReadAllBytes("c:client-ca.der"); // Configure the authenticator to pass the root certs // To a user supplied code block for authentication var callbackAuth = new ListenerCertificateAuthenticator( (object sender, X509Certificate2Collection chain) => { // . . . user supplied code block // . . . returns boolean value (true=authenticated) return true; }); (68) endpointConfig.Authenticator = callbackAuth; (69) // end::listener-config-client-auth-lambda[] } } public void datatype_usage() { #warning datatype_usage unused? // tag::datatype_usage[] // tag::datatype_usage_createdb[] // Get the database (and create it if it doesn’t exist). using var database = new Database("hoteldb"); var collection = database.GetDefaultCollection(); // end::datatype_usage_createdb[] // tag::datatype_usage_createdoc[] // Create your new document using var mutableDoc = new MutableDocument("hoteldoc"); // end::datatype_usage_createdoc[] // tag::datatype_usage_mutdict[] // Create and populate mutable dictionary var address = new MutableDictionaryObject(); address.SetString("street", "1 Main st."); address.SetString("city", "San Francisco"); address.SetString("state", "CA"); address.SetString("country", "USA"); address.SetString("code", "90210"); // end::datatype_usage_mutdict[] // tag::datatype_usage_mutarray[] // Create and populate mutable array var phones = new MutableArrayObject(); phones.AddString("650-000-0000"); phones.AddString("650-000-0001"); // end::datatype_usage_mutarray[] // tag::datatype_usage_populate[] // Initialize and populate the document // Add document type and hotel name as string mutableDoc.SetString("type", "hotel"); mutableDoc.SetString("name", "Hotel Java Mo"); // Add average room rate (float) mutableDoc.SetFloat("room_rate", 121.75f); // Add address (dictionary) mutableDoc.SetDictionary("address", address); // Add phone numbers(array) mutableDoc.SetArray("phones", phones); // end::datatype_usage_populate[] // tag::datatype_usage_persist[] collection.Save(mutableDoc); // end::datatype_usage_persist[] // tag::datatype_usage_closedb[] database.Close(); // end::datatype_usage_closedb[] // end::datatype_usage[] } public void datatype_dictionary() { var collection = _Database.GetDefaultCollection(); // tag::datatype_dictionary[] var doc = collection.GetDocument("doc1"); // Getting a dictionary from the document's properties var dict = doc.GetDictionary("address"); // Access a value with a key from the dictionary var street = dict.GetString("street"); // Iterate dictionary foreach (var key in dict.Keys) { Console.WriteLine($"Key {key} = {dict.GetValue(key)}"); } // Create a mutable copy var mutableDict = dict.ToMutable(); // end::datatype_dictionary[] } public void datatype_mutable_dictionary() { var collection = _Database.GetDefaultCollection(); // tag::datatype_mutable_dictionary[] // Create a new mutable dictionary and populate some keys/values var mutableDict = new MutableDictionaryObject(); mutableDict.SetString("street", "1 Main st."); mutableDict.SetString("city", "San Francisco"); // Add the dictionary to a document's properties and save the document using var mutableDoc = new MutableDocument("doc1"); mutableDoc.SetDictionary("address", mutableDict); collection.Save(mutableDoc); // end::datatype_mutable_dictionary[] } public void datatype_array() { var collection = _Database.GetDefaultCollection(); // tag::datatype_array[] var document = collection.GetDocument("doc1"); // Getting a phones array from the document's properties var array = document.GetArray("phones"); // Get element count var count = array.Count(); // Access an array element by index if (count >= 0) { var phone = array[1]; } // Iterate dictionary for (int i = 0; i < count; i++) { Console.WriteLine($"Item {i.ToString()} = {array[i]}"); } // Create a mutable copy var mutableArray = array.ToMutable(); // end::datatype_array[] } public void datatype_mutable_array() { var collection = _Database.GetDefaultCollection(); // tag::datatype_mutable_array[] // Create a new mutable array and populate data into the array var mutableArray = new MutableArrayObject(); mutableArray.AddString("650-000-0000"); mutableArray.AddString("650-000-0001"); // Set the array to document's properties and save the document using var mutableDoc = new MutableDocument("doc1"); mutableDoc.SetArray("phones", mutableArray); collection.Save(mutableDoc); // end::datatype_mutable_array[] } static void Main(string[] args) { // NOTE: PLEASE PLEASE PLEASE do not break the compilation of this file. It is // by far the easiest way to check for its correctness. If you don't know how to // compile a C# program, then find someone who does before you commit your changes!!! Console.WriteLine("This program is not meant to be executed, only compiled"); } } /* ----------------------------------------------------------- */ /* --------------------- ACTIVE SIDE ----------------------- */ /* --------------- stubs for documentation ----------------- */ /* ----------------------------------------------------------- */ class ActivePeer : IMessageEndpointDelegate { ActivePeer() { // tag::message-endpoint[] var database = new Database("dbname"); // The delegate must implement the `IMessageEndpointDelegate` protocol. var messageEndpointTarget = new MessageEndpoint(uid: "UID:123", target: "", protocolType: ProtocolType.MessageStream, delegateObject: this); // end::message-endpoint[] // tag::message-endpoint-replicator[] var replConfig = new ReplicatorConfiguration(messageEndpointTarget); replConfig.AddCollection(database.GetDefaultCollection()); // Create the replicator object var replicator = new Replicator(replConfig); // Start the replicator replicator.Start(); // end::message-endpoint-replicator[] } // tag::create-connection[] /* implementation of MessageEndpointDelegate */ public IMessageEndpointConnection CreateConnection(MessageEndpoint endpoint) { var connection = new ActivePeerConnection(); /* implements MessageEndpointConnection */ return connection; } // end::create-connection[] } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously class ActivePeerConnection : IMessageEndpointConnection { private IReplicatorConnection _replicatorConnection; public void Disconnect() { // tag::active-replicator-close[] _replicatorConnection?.Close(null); // end::active-replicator-close[] } public void Receive(byte[] data) { // tag::active-peer-receive[] var message = Message.FromBytes(data); _replicatorConnection?.Receive(message); // end::active-peer-receive[] } // tag::active-peer-close[] /* implementation of MessageEndpointConnection */ public async Task Close(Exception error) { // await socket.Close, etc (or do nothing if already closed) // throw MessagingException if something goes wrong (though // since it is "close" nothing special will happen) } // end::active-peer-close[] // tag::active-peer-open[] /* implementation of MessageEndpointConnection */ public async Task Open(IReplicatorConnection connection) { _replicatorConnection = connection; // await socket.Open(), etc // throw MessagingException if something goes wrong } // end::active-peer-open[] // tag::active-peer-send[] /* implementation of MessageEndpointConnection */ public async Task Send(Message message) { var data = message.ToByteArray(); // await Socket.Send(), etc // throw MessagingException if something goes wrong } // end::active-peer-send[] } /* ----------------------------------------------------------- */ /* --------------------- PASSIVE SIDE ---------------------- */ /* --------------- stubs for documentation ----------------- */ /* ----------------------------------------------------------- */ class PassivePeerConnection : IMessageEndpointConnection { private MessageEndpointListener _messageEndpointListener; private IReplicatorConnection _replicatorConnection; public void StartListener() { // tag::listener[] var database = new Database("mydb"); var endpointConfig = new MessageEndpointListenerConfiguration(new[] { database.GetDefaultCollection() }, ProtocolType.MessageStream); _messageEndpointListener = new MessageEndpointListener(endpointConfig); // end::listener[] } public void StopListener() { // tag::passive-stop-listener[] _messageEndpointListener?.CloseAll(); // end::passive-stop-listener[] } public void AcceptConnection() { // tag::advertizer-accept[] var connection = new PassivePeerConnection(); /* implements MessageEndpointConnection */ _messageEndpointListener?.Accept(connection); // end::advertizer-accept[] } public void Disconnect() { // tag::passive-replicator-close[] _replicatorConnection?.Close(null); // end::passive-replicator-close[] } public void Receive(byte[] data) { // tag::passive-peer-receive[] var message = Message.FromBytes(data); _replicatorConnection?.Receive(message); // end::passive-peer-receive[] } // tag::passive-peer-close[] /* implementation of MessageEndpointConnection */ public async Task Close(Exception error) { // await socket.Close, etc (or do nothing if already closed) // throw MessagingException if something goes wrong (though // since it is "close" nothing special will happen) } // end::passive-peer-close[] // tag::passive-peer-open[] /* implementation of MessageEndpointConnection */ public Task Open(IReplicatorConnection connection) { _replicatorConnection = connection; // socket should already be open on the passive side return Task.FromResult(true); } // end::passive-peer-open[] // tag::passive-peer-send[] /* implementation of MessageEndpointConnection */ public async Task Send(Message message) { var data = message.ToByteArray(); // await Socket.Send(), etc // throw MessagingException if something goes wrong } // end::passive-peer-send[] } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously // tag::predictive-model[] // tensorFlowModel is a fake implementation // this would be the implementation of the ml model you have chosen class TensorFlowModel { public static IDictionary<string, object> PredictImage(byte[] data) { // Do calculations, etc return null; } } class ImageClassifierModel : IPredictiveModel { public DictionaryObject Predict(DictionaryObject input) { var blob = input.GetBlob("photo"); if (blob == null) { return null; } var imageData = blob.Content; // tensorFlowModel is a fake implementation // this would be the implementation of the ml model you have chosen var modelOutput = TensorFlowModel.PredictImage(imageData); return new MutableDictionaryObject(modelOutput); (1) } } // end::predictive-model[] // tag::custom-logging[] class LogTestLogger : ILogger { public LogLevel Level { get; set; } public void Reset() { } public void Log(LogLevel level, LogDomain domain, string message) { // handle the message, for example piping it to // a third party framework } } // end::custom-logging[] // tag::local-win-conflict-resolver[] class LocalWinConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { return conflict.LocalDocument; } } // end::local-win-conflict-resolver[] // tag::remote-win-conflict-resolver[] class RemoteWinConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { return conflict.RemoteDocument; } } // end::remote-win-conflict-resolver[] // tag::merge-conflict-resolver[] class MergeConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { var localDict = conflict.LocalDocument.ToDictionary(); var remoteDict = conflict.RemoteDocument.ToDictionary(); var result = localDict.Concat(remoteDict) .GroupBy(kv => kv.Key) .ToDictionary(g => g.Key, g => g.First().Value); return new MutableDocument(conflict.DocumentID, result); } } // end::merge-conflict-resolver[] } #warning p2p-act-rep-func used, but contains nothing // tag::p2p-act-rep-func[] #warning p2p-act-rep-config-type used, but contains nothing // tag::p2p-act-rep-config-type[] // end::p2p-act-rep-config-type[] #warning autopurge-override used, but contains nothing // tag::autopurge-override[] // Set autopurge option // here we override its default // end::autopurge-override[] #warning p2p-act-rep-config-cont used, but contains nothing // tag::p2p-act-rep-config-cont[] // Configure Sync Mode // end::p2p-act-rep-config-cont[] #warning p2p-act-rep-config-self-cert used, but contains nothing // tag::p2p-act-rep-config-self-cert[] // Configure Server Security -- only accept self-signed certs // end::p2p-act-rep-config-self-cert[] // Configure Client Security (70) #warning p2p-act-rep-auth used, but contains nothing // tag::p2p-act-rep-auth[] // Configure basic auth using user credentials // end::p2p-act-rep-auth[] #warning p2p-act-rep-start-full used, but contains nothing // tag::p2p-act-rep-start-full[] // Initialize and start a replicator // Initialize replicator with configuration data #warning p2p-act-rep-add-change-listener used, but contains nothing // tag::p2p-act-rep-add-change-listener[] #warning p2p-act-rep-add-change-listener-label used, but contains nothing // tag::p2p-act-rep-add-change-listener-label[] //Optionally add a change listener (71) // end::p2p-act-rep-add-change-listener-label[] // end::p2p-act-rep-add-change-listener[] #warning p2p-act-rep-start used, but contains nothing // tag::p2p-act-rep-start[] // Start replicator // end::p2p-act-rep-start[] // end::p2p-act-rep-start-full[] // end::p2p-act-rep-func[] #warning p2p-act-rep-config-cacert used, but contains nothing // tag::p2p-act-rep-config-cacert[] // Configure Server Security -- only accept CA certs // end::p2p-act-rep-config-cacert[] #warning p2p-act-rep-config-cacert-pinned used, but contains nothing // tag::p2p-act-rep-config-cacert-pinned[] // Only CA Certs accepted // end::p2p-act-rep-config-cacert-pinned[] #warning p2p-act-rep-status used, but contains nothing // tag::p2p-act-rep-status[] // end::p2p-act-rep-status[] #warning p2p-act-rep-stop used, but contains nothing // tag::p2p-act-rep-stop[] // Stop replication. // end::p2p-act-rep-stop[] #warning p2p-tlsid-store-in-keychain used, but contains nothing // tag::p2p-tlsid-store-in-keychain[] // end::p2p-tlsid-store-in-keychain[] #warning p2p-tlsid-delete-id-from-keychain used, but contains nothing // tag::p2p-tlsid-delete-id-from-keychain[] // end::p2p-tlsid-delete-id-from-keychain[] public class MyClass { public Database Database { get; set; } public Replicator Replicator { get; set; } (72) public void StartReplication() { // tag::sgw-repl-pull[] var url = new Uri("wss://localhost:4984/db"); (73) var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target) { ReplicatorType = ReplicatorType.Pull }; config.AddCollection(Database.GetDefaultCollection()); Replicator = new Replicator(config); Replicator.Start(); // end::sgw-repl-pull[] } public void InitReplication() { // tag::sgw-act-rep-initialize[] // initialize the replicator configuration var url = new URLEndpoint(new Uri("wss://10.0.2.2:4984/anotherDB")); (74) var replConfig = new ReplicatorConfiguration(url); // Add collections to the config now // end::sgw-act-rep-initialize[] } } -
Import from an existing resource bundle
// // Program.cs // // Copyright (c) 2017 Couchbase, Inc All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using Couchbase.Lite; using Couchbase.Lite.DI; using Couchbase.Lite.Enterprise.Query; using Couchbase.Lite.Logging; using Couchbase.Lite.P2P; using Couchbase.Lite.Query; using Couchbase.Lite.Sync; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Net.NetworkInformation; using System.Security; using System.Security.Cryptography.X509Certificates; namespace api_walkthrough { class Hotel { public string Id { get; set; } public string Name { get; set; } } class Program { private static readonly Database _Database = null; private static readonly Replicator _Replicator = null; private static readonly URLEndpointListener _listener = null; public void GettingStarted() { // tag::getting-started[] // using System; // using Couchbase.Lite; // using Couchbase.Lite.Query; // using Couchbase.Lite.Sync; // Get the database (and create it if it doesn't exist) var database = new Database("mydb"); var collection = database.GetDefaultCollection(); // Create a new document (i.e. a record) in the database var id = default(string); using var createdDoc = new MutableDocument(); createdDoc.SetFloat("version", 2.0f) .SetString("type", "SDK"); // Save it to the database collection.Save(createdDoc); id = createdDoc.Id; // Update a document using var doc = collection.GetDocument(id); using var mutableDoc = doc.ToMutable(); createdDoc.SetString("language", "C#"); collection.Save(createdDoc); using var docAgain = collection.GetDocument(id); Console.WriteLine($"Document ID :: {docAgain.Id}"); Console.WriteLine($"Learning {docAgain.GetString("language")}"); // Create a query to fetch documents of type SDK // i.e. SELECT * FROM database WHERE type = "SDK" using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("SDK"))); // Run the query var result = query.Execute(); Console.WriteLine($"Number of rows :: {result.AllResults().Count}"); // Create replicator to push and pull changes to and from the cloud var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4984/getting-started-db")); var replConfig = new ReplicatorConfiguration(targetEndpoint); replConfig.AddCollection(database.GetDefaultCollection()); // Add authentication replConfig.Authenticator = new BasicAuthenticator("john", "pass"); // Create replicator (make sure to add an instance or static variable // named _Replicator) var replicator = new Replicator(replConfig); replicator.AddChangeListener((sender, args) => { if (args.Status.Error != null) { Console.WriteLine($"Error :: {args.Status.Error}"); } }); replicator.Start(); // Later, stop and dispose the replicator *before* closing/disposing the database // end::getting-started[] } private static void TestReplicatorConflictResolver() { var collection = _Database.GetDefaultCollection(); // tag::replication-conflict-resolver[] var target = new URLEndpoint(new Uri("ws://localhost:4984/mydatabase")); var replConfig = new ReplicatorConfiguration(target); replConfig.AddCollection(collection, new CollectionConfiguration() { ConflictResolver = new LocalWinConflictResolver() }); var replicator = new Replicator(replConfig); replicator.Start(); // end::replication-conflict-resolver[] } private static void TestSaveWithConflictHandler() { var collection = _Database.GetDefaultCollection(); // tag::update-document-with-conflict-handler[] using var doc = collection.GetDocument("xyz"); using var mutableDoc = doc.ToMutable(); mutableDoc.SetString("name", "apples"); collection.Save(mutableDoc, (updated, current) => { var currentDict = current.ToDictionary(); var newDict = updated.ToDictionary(); var result = newDict.Concat(currentDict) .GroupBy(kv => kv.Key) .ToDictionary(g => g.Key, g => g.First().Value); updated.SetData(result); return true; }); // end::update-document-with-conflict-handler[] } private static bool IsValidCredential(string name, SecureString password) { return true; } // helper private static void TestInitListener() { var database = new Database("other-database"); var collection = database.GetDefaultCollection(); #warning init-urllistener Unused? // tag::init-urllistener[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); endpointConfig.TlsIdentity = null; // Use with anonymous self-signed cert endpointConfig.Authenticator = new ListenerPasswordAuthenticator((sender, username, password) => { if (IsValidCredential(username, password)) { return true; } return false; }); var listener = new URLEndpointListener(endpointConfig); // end::init-urllistener[] } private static void TestListenerStart() { var listener = _listener; #warning start-urllistener Unused? // tag::start-urllistener[] // CouchbaseLiteException will be thrown when the listener cannot be started. The most common error // would be that the configured port has already been used. listener.Start(); // end::start-urllistener[] } private static void TestListenerStop() { var listener = _listener; #warning stop-urllistener Unused? // tag::stop-urllistener[] listener.Stop(); // end::stop-urllistener[] } private static void TestCreateSelfSignedCert() { X509Store store = new X509Store(StoreName.My); // The identity will be stored in the secure // storage using the given label. DateTimeOffset fiveMinToExpireCert = DateTimeOffset.UtcNow.AddMinutes(5); // tag::create-self-signed-cert[] // tag::listener-config-tls-id-SelfSigned[] var identity = TLSIdentity.CreateIdentity(true, /* isServer */ new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "Couchbase Inc" } }, // The common name attribute is required // when creating a CSR. If it is not presented // in the cert, an exception is thrown. fiveMinToExpireCert, // If the expiration date is not specified, // the certs expiration will be 365 days store, "CBL-Server-Cert", null); // The key label to get cert in certificate map. // If null, the same default directory // for a Couchbase Lite db is used for map. // end::listener-config-tls-id-SelfSigned[] // end::create-self-signed-cert[] } private static void TestImportTLSIdentity() { X509Store store = new X509Store(StoreName.My); // The identity will be stored in the secure storage using the given label byte[] data = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates // tag::import-tls-identity[] // tag::listener-config-tls-id-caCert[] var identity = TLSIdentity.ImportIdentity(store, data, "123", // The password that is needed to access the certificate data "CBL-Client-Cert", null); // The key label to get cert in certificate map. // If null, the same default directory // for a Couchbase Lite db is used for map. // end::listener-config-tls-id-caCert[] // end::import-tls-identity[] } private static void TestClientCertAuthenticatorRootCerts() { var otherDatabase = new Database("other-database"); var collection = otherDatabase.GetDefaultCollection(); X509Store store = new X509Store(StoreName.My); #warning client-cert-authenticator-root-certs unused? // tag::client-cert-authenticator-root-certs[] byte[] caData, clientData; clientData = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates caData = File.ReadAllBytes("C:\\client-ca.der"); // Root certs var rootCert = new X509Certificate2(caData); var auth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert)); // Create URL Endpoint Listener var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection }); listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default. listenerConfig.Authenticator = auth; var listener = new URLEndpointListener(listenerConfig); listener.Start(); // Client identity var identity = TLSIdentity.ImportIdentity(store, clientData, "123", "CBL-Client-Cert", null); // Replicator -- Client var database = new Database("client-database"); var builder = new UriBuilder( "wss", "localhost", listener.Port, $"/{listener.Config.Collections.First().Name}" ); var url = builder.Uri; var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.PushAndPull; config.Continuous = false; config.Authenticator = new ClientCertificateAuthenticator(identity); config.AcceptOnlySelfSignedServerCertificate = true; config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0]; var replicator = new Replicator(config); replicator.Start(); // Dispose after stop // Stop listener after replicator is stopped listener.Stop(); // end::client-cert-authenticator-root-certs[] } private static void TestClientCertAuthenticator() { var otherDatabase = new Database("other-database"); var collection = otherDatabase.GetDefaultCollection(); X509Store store = new X509Store(StoreName.My); #warning client-cert-authenticator unused? // tag::client-cert-authenticator[] // Create Listener Certificate Authenticator var auth = new ListenerCertificateAuthenticator((sender, cert) => { if (cert.Count != 1) { return false; } return cert[0].SubjectName.Name?.Replace("CN=", "") == "couchbase"; }); // Create URL Endpoint Listener var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection }); listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default. listenerConfig.Authenticator = auth; var listener = new URLEndpointListener(listenerConfig); listener.Start(); // User Identity var identity = TLSIdentity.CreateIdentity(false, new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "couchbase" } }, null, store, "ClientCertLabel", null); // Replicator -- Client var database = new Database("client-database"); var builder = new UriBuilder( "wss", "localhost", listener.Port, $"/{listener.Config.Collections.First().Name}" ); var url = builder.Uri; var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.PushAndPull; config.Continuous = false; config.Authenticator = new ClientCertificateAuthenticator(identity); config.AcceptOnlySelfSignedServerCertificate = false; config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0]; var replicator = new Replicator(config); replicator.Start(); // Dispose after stopped // Stop listener after replicator is stopped listener.Stop(); // end::client-cert-authenticator[] } public void IdentityWithLabel() { X509Store store = null; byte[] clientData = null; var replConfig = new ReplicatorConfiguration(null); // tag::p2p-tlsid-tlsidentity-with-label[] // Client identity var identity = TLSIdentity.ImportIdentity(store, clientData, "123", "CBL-Client-Cert", null); (1) replConfig.Authenticator = new ClientCertificateAuthenticator(identity); (2) // end::p2p-tlsid-tlsidentity-with-label[] } public void UseEncryption() { // Enterprise edition only // tag::database-encryption[] // Create a new, or open an existing database with encryption enabled var config = new DatabaseConfiguration { // Or, derive a key yourself and pass a byte array of the proper size EncryptionKey = new EncryptionKey("password") }; using var database = new Database("seekrit", config); // Change the encryption key (or add encryption if the DB is unencrypted) database.ChangeEncryptionKey(new EncryptionKey("betterpassw0rd")); // Remove encryption database.ChangeEncryptionKey(null); // end::database-encryption[] } private static void ResetReplicatorCheckpoint() { var url = new Uri("ws://localhost:4984/db"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); bool resetCheckpointRequired_Example = true; config.AddCollection(_Database.GetDefaultCollection()); var replicator = new Replicator(config); // tag::replication-reset-checkpoint[] // replicator is a Replicator instance if (resetCheckpointRequired_Example) { replicator.Start(true); (3) } else { replicator.Start(false); } // Stop and dispose replicator later // end::replication-reset-checkpoint[] } private static void Read1xAttachment() { using var doc = new MutableDocument(); // tag::1x-attachment[] var attachments = doc.GetDictionary("_attachments"); var avatar = attachments.GetBlob("avatar"); var content = avatar?.Content; // end::1x-attachment[] } private static void CreateNewDatabase() { // tag::new-database[] var database = new Database("my-database"); // end::new-database[] } private static void CloseDatabase() { var database = _Database; // tag::close-database[] database.Close(); // end::close-database[] } private static void DatabaseFullsync() { var config = new DatabaseConfiguration(); // tag::database-fullsync[] // this enables fullsync config.FullSync = true; // end::database-fullsync[] } private static void CreateCollection() { // tag::scopes-manage-create-collection[] var collectionWithDefaultScope = _Database.CreateCollection("colA"); var collection = _Database.CreateCollection("colA", "scopeA"); // Scope with named scopeA will be created if it's not existed. There is no public API to create a Scope. // end::scopes-manage-create-collection[] } private static void DeleteCollection() { // tag::scopes-manage-drop-collection[] _Database.DeleteCollection("colA", "scopeA"); // Scope with named scopeA will be deleted if there is no collections in the scope after the last collection is deleted via this API. There is no public API to remove a Scope. // end::scopes-manage-drop-collection[] } private static void ListCollectionsAndScopes() { // tag::scopes-manage-list[] // Get Scopes var scopes = _Database.GetScopes(); // Get Collections of a Scope named scopeA var scopeA = _Database.GetScope("scopeA"); var collectionsInScopeA = scopeA.GetCollections(); // end::scopes-manage-list[] } private static void ChangeLogging() { // tag::logging[] // This sets the overall level of console logging Database.Log.Console.Level = LogLevel.Verbose; // This flag can enable and disable specific domains Database.Log.Console.Domains = LogDomain.Couchbase | LogDomain.Database; // end::logging[] } private static void LoadPrebuilt() { // tag::prebuilt-database[] // Note: Getting the path to a database is platform-specific. For .NET Core / .NET Framework this // can be a simple filesystem path. For UWP, you will need to get the path from your assets. For // iOS you need to get the path from the main bundle. For Android you need to extract it from your // assets to a temporary directory and then pass that path. var path = Path.Combine(Environment.CurrentDirectory, "travel-sample.cblite2" + Path.DirectorySeparatorChar); if (!Database.Exists("travel-sample", null)) { Database.Copy(path, "travel-sample", null); } // end::prebuilt-database[] } private static void QueryDeletedDocuments() { var collection = _Database.GetDefaultCollection(); // tag::query-deleted-documents[] // Query documents that have been deleted var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(Meta.IsDeleted); // end::query-deleted-documents[] } private static void CreateDocument() { var collection = _Database.GetDefaultCollection(); // tag::initializer[] using var mutableDoc = new MutableDocument("xyz"); mutableDoc.SetString("type", "task") .SetString("owner", "todo") .SetDate("createdAt", DateTimeOffset.UtcNow); collection.Save(mutableDoc); // end::initializer[] } private static void UpdateDocument() { var collection = _Database.GetDefaultCollection(); // tag::update-document[] using var doc = collection.GetDocument("xyz"); using var mutableDoc = doc.ToMutable(); mutableDoc.SetString("name", "apples"); collection.Save(mutableDoc); // end::update-document[] } private static void UseTypedAccessors() { using var mutableDoc = new MutableDocument(); // tag::date-getter[] mutableDoc.SetValue("createdAt", DateTimeOffset.UtcNow); var date = mutableDoc.GetDate("createdAt"); // end::date-getter[] Console.WriteLine(date); } private static void DoBatchOperation() { var database = _Database; var collection = database.GetDefaultCollection(); // tag::batch[] database.InBatch(() => { for (var i = 0; i < 10; i++) { using var mutableDoc = new MutableDocument(); mutableDoc.SetString("type", "user"); mutableDoc.SetString("name", $"user {i}"); mutableDoc.SetBoolean("admin", false); collection.Save(mutableDoc); Console.WriteLine($"Saved user document {mutableDoc.GetString("name")}"); } }); // end::batch[] } private static void DatabaseChangeListener() { var collection = _Database.GetDefaultCollection(); // tag::document-listener[] collection.AddDocumentChangeListener("user.john", (sender, args) => { using var doc = collection.GetDocument(args.DocumentID); Console.WriteLine($"Status :: {doc.GetString("verified_account")}"); }); // end::document-listener[] } private static void DocumentExpiration() { var collection = _Database.GetDefaultCollection(); // tag::document-expiration[] // Purge the document one day from now var ttl = DateTimeOffset.UtcNow.AddDays(1); collection.SetDocumentExpiration("doc123", ttl); // Reset expiration collection.SetDocumentExpiration("doc1", null); // Query documents that will be expired in less than five minutes var fiveMinutesFromNow = DateTimeOffset.UtcNow.AddMinutes(5).ToUnixTimeMilliseconds(); using var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(Meta.Expiration.LessThan(Expression.Double(fiveMinutesFromNow))); // end::document-expiration[] } private static void UseBlob() { var collection = _Database.GetDefaultCollection(); using var newTask = new MutableDocument(); // tag::blob[] // Note: Reading the data is implementation dependent, as with prebuilt databases var image = File.ReadAllBytes("avatar.jpg"); (4) var blob = new Blob("image/jpeg", image); (5) newTask.SetBlob("avatar", blob); (6) collection.Save(newTask); // end::blob[] } public void CreateIndex() { var collection = _Database.GetDefaultCollection(); // tag::query-index[] // tag::scopes-manage-index-collection[] string[] indexProperties = new string[] { "type", "name" }; var config = new ValueIndexConfiguration(indexProperties); collection.CreateIndex("TypeNameIndex", config); // end::scopes-manage-index-collection[] // end::query-index[] } public void CreateIndex_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::query-index_Querybuilder[] // For value types, this is optional but provides performance enhancements var index = IndexBuilder.ValueIndex( ValueIndexItem.Expression(Expression.Property("type")), ValueIndexItem.Expression(Expression.Property("name"))); (7) collection.CreateIndex("TypeNameIndex", index); // end::query-index_Querybuilder[] } private static void SelectMeta() { var collection = _Database.GetDefaultCollection(); #warning query-select-meta unused? // tag::query-select-meta[] // tag::query-select-props[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("type"), SelectResult.Property("name")) .From(DataSource.Collection(collection)); foreach (var result in query.Execute()) { Console.WriteLine($"Document ID :: {result.GetString("id")}"); Console.WriteLine($"Document Name :: {result.GetString("name")}"); } // end::query-select-props[] // end::query-select-meta[] } private static void SelectAll() { var collection = _Database.GetDefaultCollection(); { // tag::query-select-all[] using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)); // end::query-select-all[] } { // tag::live-query[] var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)); (8) // Adds a query change listener. // Changes will be posted on the main queue. var token = query.AddChangeListener((sender, args) => (9) { var allResult = args.Results.AllResults(); foreach (var result in allResult) { Console.WriteLine(result.Keys); /* Update UI */ } }); // end::live-query[] // tag::stop-live-query[] query.RemoveChangeListener(token); query.Dispose(); // end::stop-live-query[] } } private static void SelectWhere() { var collection = _Database.GetDefaultCollection(); // tag::query-where[] using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { var dict = result.GetDictionary(collection.Name); Console.WriteLine($"Document Name :: {dict?.GetString("name")}"); } // end::query-where[] } private static void UseCollectionContains() { var collection = _Database.GetDefaultCollection(); // tag::query-collection-operator-contains[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name"), SelectResult.Property("public_likes")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel")) .And(ArrayFunction.Contains(Expression.Property("public_likes"), Expression.String("Armani Langworth")))); foreach (var result in query.Execute()) { var publicLikes = result.GetArray("public_likes"); var jsonString = JsonConvert.SerializeObject(publicLikes); Console.WriteLine($"Public Likes :: {jsonString}"); } // end::query-collection-operator-contains[] } private static void UseCollectionIn() { var collection = _Database.GetDefaultCollection(); // tag::query-collection-operator-in[] var values = new IExpression[] { Expression.Property("first"), Expression.Property("last"), Expression.Property("username") }; using var query = QueryBuilder.Select( SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.String("Armani").In(values)); foreach (var result in query.Execute()) { var body = result.GetDictionary(0); var jsonString = JsonConvert.SerializeObject(body); Console.WriteLine($"In results :: {jsonString}"); } // end::query-collection-operator-in[] } private static void SelectLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("Royal Engineers Museum")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator[] } private static void SelectWildcardLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator-wildcard-match[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("Eng%e%")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator-wildcard-match[] } private static void SelectWildcardCharacterLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator-wildcard-character-match[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Expression.Property("name").Like(Expression.String("Royal Eng____rs Museum")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator-wildcard-character-match[] } private static void SelectRegex() { var collection = _Database.GetDefaultCollection(); // tag::query-regex-operator[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Expression.Property("name").Regex(Expression.String("\\bEng.*e\\b")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-regex-operator[] } private static void SelectJoin() { var collection = _Database.GetDefaultCollection(); var collection2 = _Database.GetDefaultCollection(); // tag::query-join[] using var query = QueryBuilder.Select( SelectResult.Expression(Expression.Property("name").From("airline")), SelectResult.Expression(Expression.Property("callsign").From("airline")), SelectResult.Expression(Expression.Property("destinationairport").From("route")), SelectResult.Expression(Expression.Property("stops").From("route")), SelectResult.Expression(Expression.Property("airline").From("route"))) .From(DataSource.Collection(collection).As("airline")) .Join(Join.InnerJoin(DataSource.Collection(collection2).As("route")) .On(Meta.ID.From("airline").EqualTo(Expression.Property("airlineid").From("route")))) .Where(Expression.Property("type").From("route").EqualTo(Expression.String("route")) .And(Expression.Property("type").From("airline").EqualTo(Expression.String("airline"))) .And(Expression.Property("sourceairport").From("route").EqualTo(Expression.String("RIX")))); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-join[] } private static void GroupBy() { var collection = _Database.GetDefaultCollection(); // tag::query-groupby[] using var query = QueryBuilder.Select( SelectResult.Expression(Function.Count(Expression.All())), SelectResult.Property("country"), SelectResult.Property("tz")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("airport")) .And(Expression.Property("geo.alt").GreaterThanOrEqualTo(Expression.Int(300)))) .GroupBy(Expression.Property("country"), Expression.Property("tz")); foreach (var result in query.Execute()) { Console.WriteLine( $"There are {result.GetInt("$1")} airports in the {result.GetString("tz")} timezone located in {result.GetString("country")} and above 300 ft"); } // end::query-groupby[] } private static void OrderBy() { var collection = _Database.GetDefaultCollection(); // tag::query-orderby[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("title")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .OrderBy(Ordering.Property("title").Ascending()) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Title :: {result.GetString("title")}"); } // end::query-orderby[] } private static void TestExplainStatement() { var collection = _Database.GetDefaultCollection(); { // tag::query-explain-all[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .GroupBy(Expression.Property("country")) .OrderBy(Ordering.Property("title").Ascending()); (10) Console.WriteLine(query.Explain()); (11) // end::query-explain-all[] } { // tag::query-explain-like[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").Like(Expression.String("%hotel%")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (12) Console.WriteLine(query.Explain()); // end::query-explain-like[] } { // tag::query-explain-nopfx[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").Like(Expression.String("hotel%")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (13) Console.WriteLine(query.Explain()); // end::query-explain-nopfx[] } { // tag::query-explain-function[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Function.Lower(Expression.Property("type")).EqualTo(Expression.String("hotel"))); (14) Console.WriteLine(query.Explain()); // end::query-explain-function[] } { // tag::query-explain-nofunction[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))); (15) Console.WriteLine(query.Explain()); // end::query-explain-nofunction[] } } public void CreateFullTextIndex() { var collection = _Database.GetDefaultCollection(); // tag::fts-index[] string[] indexProperties = new string[] { "overview", "name" }; var config = new FullTextIndexConfiguration(indexProperties); collection.CreateIndex("overviewFTSIndex", config); // end::fts-index[] } public void FullTextSearch() { var collection = _Database.GetDefaultCollection(); // tag::fts-query[] var query = collection.CreateQuery("SELECT * FROM _ WHERE MATCH(overviewFTSIndex, 'Michigan') ORDER BY RANK(overviewFTSIndex)"); foreach (var result in query.Execute()) { Console.WriteLine($"Document id {result.GetString(0)}"); } // end::fts-query[] } private static void CreateFullTextIndex_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::fts-index_Querybuilder[] var index = IndexBuilder.FullTextIndex(FullTextIndexItem.Property("overview")).IgnoreAccents(false); collection.CreateIndex("overviewFTSIndex", index); // end::fts-index_Querybuilder[] } private static void FullTextSearch_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::fts-query_Querybuilder[] var whereClause = FullTextFunction.Match(Expression.FullTextIndex("overviewFTSIndex"), "'michigan'"); using var query = QueryBuilder.Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(whereClause); foreach (var result in query.Execute()) { Console.WriteLine($"Document id {result.GetString(0)}"); } // end::fts-query_Querybuilder[] } private static void StartReplication() { var collection = _Database.GetDefaultCollection(); #warning replication unused? // tag::replication[] // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) var url = new Uri("ws://localhost:4984/db"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target) { ReplicatorType = ReplicatorType.Pull }; config.AddCollection(collection); var replicator = new Replicator(config); replicator.Start(); // end::replication[] } private static void ConsoleLogging() { // tag::console-logging[] Database.Log.Console.Domains = LogDomain.All; (16) Database.Log.Console.Level = LogLevel.Verbose; (17) // end::console-logging[] // tag::console-logging-db[] Database.Log.Console.Domains = LogDomain.Database; // end::console-logging-db[] } private static void FileLogging() { // tag::file-logging[] var tempFolder = Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>().DefaultDirectory(), "cbllog"); var config = new LogFileConfiguration(tempFolder) (18) { MaxRotateCount = 5, (19) MaxSize = 10240, (20) UsePlaintext = false (21) }; Database.Log.File.Config = config; // Apply configuration Database.Log.File.Level = LogLevel.Info; (22) // end::file-logging[] } private static void EnableCustomLogging() { // tag::set-custom-logging[] Database.Log.Custom = new LogTestLogger(); (23) // You can also specify the level of logging the logger receives Database.Log.Custom = new LogTestLogger { Level = LogLevel.Warning }; // end::set-custom-logging[] } private static void WriteConsoleLog() { #warning write-console-logmsg unused? // tag::write-console-logmsg[] Database.Log.Console.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-console-logmsg[] } private static void WriteCustomLog() { #warning write-custom-logmsg unused? // tag::write-custom-logmsg[] Database.Log.Custom?.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-custom-logmsg[] } private static void WriteFileLog() { #warning write-file-logmsg unused? // tag::write-file-logmsg[] Database.Log.File.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-file-logmsg[] } private static void EnableBasicAuth() { var collection = _Database.GetDefaultCollection(); #warning basic-authentication unused? // tag::basic-authentication[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection); config.Authenticator = new BasicAuthenticator("john", "pass"); var replicator = new Replicator(config); replicator.Start(); // end::basic-authentication[] } private static void EnableSessionAuth() { var collection = _Database.GetDefaultCollection(); // tag::session-authentication[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection); config.Authenticator = new SessionAuthenticator("904ac010862f37c8dd99015a33ab5a3565fd8447"); var replicator = new Replicator(config); replicator.Start(); // end::session-authentication[] } private static void SetupReplicatorListener() { var replicator = _Replicator; #warning replication-status unused? // tag::replication-status[] replicator.AddChangeListener((sender, args) => { if (args.Status.Activity == ReplicatorActivityLevel.Stopped) { Console.WriteLine("Replication stopped"); } }); // end::replication-status[] } private static void ReplicatorPendingDocuments() { // tag::replication-pendingdocuments[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var database = new Database("myDB"); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.Push; // tag::replication-push-pendingdocumentids[] var replicator = new Replicator(config); var pendingDocIDs = new HashSet<string>(replicator.GetPendingDocumentIDs(database.GetDefaultCollection())); (24) // end::replication-push-pendingdocumentids[] if (pendingDocIDs.Count > 0) { Console.WriteLine($"There are {pendingDocIDs.Count} documents pending"); replicator.AddChangeListener((sender, change) => { Console.WriteLine($"Replicator activity level is " + change.Status.Activity.ToString()); // iterate and report-on previously // retrieved pending docids 'list' foreach (var docID in pendingDocIDs) #warning replication-push-isdocumentpending unused? // tag::replication-push-isdocumentpending[] if (!replicator.IsDocumentPending(docID, database.GetDefaultCollection())) (25) { Console.WriteLine($"Doc ID {docID} now pushed"); }; // end::replication-push-isdocumentpending[] }); replicator.Start(); } // end::replication-pendingdocuments[] } private static void ReplicatorDocumentEvent() { var replicator = _Replicator; // tag::add-document-replication-listener[] var token = replicator.AddDocumentReplicationListener((sender, args) => { var direction = args.IsPush ? "Push" : "Pull"; Console.WriteLine($"Replication type :: {direction}"); foreach (var doc in args.Documents) { if (doc.Error == null) { Console.WriteLine($"Doc ID :: {doc.Id}"); if (doc.Flags.HasFlag(DocumentFlags.Deleted)) { Console.WriteLine("Successfully replicated a deleted document"); } } else { // There was an error } } }); replicator.Start(); // end::add-document-replication-listener[] // tag::remove-document-replication-listener[] replicator.RemoveChangeListener(token); // end::remove-document-replication-listener[] } private static void SetupReplicatorErrorListener() { // This can be done in the SetupReplicatorListener method // But it is separate so that we can have two documentation entries var replicator = _Replicator; // tag::replication-error-handling[] replicator.AddChangeListener((sender, args) => { if (args.Status.Error != null) { Console.WriteLine($"Error :: {args.Status.Error}"); } }); // end::replication-error-handling[] } private static void DatabaseReplica() { var collection = _Database.GetDefaultCollection(); using (var database2 = new Database("backup")) { // EE feature: This code will not compile on the community edition // tag::database-replica[] var targetDatabase = new DatabaseEndpoint(database2); var config = new ReplicatorConfiguration(targetDatabase) { ReplicatorType = ReplicatorType.Push }; config.AddCollection(collection); var replicator = new Replicator(config); replicator.Start(); // end::database-replica[] } } private X509Certificate2 GetCertificate(string name) { return null; } public void PinCertificate() { var url = new Uri("wss://localhost:4984/db"); var target = new URLEndpoint(url); // tag::certificate-pinning[] // Note: `GetCertificate` is a placeholder method. This would be the platform-specific method // to find and load the certificate as an instance of `X509Certificate2`. // For .NET Core / .NET Framework this can be loaded from the filesystem path. // For WinUI, from the assets directory. // For iOS, from the main bundle. // For Android, from the assets directory. var certificate = GetCertificate("cert.cer"); var config = new ReplicatorConfiguration(target) { PinnedServerCertificate = certificate }; // end::certificate-pinning[] } public void ReplicationCustomHeaders() { var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); // tag::replication-custom-header[] var config = new ReplicatorConfiguration(target) { Headers = new Dictionary<string, string> { ["CustomHeaderName"] = "Value" } }; // end::replication-custom-header[] } private static void PushWithFilter(Database database) { var collection = _Database.GetDefaultCollection(); // tag::replication-push-filter[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection, new CollectionConfiguration() { PushFilter = (document, flags) => (1) { if (flags.HasFlag(DocumentFlags.Deleted)) { return false; } return true; } }); // Dispose() later var replicator = new Replicator(config); replicator.Start(); // end::replication-push-filter[] } private static void PullWithFilter(Database database) { var collection = _Database.GetDefaultCollection(); // tag::replication-pull-filter[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection, new CollectionConfiguration() { PullFilter = (document, flags) => (1) { if (document.GetString("type") == "draft") { return false; } return true; } }); // Dispose() later var replicator = new Replicator(config); replicator.Start(); // end::replication-pull-filter[] } public void TestCustomRetryConfig() { // tag::replication-retry-config[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); // other config as required . . . #warning replication-set-heartbeat unused? // tag::replication-set-heartbeat[] config.Heartbeat = TimeSpan.FromSeconds(120); // (26) // end::replication-set-heartbeat[] // tag::replication-set-maxattempts[] #warning replication-set-maxattempts unused? config.MaxAttempts = 20; // (27) // end::replication-set-maxattempts[] // tag::replication-set-maxattemptwaittime[] #warning replication-set-maxattemptwaittime unused? config.MaxAttemptsWaitTime = TimeSpan.FromSeconds(600); // (28) // end::replication-set-maxattemptwaittime[] // other config as required . . . var replicator = new Replicator(config); // end::replication-retry-config[] } private static void UsePredictiveModel() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::register-model[] var model = new ImageClassifierModel(); Database.Prediction.RegisterModel("ImageClassifier", model); // end::register-model[] // tag::predictive-query-value-index[] var index = IndexBuilder.ValueIndex(ValueIndexItem.Property("label")); collection.CreateIndex("value-index-image-classifier", index); // end::predictive-query-value-index[] // tag::unregister-model[] Database.Prediction.UnregisterModel("ImageClassifier"); // end::unregister-model[] } } private static void UsePredictiveIndex() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::predictive-query-predictive-index[] var input = Expression.Dictionary(new Dictionary<string, object> { ["photo"] = Expression.Property("photo") }); var index = IndexBuilder.PredictiveIndex("ImageClassifier", input); collection.CreateIndex("predictive-index-image-classifier", index); // end::predictive-query-predictive-index[] } } private static void DoPredictiveQuery() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::predictive-query[] var input = Expression.Dictionary(new Dictionary<string, object> { ["photo"] = Expression.Property("photo") }); var prediction = Function.Prediction("ImageClassifier", input); (1) using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(prediction.Property("label").EqualTo(Expression.String("car")) .And(prediction.Property("probability").GreaterThanOrEqualTo(Expression.Double(0.8)))); var result = query.Execute(); Console.WriteLine($"Number of rows: {result.Count()}"); // end::predictive-query[] } } public List<Result> docsonly_N1QLQueryString(Database argDB) { DatabaseConfiguration config = new DatabaseConfiguration(); Database database = new Database("dbName", config); // tag::query-syntax-n1ql[] using var query = database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = \"hotel\""); (29) return query.Execute().AllResults(); // end::query-syntax-n1ql[] } public void docsonly_N1QLQueryStringParams(Database argDB) { var database = _Database; // tag::query-syntax-n1ql-params[] using var query = database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = $type"); (30) var n1qlParams = new Parameters(); n1qlParams.SetString("type", "hotel"); (31) query.Parameters = n1qlParams; var results = query.Execute().AllResults(); // end::query-syntax-n1ql-params[] } public void testQuerySyntaxAll() { // tag::query-syntax-all[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-all[] // tag::query-access-all[] var results = query.Execute().AllResults(); var hotels = new List<Dictionary<string, object>>(); if (results?.Count > 0) { foreach (var result in results) { // get the result into our dictionary object var thisDocsProps = result.GetDictionary("hotels"); (32) if (thisDocsProps != null) { var docID = thisDocsProps.GetString("id"); (33) var docName = thisDocsProps.GetString("name"); var docCity = thisDocsProps.GetString("city"); var docType = thisDocsProps.GetString("type"); var hotel = thisDocsProps.ToDictionary(); hotels.Add(hotel); } } } // end::query-access-all[] // tag::query-access-json[] foreach (var result in query.Execute()) { // get the result into a JSON String var docJSONString = result.ToJSON(); // Get a native dictionary object using the JSON string var dictFromJSONstring = JsonConvert. DeserializeObject<Dictionary<string, object>> (docJSONString); // use the created dictionary if (dictFromJSONstring != null) { var docID = dictFromJSONstring["id"].ToString(); var docName = dictFromJSONstring["name"].ToString(); var docCity = dictFromJSONstring["city"].ToString(); var docType = dictFromJSONstring["type"].ToString(); } //Get a custom object using the JSON string Hotel hotel = JsonConvert.DeserializeObject<Hotel>(docJSONString); } // end::query-access-json[] } public void testQuerySyntaxProps() { // tag::query-syntax-props[] var database = new Database("hotels"); List<Dictionary<string, object>> hotels = new List<Dictionary<string, object>>(); var query = QueryBuilder.Select( SelectResult.Property("type"), SelectResult.Property("name"), SelectResult.Property("city")).From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-props[] // tag::query-access-props[] var results = query.Execute().AllResults(); foreach (var result in results) { // get the returned array of k-v pairs into a dictionary var hotel = result.ToDictionary(); // add hotel dictionary to list of hotel dictionaries hotels.Add(hotel); // use the properties of the returned array of k-v pairs directly var docType = result.GetString("type"); var docName = result.GetString("name"); var docCity = result.GetString("city"); } // end::query-access-props[] } public void testQuerySyntaxCount() { // tag::query-syntax-count-only[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount")) (34) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-count-only[] // tag::query-access-count-only[] var results = query.Execute().AllResults(); foreach (var result in results) { var numberOfDocs = result.GetInt("mycount"); (35) } // end::query-access-count-only[] } public void ibQueryForID() { // tag::query-syntax-id[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID).As("this_ID")) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-id[] // tag::query-access-id[] var results = query.Execute().AllResults(); foreach (var result in results) { var docID = result.GetString("this_ID"); (36) var doc = database.GetDefaultCollection().GetDocument(docID); } // end::query-access-id[] } #warning query-syntax-pagination-all unused (and out of place)? // tag::query-syntax-pagination-all[] public void testQueryPagination() { // tag::query-syntax-pagination[] var database = new Database("hotels"); var limit = 20; var offset = 0; // get a count of the number of docs matching the query var countQuery = QueryBuilder .Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount")) .From(DataSource.Collection(database.GetDefaultCollection())); var numberOfDocs = countQuery.Execute().First().GetInt("mycount"); if (numberOfDocs < limit) { limit = numberOfDocs; } while (offset < numberOfDocs) { var listQuery = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(database.GetDefaultCollection())) .Limit(Expression.Int(limit), Expression.Int(offset)); (37) foreach (var result in listQuery.Execute()) { // Display and or process query results batch } offset = offset + limit; } // end::query-syntax-pagination[] // end::query-syntax-pagination-all[] } public void JsonApiDocument() { var collection = _Database.GetDefaultCollection(); // tag::tojson-document[] // Get a document var doc = collection.GetDocument("hotel_10025"); // Get document data as JSON String var docJSONString = doc?.ToJSON(); // Get Json Object from the Json String JObject jsonObject = JObject.Parse(docJSONString); // Get Native Object (anhotel) from JSON String List<Hotel> hotels = new List<Hotel>(); var hotel = JsonConvert.DeserializeObject<Hotel>(docJSONString); hotels.Add(hotel); // Update the retrieved native object hotel.Name = "A Copy of " + hotel.Name; hotel.Id = "2001"; // Convert the updated object back to a JSON string var newJsonString = JsonConvert.SerializeObject(hotel); // Update new document with JSOn String MutableDocument newhotel = doc.ToMutable(); newhotel.SetJSON(newJsonString); foreach (string key in newhotel.ToDictionary().Keys) { Console.WriteLine("Data -- {0} = {1}", key, newhotel.GetValue(key)); } collection.Save(newhotel); var retrievedDoc = collection.GetDocument("2001").ToJSON(); Console.Write(retrievedDoc); // end::tojson-document[] } public void JsonApiArray() { var collection = _Database.GetDefaultCollection(); // tag::tojson-array[] // JSON String -- an Array (3 elements. including embedded arrays) var jsonString = "[{'id':'1000','type':'hotel','name':'Hotel Ted','city':'Paris','country':'France','description':'Undefined description for Hotel Ted'},{'id':'1001','type':'hotel','name':'Hotel Fred','city':'London','country':'England','description':'Undefined description for Hotel Fred'}, {'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}]".Replace("'", "\""); // Get JSON Array from JSON String var jsonArray = JArray.Parse(jsonString); // Create mutable array using JSON String Array var mutableArray = new MutableArrayObject(); mutableArray.SetJSON(jsonString); // Create a new document for each array element for (int i = 0; i < mutableArray.Count; i++) { var dict = mutableArray.GetDictionary(i); var docid = mutableArray[i].Dictionary.GetString("id"); var mutableDoc = new MutableDocument(docid, dict.ToDictionary()); collection.Save(mutableDoc); } // Get one of the created docs and iterate through one of the embedded arrays var extendedDoc = collection.GetDocument("1002"); var features = extendedDoc.GetArray("features"); // Print its elements foreach (string feature in features) { Console.Write($"{feature} "); //process array item as required } var featuresJSON = extendedDoc.GetArray("features").ToJSON(); // end::tojson-array[] } public void JsonApiDictionary() { var ourdbname = "ournewdb"; if (Database.Exists(ourdbname, "/")) { Database.Delete(ourdbname, "/"); } // tag::tojson-dictionary[] // Get dictionary from JSONstring var jsonString = "{'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}".Replace("'", "\""); var mutableDict = new MutableDictionaryObject(json: jsonString); // use dictionary to get name value var name = mutableDict.GetString("name"); // Iterate through keys foreach (string key in mutableDict.Keys) { Console.WriteLine("Data -- {0} = {1}", key, mutableDict.GetValue(key).ToString()); } // end::tojson-dictionary[] } public void JsonApiBlob() { var userName = "ian"; var collection = _Database.GetDefaultCollection(); var database = _Database; // tag::tojson-blob[] // Initialize base document for blob from a JSON string var docId = "1002"; var jsonString = "{'ref':'hotel_1002','type':'hotel','name':'Hotel Ned'," + "'city':'Balmain','country':'Australia'," + "'description':'Undefined description for Hotel Ned'," + "'features':['Cable TV','Toaster','Microwave']}".Replace("'", "\""); var mutableDoc = new MutableDocument(docId, jsonString); // Get the content (an image), create blob and add to doc) var defaultDirectory = Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>() .DefaultDirectory(), userName); var imagePath = Path.Combine(defaultDirectory, "avatarimage.jpg"); var imageUri = new Uri(imagePath.ToString()); var imageBlob = new Blob("image/jpg", imageUri); mutableDoc.SetBlob("avatar", imageBlob); // This example generates a 'blob not saved' exception try { Console.WriteLine("myBlob (unsaved) as JSON = {0}", imageBlob.ToJSON()); } catch (Exception e) { Console.WriteLine("Exception = {0}", e.Message); } collection.Save(mutableDoc); // Alternatively -- depending on use case database.SaveBlob(new Blob("image/jpg", imageUri)); // Retrieve saved doc, get blob as JSON andheck its still a 'blob' var sameDoc = collection.GetDocument(docId); var reconstitutedBlob = new MutableDictionaryObject(). SetDictionary("blobCOPY", new MutableDictionaryObject(sameDoc.GetBlob("avatar").ToJSON())); if (Blob.IsBlob( reconstitutedBlob.GetDictionary("blobCOPY").ToDictionary())) { //... process accordingly Console.WriteLine("Its a Blob!!"); } // end::tojson-blob[] } private bool ValidatePassword(SecureString password) => true; public void P2PListenerSimple() { var collection = _Database.GetDefaultCollection(); // tag::listener-simple[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (38) endpointConfig.Authenticator = new ListenerPasswordAuthenticator( (sender, username, password) => { // ValidatePassword can make use of the SecureString class // to the desired level of security (or just convert it to string // if no intense security is required) return username == "valid.user" && ValidatePassword(password); } ); (39) var listener = new URLEndpointListener(endpointConfig); (40) listener.Start(); (41) // end::listener-simple[] } public void P2PReplicatorSimple() { var collection = _Database.GetDefaultCollection(); // tag::replicator-simple[] var endpointConfig = new URLEndpoint(new Uri("wss://listener.com:4984/otherDB")); (42) var replConfig = new ReplicatorConfiguration(endpointConfig); (43) replConfig.AddCollection(collection); replConfig.AcceptOnlySelfSignedServerCertificate = true; (44) replConfig.Authenticator = new BasicAuthenticator("valid.user", "valid.password.string"); (45) var replicator = new Replicator(replConfig); (46) replicator.Start(); (47) // end::replicator-simple[] } public void GettingStarted1() { var collection = _Database.GetDefaultCollection(); // tag::listener-initialize[] // tag::listener-config-db[] // Initialize the listener config var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (48) // end::listener-config-db[] // tag::listener-config-port[] endpointConfig.Port = 55990; (49) // end::listener-config-port[] // tag::listener-config-netw-iface[] endpointConfig.NetworkInterface = "10.1.1.10"; (50) // end::listener-config-netw-iface[] // tag::listener-config-delta-sync[] endpointConfig.EnableDeltaSync = true; (51) // end::listener-config-delta-sync[] #warning listener-config-tls-full unused? // tag::listener-config-tls-full[] // tag::listener-config-tls-enable[] endpointConfig.DisableTLS = false; (52) // end::listener-config-tls-enable[] // tag::listener-config-tls-id-anon[] // Use an Anonymous Self-Signed Cert endpointConfig.TlsIdentity = null; (53) // end::listener-config-tls-id-anon[] // tag::listener-config-client-auth-pwd[] // Configure the client authenticator // Here we are using Basic Authentication) (54) SecureString validPassword = new SecureString(); /* example only */ // Get SecureString input for validPassword var validUser = "valid.username"; endpointConfig.Authenticator = new ListenerPasswordAuthenticator( (sender, username, password) => { // Implement your own ValidatePassword function return username == validUser && ValidatePassword(password); } ); // end::listener-config-client-auth-pwd[] // tag::listener-start[] // Initialize the listener var listener = new URLEndpointListener(endpointConfig); (55) // Start the listener listener.Start(); (56) // end::listener-start[] // end::listener-initialize[] #warning old-listener-config-tls-disable unused? // tag::old-listener-config-tls-disable[] endpointConfig.DisableTLS = true; // end::old-listener-config-tls-disable[] #warning listener-config-tls-id-nil-2 unused? // tag::listener-config-tls-id-nil-2[] // Use “anonymous” cert. These are self signed certs created by the system endpointConfig.TlsIdentity = null; // end::listener-config-tls-id-nil-2[] #warning old-listener-config-delta-sync unused? // tag::old-listener-config-delta-sync[] endpointConfig.EnableDeltaSync = true; // end::old-listener-config-delta-sync[] // tag::listener-status-check[] ulong connectionCount = listener.Status.ConnectionCount; (57) ulong activeConnectionCount = listener.Status.ActiveConnectionCount; (58) // end::listener-status-check[] // tag::listener-stop[] listener.Stop(); // end::listener-stop[] // tag::listener-get-network-interfaces[] foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 || ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet) { // do something with the interface(s) } } // end::listener-get-network-interfaces[] } public void GettingStarted2() { var collection = _Database.GetDefaultCollection(); { #warning listener-get-url-list unused? // tag::listener-get-url-list[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); var listener = new URLEndpointListener(endpointConfig); listener.Start(); // Note, converting to string omitted. Console.WriteLine("URLS are {0} ", listener.Urls); // end::listener-get-url-list[] #warning listener-config-tls-disable unused? // tag::listener-config-tls-disable[] endpointConfig.DisableTLS = true; (59) // end::listener-config-tls-disable[] #warning listener-local-db unused? // tag::listener-local-db[] // . . . preceding application logic . . . // Get the database (and create it if it doesn't exist) var database = new Database("mydb"); // end::listener-local-db[] // tag::listener-config-tls-id-full[] // tag::listener-config-tls-id-caCert[] // Use CA Cert // Create a TLSIdentity from an imported key-pair // . . . previously declared variables include ... X509Store store = new X509Store(StoreName.My); // create and label x509 store // Get keys and certificates from PKCS12 data byte[] certData = File.ReadAllBytes("c:client.p12"); (60) // . . . other user code . . . #warning import-tls-identity unused? // tag::import-tls-identity[] TLSIdentity identity = TLSIdentity.ImportIdentity( store, certData, (61) "123", // Password to access certificate data "couchbase-demo-cert", null); // Label to get cert in certificate map // NOTE: If a null label is supplied then the same // default directory for a Couchbase Lite database // is used for map. // end::import-tls-identity[] // end::listener-config-tls-id-caCert[] // tag::listener-config-tls-id-anon[] // Use an Anonymous Self-Signed Cert endpointConfig.TlsIdentity = null; (62) // end::listener-config-tls-id-anon[] #warning listener-config-tls-id-set unused? // tag::listener-config-tls-id-set[] // Set the TLS Identity endpointConfig.TlsIdentity = identity; (63) // end::listener-config-tls-id-set[] // end::listener-config-tls-id-full[] } { var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); // tag::listener-config-client-auth-root[] // Configure the client authenticator // to validate using ROOT CA // Get the valid cert chain, in this instance from // PKCS12 data containing private key, public key // and certificates (64) var clientData = File.ReadAllBytes("c:client.p12"); var ourCaData = File.ReadAllBytes("c:client-ca.der"); // Get the root certs from the data var rootCert = new X509Certificate2(ourCaData); (65) // Configure the authenticator to use the root certs var certAuth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert)); endpointConfig.Authenticator = certAuth; (66) // Initialize the listener using the config var listener = new URLEndpointListener(endpointConfig); // end::listener-config-client-auth-root[] // tag::listener-config-client-auth-lambda[] // Configure the client authenticator // to validate using application logic // Get the valid cert chain, in this instance from // PKCS12 data containing private key, public key // and certificates (67) clientData = File.ReadAllBytes("c:client.p12"); ourCaData = File.ReadAllBytes("c:client-ca.der"); // Configure the authenticator to pass the root certs // To a user supplied code block for authentication var callbackAuth = new ListenerCertificateAuthenticator( (object sender, X509Certificate2Collection chain) => { // . . . user supplied code block // . . . returns boolean value (true=authenticated) return true; }); (68) endpointConfig.Authenticator = callbackAuth; (69) // end::listener-config-client-auth-lambda[] } } public void datatype_usage() { #warning datatype_usage unused? // tag::datatype_usage[] // tag::datatype_usage_createdb[] // Get the database (and create it if it doesn’t exist). using var database = new Database("hoteldb"); var collection = database.GetDefaultCollection(); // end::datatype_usage_createdb[] // tag::datatype_usage_createdoc[] // Create your new document using var mutableDoc = new MutableDocument("hoteldoc"); // end::datatype_usage_createdoc[] // tag::datatype_usage_mutdict[] // Create and populate mutable dictionary var address = new MutableDictionaryObject(); address.SetString("street", "1 Main st."); address.SetString("city", "San Francisco"); address.SetString("state", "CA"); address.SetString("country", "USA"); address.SetString("code", "90210"); // end::datatype_usage_mutdict[] // tag::datatype_usage_mutarray[] // Create and populate mutable array var phones = new MutableArrayObject(); phones.AddString("650-000-0000"); phones.AddString("650-000-0001"); // end::datatype_usage_mutarray[] // tag::datatype_usage_populate[] // Initialize and populate the document // Add document type and hotel name as string mutableDoc.SetString("type", "hotel"); mutableDoc.SetString("name", "Hotel Java Mo"); // Add average room rate (float) mutableDoc.SetFloat("room_rate", 121.75f); // Add address (dictionary) mutableDoc.SetDictionary("address", address); // Add phone numbers(array) mutableDoc.SetArray("phones", phones); // end::datatype_usage_populate[] // tag::datatype_usage_persist[] collection.Save(mutableDoc); // end::datatype_usage_persist[] // tag::datatype_usage_closedb[] database.Close(); // end::datatype_usage_closedb[] // end::datatype_usage[] } public void datatype_dictionary() { var collection = _Database.GetDefaultCollection(); // tag::datatype_dictionary[] var doc = collection.GetDocument("doc1"); // Getting a dictionary from the document's properties var dict = doc.GetDictionary("address"); // Access a value with a key from the dictionary var street = dict.GetString("street"); // Iterate dictionary foreach (var key in dict.Keys) { Console.WriteLine($"Key {key} = {dict.GetValue(key)}"); } // Create a mutable copy var mutableDict = dict.ToMutable(); // end::datatype_dictionary[] } public void datatype_mutable_dictionary() { var collection = _Database.GetDefaultCollection(); // tag::datatype_mutable_dictionary[] // Create a new mutable dictionary and populate some keys/values var mutableDict = new MutableDictionaryObject(); mutableDict.SetString("street", "1 Main st."); mutableDict.SetString("city", "San Francisco"); // Add the dictionary to a document's properties and save the document using var mutableDoc = new MutableDocument("doc1"); mutableDoc.SetDictionary("address", mutableDict); collection.Save(mutableDoc); // end::datatype_mutable_dictionary[] } public void datatype_array() { var collection = _Database.GetDefaultCollection(); // tag::datatype_array[] var document = collection.GetDocument("doc1"); // Getting a phones array from the document's properties var array = document.GetArray("phones"); // Get element count var count = array.Count(); // Access an array element by index if (count >= 0) { var phone = array[1]; } // Iterate dictionary for (int i = 0; i < count; i++) { Console.WriteLine($"Item {i.ToString()} = {array[i]}"); } // Create a mutable copy var mutableArray = array.ToMutable(); // end::datatype_array[] } public void datatype_mutable_array() { var collection = _Database.GetDefaultCollection(); // tag::datatype_mutable_array[] // Create a new mutable array and populate data into the array var mutableArray = new MutableArrayObject(); mutableArray.AddString("650-000-0000"); mutableArray.AddString("650-000-0001"); // Set the array to document's properties and save the document using var mutableDoc = new MutableDocument("doc1"); mutableDoc.SetArray("phones", mutableArray); collection.Save(mutableDoc); // end::datatype_mutable_array[] } static void Main(string[] args) { // NOTE: PLEASE PLEASE PLEASE do not break the compilation of this file. It is // by far the easiest way to check for its correctness. If you don't know how to // compile a C# program, then find someone who does before you commit your changes!!! Console.WriteLine("This program is not meant to be executed, only compiled"); } } /* ----------------------------------------------------------- */ /* --------------------- ACTIVE SIDE ----------------------- */ /* --------------- stubs for documentation ----------------- */ /* ----------------------------------------------------------- */ class ActivePeer : IMessageEndpointDelegate { ActivePeer() { // tag::message-endpoint[] var database = new Database("dbname"); // The delegate must implement the `IMessageEndpointDelegate` protocol. var messageEndpointTarget = new MessageEndpoint(uid: "UID:123", target: "", protocolType: ProtocolType.MessageStream, delegateObject: this); // end::message-endpoint[] // tag::message-endpoint-replicator[] var replConfig = new ReplicatorConfiguration(messageEndpointTarget); replConfig.AddCollection(database.GetDefaultCollection()); // Create the replicator object var replicator = new Replicator(replConfig); // Start the replicator replicator.Start(); // end::message-endpoint-replicator[] } // tag::create-connection[] /* implementation of MessageEndpointDelegate */ public IMessageEndpointConnection CreateConnection(MessageEndpoint endpoint) { var connection = new ActivePeerConnection(); /* implements MessageEndpointConnection */ return connection; } // end::create-connection[] } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously class ActivePeerConnection : IMessageEndpointConnection { private IReplicatorConnection _replicatorConnection; public void Disconnect() { // tag::active-replicator-close[] _replicatorConnection?.Close(null); // end::active-replicator-close[] } public void Receive(byte[] data) { // tag::active-peer-receive[] var message = Message.FromBytes(data); _replicatorConnection?.Receive(message); // end::active-peer-receive[] } // tag::active-peer-close[] /* implementation of MessageEndpointConnection */ public async Task Close(Exception error) { // await socket.Close, etc (or do nothing if already closed) // throw MessagingException if something goes wrong (though // since it is "close" nothing special will happen) } // end::active-peer-close[] // tag::active-peer-open[] /* implementation of MessageEndpointConnection */ public async Task Open(IReplicatorConnection connection) { _replicatorConnection = connection; // await socket.Open(), etc // throw MessagingException if something goes wrong } // end::active-peer-open[] // tag::active-peer-send[] /* implementation of MessageEndpointConnection */ public async Task Send(Message message) { var data = message.ToByteArray(); // await Socket.Send(), etc // throw MessagingException if something goes wrong } // end::active-peer-send[] } /* ----------------------------------------------------------- */ /* --------------------- PASSIVE SIDE ---------------------- */ /* --------------- stubs for documentation ----------------- */ /* ----------------------------------------------------------- */ class PassivePeerConnection : IMessageEndpointConnection { private MessageEndpointListener _messageEndpointListener; private IReplicatorConnection _replicatorConnection; public void StartListener() { // tag::listener[] var database = new Database("mydb"); var endpointConfig = new MessageEndpointListenerConfiguration(new[] { database.GetDefaultCollection() }, ProtocolType.MessageStream); _messageEndpointListener = new MessageEndpointListener(endpointConfig); // end::listener[] } public void StopListener() { // tag::passive-stop-listener[] _messageEndpointListener?.CloseAll(); // end::passive-stop-listener[] } public void AcceptConnection() { // tag::advertizer-accept[] var connection = new PassivePeerConnection(); /* implements MessageEndpointConnection */ _messageEndpointListener?.Accept(connection); // end::advertizer-accept[] } public void Disconnect() { // tag::passive-replicator-close[] _replicatorConnection?.Close(null); // end::passive-replicator-close[] } public void Receive(byte[] data) { // tag::passive-peer-receive[] var message = Message.FromBytes(data); _replicatorConnection?.Receive(message); // end::passive-peer-receive[] } // tag::passive-peer-close[] /* implementation of MessageEndpointConnection */ public async Task Close(Exception error) { // await socket.Close, etc (or do nothing if already closed) // throw MessagingException if something goes wrong (though // since it is "close" nothing special will happen) } // end::passive-peer-close[] // tag::passive-peer-open[] /* implementation of MessageEndpointConnection */ public Task Open(IReplicatorConnection connection) { _replicatorConnection = connection; // socket should already be open on the passive side return Task.FromResult(true); } // end::passive-peer-open[] // tag::passive-peer-send[] /* implementation of MessageEndpointConnection */ public async Task Send(Message message) { var data = message.ToByteArray(); // await Socket.Send(), etc // throw MessagingException if something goes wrong } // end::passive-peer-send[] } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously // tag::predictive-model[] // tensorFlowModel is a fake implementation // this would be the implementation of the ml model you have chosen class TensorFlowModel { public static IDictionary<string, object> PredictImage(byte[] data) { // Do calculations, etc return null; } } class ImageClassifierModel : IPredictiveModel { public DictionaryObject Predict(DictionaryObject input) { var blob = input.GetBlob("photo"); if (blob == null) { return null; } var imageData = blob.Content; // tensorFlowModel is a fake implementation // this would be the implementation of the ml model you have chosen var modelOutput = TensorFlowModel.PredictImage(imageData); return new MutableDictionaryObject(modelOutput); (1) } } // end::predictive-model[] // tag::custom-logging[] class LogTestLogger : ILogger { public LogLevel Level { get; set; } public void Reset() { } public void Log(LogLevel level, LogDomain domain, string message) { // handle the message, for example piping it to // a third party framework } } // end::custom-logging[] // tag::local-win-conflict-resolver[] class LocalWinConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { return conflict.LocalDocument; } } // end::local-win-conflict-resolver[] // tag::remote-win-conflict-resolver[] class RemoteWinConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { return conflict.RemoteDocument; } } // end::remote-win-conflict-resolver[] // tag::merge-conflict-resolver[] class MergeConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { var localDict = conflict.LocalDocument.ToDictionary(); var remoteDict = conflict.RemoteDocument.ToDictionary(); var result = localDict.Concat(remoteDict) .GroupBy(kv => kv.Key) .ToDictionary(g => g.Key, g => g.First().Value); return new MutableDocument(conflict.DocumentID, result); } } // end::merge-conflict-resolver[] } #warning p2p-act-rep-func used, but contains nothing // tag::p2p-act-rep-func[] #warning p2p-act-rep-config-type used, but contains nothing // tag::p2p-act-rep-config-type[] // end::p2p-act-rep-config-type[] #warning autopurge-override used, but contains nothing // tag::autopurge-override[] // Set autopurge option // here we override its default // end::autopurge-override[] #warning p2p-act-rep-config-cont used, but contains nothing // tag::p2p-act-rep-config-cont[] // Configure Sync Mode // end::p2p-act-rep-config-cont[] #warning p2p-act-rep-config-self-cert used, but contains nothing // tag::p2p-act-rep-config-self-cert[] // Configure Server Security -- only accept self-signed certs // end::p2p-act-rep-config-self-cert[] // Configure Client Security (70) #warning p2p-act-rep-auth used, but contains nothing // tag::p2p-act-rep-auth[] // Configure basic auth using user credentials // end::p2p-act-rep-auth[] #warning p2p-act-rep-start-full used, but contains nothing // tag::p2p-act-rep-start-full[] // Initialize and start a replicator // Initialize replicator with configuration data #warning p2p-act-rep-add-change-listener used, but contains nothing // tag::p2p-act-rep-add-change-listener[] #warning p2p-act-rep-add-change-listener-label used, but contains nothing // tag::p2p-act-rep-add-change-listener-label[] //Optionally add a change listener (71) // end::p2p-act-rep-add-change-listener-label[] // end::p2p-act-rep-add-change-listener[] #warning p2p-act-rep-start used, but contains nothing // tag::p2p-act-rep-start[] // Start replicator // end::p2p-act-rep-start[] // end::p2p-act-rep-start-full[] // end::p2p-act-rep-func[] #warning p2p-act-rep-config-cacert used, but contains nothing // tag::p2p-act-rep-config-cacert[] // Configure Server Security -- only accept CA certs // end::p2p-act-rep-config-cacert[] #warning p2p-act-rep-config-cacert-pinned used, but contains nothing // tag::p2p-act-rep-config-cacert-pinned[] // Only CA Certs accepted // end::p2p-act-rep-config-cacert-pinned[] #warning p2p-act-rep-status used, but contains nothing // tag::p2p-act-rep-status[] // end::p2p-act-rep-status[] #warning p2p-act-rep-stop used, but contains nothing // tag::p2p-act-rep-stop[] // Stop replication. // end::p2p-act-rep-stop[] #warning p2p-tlsid-store-in-keychain used, but contains nothing // tag::p2p-tlsid-store-in-keychain[] // end::p2p-tlsid-store-in-keychain[] #warning p2p-tlsid-delete-id-from-keychain used, but contains nothing // tag::p2p-tlsid-delete-id-from-keychain[] // end::p2p-tlsid-delete-id-from-keychain[] public class MyClass { public Database Database { get; set; } public Replicator Replicator { get; set; } (72) public void StartReplication() { // tag::sgw-repl-pull[] var url = new Uri("wss://localhost:4984/db"); (73) var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target) { ReplicatorType = ReplicatorType.Pull }; config.AddCollection(Database.GetDefaultCollection()); Replicator = new Replicator(config); Replicator.Start(); // end::sgw-repl-pull[] } public void InitReplication() { // tag::sgw-act-rep-initialize[] // initialize the replicator configuration var url = new URLEndpoint(new Uri("wss://10.0.2.2:4984/anotherDB")); (74) var replConfig = new ReplicatorConfiguration(url); // Add collections to the config now // end::sgw-act-rep-initialize[] } } -
Store imported identity in keychain
//
// Program.cs
//
// Copyright (c) 2017 Couchbase, Inc All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
using Couchbase.Lite;
using Couchbase.Lite.DI;
using Couchbase.Lite.Enterprise.Query;
using Couchbase.Lite.Logging;
using Couchbase.Lite.P2P;
using Couchbase.Lite.Query;
using Couchbase.Lite.Sync;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Net.NetworkInformation;
using System.Security;
using System.Security.Cryptography.X509Certificates;
namespace api_walkthrough
{
class Hotel
{
public string Id { get; set; }
public string Name { get; set; }
}
class Program
{
private static readonly Database _Database = null;
private static readonly Replicator _Replicator = null;
private static readonly URLEndpointListener _listener = null;
public void GettingStarted()
{
// tag::getting-started[]
// using System;
// using Couchbase.Lite;
// using Couchbase.Lite.Query;
// using Couchbase.Lite.Sync;
// Get the database (and create it if it doesn't exist)
var database = new Database("mydb");
var collection = database.GetDefaultCollection();
// Create a new document (i.e. a record) in the database
var id = default(string);
using var createdDoc = new MutableDocument();
createdDoc.SetFloat("version", 2.0f)
.SetString("type", "SDK");
// Save it to the database
collection.Save(createdDoc);
id = createdDoc.Id;
// Update a document
using var doc = collection.GetDocument(id);
using var mutableDoc = doc.ToMutable();
createdDoc.SetString("language", "C#");
collection.Save(createdDoc);
using var docAgain = collection.GetDocument(id);
Console.WriteLine($"Document ID :: {docAgain.Id}");
Console.WriteLine($"Learning {docAgain.GetString("language")}");
// Create a query to fetch documents of type SDK
// i.e. SELECT * FROM database WHERE type = "SDK"
using var query = QueryBuilder.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("SDK")));
// Run the query
var result = query.Execute();
Console.WriteLine($"Number of rows :: {result.AllResults().Count}");
// Create replicator to push and pull changes to and from the cloud
var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4984/getting-started-db"));
var replConfig = new ReplicatorConfiguration(targetEndpoint);
replConfig.AddCollection(database.GetDefaultCollection());
// Add authentication
replConfig.Authenticator = new BasicAuthenticator("john", "pass");
// Create replicator (make sure to add an instance or static variable
// named _Replicator)
var replicator = new Replicator(replConfig);
replicator.AddChangeListener((sender, args) =>
{
if (args.Status.Error != null) {
Console.WriteLine($"Error :: {args.Status.Error}");
}
});
replicator.Start();
// Later, stop and dispose the replicator *before* closing/disposing the database
// end::getting-started[]
}
private static void TestReplicatorConflictResolver()
{
var collection = _Database.GetDefaultCollection();
// tag::replication-conflict-resolver[]
var target = new URLEndpoint(new Uri("ws://localhost:4984/mydatabase"));
var replConfig = new ReplicatorConfiguration(target);
replConfig.AddCollection(collection, new CollectionConfiguration()
{
ConflictResolver = new LocalWinConflictResolver()
});
var replicator = new Replicator(replConfig);
replicator.Start();
// end::replication-conflict-resolver[]
}
private static void TestSaveWithConflictHandler()
{
var collection = _Database.GetDefaultCollection();
// tag::update-document-with-conflict-handler[]
using var doc = collection.GetDocument("xyz");
using var mutableDoc = doc.ToMutable();
mutableDoc.SetString("name", "apples");
collection.Save(mutableDoc, (updated, current) =>
{
var currentDict = current.ToDictionary();
var newDict = updated.ToDictionary();
var result = newDict.Concat(currentDict)
.GroupBy(kv => kv.Key)
.ToDictionary(g => g.Key, g => g.First().Value);
updated.SetData(result);
return true;
});
// end::update-document-with-conflict-handler[]
}
private static bool IsValidCredential(string name, SecureString password) { return true; } // helper
private static void TestInitListener()
{
var database = new Database("other-database");
var collection = database.GetDefaultCollection();
#warning init-urllistener Unused?
// tag::init-urllistener[]
var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection });
endpointConfig.TlsIdentity = null; // Use with anonymous self-signed cert
endpointConfig.Authenticator = new ListenerPasswordAuthenticator((sender, username, password) =>
{
if (IsValidCredential(username, password)) {
return true;
}
return false;
});
var listener = new URLEndpointListener(endpointConfig);
// end::init-urllistener[]
}
private static void TestListenerStart()
{
var listener = _listener;
#warning start-urllistener Unused?
// tag::start-urllistener[]
// CouchbaseLiteException will be thrown when the listener cannot be started. The most common error
// would be that the configured port has already been used.
listener.Start();
// end::start-urllistener[]
}
private static void TestListenerStop()
{
var listener = _listener;
#warning stop-urllistener Unused?
// tag::stop-urllistener[]
listener.Stop();
// end::stop-urllistener[]
}
private static void TestCreateSelfSignedCert()
{
X509Store store =
new X509Store(StoreName.My);
// The identity will be stored in the secure
// storage using the given label.
DateTimeOffset fiveMinToExpireCert = DateTimeOffset.UtcNow.AddMinutes(5);
// tag::create-self-signed-cert[]
// tag::listener-config-tls-id-SelfSigned[]
var identity = TLSIdentity.CreateIdentity(true, /* isServer */
new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "Couchbase Inc" } },
// The common name attribute is required
// when creating a CSR. If it is not presented
// in the cert, an exception is thrown.
fiveMinToExpireCert,
// If the expiration date is not specified,
// the certs expiration will be 365 days
store,
"CBL-Server-Cert",
null); // The key label to get cert in certificate map.
// If null, the same default directory
// for a Couchbase Lite db is used for map.
// end::listener-config-tls-id-SelfSigned[]
// end::create-self-signed-cert[]
}
private static void TestImportTLSIdentity()
{
X509Store store = new X509Store(StoreName.My); // The identity will be stored in the secure storage using the given label
byte[] data = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates
// tag::import-tls-identity[]
// tag::listener-config-tls-id-caCert[]
var identity = TLSIdentity.ImportIdentity(store,
data,
"123", // The password that is needed to access the certificate data
"CBL-Client-Cert",
null); // The key label to get cert in certificate map.
// If null, the same default directory
// for a Couchbase Lite db is used for map.
// end::listener-config-tls-id-caCert[]
// end::import-tls-identity[]
}
private static void TestClientCertAuthenticatorRootCerts()
{
var otherDatabase = new Database("other-database");
var collection = otherDatabase.GetDefaultCollection();
X509Store store = new X509Store(StoreName.My);
#warning client-cert-authenticator-root-certs unused?
// tag::client-cert-authenticator-root-certs[]
byte[] caData, clientData;
clientData = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates
caData = File.ReadAllBytes("C:\\client-ca.der");
// Root certs
var rootCert = new X509Certificate2(caData);
var auth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert));
// Create URL Endpoint Listener
var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection });
listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default.
listenerConfig.Authenticator = auth;
var listener = new URLEndpointListener(listenerConfig);
listener.Start();
// Client identity
var identity = TLSIdentity.ImportIdentity(store,
clientData,
"123",
"CBL-Client-Cert",
null);
// Replicator -- Client
var database = new Database("client-database");
var builder = new UriBuilder(
"wss",
"localhost",
listener.Port,
$"/{listener.Config.Collections.First().Name}"
);
var url = builder.Uri;
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
config.AddCollection(database.GetDefaultCollection());
config.ReplicatorType = ReplicatorType.PushAndPull;
config.Continuous = false;
config.Authenticator = new ClientCertificateAuthenticator(identity);
config.AcceptOnlySelfSignedServerCertificate = true;
config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0];
var replicator = new Replicator(config);
replicator.Start(); // Dispose after stop
// Stop listener after replicator is stopped
listener.Stop();
// end::client-cert-authenticator-root-certs[]
}
private static void TestClientCertAuthenticator()
{
var otherDatabase = new Database("other-database");
var collection = otherDatabase.GetDefaultCollection();
X509Store store = new X509Store(StoreName.My);
#warning client-cert-authenticator unused?
// tag::client-cert-authenticator[]
// Create Listener Certificate Authenticator
var auth = new ListenerCertificateAuthenticator((sender, cert) =>
{
if (cert.Count != 1) {
return false;
}
return cert[0].SubjectName.Name?.Replace("CN=", "") == "couchbase";
});
// Create URL Endpoint Listener
var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection });
listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default.
listenerConfig.Authenticator = auth;
var listener = new URLEndpointListener(listenerConfig);
listener.Start();
// User Identity
var identity = TLSIdentity.CreateIdentity(false,
new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "couchbase" } },
null,
store,
"ClientCertLabel",
null);
// Replicator -- Client
var database = new Database("client-database");
var builder = new UriBuilder(
"wss",
"localhost",
listener.Port,
$"/{listener.Config.Collections.First().Name}"
);
var url = builder.Uri;
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
config.AddCollection(database.GetDefaultCollection());
config.ReplicatorType = ReplicatorType.PushAndPull;
config.Continuous = false;
config.Authenticator = new ClientCertificateAuthenticator(identity);
config.AcceptOnlySelfSignedServerCertificate = false;
config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0];
var replicator = new Replicator(config);
replicator.Start(); // Dispose after stopped
// Stop listener after replicator is stopped
listener.Stop();
// end::client-cert-authenticator[]
}
public void IdentityWithLabel()
{
X509Store store = null;
byte[] clientData = null;
var replConfig = new ReplicatorConfiguration(null);
// tag::p2p-tlsid-tlsidentity-with-label[]
// Client identity
var identity =
TLSIdentity.ImportIdentity(store,
clientData,
"123",
"CBL-Client-Cert",
null); (1)
replConfig.Authenticator =
new ClientCertificateAuthenticator(identity); (2)
// end::p2p-tlsid-tlsidentity-with-label[]
}
public void UseEncryption()
{
// Enterprise edition only
// tag::database-encryption[]
// Create a new, or open an existing database with encryption enabled
var config = new DatabaseConfiguration
{
// Or, derive a key yourself and pass a byte array of the proper size
EncryptionKey = new EncryptionKey("password")
};
using var database = new Database("seekrit", config);
// Change the encryption key (or add encryption if the DB is unencrypted)
database.ChangeEncryptionKey(new EncryptionKey("betterpassw0rd"));
// Remove encryption
database.ChangeEncryptionKey(null);
// end::database-encryption[]
}
private static void ResetReplicatorCheckpoint()
{
var url = new Uri("ws://localhost:4984/db");
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
bool resetCheckpointRequired_Example = true;
config.AddCollection(_Database.GetDefaultCollection());
var replicator = new Replicator(config);
// tag::replication-reset-checkpoint[]
// replicator is a Replicator instance
if (resetCheckpointRequired_Example) {
replicator.Start(true); (3)
} else {
replicator.Start(false);
}
// Stop and dispose replicator later
// end::replication-reset-checkpoint[]
}
private static void Read1xAttachment()
{
using var doc = new MutableDocument();
// tag::1x-attachment[]
var attachments = doc.GetDictionary("_attachments");
var avatar = attachments.GetBlob("avatar");
var content = avatar?.Content;
// end::1x-attachment[]
}
private static void CreateNewDatabase()
{
// tag::new-database[]
var database = new Database("my-database");
// end::new-database[]
}
private static void CloseDatabase()
{
var database = _Database;
// tag::close-database[]
database.Close();
// end::close-database[]
}
private static void DatabaseFullsync()
{
var config = new DatabaseConfiguration();
// tag::database-fullsync[]
// this enables fullsync
config.FullSync = true;
// end::database-fullsync[]
}
private static void CreateCollection()
{
// tag::scopes-manage-create-collection[]
var collectionWithDefaultScope = _Database.CreateCollection("colA");
var collection = _Database.CreateCollection("colA", "scopeA"); // Scope with named scopeA will be created if it's not existed. There is no public API to create a Scope.
// end::scopes-manage-create-collection[]
}
private static void DeleteCollection()
{
// tag::scopes-manage-drop-collection[]
_Database.DeleteCollection("colA", "scopeA"); // Scope with named scopeA will be deleted if there is no collections in the scope after the last collection is deleted via this API. There is no public API to remove a Scope.
// end::scopes-manage-drop-collection[]
}
private static void ListCollectionsAndScopes()
{
// tag::scopes-manage-list[]
// Get Scopes
var scopes = _Database.GetScopes();
// Get Collections of a Scope named scopeA
var scopeA = _Database.GetScope("scopeA");
var collectionsInScopeA = scopeA.GetCollections();
// end::scopes-manage-list[]
}
private static void ChangeLogging()
{
// tag::logging[]
// This sets the overall level of console logging
Database.Log.Console.Level = LogLevel.Verbose;
// This flag can enable and disable specific domains
Database.Log.Console.Domains = LogDomain.Couchbase | LogDomain.Database;
// end::logging[]
}
private static void LoadPrebuilt()
{
// tag::prebuilt-database[]
// Note: Getting the path to a database is platform-specific. For .NET Core / .NET Framework this
// can be a simple filesystem path. For UWP, you will need to get the path from your assets. For
// iOS you need to get the path from the main bundle. For Android you need to extract it from your
// assets to a temporary directory and then pass that path.
var path = Path.Combine(Environment.CurrentDirectory, "travel-sample.cblite2" + Path.DirectorySeparatorChar);
if (!Database.Exists("travel-sample", null)) {
Database.Copy(path, "travel-sample", null);
}
// end::prebuilt-database[]
}
private static void QueryDeletedDocuments()
{
var collection = _Database.GetDefaultCollection();
// tag::query-deleted-documents[]
// Query documents that have been deleted
var query = QueryBuilder
.Select(SelectResult.Expression(Meta.ID))
.From(DataSource.Collection(collection))
.Where(Meta.IsDeleted);
// end::query-deleted-documents[]
}
private static void CreateDocument()
{
var collection = _Database.GetDefaultCollection();
// tag::initializer[]
using var mutableDoc = new MutableDocument("xyz");
mutableDoc.SetString("type", "task")
.SetString("owner", "todo")
.SetDate("createdAt", DateTimeOffset.UtcNow);
collection.Save(mutableDoc);
// end::initializer[]
}
private static void UpdateDocument()
{
var collection = _Database.GetDefaultCollection();
// tag::update-document[]
using var doc = collection.GetDocument("xyz");
using var mutableDoc = doc.ToMutable();
mutableDoc.SetString("name", "apples");
collection.Save(mutableDoc);
// end::update-document[]
}
private static void UseTypedAccessors()
{
using var mutableDoc = new MutableDocument();
// tag::date-getter[]
mutableDoc.SetValue("createdAt", DateTimeOffset.UtcNow);
var date = mutableDoc.GetDate("createdAt");
// end::date-getter[]
Console.WriteLine(date);
}
private static void DoBatchOperation()
{
var database = _Database;
var collection = database.GetDefaultCollection();
// tag::batch[]
database.InBatch(() =>
{
for (var i = 0; i < 10; i++) {
using var mutableDoc = new MutableDocument();
mutableDoc.SetString("type", "user");
mutableDoc.SetString("name", $"user {i}");
mutableDoc.SetBoolean("admin", false);
collection.Save(mutableDoc);
Console.WriteLine($"Saved user document {mutableDoc.GetString("name")}");
}
});
// end::batch[]
}
private static void DatabaseChangeListener()
{
var collection = _Database.GetDefaultCollection();
// tag::document-listener[]
collection.AddDocumentChangeListener("user.john", (sender, args) =>
{
using var doc = collection.GetDocument(args.DocumentID);
Console.WriteLine($"Status :: {doc.GetString("verified_account")}");
});
// end::document-listener[]
}
private static void DocumentExpiration()
{
var collection = _Database.GetDefaultCollection();
// tag::document-expiration[]
// Purge the document one day from now
var ttl = DateTimeOffset.UtcNow.AddDays(1);
collection.SetDocumentExpiration("doc123", ttl);
// Reset expiration
collection.SetDocumentExpiration("doc1", null);
// Query documents that will be expired in less than five minutes
var fiveMinutesFromNow = DateTimeOffset.UtcNow.AddMinutes(5).ToUnixTimeMilliseconds();
using var query = QueryBuilder
.Select(SelectResult.Expression(Meta.ID))
.From(DataSource.Collection(collection))
.Where(Meta.Expiration.LessThan(Expression.Double(fiveMinutesFromNow)));
// end::document-expiration[]
}
private static void UseBlob()
{
var collection = _Database.GetDefaultCollection();
using var newTask = new MutableDocument();
// tag::blob[]
// Note: Reading the data is implementation dependent, as with prebuilt databases
var image = File.ReadAllBytes("avatar.jpg"); (4)
var blob = new Blob("image/jpeg", image); (5)
newTask.SetBlob("avatar", blob); (6)
collection.Save(newTask);
// end::blob[]
}
public void CreateIndex()
{
var collection = _Database.GetDefaultCollection();
// tag::query-index[]
// tag::scopes-manage-index-collection[]
string[] indexProperties = new string[] { "type", "name" };
var config = new ValueIndexConfiguration(indexProperties);
collection.CreateIndex("TypeNameIndex", config);
// end::scopes-manage-index-collection[]
// end::query-index[]
}
public void CreateIndex_Querybuilder()
{
var collection = _Database.GetDefaultCollection();
// tag::query-index_Querybuilder[]
// For value types, this is optional but provides performance enhancements
var index = IndexBuilder.ValueIndex(
ValueIndexItem.Expression(Expression.Property("type")),
ValueIndexItem.Expression(Expression.Property("name"))); (7)
collection.CreateIndex("TypeNameIndex", index);
// end::query-index_Querybuilder[]
}
private static void SelectMeta()
{
var collection = _Database.GetDefaultCollection();
#warning query-select-meta unused?
// tag::query-select-meta[]
// tag::query-select-props[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Meta.ID),
SelectResult.Property("type"),
SelectResult.Property("name"))
.From(DataSource.Collection(collection));
foreach (var result in query.Execute()) {
Console.WriteLine($"Document ID :: {result.GetString("id")}");
Console.WriteLine($"Document Name :: {result.GetString("name")}");
}
// end::query-select-props[]
// end::query-select-meta[]
}
private static void SelectAll()
{
var collection = _Database.GetDefaultCollection();
{
// tag::query-select-all[]
using var query = QueryBuilder.Select(SelectResult.All())
.From(DataSource.Collection(collection));
// end::query-select-all[]
}
{
// tag::live-query[]
var query = QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(collection)); (8)
// Adds a query change listener.
// Changes will be posted on the main queue.
var token = query.AddChangeListener((sender, args) => (9)
{
var allResult = args.Results.AllResults();
foreach (var result in allResult) {
Console.WriteLine(result.Keys);
/* Update UI */
}
});
// end::live-query[]
// tag::stop-live-query[]
query.RemoveChangeListener(token);
query.Dispose();
// end::stop-live-query[]
}
}
private static void SelectWhere()
{
var collection = _Database.GetDefaultCollection();
// tag::query-where[]
using var query = QueryBuilder.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("hotel")))
.Limit(Expression.Int(10));
foreach (var result in query.Execute()) {
var dict = result.GetDictionary(collection.Name);
Console.WriteLine($"Document Name :: {dict?.GetString("name")}");
}
// end::query-where[]
}
private static void UseCollectionContains()
{
var collection = _Database.GetDefaultCollection();
// tag::query-collection-operator-contains[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Meta.ID),
SelectResult.Property("name"),
SelectResult.Property("public_likes"))
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("hotel"))
.And(ArrayFunction.Contains(Expression.Property("public_likes"),
Expression.String("Armani Langworth"))));
foreach (var result in query.Execute()) {
var publicLikes = result.GetArray("public_likes");
var jsonString = JsonConvert.SerializeObject(publicLikes);
Console.WriteLine($"Public Likes :: {jsonString}");
}
// end::query-collection-operator-contains[]
}
private static void UseCollectionIn()
{
var collection = _Database.GetDefaultCollection();
// tag::query-collection-operator-in[]
var values = new IExpression[]
{ Expression.Property("first"), Expression.Property("last"), Expression.Property("username") };
using var query = QueryBuilder.Select(
SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Expression.String("Armani").In(values));
foreach (var result in query.Execute()) {
var body = result.GetDictionary(0);
var jsonString = JsonConvert.SerializeObject(body);
Console.WriteLine($"In results :: {jsonString}");
}
// end::query-collection-operator-in[]
}
private static void SelectLike()
{
var collection = _Database.GetDefaultCollection();
// tag::query-like-operator[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Meta.ID),
SelectResult.Property("name"))
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("landmark"))
.And(Function.Lower(Expression.Property("name")).Like(Expression.String("Royal Engineers Museum"))))
.Limit(Expression.Int(10));
foreach (var result in query.Execute()) {
Console.WriteLine($"Name Property :: {result.GetString("name")}");
}
// end::query-like-operator[]
}
private static void SelectWildcardLike()
{
var collection = _Database.GetDefaultCollection();
// tag::query-like-operator-wildcard-match[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Meta.ID),
SelectResult.Property("name"))
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("landmark"))
.And(Function.Lower(Expression.Property("name")).Like(Expression.String("Eng%e%"))))
.Limit(Expression.Int(10));
foreach (var result in query.Execute()) {
Console.WriteLine($"Name Property :: {result.GetString("name")}");
}
// end::query-like-operator-wildcard-match[]
}
private static void SelectWildcardCharacterLike()
{
var collection = _Database.GetDefaultCollection();
// tag::query-like-operator-wildcard-character-match[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Meta.ID),
SelectResult.Property("name"))
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("landmark"))
.And(Expression.Property("name").Like(Expression.String("Royal Eng____rs Museum"))))
.Limit(Expression.Int(10));
foreach (var result in query.Execute()) {
Console.WriteLine($"Name Property :: {result.GetString("name")}");
}
// end::query-like-operator-wildcard-character-match[]
}
private static void SelectRegex()
{
var collection = _Database.GetDefaultCollection();
// tag::query-regex-operator[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Meta.ID),
SelectResult.Property("name"))
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("landmark"))
.And(Expression.Property("name").Regex(Expression.String("\\bEng.*e\\b"))))
.Limit(Expression.Int(10));
foreach (var result in query.Execute()) {
Console.WriteLine($"Name Property :: {result.GetString("name")}");
}
// end::query-regex-operator[]
}
private static void SelectJoin()
{
var collection = _Database.GetDefaultCollection();
var collection2 = _Database.GetDefaultCollection();
// tag::query-join[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Expression.Property("name").From("airline")),
SelectResult.Expression(Expression.Property("callsign").From("airline")),
SelectResult.Expression(Expression.Property("destinationairport").From("route")),
SelectResult.Expression(Expression.Property("stops").From("route")),
SelectResult.Expression(Expression.Property("airline").From("route")))
.From(DataSource.Collection(collection).As("airline"))
.Join(Join.InnerJoin(DataSource.Collection(collection2).As("route"))
.On(Meta.ID.From("airline").EqualTo(Expression.Property("airlineid").From("route"))))
.Where(Expression.Property("type").From("route").EqualTo(Expression.String("route"))
.And(Expression.Property("type").From("airline").EqualTo(Expression.String("airline")))
.And(Expression.Property("sourceairport").From("route").EqualTo(Expression.String("RIX"))));
foreach (var result in query.Execute()) {
Console.WriteLine($"Name Property :: {result.GetString("name")}");
}
// end::query-join[]
}
private static void GroupBy()
{
var collection = _Database.GetDefaultCollection();
// tag::query-groupby[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Function.Count(Expression.All())),
SelectResult.Property("country"),
SelectResult.Property("tz"))
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("airport"))
.And(Expression.Property("geo.alt").GreaterThanOrEqualTo(Expression.Int(300))))
.GroupBy(Expression.Property("country"), Expression.Property("tz"));
foreach (var result in query.Execute()) {
Console.WriteLine(
$"There are {result.GetInt("$1")} airports in the {result.GetString("tz")} timezone located in {result.GetString("country")} and above 300 ft");
}
// end::query-groupby[]
}
private static void OrderBy()
{
var collection = _Database.GetDefaultCollection();
// tag::query-orderby[]
using var query = QueryBuilder.Select(
SelectResult.Expression(Meta.ID),
SelectResult.Property("title"))
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("hotel")))
.OrderBy(Ordering.Property("title").Ascending())
.Limit(Expression.Int(10));
foreach (var result in query.Execute()) {
Console.WriteLine($"Title :: {result.GetString("title")}");
}
// end::query-orderby[]
}
private static void TestExplainStatement()
{
var collection = _Database.GetDefaultCollection();
{
// tag::query-explain-all[]
using var query =
QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("hotel")))
.GroupBy(Expression.Property("country"))
.OrderBy(Ordering.Property("title").Ascending()); (10)
Console.WriteLine(query.Explain()); (11)
// end::query-explain-all[]
}
{
// tag::query-explain-like[]
using var query =
QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").Like(Expression.String("%hotel%"))
.And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (12)
Console.WriteLine(query.Explain());
// end::query-explain-like[]
}
{
// tag::query-explain-nopfx[]
using var query =
QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").Like(Expression.String("hotel%"))
.And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (13)
Console.WriteLine(query.Explain());
// end::query-explain-nopfx[]
}
{
// tag::query-explain-function[]
using var query =
QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Function.Lower(Expression.Property("type")).EqualTo(Expression.String("hotel"))); (14)
Console.WriteLine(query.Explain());
// end::query-explain-function[]
}
{
// tag::query-explain-nofunction[]
using var query =
QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(Expression.Property("type").EqualTo(Expression.String("hotel"))); (15)
Console.WriteLine(query.Explain());
// end::query-explain-nofunction[]
}
}
public void CreateFullTextIndex()
{
var collection = _Database.GetDefaultCollection();
// tag::fts-index[]
string[] indexProperties = new string[] { "overview", "name" };
var config = new FullTextIndexConfiguration(indexProperties);
collection.CreateIndex("overviewFTSIndex", config);
// end::fts-index[]
}
public void FullTextSearch()
{
var collection = _Database.GetDefaultCollection();
// tag::fts-query[]
var query = collection.CreateQuery("SELECT * FROM _ WHERE MATCH(overviewFTSIndex, 'Michigan') ORDER BY RANK(overviewFTSIndex)");
foreach (var result in query.Execute()) {
Console.WriteLine($"Document id {result.GetString(0)}");
}
// end::fts-query[]
}
private static void CreateFullTextIndex_Querybuilder()
{
var collection = _Database.GetDefaultCollection();
// tag::fts-index_Querybuilder[]
var index = IndexBuilder.FullTextIndex(FullTextIndexItem.Property("overview")).IgnoreAccents(false);
collection.CreateIndex("overviewFTSIndex", index);
// end::fts-index_Querybuilder[]
}
private static void FullTextSearch_Querybuilder()
{
var collection = _Database.GetDefaultCollection();
// tag::fts-query_Querybuilder[]
var whereClause = FullTextFunction.Match(Expression.FullTextIndex("overviewFTSIndex"), "'michigan'");
using var query = QueryBuilder.Select(SelectResult.Expression(Meta.ID))
.From(DataSource.Collection(collection))
.Where(whereClause);
foreach (var result in query.Execute()) {
Console.WriteLine($"Document id {result.GetString(0)}");
}
// end::fts-query_Querybuilder[]
}
private static void StartReplication()
{
var collection = _Database.GetDefaultCollection();
#warning replication unused?
// tag::replication[]
// Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion)
var url = new Uri("ws://localhost:4984/db");
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target)
{
ReplicatorType = ReplicatorType.Pull
};
config.AddCollection(collection);
var replicator = new Replicator(config);
replicator.Start();
// end::replication[]
}
private static void ConsoleLogging()
{
// tag::console-logging[]
Database.Log.Console.Domains = LogDomain.All; (16)
Database.Log.Console.Level = LogLevel.Verbose; (17)
// end::console-logging[]
// tag::console-logging-db[]
Database.Log.Console.Domains = LogDomain.Database;
// end::console-logging-db[]
}
private static void FileLogging()
{
// tag::file-logging[]
var tempFolder = Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>().DefaultDirectory(), "cbllog");
var config = new LogFileConfiguration(tempFolder) (18)
{
MaxRotateCount = 5, (19)
MaxSize = 10240, (20)
UsePlaintext = false (21)
};
Database.Log.File.Config = config; // Apply configuration
Database.Log.File.Level = LogLevel.Info; (22)
// end::file-logging[]
}
private static void EnableCustomLogging()
{
// tag::set-custom-logging[]
Database.Log.Custom = new LogTestLogger(); (23)
// You can also specify the level of logging the logger receives
Database.Log.Custom = new LogTestLogger { Level = LogLevel.Warning };
// end::set-custom-logging[]
}
private static void WriteConsoleLog()
{
#warning write-console-logmsg unused?
// tag::write-console-logmsg[]
Database.Log.Console.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message");
// end::write-console-logmsg[]
}
private static void WriteCustomLog()
{
#warning write-custom-logmsg unused?
// tag::write-custom-logmsg[]
Database.Log.Custom?.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message");
// end::write-custom-logmsg[]
}
private static void WriteFileLog()
{
#warning write-file-logmsg unused?
// tag::write-file-logmsg[]
Database.Log.File.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message");
// end::write-file-logmsg[]
}
private static void EnableBasicAuth()
{
var collection = _Database.GetDefaultCollection();
#warning basic-authentication unused?
// tag::basic-authentication[]
var url = new Uri("ws://localhost:4984/mydatabase");
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
config.AddCollection(collection);
config.Authenticator = new BasicAuthenticator("john", "pass");
var replicator = new Replicator(config);
replicator.Start();
// end::basic-authentication[]
}
private static void EnableSessionAuth()
{
var collection = _Database.GetDefaultCollection();
// tag::session-authentication[]
var url = new Uri("ws://localhost:4984/mydatabase");
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
config.AddCollection(collection);
config.Authenticator = new SessionAuthenticator("904ac010862f37c8dd99015a33ab5a3565fd8447");
var replicator = new Replicator(config);
replicator.Start();
// end::session-authentication[]
}
private static void SetupReplicatorListener()
{
var replicator = _Replicator;
#warning replication-status unused?
// tag::replication-status[]
replicator.AddChangeListener((sender, args) =>
{
if (args.Status.Activity == ReplicatorActivityLevel.Stopped) {
Console.WriteLine("Replication stopped");
}
});
// end::replication-status[]
}
private static void ReplicatorPendingDocuments()
{
// tag::replication-pendingdocuments[]
var url = new Uri("ws://localhost:4984/mydatabase");
var target = new URLEndpoint(url);
var database = new Database("myDB");
var config = new ReplicatorConfiguration(target);
config.AddCollection(database.GetDefaultCollection());
config.ReplicatorType = ReplicatorType.Push;
// tag::replication-push-pendingdocumentids[]
var replicator = new Replicator(config);
var pendingDocIDs =
new HashSet<string>(replicator.GetPendingDocumentIDs(database.GetDefaultCollection())); (24)
// end::replication-push-pendingdocumentids[]
if (pendingDocIDs.Count > 0) {
Console.WriteLine($"There are {pendingDocIDs.Count} documents pending");
replicator.AddChangeListener((sender, change) =>
{
Console.WriteLine($"Replicator activity level is " +
change.Status.Activity.ToString());
// iterate and report-on previously
// retrieved pending docids 'list'
foreach (var docID in pendingDocIDs)
#warning replication-push-isdocumentpending unused?
// tag::replication-push-isdocumentpending[]
if (!replicator.IsDocumentPending(docID, database.GetDefaultCollection())) (25)
{
Console.WriteLine($"Doc ID {docID} now pushed");
};
// end::replication-push-isdocumentpending[]
});
replicator.Start();
}
// end::replication-pendingdocuments[]
}
private static void ReplicatorDocumentEvent()
{
var replicator = _Replicator;
// tag::add-document-replication-listener[]
var token = replicator.AddDocumentReplicationListener((sender, args) =>
{
var direction = args.IsPush ? "Push" : "Pull";
Console.WriteLine($"Replication type :: {direction}");
foreach (var doc in args.Documents) {
if (doc.Error == null) {
Console.WriteLine($"Doc ID :: {doc.Id}");
if (doc.Flags.HasFlag(DocumentFlags.Deleted)) {
Console.WriteLine("Successfully replicated a deleted document");
}
} else {
// There was an error
}
}
});
replicator.Start();
// end::add-document-replication-listener[]
// tag::remove-document-replication-listener[]
replicator.RemoveChangeListener(token);
// end::remove-document-replication-listener[]
}
private static void SetupReplicatorErrorListener()
{
// This can be done in the SetupReplicatorListener method
// But it is separate so that we can have two documentation entries
var replicator = _Replicator;
// tag::replication-error-handling[]
replicator.AddChangeListener((sender, args) =>
{
if (args.Status.Error != null) {
Console.WriteLine($"Error :: {args.Status.Error}");
}
});
// end::replication-error-handling[]
}
private static void DatabaseReplica()
{
var collection = _Database.GetDefaultCollection();
using (var database2 = new Database("backup")) {
// EE feature: This code will not compile on the community edition
// tag::database-replica[]
var targetDatabase = new DatabaseEndpoint(database2);
var config = new ReplicatorConfiguration(targetDatabase)
{
ReplicatorType = ReplicatorType.Push
};
config.AddCollection(collection);
var replicator = new Replicator(config);
replicator.Start();
// end::database-replica[]
}
}
private X509Certificate2 GetCertificate(string name)
{
return null;
}
public void PinCertificate()
{
var url = new Uri("wss://localhost:4984/db");
var target = new URLEndpoint(url);
// tag::certificate-pinning[]
// Note: `GetCertificate` is a placeholder method. This would be the platform-specific method
// to find and load the certificate as an instance of `X509Certificate2`.
// For .NET Core / .NET Framework this can be loaded from the filesystem path.
// For WinUI, from the assets directory.
// For iOS, from the main bundle.
// For Android, from the assets directory.
var certificate = GetCertificate("cert.cer");
var config = new ReplicatorConfiguration(target)
{
PinnedServerCertificate = certificate
};
// end::certificate-pinning[]
}
public void ReplicationCustomHeaders()
{
var url = new Uri("ws://localhost:4984/mydatabase");
var target = new URLEndpoint(url);
// tag::replication-custom-header[]
var config = new ReplicatorConfiguration(target)
{
Headers = new Dictionary<string, string>
{
["CustomHeaderName"] = "Value"
}
};
// end::replication-custom-header[]
}
private static void PushWithFilter(Database database)
{
var collection = _Database.GetDefaultCollection();
// tag::replication-push-filter[]
var url = new Uri("ws://localhost:4984/mydatabase");
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
config.AddCollection(collection, new CollectionConfiguration()
{
PushFilter = (document, flags) => (1)
{
if (flags.HasFlag(DocumentFlags.Deleted)) {
return false;
}
return true;
}
});
// Dispose() later
var replicator = new Replicator(config);
replicator.Start();
// end::replication-push-filter[]
}
private static void PullWithFilter(Database database)
{
var collection = _Database.GetDefaultCollection();
// tag::replication-pull-filter[]
var url = new Uri("ws://localhost:4984/mydatabase");
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
config.AddCollection(collection, new CollectionConfiguration()
{
PullFilter = (document, flags) => (1)
{
if (document.GetString("type") == "draft") {
return false;
}
return true;
}
});
// Dispose() later
var replicator = new Replicator(config);
replicator.Start();
// end::replication-pull-filter[]
}
public void TestCustomRetryConfig()
{
// tag::replication-retry-config[]
var url = new Uri("ws://localhost:4984/mydatabase");
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target);
// other config as required . . .
#warning replication-set-heartbeat unused?
// tag::replication-set-heartbeat[]
config.Heartbeat = TimeSpan.FromSeconds(120); // (26)
// end::replication-set-heartbeat[]
// tag::replication-set-maxattempts[]
#warning replication-set-maxattempts unused?
config.MaxAttempts = 20; // (27)
// end::replication-set-maxattempts[]
// tag::replication-set-maxattemptwaittime[]
#warning replication-set-maxattemptwaittime unused?
config.MaxAttemptsWaitTime = TimeSpan.FromSeconds(600); // (28)
// end::replication-set-maxattemptwaittime[]
// other config as required . . .
var replicator = new Replicator(config);
// end::replication-retry-config[]
}
private static void UsePredictiveModel()
{
using (var database = new Database("mydb")) {
var collection = database.GetDefaultCollection();
// tag::register-model[]
var model = new ImageClassifierModel();
Database.Prediction.RegisterModel("ImageClassifier", model);
// end::register-model[]
// tag::predictive-query-value-index[]
var index = IndexBuilder.ValueIndex(ValueIndexItem.Property("label"));
collection.CreateIndex("value-index-image-classifier", index);
// end::predictive-query-value-index[]
// tag::unregister-model[]
Database.Prediction.UnregisterModel("ImageClassifier");
// end::unregister-model[]
}
}
private static void UsePredictiveIndex()
{
using (var database = new Database("mydb")) {
var collection = database.GetDefaultCollection();
// tag::predictive-query-predictive-index[]
var input = Expression.Dictionary(new Dictionary<string, object>
{
["photo"] = Expression.Property("photo")
});
var index = IndexBuilder.PredictiveIndex("ImageClassifier", input);
collection.CreateIndex("predictive-index-image-classifier", index);
// end::predictive-query-predictive-index[]
}
}
private static void DoPredictiveQuery()
{
using (var database = new Database("mydb")) {
var collection = database.GetDefaultCollection();
// tag::predictive-query[]
var input = Expression.Dictionary(new Dictionary<string, object>
{
["photo"] = Expression.Property("photo")
});
var prediction = Function.Prediction("ImageClassifier", input); (1)
using var query = QueryBuilder.Select(SelectResult.All())
.From(DataSource.Collection(collection))
.Where(prediction.Property("label").EqualTo(Expression.String("car"))
.And(prediction.Property("probability").GreaterThanOrEqualTo(Expression.Double(0.8))));
var result = query.Execute();
Console.WriteLine($"Number of rows: {result.Count()}");
// end::predictive-query[]
}
}
public List<Result> docsonly_N1QLQueryString(Database argDB)
{
DatabaseConfiguration config = new DatabaseConfiguration();
Database database = new Database("dbName", config);
// tag::query-syntax-n1ql[]
using var query =
database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = \"hotel\""); (29)
return query.Execute().AllResults();
// end::query-syntax-n1ql[]
}
public void docsonly_N1QLQueryStringParams(Database argDB)
{
var database = _Database;
// tag::query-syntax-n1ql-params[]
using var query =
database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = $type"); (30)
var n1qlParams = new Parameters();
n1qlParams.SetString("type", "hotel"); (31)
query.Parameters = n1qlParams;
var results = query.Execute().AllResults();
// end::query-syntax-n1ql-params[]
}
public void testQuerySyntaxAll()
{
// tag::query-syntax-all[]
var database = new Database("hotels");
var query = QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(database.GetDefaultCollection()));
// end::query-syntax-all[]
// tag::query-access-all[]
var results = query.Execute().AllResults();
var hotels = new List<Dictionary<string, object>>();
if (results?.Count > 0) {
foreach (var result in results) {
// get the result into our dictionary object
var thisDocsProps = result.GetDictionary("hotels"); (32)
if (thisDocsProps != null) {
var docID = thisDocsProps.GetString("id"); (33)
var docName = thisDocsProps.GetString("name");
var docCity = thisDocsProps.GetString("city");
var docType = thisDocsProps.GetString("type");
var hotel = thisDocsProps.ToDictionary();
hotels.Add(hotel);
}
}
}
// end::query-access-all[]
// tag::query-access-json[]
foreach (var result in query.Execute()) {
// get the result into a JSON String
var docJSONString = result.ToJSON();
// Get a native dictionary object using the JSON string
var dictFromJSONstring =
JsonConvert.
DeserializeObject<Dictionary<string, object>>
(docJSONString);
// use the created dictionary
if (dictFromJSONstring != null) {
var docID = dictFromJSONstring["id"].ToString();
var docName = dictFromJSONstring["name"].ToString();
var docCity = dictFromJSONstring["city"].ToString();
var docType = dictFromJSONstring["type"].ToString();
}
//Get a custom object using the JSON string
Hotel hotel =
JsonConvert.DeserializeObject<Hotel>(docJSONString);
}
// end::query-access-json[]
}
public void testQuerySyntaxProps()
{
// tag::query-syntax-props[]
var database = new Database("hotels");
List<Dictionary<string, object>> hotels = new List<Dictionary<string, object>>();
var query = QueryBuilder.Select(
SelectResult.Property("type"),
SelectResult.Property("name"),
SelectResult.Property("city")).From(DataSource.Collection(database.GetDefaultCollection()));
// end::query-syntax-props[]
// tag::query-access-props[]
var results = query.Execute().AllResults();
foreach (var result in results) {
// get the returned array of k-v pairs into a dictionary
var hotel = result.ToDictionary();
// add hotel dictionary to list of hotel dictionaries
hotels.Add(hotel);
// use the properties of the returned array of k-v pairs directly
var docType = result.GetString("type");
var docName = result.GetString("name");
var docCity = result.GetString("city");
}
// end::query-access-props[]
}
public void testQuerySyntaxCount()
{
// tag::query-syntax-count-only[]
var database = new Database("hotels");
var query =
QueryBuilder
.Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount")) (34)
.From(DataSource.Collection(database.GetDefaultCollection()));
// end::query-syntax-count-only[]
// tag::query-access-count-only[]
var results = query.Execute().AllResults();
foreach (var result in results) {
var numberOfDocs = result.GetInt("mycount"); (35)
}
// end::query-access-count-only[]
}
public void ibQueryForID()
{
// tag::query-syntax-id[]
var database = new Database("hotels");
var query = QueryBuilder
.Select(SelectResult.Expression(Meta.ID).As("this_ID"))
.From(DataSource.Collection(database.GetDefaultCollection()));
// end::query-syntax-id[]
// tag::query-access-id[]
var results = query.Execute().AllResults();
foreach (var result in results) {
var docID = result.GetString("this_ID"); (36)
var doc = database.GetDefaultCollection().GetDocument(docID);
}
// end::query-access-id[]
}
#warning query-syntax-pagination-all unused (and out of place)?
// tag::query-syntax-pagination-all[]
public void testQueryPagination()
{
// tag::query-syntax-pagination[]
var database = new Database("hotels");
var limit = 20;
var offset = 0;
// get a count of the number of docs matching the query
var countQuery =
QueryBuilder
.Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount"))
.From(DataSource.Collection(database.GetDefaultCollection()));
var numberOfDocs =
countQuery.Execute().First().GetInt("mycount");
if (numberOfDocs < limit) {
limit = numberOfDocs;
}
while (offset < numberOfDocs) {
var listQuery =
QueryBuilder
.Select(SelectResult.All())
.From(DataSource.Collection(database.GetDefaultCollection()))
.Limit(Expression.Int(limit), Expression.Int(offset)); (37)
foreach (var result in listQuery.Execute()) {
// Display and or process query results batch
}
offset = offset + limit;
}
// end::query-syntax-pagination[]
// end::query-syntax-pagination-all[]
}
public void JsonApiDocument()
{
var collection = _Database.GetDefaultCollection();
// tag::tojson-document[]
// Get a document
var doc = collection.GetDocument("hotel_10025");
// Get document data as JSON String
var docJSONString = doc?.ToJSON();
// Get Json Object from the Json String
JObject jsonObject = JObject.Parse(docJSONString);
// Get Native Object (anhotel) from JSON String
List<Hotel> hotels = new List<Hotel>();
var hotel = JsonConvert.DeserializeObject<Hotel>(docJSONString);
hotels.Add(hotel);
// Update the retrieved native object
hotel.Name = "A Copy of " + hotel.Name;
hotel.Id = "2001";
// Convert the updated object back to a JSON string
var newJsonString = JsonConvert.SerializeObject(hotel);
// Update new document with JSOn String
MutableDocument newhotel = doc.ToMutable();
newhotel.SetJSON(newJsonString);
foreach (string key in newhotel.ToDictionary().Keys) {
Console.WriteLine("Data -- {0} = {1}",
key, newhotel.GetValue(key));
}
collection.Save(newhotel);
var retrievedDoc = collection.GetDocument("2001").ToJSON();
Console.Write(retrievedDoc);
// end::tojson-document[]
}
public void JsonApiArray()
{
var collection = _Database.GetDefaultCollection();
// tag::tojson-array[]
// JSON String -- an Array (3 elements. including embedded arrays)
var jsonString = "[{'id':'1000','type':'hotel','name':'Hotel Ted','city':'Paris','country':'France','description':'Undefined description for Hotel Ted'},{'id':'1001','type':'hotel','name':'Hotel Fred','city':'London','country':'England','description':'Undefined description for Hotel Fred'}, {'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}]".Replace("'", "\"");
// Get JSON Array from JSON String
var jsonArray = JArray.Parse(jsonString);
// Create mutable array using JSON String Array
var mutableArray = new MutableArrayObject();
mutableArray.SetJSON(jsonString);
// Create a new document for each array element
for (int i = 0; i < mutableArray.Count; i++) {
var dict = mutableArray.GetDictionary(i);
var docid = mutableArray[i].Dictionary.GetString("id");
var mutableDoc = new MutableDocument(docid, dict.ToDictionary());
collection.Save(mutableDoc);
}
// Get one of the created docs and iterate through one of the embedded arrays
var extendedDoc = collection.GetDocument("1002");
var features = extendedDoc.GetArray("features");
// Print its elements
foreach (string feature in features) {
Console.Write($"{feature} ");
//process array item as required
}
var featuresJSON = extendedDoc.GetArray("features").ToJSON();
// end::tojson-array[]
}
public void JsonApiDictionary()
{
var ourdbname = "ournewdb";
if (Database.Exists(ourdbname, "/")) {
Database.Delete(ourdbname, "/");
}
// tag::tojson-dictionary[]
// Get dictionary from JSONstring
var jsonString = "{'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}".Replace("'", "\"");
var mutableDict = new MutableDictionaryObject(json: jsonString);
// use dictionary to get name value
var name = mutableDict.GetString("name");
// Iterate through keys
foreach (string key in mutableDict.Keys) {
Console.WriteLine("Data -- {0} = {1}", key, mutableDict.GetValue(key).ToString());
}
// end::tojson-dictionary[]
}
public void JsonApiBlob()
{
var userName = "ian";
var collection = _Database.GetDefaultCollection();
var database = _Database;
// tag::tojson-blob[]
// Initialize base document for blob from a JSON string
var docId = "1002";
var jsonString = "{'ref':'hotel_1002','type':'hotel','name':'Hotel Ned'," +
"'city':'Balmain','country':'Australia'," +
"'description':'Undefined description for Hotel Ned'," +
"'features':['Cable TV','Toaster','Microwave']}".Replace("'", "\"");
var mutableDoc = new MutableDocument(docId, jsonString);
// Get the content (an image), create blob and add to doc)
var defaultDirectory =
Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>()
.DefaultDirectory(),
userName);
var imagePath = Path.Combine(defaultDirectory, "avatarimage.jpg");
var imageUri = new Uri(imagePath.ToString());
var imageBlob = new Blob("image/jpg", imageUri);
mutableDoc.SetBlob("avatar", imageBlob);
// This example generates a 'blob not saved' exception
try {
Console.WriteLine("myBlob (unsaved) as JSON = {0}", imageBlob.ToJSON());
} catch (Exception e) {
Console.WriteLine("Exception = {0}", e.Message);
}
collection.Save(mutableDoc);
// Alternatively -- depending on use case
database.SaveBlob(new Blob("image/jpg", imageUri));
// Retrieve saved doc, get blob as JSON andheck its still a 'blob'
var sameDoc = collection.GetDocument(docId);
var reconstitutedBlob = new MutableDictionaryObject().
SetDictionary("blobCOPY", new MutableDictionaryObject(sameDoc.GetBlob("avatar").ToJSON()));
if (Blob.IsBlob(
reconstitutedBlob.GetDictionary("blobCOPY").ToDictionary())) {
//... process accordingly
Console.WriteLine("Its a Blob!!");
}
// end::tojson-blob[]
}
private bool ValidatePassword(SecureString password) => true;
public void P2PListenerSimple()
{
var collection = _Database.GetDefaultCollection();
// tag::listener-simple[]
var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (38)
endpointConfig.Authenticator =
new ListenerPasswordAuthenticator(
(sender, username, password) =>
{
// ValidatePassword can make use of the SecureString class
// to the desired level of security (or just convert it to string
// if no intense security is required)
return username == "valid.user" && ValidatePassword(password);
}
); (39)
var listener = new URLEndpointListener(endpointConfig); (40)
listener.Start(); (41)
// end::listener-simple[]
}
public void P2PReplicatorSimple()
{
var collection = _Database.GetDefaultCollection();
// tag::replicator-simple[]
var endpointConfig = new URLEndpoint(new Uri("wss://listener.com:4984/otherDB")); (42)
var replConfig = new ReplicatorConfiguration(endpointConfig); (43)
replConfig.AddCollection(collection);
replConfig.AcceptOnlySelfSignedServerCertificate = true; (44)
replConfig.Authenticator =
new BasicAuthenticator("valid.user", "valid.password.string"); (45)
var replicator = new Replicator(replConfig); (46)
replicator.Start(); (47)
// end::replicator-simple[]
}
public void GettingStarted1()
{
var collection = _Database.GetDefaultCollection();
// tag::listener-initialize[]
// tag::listener-config-db[]
// Initialize the listener config
var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (48)
// end::listener-config-db[]
// tag::listener-config-port[]
endpointConfig.Port = 55990; (49)
// end::listener-config-port[]
// tag::listener-config-netw-iface[]
endpointConfig.NetworkInterface = "10.1.1.10"; (50)
// end::listener-config-netw-iface[]
// tag::listener-config-delta-sync[]
endpointConfig.EnableDeltaSync = true; (51)
// end::listener-config-delta-sync[]
#warning listener-config-tls-full unused?
// tag::listener-config-tls-full[]
// tag::listener-config-tls-enable[]
endpointConfig.DisableTLS = false; (52)
// end::listener-config-tls-enable[]
// tag::listener-config-tls-id-anon[]
// Use an Anonymous Self-Signed Cert
endpointConfig.TlsIdentity = null; (53)
// end::listener-config-tls-id-anon[]
// tag::listener-config-client-auth-pwd[]
// Configure the client authenticator
// Here we are using Basic Authentication) (54)
SecureString validPassword = new SecureString(); /* example only */
// Get SecureString input for validPassword
var validUser = "valid.username";
endpointConfig.Authenticator = new ListenerPasswordAuthenticator(
(sender, username, password) =>
{
// Implement your own ValidatePassword function
return username == validUser && ValidatePassword(password);
}
);
// end::listener-config-client-auth-pwd[]
// tag::listener-start[]
// Initialize the listener
var listener = new URLEndpointListener(endpointConfig); (55)
// Start the listener
listener.Start(); (56)
// end::listener-start[]
// end::listener-initialize[]
#warning old-listener-config-tls-disable unused?
// tag::old-listener-config-tls-disable[]
endpointConfig.DisableTLS = true;
// end::old-listener-config-tls-disable[]
#warning listener-config-tls-id-nil-2 unused?
// tag::listener-config-tls-id-nil-2[]
// Use “anonymous” cert. These are self signed certs created by the system
endpointConfig.TlsIdentity = null;
// end::listener-config-tls-id-nil-2[]
#warning old-listener-config-delta-sync unused?
// tag::old-listener-config-delta-sync[]
endpointConfig.EnableDeltaSync = true;
// end::old-listener-config-delta-sync[]
// tag::listener-status-check[]
ulong connectionCount = listener.Status.ConnectionCount; (57)
ulong activeConnectionCount = listener.Status.ActiveConnectionCount; (58)
// end::listener-status-check[]
// tag::listener-stop[]
listener.Stop();
// end::listener-stop[]
// tag::listener-get-network-interfaces[]
foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) {
if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet) {
// do something with the interface(s)
}
}
// end::listener-get-network-interfaces[]
}
public void GettingStarted2()
{
var collection = _Database.GetDefaultCollection();
{
#warning listener-get-url-list unused?
// tag::listener-get-url-list[]
var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection });
var listener = new URLEndpointListener(endpointConfig);
listener.Start();
// Note, converting to string omitted.
Console.WriteLine("URLS are {0} ", listener.Urls);
// end::listener-get-url-list[]
#warning listener-config-tls-disable unused?
// tag::listener-config-tls-disable[]
endpointConfig.DisableTLS = true; (59)
// end::listener-config-tls-disable[]
#warning listener-local-db unused?
// tag::listener-local-db[]
// . . . preceding application logic . . .
// Get the database (and create it if it doesn't exist)
var database = new Database("mydb");
// end::listener-local-db[]
// tag::listener-config-tls-id-full[]
// tag::listener-config-tls-id-caCert[]
// Use CA Cert
// Create a TLSIdentity from an imported key-pair
// . . . previously declared variables include ...
X509Store store =
new X509Store(StoreName.My); // create and label x509 store
// Get keys and certificates from PKCS12 data
byte[] certData =
File.ReadAllBytes("c:client.p12"); (60)
// . . . other user code . . .
#warning import-tls-identity unused?
// tag::import-tls-identity[]
TLSIdentity identity = TLSIdentity.ImportIdentity(
store,
certData, (61)
"123", // Password to access certificate data
"couchbase-demo-cert",
null); // Label to get cert in certificate map
// NOTE: If a null label is supplied then the same
// default directory for a Couchbase Lite database
// is used for map.
// end::import-tls-identity[]
// end::listener-config-tls-id-caCert[]
// tag::listener-config-tls-id-anon[]
// Use an Anonymous Self-Signed Cert
endpointConfig.TlsIdentity = null; (62)
// end::listener-config-tls-id-anon[]
#warning listener-config-tls-id-set unused?
// tag::listener-config-tls-id-set[]
// Set the TLS Identity
endpointConfig.TlsIdentity = identity; (63)
// end::listener-config-tls-id-set[]
// end::listener-config-tls-id-full[]
}
{
var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection });
// tag::listener-config-client-auth-root[]
// Configure the client authenticator
// to validate using ROOT CA
// Get the valid cert chain, in this instance from
// PKCS12 data containing private key, public key
// and certificates (64)
var clientData = File.ReadAllBytes("c:client.p12");
var ourCaData = File.ReadAllBytes("c:client-ca.der");
// Get the root certs from the data
var rootCert = new X509Certificate2(ourCaData); (65)
// Configure the authenticator to use the root certs
var certAuth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert));
endpointConfig.Authenticator = certAuth; (66)
// Initialize the listener using the config
var listener = new URLEndpointListener(endpointConfig);
// end::listener-config-client-auth-root[]
// tag::listener-config-client-auth-lambda[]
// Configure the client authenticator
// to validate using application logic
// Get the valid cert chain, in this instance from
// PKCS12 data containing private key, public key
// and certificates (67)
clientData = File.ReadAllBytes("c:client.p12");
ourCaData = File.ReadAllBytes("c:client-ca.der");
// Configure the authenticator to pass the root certs
// To a user supplied code block for authentication
var callbackAuth =
new ListenerCertificateAuthenticator(
(object sender, X509Certificate2Collection chain) =>
{
// . . . user supplied code block
// . . . returns boolean value (true=authenticated)
return true;
}); (68)
endpointConfig.Authenticator = callbackAuth; (69)
// end::listener-config-client-auth-lambda[]
}
}
public void datatype_usage()
{
#warning datatype_usage unused?
// tag::datatype_usage[]
// tag::datatype_usage_createdb[]
// Get the database (and create it if it doesn’t exist).
using var database = new Database("hoteldb");
var collection = database.GetDefaultCollection();
// end::datatype_usage_createdb[]
// tag::datatype_usage_createdoc[]
// Create your new document
using var mutableDoc = new MutableDocument("hoteldoc");
// end::datatype_usage_createdoc[]
// tag::datatype_usage_mutdict[]
// Create and populate mutable dictionary
var address = new MutableDictionaryObject();
address.SetString("street", "1 Main st.");
address.SetString("city", "San Francisco");
address.SetString("state", "CA");
address.SetString("country", "USA");
address.SetString("code", "90210");
// end::datatype_usage_mutdict[]
// tag::datatype_usage_mutarray[]
// Create and populate mutable array
var phones = new MutableArrayObject();
phones.AddString("650-000-0000");
phones.AddString("650-000-0001");
// end::datatype_usage_mutarray[]
// tag::datatype_usage_populate[]
// Initialize and populate the document
// Add document type and hotel name as string
mutableDoc.SetString("type", "hotel");
mutableDoc.SetString("name", "Hotel Java Mo");
// Add average room rate (float)
mutableDoc.SetFloat("room_rate", 121.75f);
// Add address (dictionary)
mutableDoc.SetDictionary("address", address);
// Add phone numbers(array)
mutableDoc.SetArray("phones", phones);
// end::datatype_usage_populate[]
// tag::datatype_usage_persist[]
collection.Save(mutableDoc);
// end::datatype_usage_persist[]
// tag::datatype_usage_closedb[]
database.Close();
// end::datatype_usage_closedb[]
// end::datatype_usage[]
}
public void datatype_dictionary()
{
var collection = _Database.GetDefaultCollection();
// tag::datatype_dictionary[]
var doc = collection.GetDocument("doc1");
// Getting a dictionary from the document's properties
var dict = doc.GetDictionary("address");
// Access a value with a key from the dictionary
var street = dict.GetString("street");
// Iterate dictionary
foreach (var key in dict.Keys) {
Console.WriteLine($"Key {key} = {dict.GetValue(key)}");
}
// Create a mutable copy
var mutableDict = dict.ToMutable();
// end::datatype_dictionary[]
}
public void datatype_mutable_dictionary()
{
var collection = _Database.GetDefaultCollection();
// tag::datatype_mutable_dictionary[]
// Create a new mutable dictionary and populate some keys/values
var mutableDict = new MutableDictionaryObject();
mutableDict.SetString("street", "1 Main st.");
mutableDict.SetString("city", "San Francisco");
// Add the dictionary to a document's properties and save the document
using var mutableDoc = new MutableDocument("doc1");
mutableDoc.SetDictionary("address", mutableDict);
collection.Save(mutableDoc);
// end::datatype_mutable_dictionary[]
}
public void datatype_array()
{
var collection = _Database.GetDefaultCollection();
// tag::datatype_array[]
var document = collection.GetDocument("doc1");
// Getting a phones array from the document's properties
var array = document.GetArray("phones");
// Get element count
var count = array.Count();
// Access an array element by index
if (count >= 0) { var phone = array[1]; }
// Iterate dictionary
for (int i = 0; i < count; i++) {
Console.WriteLine($"Item {i.ToString()} = {array[i]}");
}
// Create a mutable copy
var mutableArray = array.ToMutable();
// end::datatype_array[]
}
public void datatype_mutable_array()
{
var collection = _Database.GetDefaultCollection();
// tag::datatype_mutable_array[]
// Create a new mutable array and populate data into the array
var mutableArray = new MutableArrayObject();
mutableArray.AddString("650-000-0000");
mutableArray.AddString("650-000-0001");
// Set the array to document's properties and save the document
using var mutableDoc = new MutableDocument("doc1");
mutableDoc.SetArray("phones", mutableArray);
collection.Save(mutableDoc);
// end::datatype_mutable_array[]
}
static void Main(string[] args)
{
// NOTE: PLEASE PLEASE PLEASE do not break the compilation of this file. It is
// by far the easiest way to check for its correctness. If you don't know how to
// compile a C# program, then find someone who does before you commit your changes!!!
Console.WriteLine("This program is not meant to be executed, only compiled");
}
}
/* ----------------------------------------------------------- */
/* --------------------- ACTIVE SIDE ----------------------- */
/* --------------- stubs for documentation ----------------- */
/* ----------------------------------------------------------- */
class ActivePeer : IMessageEndpointDelegate
{
ActivePeer()
{
// tag::message-endpoint[]
var database = new Database("dbname");
// The delegate must implement the `IMessageEndpointDelegate` protocol.
var messageEndpointTarget = new MessageEndpoint(uid: "UID:123", target: "",
protocolType: ProtocolType.MessageStream, delegateObject: this);
// end::message-endpoint[]
// tag::message-endpoint-replicator[]
var replConfig = new ReplicatorConfiguration(messageEndpointTarget);
replConfig.AddCollection(database.GetDefaultCollection());
// Create the replicator object
var replicator = new Replicator(replConfig);
// Start the replicator
replicator.Start();
// end::message-endpoint-replicator[]
}
// tag::create-connection[]
/* implementation of MessageEndpointDelegate */
public IMessageEndpointConnection CreateConnection(MessageEndpoint endpoint)
{
var connection = new ActivePeerConnection(); /* implements MessageEndpointConnection */
return connection;
}
// end::create-connection[]
}
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
class ActivePeerConnection : IMessageEndpointConnection
{
private IReplicatorConnection _replicatorConnection;
public void Disconnect()
{
// tag::active-replicator-close[]
_replicatorConnection?.Close(null);
// end::active-replicator-close[]
}
public void Receive(byte[] data)
{
// tag::active-peer-receive[]
var message = Message.FromBytes(data);
_replicatorConnection?.Receive(message);
// end::active-peer-receive[]
}
// tag::active-peer-close[]
/* implementation of MessageEndpointConnection */
public async Task Close(Exception error)
{
// await socket.Close, etc (or do nothing if already closed)
// throw MessagingException if something goes wrong (though
// since it is "close" nothing special will happen)
}
// end::active-peer-close[]
// tag::active-peer-open[]
/* implementation of MessageEndpointConnection */
public async Task Open(IReplicatorConnection connection)
{
_replicatorConnection = connection;
// await socket.Open(), etc
// throw MessagingException if something goes wrong
}
// end::active-peer-open[]
// tag::active-peer-send[]
/* implementation of MessageEndpointConnection */
public async Task Send(Message message)
{
var data = message.ToByteArray();
// await Socket.Send(), etc
// throw MessagingException if something goes wrong
}
// end::active-peer-send[]
}
/* ----------------------------------------------------------- */
/* --------------------- PASSIVE SIDE ---------------------- */
/* --------------- stubs for documentation ----------------- */
/* ----------------------------------------------------------- */
class PassivePeerConnection : IMessageEndpointConnection
{
private MessageEndpointListener _messageEndpointListener;
private IReplicatorConnection _replicatorConnection;
public void StartListener()
{
// tag::listener[]
var database = new Database("mydb");
var endpointConfig = new MessageEndpointListenerConfiguration(new[] { database.GetDefaultCollection() }, ProtocolType.MessageStream);
_messageEndpointListener = new MessageEndpointListener(endpointConfig);
// end::listener[]
}
public void StopListener()
{
// tag::passive-stop-listener[]
_messageEndpointListener?.CloseAll();
// end::passive-stop-listener[]
}
public void AcceptConnection()
{
// tag::advertizer-accept[]
var connection = new PassivePeerConnection(); /* implements MessageEndpointConnection */
_messageEndpointListener?.Accept(connection);
// end::advertizer-accept[]
}
public void Disconnect()
{
// tag::passive-replicator-close[]
_replicatorConnection?.Close(null);
// end::passive-replicator-close[]
}
public void Receive(byte[] data)
{
// tag::passive-peer-receive[]
var message = Message.FromBytes(data);
_replicatorConnection?.Receive(message);
// end::passive-peer-receive[]
}
// tag::passive-peer-close[]
/* implementation of MessageEndpointConnection */
public async Task Close(Exception error)
{
// await socket.Close, etc (or do nothing if already closed)
// throw MessagingException if something goes wrong (though
// since it is "close" nothing special will happen)
}
// end::passive-peer-close[]
// tag::passive-peer-open[]
/* implementation of MessageEndpointConnection */
public Task Open(IReplicatorConnection connection)
{
_replicatorConnection = connection;
// socket should already be open on the passive side
return Task.FromResult(true);
}
// end::passive-peer-open[]
// tag::passive-peer-send[]
/* implementation of MessageEndpointConnection */
public async Task Send(Message message)
{
var data = message.ToByteArray();
// await Socket.Send(), etc
// throw MessagingException if something goes wrong
}
// end::passive-peer-send[]
}
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
// tag::predictive-model[]
// tensorFlowModel is a fake implementation
// this would be the implementation of the ml model you have chosen
class TensorFlowModel
{
public static IDictionary<string, object> PredictImage(byte[] data)
{
// Do calculations, etc
return null;
}
}
class ImageClassifierModel : IPredictiveModel
{
public DictionaryObject Predict(DictionaryObject input)
{
var blob = input.GetBlob("photo");
if (blob == null) {
return null;
}
var imageData = blob.Content;
// tensorFlowModel is a fake implementation
// this would be the implementation of the ml model you have chosen
var modelOutput = TensorFlowModel.PredictImage(imageData);
return new MutableDictionaryObject(modelOutput); (1)
}
}
// end::predictive-model[]
// tag::custom-logging[]
class LogTestLogger : ILogger
{
public LogLevel Level { get; set; }
public void Reset()
{
}
public void Log(LogLevel level, LogDomain domain, string message)
{
// handle the message, for example piping it to
// a third party framework
}
}
// end::custom-logging[]
// tag::local-win-conflict-resolver[]
class LocalWinConflictResolver : IConflictResolver
{
public Document Resolve(Conflict conflict)
{
return conflict.LocalDocument;
}
}
// end::local-win-conflict-resolver[]
// tag::remote-win-conflict-resolver[]
class RemoteWinConflictResolver : IConflictResolver
{
public Document Resolve(Conflict conflict)
{
return conflict.RemoteDocument;
}
}
// end::remote-win-conflict-resolver[]
// tag::merge-conflict-resolver[]
class MergeConflictResolver : IConflictResolver
{
public Document Resolve(Conflict conflict)
{
var localDict = conflict.LocalDocument.ToDictionary();
var remoteDict = conflict.RemoteDocument.ToDictionary();
var result = localDict.Concat(remoteDict)
.GroupBy(kv => kv.Key)
.ToDictionary(g => g.Key, g => g.First().Value);
return new MutableDocument(conflict.DocumentID, result);
}
}
// end::merge-conflict-resolver[]
}
#warning p2p-act-rep-func used, but contains nothing
// tag::p2p-act-rep-func[]
#warning p2p-act-rep-config-type used, but contains nothing
// tag::p2p-act-rep-config-type[]
// end::p2p-act-rep-config-type[]
#warning autopurge-override used, but contains nothing
// tag::autopurge-override[]
// Set autopurge option
// here we override its default
// end::autopurge-override[]
#warning p2p-act-rep-config-cont used, but contains nothing
// tag::p2p-act-rep-config-cont[]
// Configure Sync Mode
// end::p2p-act-rep-config-cont[]
#warning p2p-act-rep-config-self-cert used, but contains nothing
// tag::p2p-act-rep-config-self-cert[]
// Configure Server Security -- only accept self-signed certs
// end::p2p-act-rep-config-self-cert[]
// Configure Client Security (70)
#warning p2p-act-rep-auth used, but contains nothing
// tag::p2p-act-rep-auth[]
// Configure basic auth using user credentials
// end::p2p-act-rep-auth[]
#warning p2p-act-rep-start-full used, but contains nothing
// tag::p2p-act-rep-start-full[]
// Initialize and start a replicator
// Initialize replicator with configuration data
#warning p2p-act-rep-add-change-listener used, but contains nothing
// tag::p2p-act-rep-add-change-listener[]
#warning p2p-act-rep-add-change-listener-label used, but contains nothing
// tag::p2p-act-rep-add-change-listener-label[]
//Optionally add a change listener (71)
// end::p2p-act-rep-add-change-listener-label[]
// end::p2p-act-rep-add-change-listener[]
#warning p2p-act-rep-start used, but contains nothing
// tag::p2p-act-rep-start[]
// Start replicator
// end::p2p-act-rep-start[]
// end::p2p-act-rep-start-full[]
// end::p2p-act-rep-func[]
#warning p2p-act-rep-config-cacert used, but contains nothing
// tag::p2p-act-rep-config-cacert[]
// Configure Server Security -- only accept CA certs
// end::p2p-act-rep-config-cacert[]
#warning p2p-act-rep-config-cacert-pinned used, but contains nothing
// tag::p2p-act-rep-config-cacert-pinned[]
// Only CA Certs accepted
// end::p2p-act-rep-config-cacert-pinned[]
#warning p2p-act-rep-status used, but contains nothing
// tag::p2p-act-rep-status[]
// end::p2p-act-rep-status[]
#warning p2p-act-rep-stop used, but contains nothing
// tag::p2p-act-rep-stop[]
// Stop replication.
// end::p2p-act-rep-stop[]
#warning p2p-tlsid-store-in-keychain used, but contains nothing
// tag::p2p-tlsid-store-in-keychain[]
// end::p2p-tlsid-store-in-keychain[]
#warning p2p-tlsid-delete-id-from-keychain used, but contains nothing
// tag::p2p-tlsid-delete-id-from-keychain[]
// end::p2p-tlsid-delete-id-from-keychain[]
public class MyClass
{
public Database Database { get; set; }
public Replicator Replicator { get; set; } (72)
public void StartReplication()
{
// tag::sgw-repl-pull[]
var url = new Uri("wss://localhost:4984/db"); (73)
var target = new URLEndpoint(url);
var config = new ReplicatorConfiguration(target)
{
ReplicatorType = ReplicatorType.Pull
};
config.AddCollection(Database.GetDefaultCollection());
Replicator = new Replicator(config);
Replicator.Start();
// end::sgw-repl-pull[]
}
public void InitReplication()
{
// tag::sgw-act-rep-initialize[]
// initialize the replicator configuration
var url = new URLEndpoint(new Uri("wss://10.0.2.2:4984/anotherDB")); (74)
var replConfig = new ReplicatorConfiguration(url);
// Add collections to the config now
// end::sgw-act-rep-initialize[]
}
}
-
Use keychain identity in config
// // Program.cs // // Copyright (c) 2017 Couchbase, Inc All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // using Couchbase.Lite; using Couchbase.Lite.DI; using Couchbase.Lite.Enterprise.Query; using Couchbase.Lite.Logging; using Couchbase.Lite.P2P; using Couchbase.Lite.Query; using Couchbase.Lite.Sync; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Net.NetworkInformation; using System.Security; using System.Security.Cryptography.X509Certificates; namespace api_walkthrough { class Hotel { public string Id { get; set; } public string Name { get; set; } } class Program { private static readonly Database _Database = null; private static readonly Replicator _Replicator = null; private static readonly URLEndpointListener _listener = null; public void GettingStarted() { // tag::getting-started[] // using System; // using Couchbase.Lite; // using Couchbase.Lite.Query; // using Couchbase.Lite.Sync; // Get the database (and create it if it doesn't exist) var database = new Database("mydb"); var collection = database.GetDefaultCollection(); // Create a new document (i.e. a record) in the database var id = default(string); using var createdDoc = new MutableDocument(); createdDoc.SetFloat("version", 2.0f) .SetString("type", "SDK"); // Save it to the database collection.Save(createdDoc); id = createdDoc.Id; // Update a document using var doc = collection.GetDocument(id); using var mutableDoc = doc.ToMutable(); createdDoc.SetString("language", "C#"); collection.Save(createdDoc); using var docAgain = collection.GetDocument(id); Console.WriteLine($"Document ID :: {docAgain.Id}"); Console.WriteLine($"Learning {docAgain.GetString("language")}"); // Create a query to fetch documents of type SDK // i.e. SELECT * FROM database WHERE type = "SDK" using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("SDK"))); // Run the query var result = query.Execute(); Console.WriteLine($"Number of rows :: {result.AllResults().Count}"); // Create replicator to push and pull changes to and from the cloud var targetEndpoint = new URLEndpoint(new Uri("ws://localhost:4984/getting-started-db")); var replConfig = new ReplicatorConfiguration(targetEndpoint); replConfig.AddCollection(database.GetDefaultCollection()); // Add authentication replConfig.Authenticator = new BasicAuthenticator("john", "pass"); // Create replicator (make sure to add an instance or static variable // named _Replicator) var replicator = new Replicator(replConfig); replicator.AddChangeListener((sender, args) => { if (args.Status.Error != null) { Console.WriteLine($"Error :: {args.Status.Error}"); } }); replicator.Start(); // Later, stop and dispose the replicator *before* closing/disposing the database // end::getting-started[] } private static void TestReplicatorConflictResolver() { var collection = _Database.GetDefaultCollection(); // tag::replication-conflict-resolver[] var target = new URLEndpoint(new Uri("ws://localhost:4984/mydatabase")); var replConfig = new ReplicatorConfiguration(target); replConfig.AddCollection(collection, new CollectionConfiguration() { ConflictResolver = new LocalWinConflictResolver() }); var replicator = new Replicator(replConfig); replicator.Start(); // end::replication-conflict-resolver[] } private static void TestSaveWithConflictHandler() { var collection = _Database.GetDefaultCollection(); // tag::update-document-with-conflict-handler[] using var doc = collection.GetDocument("xyz"); using var mutableDoc = doc.ToMutable(); mutableDoc.SetString("name", "apples"); collection.Save(mutableDoc, (updated, current) => { var currentDict = current.ToDictionary(); var newDict = updated.ToDictionary(); var result = newDict.Concat(currentDict) .GroupBy(kv => kv.Key) .ToDictionary(g => g.Key, g => g.First().Value); updated.SetData(result); return true; }); // end::update-document-with-conflict-handler[] } private static bool IsValidCredential(string name, SecureString password) { return true; } // helper private static void TestInitListener() { var database = new Database("other-database"); var collection = database.GetDefaultCollection(); #warning init-urllistener Unused? // tag::init-urllistener[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); endpointConfig.TlsIdentity = null; // Use with anonymous self-signed cert endpointConfig.Authenticator = new ListenerPasswordAuthenticator((sender, username, password) => { if (IsValidCredential(username, password)) { return true; } return false; }); var listener = new URLEndpointListener(endpointConfig); // end::init-urllistener[] } private static void TestListenerStart() { var listener = _listener; #warning start-urllistener Unused? // tag::start-urllistener[] // CouchbaseLiteException will be thrown when the listener cannot be started. The most common error // would be that the configured port has already been used. listener.Start(); // end::start-urllistener[] } private static void TestListenerStop() { var listener = _listener; #warning stop-urllistener Unused? // tag::stop-urllistener[] listener.Stop(); // end::stop-urllistener[] } private static void TestCreateSelfSignedCert() { X509Store store = new X509Store(StoreName.My); // The identity will be stored in the secure // storage using the given label. DateTimeOffset fiveMinToExpireCert = DateTimeOffset.UtcNow.AddMinutes(5); // tag::create-self-signed-cert[] // tag::listener-config-tls-id-SelfSigned[] var identity = TLSIdentity.CreateIdentity(true, /* isServer */ new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "Couchbase Inc" } }, // The common name attribute is required // when creating a CSR. If it is not presented // in the cert, an exception is thrown. fiveMinToExpireCert, // If the expiration date is not specified, // the certs expiration will be 365 days store, "CBL-Server-Cert", null); // The key label to get cert in certificate map. // If null, the same default directory // for a Couchbase Lite db is used for map. // end::listener-config-tls-id-SelfSigned[] // end::create-self-signed-cert[] } private static void TestImportTLSIdentity() { X509Store store = new X509Store(StoreName.My); // The identity will be stored in the secure storage using the given label byte[] data = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates // tag::import-tls-identity[] // tag::listener-config-tls-id-caCert[] var identity = TLSIdentity.ImportIdentity(store, data, "123", // The password that is needed to access the certificate data "CBL-Client-Cert", null); // The key label to get cert in certificate map. // If null, the same default directory // for a Couchbase Lite db is used for map. // end::listener-config-tls-id-caCert[] // end::import-tls-identity[] } private static void TestClientCertAuthenticatorRootCerts() { var otherDatabase = new Database("other-database"); var collection = otherDatabase.GetDefaultCollection(); X509Store store = new X509Store(StoreName.My); #warning client-cert-authenticator-root-certs unused? // tag::client-cert-authenticator-root-certs[] byte[] caData, clientData; clientData = File.ReadAllBytes("C:\\client.p12"); // PKCS12 data containing private key, public key, and certificates caData = File.ReadAllBytes("C:\\client-ca.der"); // Root certs var rootCert = new X509Certificate2(caData); var auth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert)); // Create URL Endpoint Listener var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection }); listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default. listenerConfig.Authenticator = auth; var listener = new URLEndpointListener(listenerConfig); listener.Start(); // Client identity var identity = TLSIdentity.ImportIdentity(store, clientData, "123", "CBL-Client-Cert", null); // Replicator -- Client var database = new Database("client-database"); var builder = new UriBuilder( "wss", "localhost", listener.Port, $"/{listener.Config.Collections.First().Name}" ); var url = builder.Uri; var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.PushAndPull; config.Continuous = false; config.Authenticator = new ClientCertificateAuthenticator(identity); config.AcceptOnlySelfSignedServerCertificate = true; config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0]; var replicator = new Replicator(config); replicator.Start(); // Dispose after stop // Stop listener after replicator is stopped listener.Stop(); // end::client-cert-authenticator-root-certs[] } private static void TestClientCertAuthenticator() { var otherDatabase = new Database("other-database"); var collection = otherDatabase.GetDefaultCollection(); X509Store store = new X509Store(StoreName.My); #warning client-cert-authenticator unused? // tag::client-cert-authenticator[] // Create Listener Certificate Authenticator var auth = new ListenerCertificateAuthenticator((sender, cert) => { if (cert.Count != 1) { return false; } return cert[0].SubjectName.Name?.Replace("CN=", "") == "couchbase"; }); // Create URL Endpoint Listener var listenerConfig = new URLEndpointListenerConfiguration(new[] { collection }); listenerConfig.DisableTLS = false; //The default value is false which means that the TLS will be enabled by default. listenerConfig.Authenticator = auth; var listener = new URLEndpointListener(listenerConfig); listener.Start(); // User Identity var identity = TLSIdentity.CreateIdentity(false, new Dictionary<string, string>() { { Certificate.CommonNameAttribute, "couchbase" } }, null, store, "ClientCertLabel", null); // Replicator -- Client var database = new Database("client-database"); var builder = new UriBuilder( "wss", "localhost", listener.Port, $"/{listener.Config.Collections.First().Name}" ); var url = builder.Uri; var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.PushAndPull; config.Continuous = false; config.Authenticator = new ClientCertificateAuthenticator(identity); config.AcceptOnlySelfSignedServerCertificate = false; config.PinnedServerCertificate = _listener.TlsIdentity.Certs[0]; var replicator = new Replicator(config); replicator.Start(); // Dispose after stopped // Stop listener after replicator is stopped listener.Stop(); // end::client-cert-authenticator[] } public void IdentityWithLabel() { X509Store store = null; byte[] clientData = null; var replConfig = new ReplicatorConfiguration(null); // tag::p2p-tlsid-tlsidentity-with-label[] // Client identity var identity = TLSIdentity.ImportIdentity(store, clientData, "123", "CBL-Client-Cert", null); (1) replConfig.Authenticator = new ClientCertificateAuthenticator(identity); (2) // end::p2p-tlsid-tlsidentity-with-label[] } public void UseEncryption() { // Enterprise edition only // tag::database-encryption[] // Create a new, or open an existing database with encryption enabled var config = new DatabaseConfiguration { // Or, derive a key yourself and pass a byte array of the proper size EncryptionKey = new EncryptionKey("password") }; using var database = new Database("seekrit", config); // Change the encryption key (or add encryption if the DB is unencrypted) database.ChangeEncryptionKey(new EncryptionKey("betterpassw0rd")); // Remove encryption database.ChangeEncryptionKey(null); // end::database-encryption[] } private static void ResetReplicatorCheckpoint() { var url = new Uri("ws://localhost:4984/db"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); bool resetCheckpointRequired_Example = true; config.AddCollection(_Database.GetDefaultCollection()); var replicator = new Replicator(config); // tag::replication-reset-checkpoint[] // replicator is a Replicator instance if (resetCheckpointRequired_Example) { replicator.Start(true); (3) } else { replicator.Start(false); } // Stop and dispose replicator later // end::replication-reset-checkpoint[] } private static void Read1xAttachment() { using var doc = new MutableDocument(); // tag::1x-attachment[] var attachments = doc.GetDictionary("_attachments"); var avatar = attachments.GetBlob("avatar"); var content = avatar?.Content; // end::1x-attachment[] } private static void CreateNewDatabase() { // tag::new-database[] var database = new Database("my-database"); // end::new-database[] } private static void CloseDatabase() { var database = _Database; // tag::close-database[] database.Close(); // end::close-database[] } private static void DatabaseFullsync() { var config = new DatabaseConfiguration(); // tag::database-fullsync[] // this enables fullsync config.FullSync = true; // end::database-fullsync[] } private static void CreateCollection() { // tag::scopes-manage-create-collection[] var collectionWithDefaultScope = _Database.CreateCollection("colA"); var collection = _Database.CreateCollection("colA", "scopeA"); // Scope with named scopeA will be created if it's not existed. There is no public API to create a Scope. // end::scopes-manage-create-collection[] } private static void DeleteCollection() { // tag::scopes-manage-drop-collection[] _Database.DeleteCollection("colA", "scopeA"); // Scope with named scopeA will be deleted if there is no collections in the scope after the last collection is deleted via this API. There is no public API to remove a Scope. // end::scopes-manage-drop-collection[] } private static void ListCollectionsAndScopes() { // tag::scopes-manage-list[] // Get Scopes var scopes = _Database.GetScopes(); // Get Collections of a Scope named scopeA var scopeA = _Database.GetScope("scopeA"); var collectionsInScopeA = scopeA.GetCollections(); // end::scopes-manage-list[] } private static void ChangeLogging() { // tag::logging[] // This sets the overall level of console logging Database.Log.Console.Level = LogLevel.Verbose; // This flag can enable and disable specific domains Database.Log.Console.Domains = LogDomain.Couchbase | LogDomain.Database; // end::logging[] } private static void LoadPrebuilt() { // tag::prebuilt-database[] // Note: Getting the path to a database is platform-specific. For .NET Core / .NET Framework this // can be a simple filesystem path. For UWP, you will need to get the path from your assets. For // iOS you need to get the path from the main bundle. For Android you need to extract it from your // assets to a temporary directory and then pass that path. var path = Path.Combine(Environment.CurrentDirectory, "travel-sample.cblite2" + Path.DirectorySeparatorChar); if (!Database.Exists("travel-sample", null)) { Database.Copy(path, "travel-sample", null); } // end::prebuilt-database[] } private static void QueryDeletedDocuments() { var collection = _Database.GetDefaultCollection(); // tag::query-deleted-documents[] // Query documents that have been deleted var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(Meta.IsDeleted); // end::query-deleted-documents[] } private static void CreateDocument() { var collection = _Database.GetDefaultCollection(); // tag::initializer[] using var mutableDoc = new MutableDocument("xyz"); mutableDoc.SetString("type", "task") .SetString("owner", "todo") .SetDate("createdAt", DateTimeOffset.UtcNow); collection.Save(mutableDoc); // end::initializer[] } private static void UpdateDocument() { var collection = _Database.GetDefaultCollection(); // tag::update-document[] using var doc = collection.GetDocument("xyz"); using var mutableDoc = doc.ToMutable(); mutableDoc.SetString("name", "apples"); collection.Save(mutableDoc); // end::update-document[] } private static void UseTypedAccessors() { using var mutableDoc = new MutableDocument(); // tag::date-getter[] mutableDoc.SetValue("createdAt", DateTimeOffset.UtcNow); var date = mutableDoc.GetDate("createdAt"); // end::date-getter[] Console.WriteLine(date); } private static void DoBatchOperation() { var database = _Database; var collection = database.GetDefaultCollection(); // tag::batch[] database.InBatch(() => { for (var i = 0; i < 10; i++) { using var mutableDoc = new MutableDocument(); mutableDoc.SetString("type", "user"); mutableDoc.SetString("name", $"user {i}"); mutableDoc.SetBoolean("admin", false); collection.Save(mutableDoc); Console.WriteLine($"Saved user document {mutableDoc.GetString("name")}"); } }); // end::batch[] } private static void DatabaseChangeListener() { var collection = _Database.GetDefaultCollection(); // tag::document-listener[] collection.AddDocumentChangeListener("user.john", (sender, args) => { using var doc = collection.GetDocument(args.DocumentID); Console.WriteLine($"Status :: {doc.GetString("verified_account")}"); }); // end::document-listener[] } private static void DocumentExpiration() { var collection = _Database.GetDefaultCollection(); // tag::document-expiration[] // Purge the document one day from now var ttl = DateTimeOffset.UtcNow.AddDays(1); collection.SetDocumentExpiration("doc123", ttl); // Reset expiration collection.SetDocumentExpiration("doc1", null); // Query documents that will be expired in less than five minutes var fiveMinutesFromNow = DateTimeOffset.UtcNow.AddMinutes(5).ToUnixTimeMilliseconds(); using var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(Meta.Expiration.LessThan(Expression.Double(fiveMinutesFromNow))); // end::document-expiration[] } private static void UseBlob() { var collection = _Database.GetDefaultCollection(); using var newTask = new MutableDocument(); // tag::blob[] // Note: Reading the data is implementation dependent, as with prebuilt databases var image = File.ReadAllBytes("avatar.jpg"); (4) var blob = new Blob("image/jpeg", image); (5) newTask.SetBlob("avatar", blob); (6) collection.Save(newTask); // end::blob[] } public void CreateIndex() { var collection = _Database.GetDefaultCollection(); // tag::query-index[] // tag::scopes-manage-index-collection[] string[] indexProperties = new string[] { "type", "name" }; var config = new ValueIndexConfiguration(indexProperties); collection.CreateIndex("TypeNameIndex", config); // end::scopes-manage-index-collection[] // end::query-index[] } public void CreateIndex_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::query-index_Querybuilder[] // For value types, this is optional but provides performance enhancements var index = IndexBuilder.ValueIndex( ValueIndexItem.Expression(Expression.Property("type")), ValueIndexItem.Expression(Expression.Property("name"))); (7) collection.CreateIndex("TypeNameIndex", index); // end::query-index_Querybuilder[] } private static void SelectMeta() { var collection = _Database.GetDefaultCollection(); #warning query-select-meta unused? // tag::query-select-meta[] // tag::query-select-props[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("type"), SelectResult.Property("name")) .From(DataSource.Collection(collection)); foreach (var result in query.Execute()) { Console.WriteLine($"Document ID :: {result.GetString("id")}"); Console.WriteLine($"Document Name :: {result.GetString("name")}"); } // end::query-select-props[] // end::query-select-meta[] } private static void SelectAll() { var collection = _Database.GetDefaultCollection(); { // tag::query-select-all[] using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)); // end::query-select-all[] } { // tag::live-query[] var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)); (8) // Adds a query change listener. // Changes will be posted on the main queue. var token = query.AddChangeListener((sender, args) => (9) { var allResult = args.Results.AllResults(); foreach (var result in allResult) { Console.WriteLine(result.Keys); /* Update UI */ } }); // end::live-query[] // tag::stop-live-query[] query.RemoveChangeListener(token); query.Dispose(); // end::stop-live-query[] } } private static void SelectWhere() { var collection = _Database.GetDefaultCollection(); // tag::query-where[] using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { var dict = result.GetDictionary(collection.Name); Console.WriteLine($"Document Name :: {dict?.GetString("name")}"); } // end::query-where[] } private static void UseCollectionContains() { var collection = _Database.GetDefaultCollection(); // tag::query-collection-operator-contains[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name"), SelectResult.Property("public_likes")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel")) .And(ArrayFunction.Contains(Expression.Property("public_likes"), Expression.String("Armani Langworth")))); foreach (var result in query.Execute()) { var publicLikes = result.GetArray("public_likes"); var jsonString = JsonConvert.SerializeObject(publicLikes); Console.WriteLine($"Public Likes :: {jsonString}"); } // end::query-collection-operator-contains[] } private static void UseCollectionIn() { var collection = _Database.GetDefaultCollection(); // tag::query-collection-operator-in[] var values = new IExpression[] { Expression.Property("first"), Expression.Property("last"), Expression.Property("username") }; using var query = QueryBuilder.Select( SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.String("Armani").In(values)); foreach (var result in query.Execute()) { var body = result.GetDictionary(0); var jsonString = JsonConvert.SerializeObject(body); Console.WriteLine($"In results :: {jsonString}"); } // end::query-collection-operator-in[] } private static void SelectLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("Royal Engineers Museum")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator[] } private static void SelectWildcardLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator-wildcard-match[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("Eng%e%")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator-wildcard-match[] } private static void SelectWildcardCharacterLike() { var collection = _Database.GetDefaultCollection(); // tag::query-like-operator-wildcard-character-match[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Expression.Property("name").Like(Expression.String("Royal Eng____rs Museum")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-like-operator-wildcard-character-match[] } private static void SelectRegex() { var collection = _Database.GetDefaultCollection(); // tag::query-regex-operator[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("name")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("landmark")) .And(Expression.Property("name").Regex(Expression.String("\\bEng.*e\\b")))) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-regex-operator[] } private static void SelectJoin() { var collection = _Database.GetDefaultCollection(); var collection2 = _Database.GetDefaultCollection(); // tag::query-join[] using var query = QueryBuilder.Select( SelectResult.Expression(Expression.Property("name").From("airline")), SelectResult.Expression(Expression.Property("callsign").From("airline")), SelectResult.Expression(Expression.Property("destinationairport").From("route")), SelectResult.Expression(Expression.Property("stops").From("route")), SelectResult.Expression(Expression.Property("airline").From("route"))) .From(DataSource.Collection(collection).As("airline")) .Join(Join.InnerJoin(DataSource.Collection(collection2).As("route")) .On(Meta.ID.From("airline").EqualTo(Expression.Property("airlineid").From("route")))) .Where(Expression.Property("type").From("route").EqualTo(Expression.String("route")) .And(Expression.Property("type").From("airline").EqualTo(Expression.String("airline"))) .And(Expression.Property("sourceairport").From("route").EqualTo(Expression.String("RIX")))); foreach (var result in query.Execute()) { Console.WriteLine($"Name Property :: {result.GetString("name")}"); } // end::query-join[] } private static void GroupBy() { var collection = _Database.GetDefaultCollection(); // tag::query-groupby[] using var query = QueryBuilder.Select( SelectResult.Expression(Function.Count(Expression.All())), SelectResult.Property("country"), SelectResult.Property("tz")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("airport")) .And(Expression.Property("geo.alt").GreaterThanOrEqualTo(Expression.Int(300)))) .GroupBy(Expression.Property("country"), Expression.Property("tz")); foreach (var result in query.Execute()) { Console.WriteLine( $"There are {result.GetInt("$1")} airports in the {result.GetString("tz")} timezone located in {result.GetString("country")} and above 300 ft"); } // end::query-groupby[] } private static void OrderBy() { var collection = _Database.GetDefaultCollection(); // tag::query-orderby[] using var query = QueryBuilder.Select( SelectResult.Expression(Meta.ID), SelectResult.Property("title")) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .OrderBy(Ordering.Property("title").Ascending()) .Limit(Expression.Int(10)); foreach (var result in query.Execute()) { Console.WriteLine($"Title :: {result.GetString("title")}"); } // end::query-orderby[] } private static void TestExplainStatement() { var collection = _Database.GetDefaultCollection(); { // tag::query-explain-all[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))) .GroupBy(Expression.Property("country")) .OrderBy(Ordering.Property("title").Ascending()); (10) Console.WriteLine(query.Explain()); (11) // end::query-explain-all[] } { // tag::query-explain-like[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").Like(Expression.String("%hotel%")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (12) Console.WriteLine(query.Explain()); // end::query-explain-like[] } { // tag::query-explain-nopfx[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").Like(Expression.String("hotel%")) .And(Function.Lower(Expression.Property("name")).Like(Expression.String("%royal%")))); (13) Console.WriteLine(query.Explain()); // end::query-explain-nopfx[] } { // tag::query-explain-function[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Function.Lower(Expression.Property("type")).EqualTo(Expression.String("hotel"))); (14) Console.WriteLine(query.Explain()); // end::query-explain-function[] } { // tag::query-explain-nofunction[] using var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(Expression.Property("type").EqualTo(Expression.String("hotel"))); (15) Console.WriteLine(query.Explain()); // end::query-explain-nofunction[] } } public void CreateFullTextIndex() { var collection = _Database.GetDefaultCollection(); // tag::fts-index[] string[] indexProperties = new string[] { "overview", "name" }; var config = new FullTextIndexConfiguration(indexProperties); collection.CreateIndex("overviewFTSIndex", config); // end::fts-index[] } public void FullTextSearch() { var collection = _Database.GetDefaultCollection(); // tag::fts-query[] var query = collection.CreateQuery("SELECT * FROM _ WHERE MATCH(overviewFTSIndex, 'Michigan') ORDER BY RANK(overviewFTSIndex)"); foreach (var result in query.Execute()) { Console.WriteLine($"Document id {result.GetString(0)}"); } // end::fts-query[] } private static void CreateFullTextIndex_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::fts-index_Querybuilder[] var index = IndexBuilder.FullTextIndex(FullTextIndexItem.Property("overview")).IgnoreAccents(false); collection.CreateIndex("overviewFTSIndex", index); // end::fts-index_Querybuilder[] } private static void FullTextSearch_Querybuilder() { var collection = _Database.GetDefaultCollection(); // tag::fts-query_Querybuilder[] var whereClause = FullTextFunction.Match(Expression.FullTextIndex("overviewFTSIndex"), "'michigan'"); using var query = QueryBuilder.Select(SelectResult.Expression(Meta.ID)) .From(DataSource.Collection(collection)) .Where(whereClause); foreach (var result in query.Execute()) { Console.WriteLine($"Document id {result.GetString(0)}"); } // end::fts-query_Querybuilder[] } private static void StartReplication() { var collection = _Database.GetDefaultCollection(); #warning replication unused? // tag::replication[] // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) var url = new Uri("ws://localhost:4984/db"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target) { ReplicatorType = ReplicatorType.Pull }; config.AddCollection(collection); var replicator = new Replicator(config); replicator.Start(); // end::replication[] } private static void ConsoleLogging() { // tag::console-logging[] Database.Log.Console.Domains = LogDomain.All; (16) Database.Log.Console.Level = LogLevel.Verbose; (17) // end::console-logging[] // tag::console-logging-db[] Database.Log.Console.Domains = LogDomain.Database; // end::console-logging-db[] } private static void FileLogging() { // tag::file-logging[] var tempFolder = Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>().DefaultDirectory(), "cbllog"); var config = new LogFileConfiguration(tempFolder) (18) { MaxRotateCount = 5, (19) MaxSize = 10240, (20) UsePlaintext = false (21) }; Database.Log.File.Config = config; // Apply configuration Database.Log.File.Level = LogLevel.Info; (22) // end::file-logging[] } private static void EnableCustomLogging() { // tag::set-custom-logging[] Database.Log.Custom = new LogTestLogger(); (23) // You can also specify the level of logging the logger receives Database.Log.Custom = new LogTestLogger { Level = LogLevel.Warning }; // end::set-custom-logging[] } private static void WriteConsoleLog() { #warning write-console-logmsg unused? // tag::write-console-logmsg[] Database.Log.Console.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-console-logmsg[] } private static void WriteCustomLog() { #warning write-custom-logmsg unused? // tag::write-custom-logmsg[] Database.Log.Custom?.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-custom-logmsg[] } private static void WriteFileLog() { #warning write-file-logmsg unused? // tag::write-file-logmsg[] Database.Log.File.Log(LogLevel.Warning, LogDomain.Replicator, "Any old log message"); // end::write-file-logmsg[] } private static void EnableBasicAuth() { var collection = _Database.GetDefaultCollection(); #warning basic-authentication unused? // tag::basic-authentication[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection); config.Authenticator = new BasicAuthenticator("john", "pass"); var replicator = new Replicator(config); replicator.Start(); // end::basic-authentication[] } private static void EnableSessionAuth() { var collection = _Database.GetDefaultCollection(); // tag::session-authentication[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection); config.Authenticator = new SessionAuthenticator("904ac010862f37c8dd99015a33ab5a3565fd8447"); var replicator = new Replicator(config); replicator.Start(); // end::session-authentication[] } private static void SetupReplicatorListener() { var replicator = _Replicator; #warning replication-status unused? // tag::replication-status[] replicator.AddChangeListener((sender, args) => { if (args.Status.Activity == ReplicatorActivityLevel.Stopped) { Console.WriteLine("Replication stopped"); } }); // end::replication-status[] } private static void ReplicatorPendingDocuments() { // tag::replication-pendingdocuments[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var database = new Database("myDB"); var config = new ReplicatorConfiguration(target); config.AddCollection(database.GetDefaultCollection()); config.ReplicatorType = ReplicatorType.Push; // tag::replication-push-pendingdocumentids[] var replicator = new Replicator(config); var pendingDocIDs = new HashSet<string>(replicator.GetPendingDocumentIDs(database.GetDefaultCollection())); (24) // end::replication-push-pendingdocumentids[] if (pendingDocIDs.Count > 0) { Console.WriteLine($"There are {pendingDocIDs.Count} documents pending"); replicator.AddChangeListener((sender, change) => { Console.WriteLine($"Replicator activity level is " + change.Status.Activity.ToString()); // iterate and report-on previously // retrieved pending docids 'list' foreach (var docID in pendingDocIDs) #warning replication-push-isdocumentpending unused? // tag::replication-push-isdocumentpending[] if (!replicator.IsDocumentPending(docID, database.GetDefaultCollection())) (25) { Console.WriteLine($"Doc ID {docID} now pushed"); }; // end::replication-push-isdocumentpending[] }); replicator.Start(); } // end::replication-pendingdocuments[] } private static void ReplicatorDocumentEvent() { var replicator = _Replicator; // tag::add-document-replication-listener[] var token = replicator.AddDocumentReplicationListener((sender, args) => { var direction = args.IsPush ? "Push" : "Pull"; Console.WriteLine($"Replication type :: {direction}"); foreach (var doc in args.Documents) { if (doc.Error == null) { Console.WriteLine($"Doc ID :: {doc.Id}"); if (doc.Flags.HasFlag(DocumentFlags.Deleted)) { Console.WriteLine("Successfully replicated a deleted document"); } } else { // There was an error } } }); replicator.Start(); // end::add-document-replication-listener[] // tag::remove-document-replication-listener[] replicator.RemoveChangeListener(token); // end::remove-document-replication-listener[] } private static void SetupReplicatorErrorListener() { // This can be done in the SetupReplicatorListener method // But it is separate so that we can have two documentation entries var replicator = _Replicator; // tag::replication-error-handling[] replicator.AddChangeListener((sender, args) => { if (args.Status.Error != null) { Console.WriteLine($"Error :: {args.Status.Error}"); } }); // end::replication-error-handling[] } private static void DatabaseReplica() { var collection = _Database.GetDefaultCollection(); using (var database2 = new Database("backup")) { // EE feature: This code will not compile on the community edition // tag::database-replica[] var targetDatabase = new DatabaseEndpoint(database2); var config = new ReplicatorConfiguration(targetDatabase) { ReplicatorType = ReplicatorType.Push }; config.AddCollection(collection); var replicator = new Replicator(config); replicator.Start(); // end::database-replica[] } } private X509Certificate2 GetCertificate(string name) { return null; } public void PinCertificate() { var url = new Uri("wss://localhost:4984/db"); var target = new URLEndpoint(url); // tag::certificate-pinning[] // Note: `GetCertificate` is a placeholder method. This would be the platform-specific method // to find and load the certificate as an instance of `X509Certificate2`. // For .NET Core / .NET Framework this can be loaded from the filesystem path. // For WinUI, from the assets directory. // For iOS, from the main bundle. // For Android, from the assets directory. var certificate = GetCertificate("cert.cer"); var config = new ReplicatorConfiguration(target) { PinnedServerCertificate = certificate }; // end::certificate-pinning[] } public void ReplicationCustomHeaders() { var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); // tag::replication-custom-header[] var config = new ReplicatorConfiguration(target) { Headers = new Dictionary<string, string> { ["CustomHeaderName"] = "Value" } }; // end::replication-custom-header[] } private static void PushWithFilter(Database database) { var collection = _Database.GetDefaultCollection(); // tag::replication-push-filter[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection, new CollectionConfiguration() { PushFilter = (document, flags) => (1) { if (flags.HasFlag(DocumentFlags.Deleted)) { return false; } return true; } }); // Dispose() later var replicator = new Replicator(config); replicator.Start(); // end::replication-push-filter[] } private static void PullWithFilter(Database database) { var collection = _Database.GetDefaultCollection(); // tag::replication-pull-filter[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); config.AddCollection(collection, new CollectionConfiguration() { PullFilter = (document, flags) => (1) { if (document.GetString("type") == "draft") { return false; } return true; } }); // Dispose() later var replicator = new Replicator(config); replicator.Start(); // end::replication-pull-filter[] } public void TestCustomRetryConfig() { // tag::replication-retry-config[] var url = new Uri("ws://localhost:4984/mydatabase"); var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target); // other config as required . . . #warning replication-set-heartbeat unused? // tag::replication-set-heartbeat[] config.Heartbeat = TimeSpan.FromSeconds(120); // (26) // end::replication-set-heartbeat[] // tag::replication-set-maxattempts[] #warning replication-set-maxattempts unused? config.MaxAttempts = 20; // (27) // end::replication-set-maxattempts[] // tag::replication-set-maxattemptwaittime[] #warning replication-set-maxattemptwaittime unused? config.MaxAttemptsWaitTime = TimeSpan.FromSeconds(600); // (28) // end::replication-set-maxattemptwaittime[] // other config as required . . . var replicator = new Replicator(config); // end::replication-retry-config[] } private static void UsePredictiveModel() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::register-model[] var model = new ImageClassifierModel(); Database.Prediction.RegisterModel("ImageClassifier", model); // end::register-model[] // tag::predictive-query-value-index[] var index = IndexBuilder.ValueIndex(ValueIndexItem.Property("label")); collection.CreateIndex("value-index-image-classifier", index); // end::predictive-query-value-index[] // tag::unregister-model[] Database.Prediction.UnregisterModel("ImageClassifier"); // end::unregister-model[] } } private static void UsePredictiveIndex() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::predictive-query-predictive-index[] var input = Expression.Dictionary(new Dictionary<string, object> { ["photo"] = Expression.Property("photo") }); var index = IndexBuilder.PredictiveIndex("ImageClassifier", input); collection.CreateIndex("predictive-index-image-classifier", index); // end::predictive-query-predictive-index[] } } private static void DoPredictiveQuery() { using (var database = new Database("mydb")) { var collection = database.GetDefaultCollection(); // tag::predictive-query[] var input = Expression.Dictionary(new Dictionary<string, object> { ["photo"] = Expression.Property("photo") }); var prediction = Function.Prediction("ImageClassifier", input); (1) using var query = QueryBuilder.Select(SelectResult.All()) .From(DataSource.Collection(collection)) .Where(prediction.Property("label").EqualTo(Expression.String("car")) .And(prediction.Property("probability").GreaterThanOrEqualTo(Expression.Double(0.8)))); var result = query.Execute(); Console.WriteLine($"Number of rows: {result.Count()}"); // end::predictive-query[] } } public List<Result> docsonly_N1QLQueryString(Database argDB) { DatabaseConfiguration config = new DatabaseConfiguration(); Database database = new Database("dbName", config); // tag::query-syntax-n1ql[] using var query = database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = \"hotel\""); (29) return query.Execute().AllResults(); // end::query-syntax-n1ql[] } public void docsonly_N1QLQueryStringParams(Database argDB) { var database = _Database; // tag::query-syntax-n1ql-params[] using var query = database.CreateQuery("SELECT META().id AS thisId FROM _ WHERE type = $type"); (30) var n1qlParams = new Parameters(); n1qlParams.SetString("type", "hotel"); (31) query.Parameters = n1qlParams; var results = query.Execute().AllResults(); // end::query-syntax-n1ql-params[] } public void testQuerySyntaxAll() { // tag::query-syntax-all[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-all[] // tag::query-access-all[] var results = query.Execute().AllResults(); var hotels = new List<Dictionary<string, object>>(); if (results?.Count > 0) { foreach (var result in results) { // get the result into our dictionary object var thisDocsProps = result.GetDictionary("hotels"); (32) if (thisDocsProps != null) { var docID = thisDocsProps.GetString("id"); (33) var docName = thisDocsProps.GetString("name"); var docCity = thisDocsProps.GetString("city"); var docType = thisDocsProps.GetString("type"); var hotel = thisDocsProps.ToDictionary(); hotels.Add(hotel); } } } // end::query-access-all[] // tag::query-access-json[] foreach (var result in query.Execute()) { // get the result into a JSON String var docJSONString = result.ToJSON(); // Get a native dictionary object using the JSON string var dictFromJSONstring = JsonConvert. DeserializeObject<Dictionary<string, object>> (docJSONString); // use the created dictionary if (dictFromJSONstring != null) { var docID = dictFromJSONstring["id"].ToString(); var docName = dictFromJSONstring["name"].ToString(); var docCity = dictFromJSONstring["city"].ToString(); var docType = dictFromJSONstring["type"].ToString(); } //Get a custom object using the JSON string Hotel hotel = JsonConvert.DeserializeObject<Hotel>(docJSONString); } // end::query-access-json[] } public void testQuerySyntaxProps() { // tag::query-syntax-props[] var database = new Database("hotels"); List<Dictionary<string, object>> hotels = new List<Dictionary<string, object>>(); var query = QueryBuilder.Select( SelectResult.Property("type"), SelectResult.Property("name"), SelectResult.Property("city")).From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-props[] // tag::query-access-props[] var results = query.Execute().AllResults(); foreach (var result in results) { // get the returned array of k-v pairs into a dictionary var hotel = result.ToDictionary(); // add hotel dictionary to list of hotel dictionaries hotels.Add(hotel); // use the properties of the returned array of k-v pairs directly var docType = result.GetString("type"); var docName = result.GetString("name"); var docCity = result.GetString("city"); } // end::query-access-props[] } public void testQuerySyntaxCount() { // tag::query-syntax-count-only[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount")) (34) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-count-only[] // tag::query-access-count-only[] var results = query.Execute().AllResults(); foreach (var result in results) { var numberOfDocs = result.GetInt("mycount"); (35) } // end::query-access-count-only[] } public void ibQueryForID() { // tag::query-syntax-id[] var database = new Database("hotels"); var query = QueryBuilder .Select(SelectResult.Expression(Meta.ID).As("this_ID")) .From(DataSource.Collection(database.GetDefaultCollection())); // end::query-syntax-id[] // tag::query-access-id[] var results = query.Execute().AllResults(); foreach (var result in results) { var docID = result.GetString("this_ID"); (36) var doc = database.GetDefaultCollection().GetDocument(docID); } // end::query-access-id[] } #warning query-syntax-pagination-all unused (and out of place)? // tag::query-syntax-pagination-all[] public void testQueryPagination() { // tag::query-syntax-pagination[] var database = new Database("hotels"); var limit = 20; var offset = 0; // get a count of the number of docs matching the query var countQuery = QueryBuilder .Select(SelectResult.Expression(Function.Count(Expression.All())).As("mycount")) .From(DataSource.Collection(database.GetDefaultCollection())); var numberOfDocs = countQuery.Execute().First().GetInt("mycount"); if (numberOfDocs < limit) { limit = numberOfDocs; } while (offset < numberOfDocs) { var listQuery = QueryBuilder .Select(SelectResult.All()) .From(DataSource.Collection(database.GetDefaultCollection())) .Limit(Expression.Int(limit), Expression.Int(offset)); (37) foreach (var result in listQuery.Execute()) { // Display and or process query results batch } offset = offset + limit; } // end::query-syntax-pagination[] // end::query-syntax-pagination-all[] } public void JsonApiDocument() { var collection = _Database.GetDefaultCollection(); // tag::tojson-document[] // Get a document var doc = collection.GetDocument("hotel_10025"); // Get document data as JSON String var docJSONString = doc?.ToJSON(); // Get Json Object from the Json String JObject jsonObject = JObject.Parse(docJSONString); // Get Native Object (anhotel) from JSON String List<Hotel> hotels = new List<Hotel>(); var hotel = JsonConvert.DeserializeObject<Hotel>(docJSONString); hotels.Add(hotel); // Update the retrieved native object hotel.Name = "A Copy of " + hotel.Name; hotel.Id = "2001"; // Convert the updated object back to a JSON string var newJsonString = JsonConvert.SerializeObject(hotel); // Update new document with JSOn String MutableDocument newhotel = doc.ToMutable(); newhotel.SetJSON(newJsonString); foreach (string key in newhotel.ToDictionary().Keys) { Console.WriteLine("Data -- {0} = {1}", key, newhotel.GetValue(key)); } collection.Save(newhotel); var retrievedDoc = collection.GetDocument("2001").ToJSON(); Console.Write(retrievedDoc); // end::tojson-document[] } public void JsonApiArray() { var collection = _Database.GetDefaultCollection(); // tag::tojson-array[] // JSON String -- an Array (3 elements. including embedded arrays) var jsonString = "[{'id':'1000','type':'hotel','name':'Hotel Ted','city':'Paris','country':'France','description':'Undefined description for Hotel Ted'},{'id':'1001','type':'hotel','name':'Hotel Fred','city':'London','country':'England','description':'Undefined description for Hotel Fred'}, {'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}]".Replace("'", "\""); // Get JSON Array from JSON String var jsonArray = JArray.Parse(jsonString); // Create mutable array using JSON String Array var mutableArray = new MutableArrayObject(); mutableArray.SetJSON(jsonString); // Create a new document for each array element for (int i = 0; i < mutableArray.Count; i++) { var dict = mutableArray.GetDictionary(i); var docid = mutableArray[i].Dictionary.GetString("id"); var mutableDoc = new MutableDocument(docid, dict.ToDictionary()); collection.Save(mutableDoc); } // Get one of the created docs and iterate through one of the embedded arrays var extendedDoc = collection.GetDocument("1002"); var features = extendedDoc.GetArray("features"); // Print its elements foreach (string feature in features) { Console.Write($"{feature} "); //process array item as required } var featuresJSON = extendedDoc.GetArray("features").ToJSON(); // end::tojson-array[] } public void JsonApiDictionary() { var ourdbname = "ournewdb"; if (Database.Exists(ourdbname, "/")) { Database.Delete(ourdbname, "/"); } // tag::tojson-dictionary[] // Get dictionary from JSONstring var jsonString = "{'id':'1002','type':'hotel','name':'Hotel Ned','city':'Balmain','country':'Australia','description':'Undefined description for Hotel Ned','features':['Cable TV','Toaster','Microwave']}".Replace("'", "\""); var mutableDict = new MutableDictionaryObject(json: jsonString); // use dictionary to get name value var name = mutableDict.GetString("name"); // Iterate through keys foreach (string key in mutableDict.Keys) { Console.WriteLine("Data -- {0} = {1}", key, mutableDict.GetValue(key).ToString()); } // end::tojson-dictionary[] } public void JsonApiBlob() { var userName = "ian"; var collection = _Database.GetDefaultCollection(); var database = _Database; // tag::tojson-blob[] // Initialize base document for blob from a JSON string var docId = "1002"; var jsonString = "{'ref':'hotel_1002','type':'hotel','name':'Hotel Ned'," + "'city':'Balmain','country':'Australia'," + "'description':'Undefined description for Hotel Ned'," + "'features':['Cable TV','Toaster','Microwave']}".Replace("'", "\""); var mutableDoc = new MutableDocument(docId, jsonString); // Get the content (an image), create blob and add to doc) var defaultDirectory = Path.Combine(Service.GetInstance<IDefaultDirectoryResolver>() .DefaultDirectory(), userName); var imagePath = Path.Combine(defaultDirectory, "avatarimage.jpg"); var imageUri = new Uri(imagePath.ToString()); var imageBlob = new Blob("image/jpg", imageUri); mutableDoc.SetBlob("avatar", imageBlob); // This example generates a 'blob not saved' exception try { Console.WriteLine("myBlob (unsaved) as JSON = {0}", imageBlob.ToJSON()); } catch (Exception e) { Console.WriteLine("Exception = {0}", e.Message); } collection.Save(mutableDoc); // Alternatively -- depending on use case database.SaveBlob(new Blob("image/jpg", imageUri)); // Retrieve saved doc, get blob as JSON andheck its still a 'blob' var sameDoc = collection.GetDocument(docId); var reconstitutedBlob = new MutableDictionaryObject(). SetDictionary("blobCOPY", new MutableDictionaryObject(sameDoc.GetBlob("avatar").ToJSON())); if (Blob.IsBlob( reconstitutedBlob.GetDictionary("blobCOPY").ToDictionary())) { //... process accordingly Console.WriteLine("Its a Blob!!"); } // end::tojson-blob[] } private bool ValidatePassword(SecureString password) => true; public void P2PListenerSimple() { var collection = _Database.GetDefaultCollection(); // tag::listener-simple[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (38) endpointConfig.Authenticator = new ListenerPasswordAuthenticator( (sender, username, password) => { // ValidatePassword can make use of the SecureString class // to the desired level of security (or just convert it to string // if no intense security is required) return username == "valid.user" && ValidatePassword(password); } ); (39) var listener = new URLEndpointListener(endpointConfig); (40) listener.Start(); (41) // end::listener-simple[] } public void P2PReplicatorSimple() { var collection = _Database.GetDefaultCollection(); // tag::replicator-simple[] var endpointConfig = new URLEndpoint(new Uri("wss://listener.com:4984/otherDB")); (42) var replConfig = new ReplicatorConfiguration(endpointConfig); (43) replConfig.AddCollection(collection); replConfig.AcceptOnlySelfSignedServerCertificate = true; (44) replConfig.Authenticator = new BasicAuthenticator("valid.user", "valid.password.string"); (45) var replicator = new Replicator(replConfig); (46) replicator.Start(); (47) // end::replicator-simple[] } public void GettingStarted1() { var collection = _Database.GetDefaultCollection(); // tag::listener-initialize[] // tag::listener-config-db[] // Initialize the listener config var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); (48) // end::listener-config-db[] // tag::listener-config-port[] endpointConfig.Port = 55990; (49) // end::listener-config-port[] // tag::listener-config-netw-iface[] endpointConfig.NetworkInterface = "10.1.1.10"; (50) // end::listener-config-netw-iface[] // tag::listener-config-delta-sync[] endpointConfig.EnableDeltaSync = true; (51) // end::listener-config-delta-sync[] #warning listener-config-tls-full unused? // tag::listener-config-tls-full[] // tag::listener-config-tls-enable[] endpointConfig.DisableTLS = false; (52) // end::listener-config-tls-enable[] // tag::listener-config-tls-id-anon[] // Use an Anonymous Self-Signed Cert endpointConfig.TlsIdentity = null; (53) // end::listener-config-tls-id-anon[] // tag::listener-config-client-auth-pwd[] // Configure the client authenticator // Here we are using Basic Authentication) (54) SecureString validPassword = new SecureString(); /* example only */ // Get SecureString input for validPassword var validUser = "valid.username"; endpointConfig.Authenticator = new ListenerPasswordAuthenticator( (sender, username, password) => { // Implement your own ValidatePassword function return username == validUser && ValidatePassword(password); } ); // end::listener-config-client-auth-pwd[] // tag::listener-start[] // Initialize the listener var listener = new URLEndpointListener(endpointConfig); (55) // Start the listener listener.Start(); (56) // end::listener-start[] // end::listener-initialize[] #warning old-listener-config-tls-disable unused? // tag::old-listener-config-tls-disable[] endpointConfig.DisableTLS = true; // end::old-listener-config-tls-disable[] #warning listener-config-tls-id-nil-2 unused? // tag::listener-config-tls-id-nil-2[] // Use “anonymous” cert. These are self signed certs created by the system endpointConfig.TlsIdentity = null; // end::listener-config-tls-id-nil-2[] #warning old-listener-config-delta-sync unused? // tag::old-listener-config-delta-sync[] endpointConfig.EnableDeltaSync = true; // end::old-listener-config-delta-sync[] // tag::listener-status-check[] ulong connectionCount = listener.Status.ConnectionCount; (57) ulong activeConnectionCount = listener.Status.ActiveConnectionCount; (58) // end::listener-status-check[] // tag::listener-stop[] listener.Stop(); // end::listener-stop[] // tag::listener-get-network-interfaces[] foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces()) { if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 || ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet) { // do something with the interface(s) } } // end::listener-get-network-interfaces[] } public void GettingStarted2() { var collection = _Database.GetDefaultCollection(); { #warning listener-get-url-list unused? // tag::listener-get-url-list[] var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); var listener = new URLEndpointListener(endpointConfig); listener.Start(); // Note, converting to string omitted. Console.WriteLine("URLS are {0} ", listener.Urls); // end::listener-get-url-list[] #warning listener-config-tls-disable unused? // tag::listener-config-tls-disable[] endpointConfig.DisableTLS = true; (59) // end::listener-config-tls-disable[] #warning listener-local-db unused? // tag::listener-local-db[] // . . . preceding application logic . . . // Get the database (and create it if it doesn't exist) var database = new Database("mydb"); // end::listener-local-db[] // tag::listener-config-tls-id-full[] // tag::listener-config-tls-id-caCert[] // Use CA Cert // Create a TLSIdentity from an imported key-pair // . . . previously declared variables include ... X509Store store = new X509Store(StoreName.My); // create and label x509 store // Get keys and certificates from PKCS12 data byte[] certData = File.ReadAllBytes("c:client.p12"); (60) // . . . other user code . . . #warning import-tls-identity unused? // tag::import-tls-identity[] TLSIdentity identity = TLSIdentity.ImportIdentity( store, certData, (61) "123", // Password to access certificate data "couchbase-demo-cert", null); // Label to get cert in certificate map // NOTE: If a null label is supplied then the same // default directory for a Couchbase Lite database // is used for map. // end::import-tls-identity[] // end::listener-config-tls-id-caCert[] // tag::listener-config-tls-id-anon[] // Use an Anonymous Self-Signed Cert endpointConfig.TlsIdentity = null; (62) // end::listener-config-tls-id-anon[] #warning listener-config-tls-id-set unused? // tag::listener-config-tls-id-set[] // Set the TLS Identity endpointConfig.TlsIdentity = identity; (63) // end::listener-config-tls-id-set[] // end::listener-config-tls-id-full[] } { var endpointConfig = new URLEndpointListenerConfiguration(new[] { collection }); // tag::listener-config-client-auth-root[] // Configure the client authenticator // to validate using ROOT CA // Get the valid cert chain, in this instance from // PKCS12 data containing private key, public key // and certificates (64) var clientData = File.ReadAllBytes("c:client.p12"); var ourCaData = File.ReadAllBytes("c:client-ca.der"); // Get the root certs from the data var rootCert = new X509Certificate2(ourCaData); (65) // Configure the authenticator to use the root certs var certAuth = new ListenerCertificateAuthenticator(new X509Certificate2Collection(rootCert)); endpointConfig.Authenticator = certAuth; (66) // Initialize the listener using the config var listener = new URLEndpointListener(endpointConfig); // end::listener-config-client-auth-root[] // tag::listener-config-client-auth-lambda[] // Configure the client authenticator // to validate using application logic // Get the valid cert chain, in this instance from // PKCS12 data containing private key, public key // and certificates (67) clientData = File.ReadAllBytes("c:client.p12"); ourCaData = File.ReadAllBytes("c:client-ca.der"); // Configure the authenticator to pass the root certs // To a user supplied code block for authentication var callbackAuth = new ListenerCertificateAuthenticator( (object sender, X509Certificate2Collection chain) => { // . . . user supplied code block // . . . returns boolean value (true=authenticated) return true; }); (68) endpointConfig.Authenticator = callbackAuth; (69) // end::listener-config-client-auth-lambda[] } } public void datatype_usage() { #warning datatype_usage unused? // tag::datatype_usage[] // tag::datatype_usage_createdb[] // Get the database (and create it if it doesn’t exist). using var database = new Database("hoteldb"); var collection = database.GetDefaultCollection(); // end::datatype_usage_createdb[] // tag::datatype_usage_createdoc[] // Create your new document using var mutableDoc = new MutableDocument("hoteldoc"); // end::datatype_usage_createdoc[] // tag::datatype_usage_mutdict[] // Create and populate mutable dictionary var address = new MutableDictionaryObject(); address.SetString("street", "1 Main st."); address.SetString("city", "San Francisco"); address.SetString("state", "CA"); address.SetString("country", "USA"); address.SetString("code", "90210"); // end::datatype_usage_mutdict[] // tag::datatype_usage_mutarray[] // Create and populate mutable array var phones = new MutableArrayObject(); phones.AddString("650-000-0000"); phones.AddString("650-000-0001"); // end::datatype_usage_mutarray[] // tag::datatype_usage_populate[] // Initialize and populate the document // Add document type and hotel name as string mutableDoc.SetString("type", "hotel"); mutableDoc.SetString("name", "Hotel Java Mo"); // Add average room rate (float) mutableDoc.SetFloat("room_rate", 121.75f); // Add address (dictionary) mutableDoc.SetDictionary("address", address); // Add phone numbers(array) mutableDoc.SetArray("phones", phones); // end::datatype_usage_populate[] // tag::datatype_usage_persist[] collection.Save(mutableDoc); // end::datatype_usage_persist[] // tag::datatype_usage_closedb[] database.Close(); // end::datatype_usage_closedb[] // end::datatype_usage[] } public void datatype_dictionary() { var collection = _Database.GetDefaultCollection(); // tag::datatype_dictionary[] var doc = collection.GetDocument("doc1"); // Getting a dictionary from the document's properties var dict = doc.GetDictionary("address"); // Access a value with a key from the dictionary var street = dict.GetString("street"); // Iterate dictionary foreach (var key in dict.Keys) { Console.WriteLine($"Key {key} = {dict.GetValue(key)}"); } // Create a mutable copy var mutableDict = dict.ToMutable(); // end::datatype_dictionary[] } public void datatype_mutable_dictionary() { var collection = _Database.GetDefaultCollection(); // tag::datatype_mutable_dictionary[] // Create a new mutable dictionary and populate some keys/values var mutableDict = new MutableDictionaryObject(); mutableDict.SetString("street", "1 Main st."); mutableDict.SetString("city", "San Francisco"); // Add the dictionary to a document's properties and save the document using var mutableDoc = new MutableDocument("doc1"); mutableDoc.SetDictionary("address", mutableDict); collection.Save(mutableDoc); // end::datatype_mutable_dictionary[] } public void datatype_array() { var collection = _Database.GetDefaultCollection(); // tag::datatype_array[] var document = collection.GetDocument("doc1"); // Getting a phones array from the document's properties var array = document.GetArray("phones"); // Get element count var count = array.Count(); // Access an array element by index if (count >= 0) { var phone = array[1]; } // Iterate dictionary for (int i = 0; i < count; i++) { Console.WriteLine($"Item {i.ToString()} = {array[i]}"); } // Create a mutable copy var mutableArray = array.ToMutable(); // end::datatype_array[] } public void datatype_mutable_array() { var collection = _Database.GetDefaultCollection(); // tag::datatype_mutable_array[] // Create a new mutable array and populate data into the array var mutableArray = new MutableArrayObject(); mutableArray.AddString("650-000-0000"); mutableArray.AddString("650-000-0001"); // Set the array to document's properties and save the document using var mutableDoc = new MutableDocument("doc1"); mutableDoc.SetArray("phones", mutableArray); collection.Save(mutableDoc); // end::datatype_mutable_array[] } static void Main(string[] args) { // NOTE: PLEASE PLEASE PLEASE do not break the compilation of this file. It is // by far the easiest way to check for its correctness. If you don't know how to // compile a C# program, then find someone who does before you commit your changes!!! Console.WriteLine("This program is not meant to be executed, only compiled"); } } /* ----------------------------------------------------------- */ /* --------------------- ACTIVE SIDE ----------------------- */ /* --------------- stubs for documentation ----------------- */ /* ----------------------------------------------------------- */ class ActivePeer : IMessageEndpointDelegate { ActivePeer() { // tag::message-endpoint[] var database = new Database("dbname"); // The delegate must implement the `IMessageEndpointDelegate` protocol. var messageEndpointTarget = new MessageEndpoint(uid: "UID:123", target: "", protocolType: ProtocolType.MessageStream, delegateObject: this); // end::message-endpoint[] // tag::message-endpoint-replicator[] var replConfig = new ReplicatorConfiguration(messageEndpointTarget); replConfig.AddCollection(database.GetDefaultCollection()); // Create the replicator object var replicator = new Replicator(replConfig); // Start the replicator replicator.Start(); // end::message-endpoint-replicator[] } // tag::create-connection[] /* implementation of MessageEndpointDelegate */ public IMessageEndpointConnection CreateConnection(MessageEndpoint endpoint) { var connection = new ActivePeerConnection(); /* implements MessageEndpointConnection */ return connection; } // end::create-connection[] } #pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously class ActivePeerConnection : IMessageEndpointConnection { private IReplicatorConnection _replicatorConnection; public void Disconnect() { // tag::active-replicator-close[] _replicatorConnection?.Close(null); // end::active-replicator-close[] } public void Receive(byte[] data) { // tag::active-peer-receive[] var message = Message.FromBytes(data); _replicatorConnection?.Receive(message); // end::active-peer-receive[] } // tag::active-peer-close[] /* implementation of MessageEndpointConnection */ public async Task Close(Exception error) { // await socket.Close, etc (or do nothing if already closed) // throw MessagingException if something goes wrong (though // since it is "close" nothing special will happen) } // end::active-peer-close[] // tag::active-peer-open[] /* implementation of MessageEndpointConnection */ public async Task Open(IReplicatorConnection connection) { _replicatorConnection = connection; // await socket.Open(), etc // throw MessagingException if something goes wrong } // end::active-peer-open[] // tag::active-peer-send[] /* implementation of MessageEndpointConnection */ public async Task Send(Message message) { var data = message.ToByteArray(); // await Socket.Send(), etc // throw MessagingException if something goes wrong } // end::active-peer-send[] } /* ----------------------------------------------------------- */ /* --------------------- PASSIVE SIDE ---------------------- */ /* --------------- stubs for documentation ----------------- */ /* ----------------------------------------------------------- */ class PassivePeerConnection : IMessageEndpointConnection { private MessageEndpointListener _messageEndpointListener; private IReplicatorConnection _replicatorConnection; public void StartListener() { // tag::listener[] var database = new Database("mydb"); var endpointConfig = new MessageEndpointListenerConfiguration(new[] { database.GetDefaultCollection() }, ProtocolType.MessageStream); _messageEndpointListener = new MessageEndpointListener(endpointConfig); // end::listener[] } public void StopListener() { // tag::passive-stop-listener[] _messageEndpointListener?.CloseAll(); // end::passive-stop-listener[] } public void AcceptConnection() { // tag::advertizer-accept[] var connection = new PassivePeerConnection(); /* implements MessageEndpointConnection */ _messageEndpointListener?.Accept(connection); // end::advertizer-accept[] } public void Disconnect() { // tag::passive-replicator-close[] _replicatorConnection?.Close(null); // end::passive-replicator-close[] } public void Receive(byte[] data) { // tag::passive-peer-receive[] var message = Message.FromBytes(data); _replicatorConnection?.Receive(message); // end::passive-peer-receive[] } // tag::passive-peer-close[] /* implementation of MessageEndpointConnection */ public async Task Close(Exception error) { // await socket.Close, etc (or do nothing if already closed) // throw MessagingException if something goes wrong (though // since it is "close" nothing special will happen) } // end::passive-peer-close[] // tag::passive-peer-open[] /* implementation of MessageEndpointConnection */ public Task Open(IReplicatorConnection connection) { _replicatorConnection = connection; // socket should already be open on the passive side return Task.FromResult(true); } // end::passive-peer-open[] // tag::passive-peer-send[] /* implementation of MessageEndpointConnection */ public async Task Send(Message message) { var data = message.ToByteArray(); // await Socket.Send(), etc // throw MessagingException if something goes wrong } // end::passive-peer-send[] } #pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously // tag::predictive-model[] // tensorFlowModel is a fake implementation // this would be the implementation of the ml model you have chosen class TensorFlowModel { public static IDictionary<string, object> PredictImage(byte[] data) { // Do calculations, etc return null; } } class ImageClassifierModel : IPredictiveModel { public DictionaryObject Predict(DictionaryObject input) { var blob = input.GetBlob("photo"); if (blob == null) { return null; } var imageData = blob.Content; // tensorFlowModel is a fake implementation // this would be the implementation of the ml model you have chosen var modelOutput = TensorFlowModel.PredictImage(imageData); return new MutableDictionaryObject(modelOutput); (1) } } // end::predictive-model[] // tag::custom-logging[] class LogTestLogger : ILogger { public LogLevel Level { get; set; } public void Reset() { } public void Log(LogLevel level, LogDomain domain, string message) { // handle the message, for example piping it to // a third party framework } } // end::custom-logging[] // tag::local-win-conflict-resolver[] class LocalWinConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { return conflict.LocalDocument; } } // end::local-win-conflict-resolver[] // tag::remote-win-conflict-resolver[] class RemoteWinConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { return conflict.RemoteDocument; } } // end::remote-win-conflict-resolver[] // tag::merge-conflict-resolver[] class MergeConflictResolver : IConflictResolver { public Document Resolve(Conflict conflict) { var localDict = conflict.LocalDocument.ToDictionary(); var remoteDict = conflict.RemoteDocument.ToDictionary(); var result = localDict.Concat(remoteDict) .GroupBy(kv => kv.Key) .ToDictionary(g => g.Key, g => g.First().Value); return new MutableDocument(conflict.DocumentID, result); } } // end::merge-conflict-resolver[] } #warning p2p-act-rep-func used, but contains nothing // tag::p2p-act-rep-func[] #warning p2p-act-rep-config-type used, but contains nothing // tag::p2p-act-rep-config-type[] // end::p2p-act-rep-config-type[] #warning autopurge-override used, but contains nothing // tag::autopurge-override[] // Set autopurge option // here we override its default // end::autopurge-override[] #warning p2p-act-rep-config-cont used, but contains nothing // tag::p2p-act-rep-config-cont[] // Configure Sync Mode // end::p2p-act-rep-config-cont[] #warning p2p-act-rep-config-self-cert used, but contains nothing // tag::p2p-act-rep-config-self-cert[] // Configure Server Security -- only accept self-signed certs // end::p2p-act-rep-config-self-cert[] // Configure Client Security (70) #warning p2p-act-rep-auth used, but contains nothing // tag::p2p-act-rep-auth[] // Configure basic auth using user credentials // end::p2p-act-rep-auth[] #warning p2p-act-rep-start-full used, but contains nothing // tag::p2p-act-rep-start-full[] // Initialize and start a replicator // Initialize replicator with configuration data #warning p2p-act-rep-add-change-listener used, but contains nothing // tag::p2p-act-rep-add-change-listener[] #warning p2p-act-rep-add-change-listener-label used, but contains nothing // tag::p2p-act-rep-add-change-listener-label[] //Optionally add a change listener (71) // end::p2p-act-rep-add-change-listener-label[] // end::p2p-act-rep-add-change-listener[] #warning p2p-act-rep-start used, but contains nothing // tag::p2p-act-rep-start[] // Start replicator // end::p2p-act-rep-start[] // end::p2p-act-rep-start-full[] // end::p2p-act-rep-func[] #warning p2p-act-rep-config-cacert used, but contains nothing // tag::p2p-act-rep-config-cacert[] // Configure Server Security -- only accept CA certs // end::p2p-act-rep-config-cacert[] #warning p2p-act-rep-config-cacert-pinned used, but contains nothing // tag::p2p-act-rep-config-cacert-pinned[] // Only CA Certs accepted // end::p2p-act-rep-config-cacert-pinned[] #warning p2p-act-rep-status used, but contains nothing // tag::p2p-act-rep-status[] // end::p2p-act-rep-status[] #warning p2p-act-rep-stop used, but contains nothing // tag::p2p-act-rep-stop[] // Stop replication. // end::p2p-act-rep-stop[] #warning p2p-tlsid-store-in-keychain used, but contains nothing // tag::p2p-tlsid-store-in-keychain[] // end::p2p-tlsid-store-in-keychain[] #warning p2p-tlsid-delete-id-from-keychain used, but contains nothing // tag::p2p-tlsid-delete-id-from-keychain[] // end::p2p-tlsid-delete-id-from-keychain[] public class MyClass { public Database Database { get; set; } public Replicator Replicator { get; set; } (72) public void StartReplication() { // tag::sgw-repl-pull[] var url = new Uri("wss://localhost:4984/db"); (73) var target = new URLEndpoint(url); var config = new ReplicatorConfiguration(target) { ReplicatorType = ReplicatorType.Pull }; config.AddCollection(Database.GetDefaultCollection()); Replicator = new Replicator(config); Replicator.Start(); // end::sgw-repl-pull[] } public void InitReplication() { // tag::sgw-act-rep-initialize[] // initialize the replicator configuration var url = new URLEndpoint(new Uri("wss://10.0.2.2:4984/anotherDB")); (74) var replConfig = new ReplicatorConfiguration(url); // Add collections to the config now // end::sgw-act-rep-initialize[] } }