public async Task TestAddSameChangeListeners()
        {
            var doc1 = new MutableDocument("doc1");

            doc1.SetString("name", "Scott");
            var saved = Db.Save(doc1);

            doc1.Dispose();
            doc1 = saved.ToMutable();

            Db.AddDocumentChangeListener("doc1", DocumentChanged);
            Db.AddDocumentChangeListener("doc1", DocumentChanged);
            Db.AddDocumentChangeListener("doc1", DocumentChanged);
            Db.AddDocumentChangeListener("doc1", DocumentChanged);
            Db.AddDocumentChangeListener("doc1", DocumentChanged);

            _wa = new WaitAssert();
            _expectedDocumentChanges = new HashSet <string> {
                "doc1"
            };
            doc1.SetString("name", "Scott Tiger");
            Db.Save(doc1);

            await Task.Delay(500);

            _wa.CaughtExceptions.Should().BeEmpty("because otherwise too many callbacks happened");
        }
        public void TestConflict()
        {
            ConflictResolver = new TheirsWins();
            ReopenDB();
            var doc1      = SetupConflict();
            var savedDoc1 = Db.Save(doc1);

            savedDoc1["name"].ToString().Should().Be("Scotty", "because the 'theirs' version should win");
            doc1.Dispose();
            savedDoc1.Dispose();

            ConflictResolver = new MergeThenTheirsWins();
            ReopenDB();

            var doc2 = new MutableDocument("doc2");

            doc2.SetString("type", "profile");
            doc2.SetString("name", "Scott");
            var savedDoc2 = Db.Save(doc2);

            // Force a conflict again
            var properties = doc2.ToDictionary();

            properties["type"]   = "bio";
            properties["gender"] = "male";
            SaveProperties(properties, doc2.Id);
            doc2.Dispose();

            // Save and make sure that the correct conflict resolver won
            doc2 = savedDoc2.ToMutable();
            doc2.SetString("type", "bio");
            doc2.SetInt("age", 31);

            savedDoc2.Dispose();
            savedDoc2 = Db.Save(doc2);
            doc2.Dispose();

            savedDoc2["age"].Long.Should().Be(31L, "because 'age' was changed by 'mine' and not 'theirs'");
            savedDoc2["type"].String.Should().Be("bio", "because 'type' was changed by 'mine' and 'theirs' so 'theirs' should win");
            savedDoc2["gender"].String.Should().Be("male", "because 'gender' was changed by 'theirs' but not 'mine'");
            savedDoc2["name"].String.Should().Be("Scott", "because 'name' was unchanged");
            savedDoc2.Dispose();
        }
        public async Task TestRemoveDocumentChangeListener()
        {
            var doc1 = new MutableDocument("doc1");

            doc1.SetString("name", "Scott");
            var saved = Db.Save(doc1);

            doc1.Dispose();
            doc1 = saved.ToMutable();

            var token = Db.AddDocumentChangeListener("doc1", DocumentChanged);

            _wa = new WaitAssert();
            _expectedDocumentChanges = new HashSet <string> {
                "doc1"
            };

            doc1.SetString("name", "Scott Tiger");
            saved = Db.Save(doc1);
            doc1.Dispose();
            doc1 = saved.ToMutable();
            _wa.WaitForResult(TimeSpan.FromSeconds(5));

            Db.RemoveChangeListener(token);

            _wa = new WaitAssert();
            _docCallbackShouldThrow = true;
            doc1.SetString("name", "Scott Pilgrim");
            Db.Save(doc1);

            await Task.Delay(500);

            _wa.CaughtExceptions.Should().BeEmpty("because otherwise too many callbacks happened");

            // Remove again
            Db.RemoveChangeListener(token);
        }
        public void TestNonDictionaryFragmentSetValue()
        {
            var doc = new MutableDocument("doc1");

            doc.SetString("string1", "value1").SetString("string2", "value2");
            SaveDocument(doc, d =>
            {
                var md = d.ToMutable();
                md["string1"].Value = 10;
                md["string1"].Int.Should().Be(10, "because the value was changed");
                md["string2"].String.Should().Be("value2", "because that is what was stored");
                md.Dispose();
            });

            doc.Dispose();
        }
        private MutableDocument SetupConflict()
        {
            var doc = new MutableDocument("doc1");

            doc.SetString("type", "profile");
            doc.SetString("name", "Scott");
            var savedDoc = Db.Save(doc);

            // Force a conflict
            var properties = doc.ToDictionary();

            properties["name"] = "Scotty";
            SaveProperties(properties, doc.Id);

            doc.Dispose();
            doc = savedDoc.ToMutable();
            doc.SetString("name", "Scott Pilgrim");
            return(doc);
        }
        public void TestDictionaryFragmentSetCSharpList()
        {
            var doc = new MutableDocument("doc1");

            doc["dict"].Value          = new Dictionary <string, object>();
            doc["dict"]["array"].Value = new[] { 0, 1, 2 };

            SaveDocument(doc, d =>
            {
                d["dict"]["array"][-1].Value.Should().BeNull("because that is an invalid index");
                d["dict"]["array"][-1].Exists.Should().BeFalse("because there is no data at the invalid index");
                d["dict"]["array"][0].Int.Should().Be(0, "because that is what was stored");
                d["dict"]["array"][1].Int.Should().Be(1, "because that is what was stored");
                d["dict"]["array"][2].Int.Should().Be(2, "because that is what was stored");
                d["dict"]["array"][3].Value.Should().BeNull("because that is an invalid index");
                d["dict"]["array"][3].Exists.Should().BeFalse("because there is no data at the invalid index");
            });

            doc.Dispose();
        }
