public ICollection <BlobKey> FindAllAttachmentKeys() { var keys = new HashSet <BlobKey>(); var options = C4EnumeratorOptions.DEFAULT; options.flags &= ~C4EnumeratorFlags.IncludeBodies; options.flags |= C4EnumeratorFlags.IncludeDeleted; var e = new CBForestDocEnumerator(Forest, null, null, options); foreach (var next in e) { var docInfo = next.DocumentInfo; if (!docInfo->HasAttachments || (docInfo->IsDeleted && !docInfo->IsConflicted)) { continue; } var doc = next.GetDocument(); // Since db is assumed to have just been compacted, we know that non-current revisions // won't have any bodies. So only scan the current revs. do { if (doc->selectedRev.IsActive && doc->selectedRev.HasAttachments) { ForestDBBridge.Check(err => Native.c4doc_loadRevisionBody(doc, err)); var body = doc->selectedRev.body; if (body.size > 0) { var rev = Manager.GetObjectMapper().ReadValue <IDictionary <string, object> >(body); foreach (var entry in rev.Get("_attachments").AsDictionary <string, IDictionary <string, object> >()) { try { var key = new BlobKey(entry.Value.GetCast <string>("digest")); keys.Add(key); } catch (Exception) { Log.W(TAG, "Invalid digest {0}; skipping", entry.Value.GetCast <string>("digest")); } } } } } while(Native.c4doc_selectNextLeafRevision(doc, true, true, null)); } return(keys); }
private CBForestDocEnumerator GetDocEnumerator(QueryOptions options, out List <string> remainingIDs) { var forestOps = options.AsC4EnumeratorOptions(); var enumerator = default(CBForestDocEnumerator); remainingIDs = new List <string>(); if (options.Keys != null) { try { remainingIDs = options.Keys.Cast <string>().ToList(); enumerator = new CBForestDocEnumerator(Forest, remainingIDs.ToArray(), forestOps); } catch (InvalidCastException) { Log.E(TAG, "options.keys must contain strings"); throw; } } else { enumerator = new CBForestDocEnumerator(Forest, options.StartKey as string, options.EndKey as string, forestOps); } return(enumerator); }
public ICollection<BlobKey> FindAllAttachmentKeys() { var keys = new HashSet<BlobKey>(); var options = C4EnumeratorOptions.DEFAULT; options.flags &= ~C4EnumeratorFlags.IncludeBodies; options.flags |= C4EnumeratorFlags.IncludeDeleted; var e = new CBForestDocEnumerator(Forest, null, null, options); foreach(var next in e) { var doc = next.Document; if (!doc->HasAttachments || (doc->IsDeleted && !doc->IsConflicted)) { continue; } // Since db is assumed to have just been compacted, we know that non-current revisions // won't have any bodies. So only scan the current revs. do { if(doc->selectedRev.IsActive && doc->selectedRev.HasAttachments) { ForestDBBridge.Check(err => Native.c4doc_loadRevisionBody(doc, err)); var body = doc->selectedRev.body; if(body.size > 0) { var rev = Manager.GetObjectMapper().ReadValue<IDictionary<string, object>>(body); foreach(var entry in rev.Get("_attachments").AsDictionary<string, IDictionary<string, object>>()) { try { var key = new BlobKey(entry.Value.GetCast<string>("digest")); keys.Add(key); } catch(Exception){ Log.W(TAG, "Invalid digest {0}; skipping", entry.Value.GetCast<string>("digest")); } } } } } while(Native.c4doc_selectNextLeafRevision(doc, true, true, null)); } return keys; }
public RevisionList ChangesSince(Int64 lastSequence, ChangesOptions options, RevisionFilter filter) { // http://wiki.apache.org/couchdb/HTTP_database_API#Changes // Translate options to ForestDB: if (options.Descending) { // https://github.com/couchbase/couchbase-lite-ios/issues/641 throw new CouchbaseLiteException(StatusCode.NotImplemented); } var forestOps = C4EnumeratorOptions.DEFAULT; forestOps.flags |= C4EnumeratorFlags.IncludeDeleted | C4EnumeratorFlags.IncludeNonConflicted; if (options.IncludeDocs || options.IncludeConflicts || filter != null) { forestOps.flags |= C4EnumeratorFlags.IncludeBodies; } var changes = new RevisionList(); var e = new CBForestDocEnumerator(Forest, lastSequence, forestOps); foreach (var next in e) { var doc = next.Document; var revs = default(IEnumerable<RevisionInternal>); if (options.IncludeConflicts) { using (var enumerator = new CBForestHistoryEnumerator(doc, true, false)) { var includeBody = forestOps.flags.HasFlag(C4EnumeratorFlags.IncludeBodies); revs = enumerator.Select(x => new RevisionInternal(x.Document, includeBody)).ToList(); } } else { revs = new List<RevisionInternal> { new RevisionInternal(doc, forestOps.flags.HasFlag(C4EnumeratorFlags.IncludeBodies)) }; } foreach (var rev in revs) { Debug.Assert(rev != null); if (filter == null || filter(rev)) { if (!options.IncludeDocs) { rev.SetBody(null); } if(filter == null || filter(rev)) { changes.Add(rev); } } } } if (options.SortBySequence) { changes.SortBySequence(!options.Descending); changes.Limit(options.Limit); } return changes; }
private CBForestDocEnumerator GetDocEnumerator(QueryOptions options, out List<string> remainingIDs) { var forestOps = options.AsC4EnumeratorOptions(); var enumerator = default(CBForestDocEnumerator); remainingIDs = new List<string>(); if(options.Keys != null) { try { remainingIDs = options.Keys.Cast<string>().ToList(); enumerator = new CBForestDocEnumerator(Forest, remainingIDs.ToArray(), forestOps); } catch(InvalidCastException) { Log.E(TAG, "options.keys must contain strings"); throw; } } else { enumerator = new CBForestDocEnumerator(Forest, options.StartKey as string, options.EndKey as string, forestOps); } return enumerator; }
public RevisionList ChangesSince(Int64 lastSequence, ChangesOptions options, RevisionFilter filter) { // http://wiki.apache.org/couchdb/HTTP_database_API#Changes // Translate options to ForestDB: if (options.Descending) { // https://github.com/couchbase/couchbase-lite-ios/issues/641 throw new CouchbaseLiteException(StatusCode.NotImplemented); } var forestOps = C4EnumeratorOptions.DEFAULT; forestOps.flags |= C4EnumeratorFlags.IncludeDeleted | C4EnumeratorFlags.IncludeNonConflicted; if (options.IncludeDocs || options.IncludeConflicts || filter != null) { forestOps.flags |= C4EnumeratorFlags.IncludeBodies; } var changes = new RevisionList(); var e = new CBForestDocEnumerator(Forest, lastSequence, forestOps); foreach (var next in e) { var revs = default(IEnumerable <RevisionInternal>); if (options.IncludeConflicts) { using (var enumerator = new CBForestHistoryEnumerator(next.GetDocument(), true, false)) { var includeBody = forestOps.flags.HasFlag(C4EnumeratorFlags.IncludeBodies); revs = enumerator.Select(x => new RevisionInternal(x.GetDocument(), includeBody)).ToList(); } } else { revs = new List <RevisionInternal> { new RevisionInternal(next.GetDocument(), forestOps.flags.HasFlag(C4EnumeratorFlags.IncludeBodies)) }; } foreach (var rev in revs) { Debug.Assert(rev != null); if (filter == null || filter(rev)) { if (!options.IncludeDocs) { rev.SetBody(null); } if (filter == null || filter(rev)) { changes.Add(rev); } } } } if (options.SortBySequence) { changes.SortBySequence(!options.Descending); changes.Limit(options.Limit); } return(changes); }
public bool UpdateIndexes(IEnumerable <IViewStore> views) { Log.To.Query.V(Tag, "Checking indexes of ({0}) for {1}", ViewNames(views), Name); // Creates an array of tuples -> [[view1, view1 last sequence, view1 native handle], // [view2, view2 last sequence, view2 native handle], ...] var viewsArray = views.Where(x => { var viewDelegate = x.Delegate; if (viewDelegate == null || viewDelegate.Map == null) { Log.To.Query.V(Tag, " {0} has no map block; skipping it", x.Name); return(false); } return(true); }).Cast <ForestDBViewStore> ().ToArray(); var nativeViews = new C4View *[viewsArray.Length]; for (int i = 0; i < viewsArray.Length; i++) { nativeViews[i] = viewsArray[i].IndexDB; } var indexer = (C4Indexer *)ForestDBBridge.Check(err => Native.c4indexer_begin(_dbStorage.Forest, nativeViews, err)); var enumerator = new CBForestDocEnumerator(indexer); var commit = false; try { foreach (var next in enumerator) { var seq = next.Sequence; for (int i = 0; i < viewsArray.Length; i++) { var info = viewsArray [i]; if (seq <= info.LastSequenceIndexed) { continue; // This view has already indexed this sequence } var rev = new ForestRevisionInternal(next, true); var keys = new List <object>(); var values = new List <string>(); var conflicts = default(List <string>); foreach (var leaf in new CBForestHistoryEnumerator(_dbStorage.Forest, next.Sequence, true)) { if (leaf.SelectedRev.revID.Equals(leaf.CurrentRevID)) { continue; } if (leaf.IsDeleted) { break; } if (conflicts == null) { conflicts = new List <string>(); } conflicts.Add((string)leaf.SelectedRev.revID); } if (conflicts != null) { rev.SetPropertyForKey("_conflicts", conflicts); } try { var props = rev.GetProperties(); info.Delegate.Map(props, (key, value) => { if (key == null) { Log.To.Query.W(Tag, "Emit function called with a null key; ignoring"); return; } keys.Add(key); if (props == value) { values.Add("*"); } else { values.Add(Manager.GetObjectMapper().WriteValueAsString(value)); } }); } catch (Exception e) { Log.To.Query.W(Tag, String.Format("Exception thrown in map function of {0}, continuing", info.Name), e); continue; } WithC4Keys(keys.ToArray(), true, c4keys => ForestDBBridge.Check(err => Native.c4indexer_emit(indexer, next.GetDocument(), (uint)i, c4keys, values.ToArray(), err)) ); } } commit = true; } catch (Exception e) { Log.To.Query.W(Tag, "Error updates indexes, returning false", e); return(false); } finally { ForestDBBridge.Check(err => Native.c4indexer_end(indexer, commit, err)); } return(true); }
public bool UpdateIndexes(IEnumerable <IViewStore> views) { Log.D(TAG, "Checking indexes of ({0}) for {1}", ViewNames(views), Name); // Creates an array of tuples -> [[view1, view1 last sequence, view1 native handle], // [view2, view2 last sequence, view2 native handle], ...] var viewsArray = views.Cast <ForestDBViewStore>().ToArray(); var viewInfo = viewsArray.Select(x => Tuple.Create(x, x.LastSequenceIndexed)).ToArray(); var nativeViews = new C4View *[viewsArray.Length]; for (int i = 0; i < viewsArray.Length; i++) { nativeViews[i] = viewsArray[i]._indexDB; } var indexer = (C4Indexer *)ForestDBBridge.Check(err => Native.c4indexer_begin(_dbStorage.Forest, nativeViews, err)); var enumerator = new CBForestDocEnumerator(indexer); var commit = false; try { foreach (var next in enumerator) { var seq = next.SelectedRev.sequence; for (int i = 0; i < viewInfo.Length; i++) { var info = viewInfo[i]; if (seq <= (ulong)info.Item2) { continue; // This view has already indexed this sequence } var viewDelegate = info.Item1.Delegate; if (viewDelegate == null || viewDelegate.Map == null) { Log.V(TAG, " {0} has no map block; skipping it", info.Item1.Name); continue; } var rev = new RevisionInternal(next, true); var keys = new List <object>(); var values = new List <string>(); try { viewDelegate.Map(rev.GetProperties(), (key, value) => { keys.Add(key); values.Add(Manager.GetObjectMapper().WriteValueAsString(value)); }); } catch (Exception e) { Log.W(TAG, String.Format("Exception thrown in map function of {0}", info.Item1.Name), e); continue; } WithC4Keys(keys.ToArray(), true, c4keys => ForestDBBridge.Check(err => Native.c4indexer_emit(indexer, next.Document, (uint)i, c4keys, values.ToArray(), err)) ); } } commit = true; } catch (Exception e) { Log.W(TAG, "Error updates indexes", e); } finally { ForestDBBridge.Check(err => Native.c4indexer_end(indexer, commit, err)); } return(true); }
private CBForestDocEnumerator GetDocEnumerator(QueryOptions options, out List<string> remainingIDs) { var forestOps = options.AsC4EnumeratorOptions(); var enumerator = default(CBForestDocEnumerator); remainingIDs = new List<string>(); if(options.Keys != null) { try { remainingIDs = options.Keys.Cast<string>().ToList(); enumerator = new CBForestDocEnumerator(Forest, remainingIDs.ToArray(), forestOps); } catch(InvalidCastException) { Log.To.Database.E(TAG, "options.keys must contain strings"); throw; } } else { string startKey, endKey; if(options.Descending) { startKey = Misc.KeyForPrefixMatch(options.StartKey, options.PrefixMatchLevel) as string; endKey = options.EndKey as string; } else { startKey = options.StartKey as string; endKey = Misc.KeyForPrefixMatch(options.EndKey, options.PrefixMatchLevel) as string; } enumerator = new CBForestDocEnumerator(Forest, startKey, endKey, forestOps); } return enumerator; }