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(); }
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(); }