// Apply the options in the URL query to the specified revision and create a new revision object internal static RevisionInternal ApplyOptions(DocumentContentOptions options, RevisionInternal rev, ICouchbaseListenerContext context, Database db, Status outStatus) { if ((options & (DocumentContentOptions.IncludeRevs | DocumentContentOptions.IncludeRevsInfo | DocumentContentOptions.IncludeConflicts | DocumentContentOptions.IncludeAttachments | DocumentContentOptions.IncludeLocalSeq) | DocumentContentOptions.IncludeExpiration) != 0) { var dst = rev.GetProperties() ?? new Dictionary <string, object>(); if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq)) { dst["_local_seq"] = rev.Sequence; } if (options.HasFlag(DocumentContentOptions.IncludeRevs)) { var revs = db.GetRevisionHistory(rev, null); dst["_revisions"] = TreeRevisionID.MakeRevisionHistoryDict(revs); } if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo)) { dst["_revs_info"] = db.GetRevisionHistory(rev, null).Select(x => { string status = "available"; var ancestor = db.GetDocument(rev.DocID, x, true); if (ancestor.Deleted) { status = "deleted"; } else if (ancestor.Missing) { status = "missing"; } return(new Dictionary <string, object> { { "rev", x.ToString() }, { "status", status } }); }); } if (options.HasFlag(DocumentContentOptions.IncludeConflicts)) { RevisionList revs = db.Storage.GetAllDocumentRevisions(rev.DocID, true, false); if (revs.Count > 1) { dst["_conflicts"] = from r in revs where !r.Equals(rev) && !r.Deleted select r.RevID.ToString(); } } if (options.HasFlag(DocumentContentOptions.IncludeExpiration)) { var expirationTime = db.Storage?.GetDocumentExpiration(rev.DocID); if (expirationTime.HasValue) { dst["_exp"] = expirationTime; } } RevisionInternal nuRev = new RevisionInternal(dst); if (options.HasFlag(DocumentContentOptions.IncludeAttachments)) { bool attEncodingInfo = context != null && context.GetQueryParam <bool>("att_encoding_info", bool.TryParse, false); db.ExpandAttachments(nuRev, 0, false, !attEncodingInfo); } rev = nuRev; } return(rev); }
public virtual void SetContentOptions(DocumentContentOptions contentOptions ) { this.contentOptions = contentOptions; }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> internal RevisionInternal LoadRevisionBody(RevisionInternal rev, DocumentContentOptions contentOptions) { if (rev.GetBody() != null && contentOptions == DocumentContentOptions.None && rev.GetSequence() != 0) { return rev; } if ((rev.GetDocId() == null) || (rev.GetRevId() == null)) { Log.E(Database.Tag, "Error loading revision body"); throw new CouchbaseLiteException(StatusCode.PreconditionFailed); } Cursor cursor = null; var result = new Status(StatusCode.NotFound); try { // TODO: on ios this query is: // TODO: "SELECT sequence, json FROM revs WHERE doc_id=@ AND revid=@ LIMIT 1" var sql = "SELECT sequence, json FROM revs, docs WHERE revid=? AND docs.docid=? AND revs.doc_id=docs.doc_id LIMIT 1"; var args = new [] { rev.GetRevId(), rev.GetDocId() }; cursor = StorageEngine.RawQuery(sql, CommandBehavior.SequentialAccess, args); if (cursor.MoveToNext()) { result.SetCode(StatusCode.Ok); rev.SetSequence(cursor.GetLong(0)); ExpandStoredJSONIntoRevisionWithAttachments(cursor.GetBlob(1), rev, contentOptions); } } catch (SQLException e) { Log.E(Tag, "Error loading revision body", e); throw new CouchbaseLiteException(StatusCode.InternalServerError); } finally { if (cursor != null) { cursor.Close(); } } if (result.GetCode() == StatusCode.NotFound) { throw new CouchbaseLiteException(result.GetCode()); } return rev; }
/// <summary> /// Returns document by the specified docid from the specified db. Unless you request a /// specific revision, the latest revision of the document will always be returned. /// </summary> /// <returns>The response state for further HTTP processing</returns> /// <param name="context">The context of the Couchbase Lite HTTP request</param> /// <remarks> /// http://docs.couchdb.org/en/latest/api/document/common.html#get--db-docid /// </remarks> public static ICouchbaseResponseState GetDocument(ICouchbaseListenerContext context) { return(DatabaseMethods.PerformLogicWithDatabase(context, true, db => { var response = context.CreateResponse(); string docId = context.DocumentName; bool isLocalDoc = docId.StartsWith("_local", StringComparison.InvariantCulture); DocumentContentOptions options = context.ContentOptions; string openRevsParam = context.GetQueryParam("open_revs"); bool mustSendJson = context.ExplicitlyAcceptsType("application/json"); Status status = new Status(); if (openRevsParam == null || isLocalDoc) { //Regular GET: var revId = context.GetQueryParam("rev").AsRevID(); //often null RevisionInternal rev; bool includeAttachments = false, sendMultipart = false; if (isLocalDoc) { rev = db.Storage.GetLocalDocument(docId, revId); } else { includeAttachments = options.HasFlag(DocumentContentOptions.IncludeAttachments); if (includeAttachments) { sendMultipart = !mustSendJson; options &= ~DocumentContentOptions.IncludeAttachments; } rev = db.GetDocument(docId, revId, true, status); if (rev != null) { rev = ApplyOptions(options, rev, context, db, status); } if (rev == null) { response.StatusReason = status.Code == StatusCode.Deleted ? "deleted" : "missing"; response.InternalStatus = StatusCode.NotFound; return response; } } if (rev == null) { response.InternalStatus = StatusCode.NotFound; return response; } if (context.CacheWithEtag(rev.RevID?.ToString())) { response.InternalStatus = StatusCode.NotModified; return response; } if (!isLocalDoc && includeAttachments) { int minRevPos = 1; var attsSince = context.GetJsonQueryParam("atts_since")?.AsList <string>()?.AsRevIDs(); var ancestorId = db.Storage.FindCommonAncestor(rev, attsSince); if (ancestorId != null) { minRevPos = ancestorId.Generation + 1; } bool attEncodingInfo = context.GetQueryParam <bool>("att_encoding_info", bool.TryParse, false); db.ExpandAttachments(rev, minRevPos, sendMultipart, attEncodingInfo); } if (sendMultipart) { response.MultipartWriter = MultipartWriterForRev(db, rev, "multipart/related"); } else { response.JsonBody = rev.GetBody(); } } else { // open_revs query: IList <IDictionary <string, object> > result; if (openRevsParam.Equals("all")) { // ?open_revs=all returns all current/leaf revisions: bool includeDeleted = context.GetQueryParam <bool>("include_deleted", bool.TryParse, false); RevisionList allRevs = db.Storage.GetAllDocumentRevisions(docId, true, includeDeleted); result = new List <IDictionary <string, object> >(); foreach (var rev in allRevs) { if (!includeDeleted && rev.Deleted) { continue; } var loadedRev = db.RevisionByLoadingBody(rev, status); if (loadedRev != null) { ApplyOptions(options, loadedRev, context, db, status); } if (loadedRev != null) { result.Add(new Dictionary <string, object> { { "ok", loadedRev.GetProperties() } }); } else if (status.Code <= StatusCode.InternalServerError) { result.Add(new Dictionary <string, object> { { "missing", rev.RevID } }); } else { response.InternalStatus = status.Code; return response; } } } else { // ?open_revs=[...] returns an array of specific revisions of the document: var openRevs = context.GetJsonQueryParam("open_revs").AsList <object>(); if (openRevs == null) { response.InternalStatus = StatusCode.BadParam; return response; } result = new List <IDictionary <string, object> >(); foreach (var revIDObj in openRevs) { var revID = revIDObj as string; if (revID == null) { response.InternalStatus = StatusCode.BadId; return response; } var rev = db.GetDocument(docId, revID.AsRevID(), true); if (rev != null) { rev = ApplyOptions(options, rev, context, db, status); } if (rev != null) { result.Add(new Dictionary <string, object> { { "ok", rev.GetProperties() } }); } else { result.Add(new Dictionary <string, object> { { "missing", revID } }); } } } if (mustSendJson) { response["Content-Type"] = "application/json"; response.JsonBody = new Body(result.Cast <object>().ToList()); } else { response.SetMultipartBody(result.Cast <object>().ToList(), "multipart/mixed"); } } return response; }).AsDefaultState()); }
/// <summary>Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev. /// </summary> /// <remarks> /// Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev. /// Rev must already have its revID and sequence properties set. /// </remarks> internal IDictionary<String, Object> ExtraPropertiesForRevision(RevisionInternal rev, DocumentContentOptions contentOptions) { var docId = rev.GetDocId(); var revId = rev.GetRevId(); var sequenceNumber = rev.GetSequence(); Debug.Assert((revId != null)); Debug.Assert((sequenceNumber > 0)); // Get attachment metadata, and optionally the contents: IDictionary<string, object> attachmentsDict = null; if (!contentOptions.HasFlag(DocumentContentOptions.NoAttachments)) { attachmentsDict = GetAttachmentsDictForSequenceWithContent (sequenceNumber, contentOptions); } // Get more optional stuff to put in the properties: //OPT: This probably ends up making redundant SQL queries if multiple options are enabled. var localSeq = -1L; if (contentOptions.HasFlag(DocumentContentOptions.IncludeLocalSeq)) { localSeq = sequenceNumber; } IDictionary<string, object> revHistory = null; if (contentOptions.HasFlag(DocumentContentOptions.IncludeRevs)) { revHistory = GetRevisionHistoryDict(rev); } IList<object> revsInfo = null; if (contentOptions.HasFlag(DocumentContentOptions.IncludeRevsInfo)) { revsInfo = new AList<object>(); var revHistoryFull = GetRevisionHistory(rev); foreach (RevisionInternal historicalRev in revHistoryFull) { var revHistoryItem = new Dictionary<string, object>(); var status = "available"; if (historicalRev.IsDeleted()) { status = "deleted"; } if (historicalRev.IsMissing()) { status = "missing"; } revHistoryItem.Put("rev", historicalRev.GetRevId()); revHistoryItem["status"] = status; revsInfo.AddItem(revHistoryItem); } } IList<string> conflicts = null; if (contentOptions.HasFlag(DocumentContentOptions.IncludeConflicts)) { var revs = GetAllRevisionsOfDocumentID(docId, true); if (revs.Count > 1) { conflicts = new AList<string>(); foreach (RevisionInternal savedRev in revs) { if (!(savedRev.Equals(rev) || savedRev.IsDeleted())) { conflicts.AddItem(savedRev.GetRevId()); } } } } var result = new Dictionary<string, object>(); result["_id"] = docId; result["_rev"] = revId; if (rev.IsDeleted()) { result["_deleted"] = true; } if (attachmentsDict != null) { result["_attachments"] = attachmentsDict; } if (localSeq > -1) { result["_local_seq"] = localSeq; } if (revHistory != null) { result["_revisions"] = revHistory; } if (revsInfo != null) { result["_revs_info"] = revsInfo; } if (conflicts != null) { result["_conflicts"] = conflicts; } return result; }
/// <summary>Constructs an "_attachments" dictionary for a revision, to be inserted in its JSON body.</summary> internal IDictionary<String, Object> GetAttachmentsDictForSequenceWithContent(long sequence, DocumentContentOptions contentOptions) { Debug.Assert((sequence > 0)); Cursor cursor = null; var args = new Object[] { sequence }; try { cursor = StorageEngine.RawQuery("SELECT filename, key, type, length, revpos FROM attachments WHERE sequence=?", CommandBehavior.SequentialAccess, args); if (!cursor.MoveToNext()) { return null; } var result = new Dictionary<String, Object>(); while (!cursor.IsAfterLast()) { var dataSuppressed = false; var filename = cursor.GetString(0); var keyData = cursor.GetBlob(1); var contentType = cursor.GetString(2); var length = cursor.GetInt(3); var revpos = cursor.GetInt(4); var key = new BlobKey(keyData); var digestString = "sha1-" + Convert.ToBase64String(keyData); var dataBase64 = (string) null; if (contentOptions.HasFlag(DocumentContentOptions.IncludeAttachments)) { if (contentOptions.HasFlag(DocumentContentOptions.BigAttachmentsFollow) && length >= Database.BigAttachmentLength) { dataSuppressed = true; } else { byte[] data = Attachments.BlobForKey(key); if (data != null) { dataBase64 = Convert.ToBase64String(data); } else { // <-- very expensive Log.W(Tag, "Error loading attachment"); } } } var attachment = new Dictionary<string, object>(); if (!(dataBase64 != null || dataSuppressed)) { attachment["stub"] = true; } if (dataBase64 != null) { attachment["data"] = dataBase64; } if (dataSuppressed) { attachment.Put ("follows", true); } attachment["digest"] = digestString; attachment["content_type"] = contentType; attachment["length"] = length; attachment["revpos"] = revpos; result[filename] = attachment; cursor.MoveToNext(); } return result; } catch (SQLException e) { Log.E(Tag, "Error getting attachments for sequence", e); return null; } finally { if (cursor != null) { cursor.Close(); } } }
internal RevisionInternal GetDocumentWithIDAndRev(String id, String rev, DocumentContentOptions contentOptions) { RevisionInternal result = null; string sql; Cursor cursor = null; try { cursor = null; var cols = "revid, deleted, sequence, no_attachments"; if (!contentOptions.HasFlag(DocumentContentOptions.NoBody)) { cols += ", json"; } if (rev != null) { sql = "SELECT " + cols + " FROM revs, docs WHERE docs.docid=? AND revs.doc_id=docs.doc_id AND revid=? LIMIT 1"; //TODO: mismatch w iOS: {sql = "SELECT " + cols + " FROM revs WHERE revs.doc_id=? AND revid=? AND json notnull LIMIT 1";} var args = new[] { id, rev }; cursor = StorageEngine.RawQuery(sql, args); } else { sql = "SELECT " + cols + " FROM revs, docs WHERE docs.docid=? AND revs.doc_id=docs.doc_id and current=1 and deleted=0 ORDER BY revid DESC LIMIT 1"; //TODO: mismatch w iOS: {sql = "SELECT " + cols + " FROM revs WHERE revs.doc_id=? and current=1 and deleted=0 ORDER BY revid DESC LIMIT 1";} var args = new[] { id }; cursor = StorageEngine.RawQuery(sql, CommandBehavior.SequentialAccess, args); } if (cursor.MoveToNext()) { if (rev == null) { rev = cursor.GetString(0); } var deleted = cursor.GetInt(1) > 0; result = new RevisionInternal(id, rev, deleted, this); result.SetSequence(cursor.GetLong(2)); if (contentOptions != DocumentContentOptions.NoBody) { byte[] json = null; if (!contentOptions.HasFlag(DocumentContentOptions.NoBody)) { json = cursor.GetBlob(4); } if (cursor.GetInt(3) > 0) { // no_attachments == true contentOptions |= DocumentContentOptions.NoAttachments; } ExpandStoredJSONIntoRevisionWithAttachments(json, result, contentOptions); } } } catch (Exception e) { Log.E(Tag, "Error getting document with id and rev", e); } finally { if (cursor != null) { cursor.Close(); } } return result; }
/// <summary>Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev. /// </summary> /// <remarks> /// Inserts the _id, _rev and _attachments properties into the JSON data and stores it in rev. /// Rev must already have its revID and sequence properties set. /// </remarks> internal void ExpandStoredJSONIntoRevisionWithAttachments(IEnumerable<Byte> json, RevisionInternal rev, DocumentContentOptions contentOptions) { var extra = ExtraPropertiesForRevision(rev, contentOptions); if (json != null && json.Any()) { rev.SetJson(AppendDictToJSON(json, extra)); } else { rev.SetProperties(extra); if (json == null) { rev.SetMissing(true); } } }
// Apply the options in the URL query to the specified revision and create a new revision object internal static RevisionInternal ApplyOptions(DocumentContentOptions options, RevisionInternal rev, ICouchbaseListenerContext context, Database db, Status outStatus) { if ((options & (DocumentContentOptions.IncludeRevs | DocumentContentOptions.IncludeRevsInfo | DocumentContentOptions.IncludeConflicts | DocumentContentOptions.IncludeAttachments | DocumentContentOptions.IncludeLocalSeq)) != 0) { var dst = rev.GetProperties(); if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq)) { dst["_local_seq"] = rev.GetSequence(); } if (options.HasFlag(DocumentContentOptions.IncludeRevs)) { var revs = db.GetRevisionHistory(rev, null); dst["_revisions"] = Database.MakeRevisionHistoryDict(revs); } if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo)) { dst["_revs_info"] = db.Storage.GetRevisionHistory(rev, null).Select(x => { string status = "available"; if(x.IsDeleted()) { status = "deleted"; } else if(x.IsMissing()) { status = "missing"; } return new Dictionary<string, object> { { "rev", x.GetRevId() }, { "status", status } }; }); } if (options.HasFlag(DocumentContentOptions.IncludeConflicts)) { RevisionList revs = db.Storage.GetAllDocumentRevisions(rev.GetDocId(), true); if (revs.Count > 1) { dst["_conflicts"] = revs.Select(x => { return x.Equals(rev) || x.IsDeleted() ? null : x.GetRevId(); }); } } RevisionInternal nuRev = new RevisionInternal(dst); if (options.HasFlag(DocumentContentOptions.IncludeAttachments)) { bool attEncodingInfo = context != null && context.GetQueryParam<bool>("att_encoding_info", bool.TryParse, false); if(!db.ExpandAttachments(nuRev, 0, false, !attEncodingInfo, outStatus)) { return null; } } rev = nuRev; } return rev; }
internal IDictionary<String, Object> DocumentPropertiesFromJSON(IEnumerable<Byte> json, String docId, String revId, Boolean deleted, Int64 sequence, DocumentContentOptions contentOptions) { var rev = new RevisionInternal(docId, revId, deleted, this); rev.SetSequence(sequence); IDictionary<String, Object> extra = ExtraPropertiesForRevision(rev, contentOptions); if (json == null) { return extra; } IDictionary<String, Object> docProperties = null; try { docProperties = Manager.GetObjectMapper().ReadValue<IDictionary<string, object>>(json); docProperties.PutAll(extra); } catch (Exception e) { Log.E(Tag, "Error serializing properties to JSON", e); } return docProperties; }
public void SetContentOptions(DocumentContentOptions contentOptions ) { ContentOptions = contentOptions; }
public void SetContentOptions(DocumentContentOptions contentOptions ) { this.contentOptions = contentOptions; }
public void TestPutLargeAttachment() { var testAttachmentName = "test_attachment"; var attachments = database.Attachments; attachments.DeleteBlobs(); Assert.AreEqual(0, attachments.Count()); var status = new Status(); var rev1Properties = new Dictionary <string, object>(); rev1Properties["foo"] = 1; rev1Properties["bar"] = false; database.BeginTransaction(); var rev1 = database.PutRevision(new RevisionInternal(rev1Properties), null, false, status); Assert.AreEqual(StatusCode.Created, status.GetCode()); var largeAttachment = new StringBuilder(); for (int i = 0; i < Database.BigAttachmentLength; i++) { largeAttachment.Append("big attachment!"); } var attach1 = Encoding.UTF8.GetBytes(largeAttachment.ToString()); database.InsertAttachmentForSequenceWithNameAndType( new ByteArrayInputStream(attach1), rev1.GetSequence(), testAttachmentName, "text/plain", rev1.GetGeneration()); database.EndTransaction(true); var attachment = database.GetAttachmentForSequence(rev1.GetSequence(), testAttachmentName); Assert.AreEqual("text/plain", attachment.ContentType); var data = attachment.Content.ToArray(); Assert.IsTrue(Arrays.Equals(attach1, data)); attachment.Dispose(); const DocumentContentOptions contentOptions = DocumentContentOptions.IncludeAttachments | DocumentContentOptions.BigAttachmentsFollow; var attachmentDictForSequence = database.GetAttachmentsDictForSequenceWithContent(rev1.GetSequence(), contentOptions); var innerDict = (IDictionary <string, object>)attachmentDictForSequence[testAttachmentName]; if (innerDict.ContainsKey("stub")) { if (((bool)innerDict["stub"])) { throw new RuntimeException("Expected attachment dict 'stub' key to be true"); } else { throw new RuntimeException("Expected attachment dict to have 'stub' key"); } } if (!innerDict.ContainsKey("follows")) { throw new RuntimeException("Expected attachment dict to have 'follows' key"); } attachment.Dispose(); var rev1WithAttachments = database.GetDocumentWithIDAndRev( rev1.GetDocId(), rev1.GetRevId(), contentOptions); var rev1WithAttachmentsProperties = rev1WithAttachments.GetProperties(); var rev2Properties = new Dictionary <string, object>(); rev2Properties.Put("_id", rev1WithAttachmentsProperties["_id"]); rev2Properties["foo"] = 2; database.BeginTransaction(); var newRev = new RevisionInternal(rev2Properties); var rev2 = database.PutRevision(newRev, rev1WithAttachments.GetRevId(), false, status); Assert.AreEqual(StatusCode.Created, status.GetCode()); database.CopyAttachmentNamedFromSequenceToSequence( testAttachmentName, rev1WithAttachments.GetSequence(), rev2.GetSequence()); database.EndTransaction(true); // Check the 2nd revision's attachment: var rev2FetchedAttachment = database.GetAttachmentForSequence(rev2.GetSequence(), testAttachmentName); Assert.AreEqual(attachment.Length, rev2FetchedAttachment.Length); AssertPropertiesAreEqual(attachment.Metadata, rev2FetchedAttachment.Metadata); Assert.AreEqual(attachment.ContentType, rev2FetchedAttachment.ContentType); rev2FetchedAttachment.Dispose(); // Add a third revision of the same document: var rev3Properties = new Dictionary <string, object>(); rev3Properties.Put("_id", rev2.GetProperties().Get("_id")); rev3Properties["foo"] = 3; rev3Properties["baz"] = false; var rev3 = new RevisionInternal(rev3Properties); rev3 = database.PutRevision(rev3, rev2.GetRevId(), false, status); Assert.AreEqual(StatusCode.Created, status.GetCode()); var attach3 = Encoding.UTF8.GetBytes("<html><blink>attach3</blink></html>"); database.InsertAttachmentForSequenceWithNameAndType( new ByteArrayInputStream(attach3), rev3.GetSequence(), testAttachmentName, "text/html", rev3.GetGeneration()); // Check the 3rd revision's attachment: var rev3FetchedAttachment = database.GetAttachmentForSequence( rev3.GetSequence(), testAttachmentName); data = rev3FetchedAttachment.Content.ToArray(); Assert.IsTrue(Arrays.Equals(attach3, data)); Assert.AreEqual("text/html", rev3FetchedAttachment.ContentType); rev3FetchedAttachment.Dispose(); // TODO: why doesn't this work? // Assert.assertEquals(attach3.length, rev3FetchedAttachment.getLength()); var blobKeys = database.Attachments.AllKeys(); Assert.AreEqual(2, blobKeys.Count); database.Compact(); blobKeys = database.Attachments.AllKeys(); Assert.AreEqual(1, blobKeys.Count); }
// Apply the options in the URL query to the specified revision and create a new revision object private static RevisionInternal ApplyOptions(DocumentContentOptions options, RevisionInternal rev, ICouchbaseListenerContext context, Database db, Status outStatus) { if ((options & (DocumentContentOptions.IncludeRevs | DocumentContentOptions.IncludeRevsInfo | DocumentContentOptions.IncludeConflicts | DocumentContentOptions.IncludeAttachments | DocumentContentOptions.IncludeLocalSeq)) != 0) { var dst = rev.GetProperties(); if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq)) { dst["_local_seq"] = rev.GetSequence(); } if (options.HasFlag(DocumentContentOptions.IncludeRevs)) { dst["_revisions"] = db.GetRevisionHistoryDict(rev); } if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo)) { dst["_revs_info"] = db.GetRevisionHistory(rev).Select(x => { string status = "available"; if (x.IsDeleted()) { status = "deleted"; } else if (x.IsMissing()) { status = "missing"; } return(new Dictionary <string, object> { { "rev", x.GetRevId() }, { "status", status } }); }); } if (options.HasFlag(DocumentContentOptions.IncludeConflicts)) { RevisionList revs = db.GetAllRevisionsOfDocumentID(rev.GetDocId(), true); if (revs.Count > 1) { dst["_conflicts"] = revs.Select(x => { return(x.Equals(rev) || x.IsDeleted() ? null : x.GetRevId()); }); } } RevisionInternal nuRev = new RevisionInternal(dst); if (options.HasFlag(DocumentContentOptions.IncludeAttachments)) { bool attEncodingInfo = context.GetQueryParam <bool>("att_encoding_info", bool.TryParse, false); if (!db.ExpandAttachments(nuRev, 0, false, !attEncodingInfo, outStatus)) { return(null); } } rev = nuRev; } return(rev); }
// Apply the options in the URL query to the specified revision and create a new revision object internal static RevisionInternal ApplyOptions(DocumentContentOptions options, RevisionInternal rev, ICouchbaseListenerContext context, Database db, Status outStatus) { if ((options & (DocumentContentOptions.IncludeRevs | DocumentContentOptions.IncludeRevsInfo | DocumentContentOptions.IncludeConflicts | DocumentContentOptions.IncludeAttachments | DocumentContentOptions.IncludeLocalSeq) | DocumentContentOptions.IncludeExpiration) != 0) { var dst = rev.GetProperties() ?? new Dictionary<string, object>(); if (options.HasFlag(DocumentContentOptions.IncludeLocalSeq)) { dst["_local_seq"] = rev.Sequence; } if (options.HasFlag(DocumentContentOptions.IncludeRevs)) { var revs = db.GetRevisionHistory(rev, null); dst["_revisions"] = TreeRevisionID.MakeRevisionHistoryDict(revs); } if (options.HasFlag(DocumentContentOptions.IncludeRevsInfo)) { dst["_revs_info"] = db.GetRevisionHistory(rev, null).Select(x => { string status = "available"; var ancestor = db.GetDocument(rev.DocID, x, true); if(ancestor.Deleted) { status = "deleted"; } else if(ancestor.Missing) { status = "missing"; } return new Dictionary<string, object> { { "rev", x.ToString() }, { "status", status } }; }); } if (options.HasFlag(DocumentContentOptions.IncludeConflicts)) { RevisionList revs = db.Storage.GetAllDocumentRevisions(rev.DocID, true, false); if (revs.Count > 1) { dst["_conflicts"] = from r in revs where !r.Equals(rev) && !r.Deleted select r.RevID.ToString(); } } if(options.HasFlag(DocumentContentOptions.IncludeExpiration)) { var expirationTime = db.Storage?.GetDocumentExpiration(rev.DocID); if(expirationTime.HasValue) { dst["_exp"] = expirationTime; } } RevisionInternal nuRev = new RevisionInternal(dst); if (options.HasFlag(DocumentContentOptions.IncludeAttachments)) { bool attEncodingInfo = context != null && context.GetQueryParam<bool>("att_encoding_info", bool.TryParse, false); db.ExpandAttachments(nuRev, 0, false, !attEncodingInfo); } rev = nuRev; } return rev; }