From 479ff1bef5a33644a92c2393298e079e0bc3b29f Mon Sep 17 00:00:00 2001 From: Jim Borden Date: Thu, 30 Mar 2017 15:10:26 +0900 Subject: [PATCH] Some refactoring work on the model API, and initial (non functioning) replication API implementation --- src/Couchbase.Lite.Tests.Shared/ModelTest.cs | 37 ++-- src/Couchbase.Lite.Tests.Shared/QueryTest.cs | 2 +- src/Couchbase.Lite/API/IDatabase.cs | 2 - src/Couchbase.Lite/API/IDocument.cs | 2 +- src/Couchbase.Lite/API/IDocumentMetadata.cs | 53 ----- src/Couchbase.Lite/API/IDocumentModel.cs | 8 +- src/Couchbase.Lite/API/IModeledDocument.cs | 59 ----- .../API/Query/DataSourceFactory.cs | 17 ++ src/Couchbase.Lite/API/QueryableFactory.cs | 58 ----- src/Couchbase.Lite/Couchbase.Lite.csproj | 1 + src/Couchbase.Lite/Database/Database.cs | 107 +++++---- src/Couchbase.Lite/Database/Document.cs | 127 +++++++---- .../Database/DocumentMetadata.cs | 51 ----- .../Database/ModeledDocument.cs | 208 ------------------ .../Linq/LiteCoreQueryExecutor.cs | 6 +- src/Couchbase.Lite/Query/DatabaseQueryable.cs | 5 +- src/Couchbase.Lite/Query/QueryEnumerator.cs | 26 ++- .../CouchbaseLiteContractResolver.cs | 2 +- vendor/couchbase-lite-core | 2 +- 19 files changed, 204 insertions(+), 569 deletions(-) delete mode 100644 src/Couchbase.Lite/API/IDocumentMetadata.cs delete mode 100644 src/Couchbase.Lite/API/IModeledDocument.cs delete mode 100644 src/Couchbase.Lite/API/QueryableFactory.cs delete mode 100644 src/Couchbase.Lite/Database/DocumentMetadata.cs delete mode 100644 src/Couchbase.Lite/Database/ModeledDocument.cs diff --git a/src/Couchbase.Lite.Tests.Shared/ModelTest.cs b/src/Couchbase.Lite.Tests.Shared/ModelTest.cs index c1c47f387..a20c11e5d 100644 --- a/src/Couchbase.Lite.Tests.Shared/ModelTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/ModelTest.cs @@ -24,6 +24,7 @@ using System.Linq; using Couchbase.Lite; +using Couchbase.Lite.Query; using FluentAssertions; using Newtonsoft.Json; using Xunit; @@ -40,7 +41,7 @@ public class TestModel : IDocumentModel public TestModelReference Child { get; set; } = new TestModelReference(); - public IDocumentMetadata Metadata + public IDocument Document { get; set; } @@ -66,22 +67,28 @@ public ModelTest(ITestOutputHelper output) : base(output) //[Fact] //public void TestModel() //{ - // var model = Db.GetDocument(); - // var item = model.Item; - // item.IntValue = 42; - // item.StringValue = "Jim"; - // item.Child.IntValues = new[] { 1, 2, 3, 4 }; - // model.Save().Should().BeTrue("because otherwise the save failed"); + // Db.DoSync(() => + // { + // var model = Db.CreateDocument().AsModel(); + // model.IntValue = 42; + // model.StringValue = "Jim"; + // model.Child.IntValues = new[] {1, 2, 3, 4}; + // model.Save(); - // var model2 = Db.GetDocument(); - // item = model2.Item; - // item.IntValue = 43; - // item.StringValue = "Jim"; - // item.Child.IntValues = new[] { 1, 2, 3, 4, 5 }; - // model2.Save(); + // var model2 = Db.CreateDocument().AsModel(); + // model2.IntValue = 43; + // model2.StringValue = "Jim"; + // model2.Child.IntValues = new[] {1, 2, 3, 4, 5}; + // model2.Save(); - // var all = from x in QueryableFactory.MakeQueryable(Db) where x.Child.IntValues.Sum() > 10 select x; - // all.ToArray(); + // var all = from x in DataSourceFactory.LinqDataSource(Db, true) + // where x.Child.IntValues.Sum() > 10 + // select x; + + // var test = all.ToArray(); + // test.Count().Should().Be(1); + // test[0].IntValue.Should().Be(43); + // }); //} } } diff --git a/src/Couchbase.Lite.Tests.Shared/QueryTest.cs b/src/Couchbase.Lite.Tests.Shared/QueryTest.cs index a12a349c7..8710057a1 100644 --- a/src/Couchbase.Lite.Tests.Shared/QueryTest.cs +++ b/src/Couchbase.Lite.Tests.Shared/QueryTest.cs @@ -57,7 +57,7 @@ public class NamesModel : IDocumentModel [JsonProperty(PropertyName = "memberSince")] public string MemberSince { get; set; } - public IDocumentMetadata Metadata { get; set; } + public IDocument Document { get; set; } public NamesModel() { diff --git a/src/Couchbase.Lite/API/IDatabase.cs b/src/Couchbase.Lite/API/IDatabase.cs index d4e64458f..59e1ab6ea 100644 --- a/src/Couchbase.Lite/API/IDatabase.cs +++ b/src/Couchbase.Lite/API/IDatabase.cs @@ -142,8 +142,6 @@ public interface IDatabase : IThreadSafe, IDisposable [AccessibilityMode(AccessMode.FromQueueOnly)] IDocument GetDocument(string id); - //IModeledDocument GetDocument(string id) where T : class, new(); - /// /// Runs the given batch of operations as an atomic unit /// diff --git a/src/Couchbase.Lite/API/IDocument.cs b/src/Couchbase.Lite/API/IDocument.cs index 3b491b837..fb5a7f14b 100644 --- a/src/Couchbase.Lite/API/IDocument.cs +++ b/src/Couchbase.Lite/API/IDocument.cs @@ -26,7 +26,7 @@ namespace Couchbase.Lite /// /// An interface describing a Couchbase Lite document /// - public interface IDocument : IPropertyContainer, IDisposable + public interface IDocument : IPropertyContainer, IDisposable/*, IModellable*/ { #region Properties diff --git a/src/Couchbase.Lite/API/IDocumentMetadata.cs b/src/Couchbase.Lite/API/IDocumentMetadata.cs deleted file mode 100644 index ea7b34585..000000000 --- a/src/Couchbase.Lite/API/IDocumentMetadata.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// IDocumentMetadata.cs -// -// Author: -// Jim Borden -// -// 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. -// - -namespace Couchbase.Lite -{ - /// - /// An interface describing metadata for an - /// - public interface IDocumentMetadata - { - #region Properties - - /// - /// Gets the unique ID of the document - /// - string Id { get; } - - /// - /// Gets whether or not the document is deleted - /// - bool IsDeleted { get; } - - /// - /// Gets the unique sequence number of the document - /// - ulong Sequence { get; } - - /// - /// Gets or sets the type of the document - /// - string Type { get; set; } - - #endregion - } -} diff --git a/src/Couchbase.Lite/API/IDocumentModel.cs b/src/Couchbase.Lite/API/IDocumentModel.cs index 82775632a..1576f0cbf 100644 --- a/src/Couchbase.Lite/API/IDocumentModel.cs +++ b/src/Couchbase.Lite/API/IDocumentModel.cs @@ -25,15 +25,15 @@ namespace Couchbase.Lite /// Using this interface, an arbitrary non-Couchbase class can become /// the model for retrieving data /// - public interface IDocumentModel + internal interface IDocumentModel { #region Properties /// - /// Gets or sets the metadata for the document (note: - /// this should only be set by the library) + /// Gets or sets the document that is used to populate this instance + /// of a concrete class /// - IDocumentMetadata Metadata { get; set; } + IDocument Document { get; set; } #endregion } diff --git a/src/Couchbase.Lite/API/IModeledDocument.cs b/src/Couchbase.Lite/API/IModeledDocument.cs deleted file mode 100644 index 37598fd75..000000000 --- a/src/Couchbase.Lite/API/IModeledDocument.cs +++ /dev/null @@ -1,59 +0,0 @@ -// -// IModeledDocument.cs -// -// Author: -// Jim Borden -// -// 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 System; - -namespace Couchbase.Lite -{ - internal interface IModeledDocument : IThreadSafe, IDisposable where T : class, new() - { - #region Properties - - [AccessibilityMode(AccessMode.FromAnywhere)] - IDatabase Db { get; } - - [AccessibilityMode(AccessMode.FromAnywhere)] - string Id { get; } - - [AccessibilityMode(AccessMode.FromQueueOnly)] - bool IsDeleted { get; } - - [AccessibilityMode(AccessMode.FromQueueOnly)] - T Item { get; set; } - - [AccessibilityMode(AccessMode.FromAnywhere)] - ulong Sequence { get; } - - [AccessibilityMode(AccessMode.FromQueueOnly)] - string Type { get; set; } - - #endregion - - #region Public Methods - - [AccessibilityMode(AccessMode.FromQueueOnly)] - void Delete(); - - [AccessibilityMode(AccessMode.FromQueueOnly)] - void Save(); - - #endregion - } -} diff --git a/src/Couchbase.Lite/API/Query/DataSourceFactory.cs b/src/Couchbase.Lite/API/Query/DataSourceFactory.cs index 51b806aef..a4075399b 100644 --- a/src/Couchbase.Lite/API/Query/DataSourceFactory.cs +++ b/src/Couchbase.Lite/API/Query/DataSourceFactory.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using Couchbase.Lite.DB; +using Couchbase.Lite.Querying; namespace Couchbase.Lite.Query { @@ -19,5 +21,20 @@ public static IDatabaseSource Database(IDatabase database) return new DatabaseSource(db); } + + internal static IQueryable LinqDataSource(IDatabase database, bool prefetch) + where TElement : class, IDocumentModel, new() + { + if (database == null) { + throw new ArgumentNullException(nameof(database)); + } + + var db = database as Database; + if (db == null) { + throw new NotSupportedException("Custom IDatabase not supported"); + } + + return new DatabaseQueryable(db, prefetch); + } } } diff --git a/src/Couchbase.Lite/API/QueryableFactory.cs b/src/Couchbase.Lite/API/QueryableFactory.cs deleted file mode 100644 index ed93cec8e..000000000 --- a/src/Couchbase.Lite/API/QueryableFactory.cs +++ /dev/null @@ -1,58 +0,0 @@ -// -// QueryableFactory.cs -// -// Author: -// Jim Borden -// -// 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 System.Linq; - -using Couchbase.Lite.DB; -using Couchbase.Lite.Querying; - -namespace Couchbase.Lite -{ - /// - /// A factory for creating instances based on a given database - /// - public static class QueryableFactory - { - #region Public Methods - - /// - /// Creates an instance based on the given database - /// - /// The type of element to return from the query - /// The database to operate on - /// The instantiated instance - public static IQueryable MakeQueryable(IDatabase db) where T : class, IDocumentModel, new() - { - return new DatabaseQueryable(db as Database); - } - - #endregion - - #region Internal Methods - - internal static IQueryable MakeDebugQueryable() - { - return new DatabaseDebugQueryable(); - } - - #endregion - } -} diff --git a/src/Couchbase.Lite/Couchbase.Lite.csproj b/src/Couchbase.Lite/Couchbase.Lite.csproj index d48e58ba1..a2c891f58 100644 --- a/src/Couchbase.Lite/Couchbase.Lite.csproj +++ b/src/Couchbase.Lite/Couchbase.Lite.csproj @@ -72,6 +72,7 @@ + diff --git a/src/Couchbase.Lite/Database/Database.cs b/src/Couchbase.Lite/Database/Database.cs index 0b68e10f8..8dd2b03bb 100644 --- a/src/Couchbase.Lite/Database/Database.cs +++ b/src/Couchbase.Lite/Database/Database.cs @@ -30,6 +30,7 @@ using Couchbase.Lite.Query; using Couchbase.Lite.Serialization; using Couchbase.Lite.Support; +using Couchbase.Lite.Sync; using Couchbase.Lite.Util; using LiteCore; using LiteCore.Interop; @@ -69,7 +70,7 @@ internal sealed unsafe class Database : ThreadSafe, IDatabase private LruCache _documents = new LruCache(100); private IJsonSerializer _jsonSerializer; private DatabaseObserver _obs; - private HashSet _unsavedDocuments = new HashSet(); + private readonly HashSet _unsavedDocuments = new HashSet(); private long p_c4db; #endregion @@ -88,12 +89,7 @@ public IConflictResolver ConflictResolver } } - public IDocument this[string id] - { - get { - return GetDocument(id); - } - } + public IDocument this[string id] => GetDocument(id); public string Name { get; } @@ -126,10 +122,8 @@ internal C4Database* c4db internal IJsonSerializer JsonSerializer { - get { return _jsonSerializer ?? (_jsonSerializer = Serializer.CreateDefaultFor(this)); } - set { - _jsonSerializer = value; - } + get => _jsonSerializer ?? (_jsonSerializer = Serializer.CreateDefaultFor(this)); + set => _jsonSerializer = value; } internal SharedStringCache SharedStrings @@ -140,14 +134,14 @@ internal SharedStringCache SharedStrings } } + internal IDictionary Replications { get; } = new Dictionary(); + + internal ICollection ActiveReplications { get; } = new HashSet(); + private C4Database *_c4db { - get { - return (C4Database *)p_c4db; - } - set { - p_c4db = (long)value; - } + get => (C4Database *)p_c4db; + set => p_c4db = (long)value; } #endregion @@ -216,11 +210,6 @@ public void ChangeEncryptionKey(object key) throw new NotImplementedException(); } - public IModeledDocument CreateDocument() where T : class, new() - { - return GetDocument(Misc.CreateGuid(), false); - } - public void CreateIndex(IList expressions, IndexType indexType, IndexOptions options) { AssertSafety(); @@ -238,12 +227,6 @@ public void CreateIndex(IList expressions, IndexType indexType, IndexOptions opt }); } - public IModeledDocument GetDocument(string id) where T : class, new() - { - AssertSafety(); - return GetDocument(id, false); - } - #endregion #region Internal Methods @@ -336,33 +319,6 @@ private void Dispose(bool disposing) } } - [SuppressMessage("ReSharper", "PossibleNullReferenceException", Justification = "GetItem() never returns null")] - private ModeledDocument GetDocument(string docID, bool mustExist) where T : class, new() - { - CheckOpen(); - PerfTimer.StartEvent("GetDocument_Native.c4doc_get"); - var doc = (C4Document*)RetryHandler.RetryIfBusy() - .AllowError((int)LiteCoreError.NotFound, C4ErrorDomain.LiteCoreDomain) - .Execute(err => Native.c4doc_get(_c4db, docID, mustExist, err)); - PerfTimer.StopEvent("GetDocument_Native.c4doc_get"); - - if(doc == null) { - return null; - } - - PerfTimer.StartEvent("GetDocument_NativeRaw.FLValue_FromTrustedData"); - var value = NativeRaw.FLValue_FromTrustedData((FLSlice)doc->selectedRev.body); - PerfTimer.StopEvent("GetDocument_NativeRaw.FLValue_FromTrustedData"); - PerfTimer.StartEvent("GetDocument_CreateModeledDocument"); - var poolObject = new ModeledDocument(this, doc); - if(value != null) { - JsonSerializer.Populate(poolObject.Item, value); - } - - PerfTimer.StopEvent("GetDocument_CreateModeledDocument"); - return poolObject; - } - private Document GetDocument(string docID, bool mustExist) { CheckOpen(); @@ -562,6 +518,47 @@ public bool InBatch(Func a) return success; } + public IReplication CreateReplication(Uri remoteUrl) + { + if (remoteUrl == null) { + throw new ArgumentNullException(nameof(remoteUrl)); + } + + var repl = Replications.Get(remoteUrl); + if (repl == null) { + repl = new Replication(this, remoteUrl, null) { + ActionQueue = ActionQueue, + CheckThreadSafety = CheckThreadSafety + }; + Replications[remoteUrl] = repl; + } + + return repl; + } + + public IReplication CreateReplication(IDatabase otherDatabase) + { + if (otherDatabase == null) { + throw new ArgumentNullException(nameof(otherDatabase)); + } + + if (otherDatabase == this) { + throw new InvalidOperationException("Source and target database are the same"); + } + + var key = new Uri(otherDatabase.Path); + var repl = Replications.Get(key); + if (repl == null) { + repl = new Replication(this, null, otherDatabase) { + ActionQueue = ActionQueue, + CheckThreadSafety = CheckThreadSafety + }; + Replications[key] = repl; + } + + return repl; + } + #endregion #region IDisposable diff --git a/src/Couchbase.Lite/Database/Document.cs b/src/Couchbase.Lite/Database/Document.cs index 23c49fabf..96dddf6b2 100644 --- a/src/Couchbase.Lite/Database/Document.cs +++ b/src/Couchbase.Lite/Database/Document.cs @@ -1,24 +1,23 @@ -// -// Document.cs -// -// Author: -// Jim Borden -// -// 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. -// - +// +// Document.cs +// +// Author: +// Jim Borden +// +// 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 System; using System.Collections; using System.Collections.Generic; @@ -45,9 +44,9 @@ internal sealed unsafe class Document : PropertyContainer, IDocument private readonly C4Database* _c4Db; private readonly Database _database; + public event EventHandler Changed; public event EventHandler Saved; - public event EventHandler Changed; private C4Document* _c4Doc; private IConflictResolver _conflictResolver; @@ -99,28 +98,28 @@ public ulong Sequence } } - private IConflictResolver EffectiveConflictResolver + internal override bool HasChanges { get { - return ConflictResolver ?? Database.ConflictResolver; + return base.HasChanges; + } + set { + base.HasChanges = value; + _database.SetHasUnsavedChanges(this, value); } } - private uint Generation + private IConflictResolver EffectiveConflictResolver { get { - return NativeRaw.c4rev_getGeneration(_c4Doc->revID); + return ConflictResolver ?? Database.ConflictResolver; } } - internal override bool HasChanges + private uint Generation { get { - return base.HasChanges; - } - set { - base.HasChanges = value; - _database.SetHasUnsavedChanges(this, value); + return NativeRaw.c4rev_getGeneration(_c4Doc->revID); } } @@ -137,6 +136,15 @@ internal Document(Database db, string docID, bool mustExist) LoadDoc(mustExist); } + internal Document(Database db, C4Document* doc) + : base(db.SharedStrings) + { + _database = db; + Id = doc->docID.CreateString(); + _c4Db = db.c4db; + SetC4Doc(doc); + } + ~Document() { Dispose(false); @@ -163,16 +171,6 @@ internal void ChangedExternally() } } - internal override void MarkChangedKey(string key) - { - base.MarkChangedKey(key); - - ActionQueue.DispatchAsync(() => - { - Changed?.Invoke(this, null); - }); - } - internal void PostChangedNotifications(bool external) { ActionQueue.DispatchAsync(() => @@ -290,7 +288,7 @@ private void Merge(IConflictResolver resolver, bool deletion) } } - private void Save(IConflictResolver resolver, bool deletion) + private void Save(IConflictResolver resolver, bool deletion, IDocumentModel model = null) { if(!HasChanges && !deletion && Exists) { return; @@ -301,7 +299,7 @@ private void Save(IConflictResolver resolver, bool deletion) var success = Database.InBatch(() => { var tmp = default(C4Document*); - SaveInto(&tmp, deletion); + SaveInto(&tmp, deletion, model); if (tmp == null) { Merge(resolver, deletion); if (!HasChanges) { @@ -309,7 +307,7 @@ private void Save(IConflictResolver resolver, bool deletion) return false; } - SaveInto(&tmp, deletion); + SaveInto(&tmp, deletion, model); if (tmp == null) { throw new CouchbaseLiteException("Conflict still occuring after resolution", StatusCode.DbError); } @@ -338,7 +336,7 @@ private void Save(IConflictResolver resolver, bool deletion) } [SuppressMessage("ReSharper", "AccessToDisposedClosure", Justification = "The closure is executed synchronously")] - private void SaveInto(C4Document** outDoc, bool deletion) + private void SaveInto(C4Document** outDoc, bool deletion, IDocumentModel model = null) { //TODO: Need to be able to save a deletion that has properties on it var propertiesToSave = deletion ? null : Properties; @@ -358,7 +356,10 @@ private void SaveInto(C4Document** outDoc, bool deletion) } var body = new FLSliceResult(); - if(propertiesToSave?.Count > 0) { + if (model != null) { + body = _database.JsonSerializer.Serialize(model); + put.body = body; + } else if (propertiesToSave?.Count > 0) { body = _database.JsonSerializer.Serialize(propertiesToSave); put.body = body; } @@ -408,6 +409,16 @@ protected internal override IBlob CreateBlob(IDictionary propert }; } + internal override void MarkChangedKey(string key) + { + base.MarkChangedKey(key); + + ActionQueue.DispatchAsync(() => + { + Changed?.Invoke(this, null); + }); + } + public override string ToString() { var id = new SecureLogString(Id, LogMessageSensitivity.PotentiallyInsecure); @@ -471,5 +482,27 @@ public void Save() } #endregion + + #region IModellable + + public T AsModel() where T : IDocumentModel, new() + { + FLValue* value = NativeRaw.FLValue_FromTrustedData((FLSlice)_c4Doc->selectedRev.body); + var retVal = _database.JsonSerializer.Deserialize(value); + retVal.Document = this; + return retVal; + } + + public void Set(IDocumentModel model) + { + if (model == null) { + throw new ArgumentNullException(nameof(model)); + } + + HasChanges = true; + Save(EffectiveConflictResolver, false, model); + } + + #endregion } } diff --git a/src/Couchbase.Lite/Database/DocumentMetadata.cs b/src/Couchbase.Lite/Database/DocumentMetadata.cs deleted file mode 100644 index 670a511a8..000000000 --- a/src/Couchbase.Lite/Database/DocumentMetadata.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -// DocumentMetadata.cs -// -// Author: -// Jim Borden -// -// 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. -// - - -namespace Couchbase.Lite.DB -{ - internal sealed class DocumentMetadata : IDocumentMetadata - { - #region Properties - - public string Id { get; } - - public bool IsDeleted { get; } - - public ulong Sequence { get; } - - public string Type { get; set; } - - #endregion - - #region Constructors - - internal DocumentMetadata(string id, string type, bool isDeleted, ulong sequence) - { - Id = id; - Type = type; - IsDeleted = isDeleted; - Sequence = sequence; - } - - #endregion - } -} diff --git a/src/Couchbase.Lite/Database/ModeledDocument.cs b/src/Couchbase.Lite/Database/ModeledDocument.cs deleted file mode 100644 index 37f1db2fc..000000000 --- a/src/Couchbase.Lite/Database/ModeledDocument.cs +++ /dev/null @@ -1,208 +0,0 @@ -// -// ModeledDocument.cs -// -// Author: -// Jim Borden -// -// 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 System; - -using Couchbase.Lite.Support; -using LiteCore; -using LiteCore.Interop; -using LiteCore.Util; - -namespace Couchbase.Lite.DB -{ - internal sealed unsafe class ModeledDocument : ThreadSafe, IModeledDocument where T : class, new() - { - #region Variables - - private Database _db; - - private C4Document* _document; - private bool _isDeleted; - private T _item; - private string _type; - - #endregion - - #region Properties - - public IDatabase Db - { - get { - return _db; - } - } - - public string Id { get; private set; } - - public bool IsDeleted - { - get { - AssertSafety(); - return _isDeleted; - } - private set { - AssertSafety(); - _isDeleted = value; - } - } - - public T Item - { - get { - AssertSafety(); - return _item; - } - set { - AssertSafety(); - _item = value; - } - } - - public ulong Sequence { get; private set; } - - public string Type - { - get { - AssertSafety(); - return _type; - } - set { - AssertSafety(); - _type = value; - } - } - - #endregion - - #region Constructors - - internal ModeledDocument(Database db, C4Document* native) - : this(Activator.CreateInstance(), db, native) - { - - } - - internal ModeledDocument(T item, Database db, C4Document* native) - { - _item = item; - Reconstruct(db, native); - } - - #endregion - - #region Internal Methods - - internal void Reconstruct(Database db, C4Document* native) - { - _db = db; - Id = native->docID.CreateString(); - Sequence = native->sequence; - _document = native; - } - - #endregion - - #region Private Methods - - private void Save(IConflictResolver conflictResolver, bool deletion) - { - C4Document* newDoc = null; - PerfTimer.StartEvent("Save_BeginInBatch"); - var success = Db.InBatch(() => { - PerfTimer.StopEvent("Save_BeginInBatch"); - var put = new C4DocPutRequest { - docID = _document->docID, - history = &_document->revID, - historyCount = 1, - save = true - }; - - if(deletion) { - put.revFlags = C4RevisionFlags.Deleted; - } - - var body = default(FLSliceResult); - if(!deletion) { - PerfTimer.StartEvent("Save_Serialize"); - body = _db.JsonSerializer.Serialize(Item); - PerfTimer.StopEvent("Save_Serialize"); - put.body = body; - } - - try { - using(var type = new C4String(Type)) { - PerfTimer.StartEvent("Save_c4doc_put"); - newDoc = (C4Document*)LiteCoreBridge.Check(err => { - var localPut = put; - localPut.docType = type.AsC4Slice(); - return Native.c4doc_put(_db.c4db, &localPut, null, err); - }); - PerfTimer.StopEvent("Save_c4doc_put"); - } - } finally { - body.Dispose(); - } - - return true; - }); - - if(!success) { - Native.c4doc_free(newDoc); - return; - } - - PerfTimer.StartEvent("Save_c4doc_free"); - Native.c4doc_free(_document); - PerfTimer.StopEvent("Save_c4doc_free"); - _document = newDoc; - if(deletion) { - IsDeleted = true; - } - } - - #endregion - - #region IDisposable - - public void Dispose() - { - Native.c4doc_free(_document); - _document = null; - } - - #endregion - - #region IModeledDocument - - public void Delete() - { - AssertSafety(); - Save(null, true); - } - - public void Save() - { - AssertSafety(); - Save(null, false); - } - - #endregion - } -} diff --git a/src/Couchbase.Lite/Linq/LiteCoreQueryExecutor.cs b/src/Couchbase.Lite/Linq/LiteCoreQueryExecutor.cs index 76b82d95d..bf4bd7b33 100644 --- a/src/Couchbase.Lite/Linq/LiteCoreQueryExecutor.cs +++ b/src/Couchbase.Lite/Linq/LiteCoreQueryExecutor.cs @@ -35,14 +35,16 @@ internal unsafe class LiteCoreQueryExecutor : IQueryExecutor #region Variables private readonly Database _db; + private readonly bool _prefetch; #endregion #region Constructors - internal LiteCoreQueryExecutor(Database db) + internal LiteCoreQueryExecutor(Database db, bool prefetch) { _db = db; + _prefetch = prefetch; } #endregion @@ -57,7 +59,7 @@ public IEnumerable ExecuteCollection(QueryModel queryModel) return _db.ActionQueue.DispatchSync(() => { var queryObj = (C4Query*)LiteCoreBridge.Check(err => Native.c4query_new(_db.c4db, query, err)); - return new LinqQueryEnumerable(_db, queryObj, C4QueryOptions.Default, null); + return new LinqQueryEnumerable(_db, queryObj, C4QueryOptions.Default, null, _prefetch); }); } diff --git a/src/Couchbase.Lite/Query/DatabaseQueryable.cs b/src/Couchbase.Lite/Query/DatabaseQueryable.cs index 5674b0fe2..14bba16d2 100644 --- a/src/Couchbase.Lite/Query/DatabaseQueryable.cs +++ b/src/Couchbase.Lite/Query/DatabaseQueryable.cs @@ -23,6 +23,7 @@ using Couchbase.Lite.DB; using Couchbase.Lite.Linq; +using Couchbase.Lite.Query; using Remotion.Linq; using Remotion.Linq.Parsing.Structure; @@ -32,8 +33,8 @@ namespace Couchbase.Lite.Querying { #region Constructors - public DatabaseQueryable(Database db) - : base(QueryParser.CreateDefault(), new LiteCoreQueryExecutor(db)) + public DatabaseQueryable(Database db, bool prefetch) + : base(QueryParser.CreateDefault(), new LiteCoreQueryExecutor(db, prefetch)) { } diff --git a/src/Couchbase.Lite/Query/QueryEnumerator.cs b/src/Couchbase.Lite/Query/QueryEnumerator.cs index fa1e828b5..991dc43d3 100644 --- a/src/Couchbase.Lite/Query/QueryEnumerator.cs +++ b/src/Couchbase.Lite/Query/QueryEnumerator.cs @@ -96,12 +96,14 @@ public override IEnumerator GetEnumerator() internal sealed unsafe class LinqQueryEnumerable : QueryEnumerable { + private readonly bool _prefetch; + #region Constructors - internal LinqQueryEnumerable(Database db, C4Query* query, C4QueryOptions options, string encodedParameters) + internal LinqQueryEnumerable(Database db, C4Query* query, C4QueryOptions options, string encodedParameters, bool prefetch) : base(db, query, options, encodedParameters) { - + _prefetch = prefetch; } #endregion @@ -110,7 +112,7 @@ internal LinqQueryEnumerable(Database db, C4Query* query, C4QueryOptions options public override IEnumerator GetEnumerator() { - return new LinqQueryEnumerator(_db, _query, _options, _encodedParameters); + return new LinqQueryEnumerator(_db, _query, _options, _encodedParameters, _prefetch); } #endregion @@ -242,18 +244,19 @@ protected override void Dispose(bool finalizing) internal sealed unsafe class LinqQueryEnumerator : QueryEnumerator { + private readonly bool _prefetch; + #region Constructors - public LinqQueryEnumerator(Database db, C4Query* query, C4QueryOptions options, string encodedParameters) + public LinqQueryEnumerator(Database db, C4Query* query, C4QueryOptions options, string encodedParameters, bool prefetch) : base(db, query, options, encodedParameters) { - + _prefetch = prefetch; } #endregion #region Overrides -#warning fix DocumentMetadata type [SuppressMessage("ReSharper", "PossibleNullReferenceException", Justification = "Current will always be IDocumentModel")] protected override void SetCurrent(C4QueryEnumerator* enumerator) @@ -261,10 +264,15 @@ protected override void SetCurrent(C4QueryEnumerator* enumerator) var doc = default(C4Document*); _db.ActionQueue.DispatchSync(() => doc = (C4Document*)LiteCoreBridge.Check(err => Native.c4doc_getBySequence(_db.c4db, enumerator->docSequence, err))); try { - FLValue* value = NativeRaw.FLValue_FromTrustedData((FLSlice)doc->selectedRev.body); - Current = _db.JsonSerializer.Deserialize(value); + if (_prefetch) { + FLValue* value = NativeRaw.FLValue_FromTrustedData((FLSlice) doc->selectedRev.body); + Current = _db.JsonSerializer.Deserialize(value); + } else { + Current = Activator.CreateInstance(); + } + var idm = Current as IDocumentModel; - idm.Metadata = new DocumentMetadata(doc->docID.CreateString(), null, doc->flags.HasFlag(C4DocumentFlags.Deleted), doc->sequence); + idm.Document = new Document(_db, doc); } finally { Native.c4doc_free(doc); } diff --git a/src/Couchbase.Lite/Serialization/CouchbaseLiteContractResolver.cs b/src/Couchbase.Lite/Serialization/CouchbaseLiteContractResolver.cs index cb0abdf5a..bbbb6c432 100644 --- a/src/Couchbase.Lite/Serialization/CouchbaseLiteContractResolver.cs +++ b/src/Couchbase.Lite/Serialization/CouchbaseLiteContractResolver.cs @@ -40,7 +40,7 @@ protected override IList CreateProperties(Type type, MemberSeriali return baseProperties; } - return baseProperties.Where(x => x.PropertyName != "Metadata").ToList(); + return baseProperties.Where(x => x.PropertyName != "Document").ToList(); } #endregion diff --git a/vendor/couchbase-lite-core b/vendor/couchbase-lite-core index c41b27bd6..d586e02fa 160000 --- a/vendor/couchbase-lite-core +++ b/vendor/couchbase-lite-core @@ -1 +1 @@ -Subproject commit c41b27bd695755c762b52b500476a93012080145 +Subproject commit d586e02fabbd77a2dea1b7bb14fb2f414dcb1f72