Example #7
0
        static void Main(string[] args)
        {
            // This only needs to be done once for whatever platform the executable is running
            // (UWP, iOS, Android, or desktop)
            Couchbase.Lite.Support.NetDesktop.Activate();

            // create database
            var config = new DatabaseConfiguration();

            config.ConflictResolver = new ExampleConflictResolver();
            var database = new Database("my-database", config);

            // create document
            var newTask = new MutableDocument();

            newTask.SetString("type", "task");
            newTask.SetString("owner", "todo");
            newTask.SetDate("createdAt", DateTimeOffset.UtcNow);
            newTask = database.Save(newTask).ToMutable();

            // mutate document
            newTask.SetString("name", "Apples");
            newTask = database.Save(newTask).ToMutable();

            // typed accessors
            newTask.SetDate("createdAt", DateTimeOffset.UtcNow);
            var date = newTask.GetDate("createdAt");

            // database transaction
            database.InBatch(() =>
            {
                for (int i = 0; i < 10; i++)
                {
                    using (var doc = new MutableDocument()) {
                        doc.SetString("type", "user");
                        doc.SetString("name", $"user {i}");
                        using (var saved = database.Save(doc)) {
                            Console.WriteLine($"saved user document {saved.GetString("name")}");
                        }
                    }
                }
            });

            // blob
            var bytes = File.ReadAllBytes("avatar.jpg");
            var blob  = new Blob("image/jpg", bytes);

            newTask.SetBlob("avatar", blob);
            newTask = database.Save(newTask).ToMutable();
            var taskBlob = newTask.GetBlob("avatar");
            var data     = taskBlob.Content;

            newTask.Dispose();

            // query
            var query = QueryBuilder.Select(SelectResult.Expression(Meta.ID))
                        .From(DataSource.Database(database))
                        .Where(Expression.Property("type").EqualTo(Expression.String("user"))
                               .And(Expression.Property("admin").EqualTo(Expression.Boolean(false))));

            var rows = query.Execute();

            foreach (var row in rows)
            {
                Console.WriteLine($"doc ID :: ${row.GetString(0)}");
            }


            // live query
            query.AddChangeListener((sender, e) => {
                Console.WriteLine($"Number of rows :: {e.Results.Count()}");
            });

            using (var newDoc = new MutableDocument()) {
                newDoc.SetString("type", "user");
                newDoc.SetBoolean("admin", false);
                database.Save(newDoc);
            }

            // fts example
            // insert documents
            var tasks = new[] { "buy groceries", "play chess", "book travels", "buy museum tickets" };

            foreach (string task in tasks)
            {
                using (var doc = new MutableDocument()) {
                    doc.SetString("type", "task").SetString("name", task); // Chaining is possible
                    database.Save(doc);
                }
            }

            // create Index
            var index = IndexBuilder.FullTextIndex(FullTextIndexItem.Property("name"));

            database.CreateIndex("byName", index);

            using (var ftsQuery = QueryBuilder.Select(SelectResult.Expression(Meta.ID).As("id"))
                                  .From(DataSource.Database(database))
                                  .Where(FullTextExpression.Index("byName").Match("'buy'"))) {
                var ftsRows = ftsQuery.Execute();
                foreach (var row in ftsRows)
                {
                    var doc = database.GetDocument(row.GetString("id")); // Use alias instead of index
                    Console.WriteLine(
                        $"document properties {JsonConvert.SerializeObject(doc.ToDictionary(), Formatting.Indented)}");
                }
            }

            // create conflict

            /*
             * 1. Create a document twice with the same ID (the document will have two conflicting revisions).
             * 2. Upon saving the second revision, the ExampleConflictResolver's resolve method is called.
             * The `theirs` ReadOnlyDocument in the conflict resolver represents the current rev and `mine` is what's being saved.
             * 3. Read the document after the second save operation and verify its property is as expected.
             * The conflict resolver will have deleted the obsolete revision.
             */
            using (var theirs = new MutableDocument("buzz"))
                using (var mine = new MutableDocument("buzz")) {
                    theirs.SetString("status", "theirs");
                    mine.SetString("status", "mine");
                    database.Save(theirs);
                    database.Save(mine);
                }

            var conflictResolverResult = database.GetDocument("buzz");

            Console.WriteLine($"conflictResolverResult doc.status ::: {conflictResolverResult.GetString("status")}");

            // replication (Note: Linux / Mac requires .NET Core 2.0+ due to
            // https://github.com/dotnet/corefx/issues/8768)

            /*
             * Tested with SG 1.5 https://www.couchbase.com/downloads
             * Config file:
             * {
             *        "databases": {
             *          "db": {
             *            "server":"walrus:",
             *            "users": {
             *              "GUEST": {"disabled": false, "admin_channels": ["*"]}
             *            },
             *            "unsupported": {
             *              "replicator_2":true
             *            }
             *          }
             *        }
             *      }
             */
            var url         = new Uri("ws://localhost:4984/db");
            var replConfig  = new ReplicatorConfiguration(database, new URLEndpoint(url));
            var replication = new Replicator(replConfig);

            replication.Start();

            // replication change listener
            replication.AddChangeListener((sender, e) => {
                if (e.Status.Activity == ReplicatorActivityLevel.Stopped)
                {
                    Console.WriteLine("Replication has completed.");
                }
            });

            Console.ReadLine();

            // This is important to do because otherwise the native connection
            // won't be released until the next garbage collection
            query.Dispose();
            database.Dispose();
        }