internal IList <QueryRow> ReducedQuery(Cursor cursor, bool group, int groupLevel) { IList <object> keysToReduce = null; IList <object> valuesToReduce = null; object lastKey = null; if (GetReduce() != null) { keysToReduce = new AList <object>(ReduceBatchSize); valuesToReduce = new AList <object>(ReduceBatchSize); } IList <QueryRow> rows = new AList <QueryRow>(); cursor.MoveToNext(); while (!cursor.IsAfterLast()) { JsonDocument keyDoc = new JsonDocument(cursor.GetBlob(0)); JsonDocument valueDoc = new JsonDocument(cursor.GetBlob(1)); System.Diagnostics.Debug.Assert((keyDoc != null)); object keyObject = keyDoc.JsonObject(); if (group && !GroupTogether(keyObject, lastKey, groupLevel)) { if (lastKey != null) { // This pair starts a new group, so reduce & record the last one: object reduced = (reduceBlock != null) ? reduceBlock.Reduce(keysToReduce, valuesToReduce , false) : null; object key = GroupKey(lastKey, groupLevel); QueryRow row = new QueryRow(null, 0, key, reduced, null); row.SetDatabase(database); rows.AddItem(row); keysToReduce.Clear(); valuesToReduce.Clear(); } lastKey = keyObject; } keysToReduce.AddItem(keyObject); valuesToReduce.AddItem(valueDoc.JsonObject()); cursor.MoveToNext(); } if (keysToReduce.Count > 0) { // Finish the last group (or the entire list, if no grouping): object key = group ? GroupKey(lastKey, groupLevel) : null; object reduced = (reduceBlock != null) ? reduceBlock.Reduce(keysToReduce, valuesToReduce , false) : null; QueryRow row = new QueryRow(null, 0, key, reduced, null); row.SetDatabase(database); rows.AddItem(row); } return(rows); }
public IDictionary<string, object> GetAllDocs(QueryOptions options) { IDictionary<string, object> result = new Dictionary<string, object>(); IList<QueryRow> rows = new AList<QueryRow>(); if (options == null) { options = new QueryOptions(); } bool includeDeletedDocs = (options.GetAllDocsMode() == Query.AllDocsMode.IncludeDeleted ); long updateSeq = 0; if (options.IsUpdateSeq()) { updateSeq = GetLastSequenceNumber(); } // TODO: needs to be atomic with the following SELECT StringBuilder sql = new StringBuilder("SELECT revs.doc_id, docid, revid, sequence" ); if (options.IsIncludeDocs()) { sql.Append(", json"); } if (includeDeletedDocs) { sql.Append(", deleted"); } sql.Append(" FROM revs, docs WHERE"); if (options.GetKeys() != null) { if (options.GetKeys().Count == 0) { return result; } string commaSeperatedIds = JoinQuotedObjects(options.GetKeys()); sql.Append(string.Format(" revs.doc_id IN (SELECT doc_id FROM docs WHERE docid IN (%s)) AND" , commaSeperatedIds)); } sql.Append(" docs.doc_id = revs.doc_id AND current=1"); if (!includeDeletedDocs) { sql.Append(" AND deleted=0"); } IList<string> args = new AList<string>(); object minKey = options.GetStartKey(); object maxKey = options.GetEndKey(); bool inclusiveMin = true; bool inclusiveMax = options.IsInclusiveEnd(); if (options.IsDescending()) { minKey = maxKey; maxKey = options.GetStartKey(); inclusiveMin = inclusiveMax; inclusiveMax = true; } if (minKey != null) { System.Diagnostics.Debug.Assert((minKey is string)); sql.Append((inclusiveMin ? " AND docid >= ?" : " AND docid > ?")); args.AddItem((string)minKey); } if (maxKey != null) { System.Diagnostics.Debug.Assert((maxKey is string)); sql.Append((inclusiveMax ? " AND docid <= ?" : " AND docid < ?")); args.AddItem((string)maxKey); } sql.Append(string.Format(" ORDER BY docid %s, %s revid DESC LIMIT ? OFFSET ?", (options .IsDescending() ? "DESC" : "ASC"), (includeDeletedDocs ? "deleted ASC," : string.Empty ))); args.AddItem(Sharpen.Extensions.ToString(options.GetLimit())); args.AddItem(Sharpen.Extensions.ToString(options.GetSkip())); Cursor cursor = null; IDictionary<string, QueryRow> docs = new Dictionary<string, QueryRow>(); try { cursor = database.RawQuery(sql.ToString(), Sharpen.Collections.ToArray(args, new string[args.Count])); bool keepGoing = cursor.MoveToNext(); while (keepGoing) { long docNumericID = cursor.GetLong(0); string docId = cursor.GetString(1); string revId = cursor.GetString(2); long sequenceNumber = cursor.GetLong(3); bool deleted = includeDeletedDocs && cursor.GetInt(GetDeletedColumnIndex(options) ) > 0; IDictionary<string, object> docContents = null; if (options.IsIncludeDocs()) { byte[] json = cursor.GetBlob(4); docContents = DocumentPropertiesFromJSON(json, docId, revId, deleted, sequenceNumber , options.GetContentOptions()); } // Iterate over following rows with the same doc_id -- these are conflicts. // Skip them, but collect their revIDs if the 'conflicts' option is set: IList<string> conflicts = new AList<string>(); while (((keepGoing = cursor.MoveToNext()) == true) && cursor.GetLong(0) == docNumericID ) { if (options.GetAllDocsMode() == Query.AllDocsMode.ShowConflicts || options.GetAllDocsMode () == Query.AllDocsMode.OnlyConflicts) { if (conflicts.IsEmpty()) { conflicts.AddItem(revId); } conflicts.AddItem(cursor.GetString(2)); } } if (options.GetAllDocsMode() == Query.AllDocsMode.OnlyConflicts && conflicts.IsEmpty ()) { continue; } IDictionary<string, object> value = new Dictionary<string, object>(); value.Put("rev", revId); value.Put("_conflicts", conflicts); if (includeDeletedDocs) { value.Put("deleted", (deleted ? true : null)); } QueryRow change = new QueryRow(docId, sequenceNumber, docId, value, docContents); change.SetDatabase(this); if (options.GetKeys() != null) { docs.Put(docId, change); } else { rows.AddItem(change); } } if (options.GetKeys() != null) { foreach (object docIdObject in options.GetKeys()) { if (docIdObject is string) { string docId = (string)docIdObject; QueryRow change = docs.Get(docId); if (change == null) { IDictionary<string, object> value = new Dictionary<string, object>(); long docNumericID = GetDocNumericID(docId); if (docNumericID > 0) { bool deleted; IList<bool> outIsDeleted = new AList<bool>(); IList<bool> outIsConflict = new AList<bool>(); string revId = WinningRevIDOfDoc(docNumericID, outIsDeleted, outIsConflict); if (outIsDeleted.Count > 0) { deleted = true; } if (revId != null) { value.Put("rev", revId); value.Put("deleted", true); } } change = new QueryRow((value != null ? docId : null), 0, docId, value, null); change.SetDatabase(this); } rows.AddItem(change); } } } } catch (SQLException e) { Log.E(Database.Tag, "Error getting all docs", e); throw new CouchbaseLiteException("Error getting all docs", e, new Status(Status.InternalServerError )); } finally { if (cursor != null) { cursor.Close(); } } result.Put("rows", rows); result.Put("total_rows", rows.Count); result.Put("offset", options.GetSkip()); if (updateSeq != 0) { result.Put("update_seq", updateSeq); } return result; }
public IList <QueryRow> QueryWithOptions(QueryOptions options) { if (options == null) { options = new QueryOptions(); } Cursor cursor = null; IList <QueryRow> rows = new AList <QueryRow>(); try { cursor = ResultSetWithOptions(options); int groupLevel = options.GetGroupLevel(); bool group = options.IsGroup() || (groupLevel > 0); bool reduce = options.IsReduce() || group; if (reduce && (reduceBlock == null) && !group) { Log.W(Log.TagView, "Cannot use reduce option in view %s which has no reduce block defined" , name); throw new CouchbaseLiteException(new Status(Status.BadRequest)); } if (reduce || group) { // Reduced or grouped query: rows = ReducedQuery(cursor, group, groupLevel); } else { // regular query cursor.MoveToNext(); while (!cursor.IsAfterLast()) { JsonDocument keyDoc = new JsonDocument(cursor.GetBlob(0)); JsonDocument valueDoc = new JsonDocument(cursor.GetBlob(1)); string docId = cursor.GetString(2); int sequence = Sharpen.Extensions.ValueOf(cursor.GetString(3)); IDictionary <string, object> docContents = null; if (options.IsIncludeDocs()) { object valueObject = valueDoc.JsonObject(); // http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents if (valueObject is IDictionary && ((IDictionary)valueObject).ContainsKey("_id")) { string linkedDocId = (string)((IDictionary)valueObject).Get("_id"); RevisionInternal linkedDoc = database.GetDocumentWithIDAndRev(linkedDocId, null, EnumSet.NoneOf <Database.TDContentOptions>()); docContents = linkedDoc.GetProperties(); } else { docContents = database.DocumentPropertiesFromJSON(cursor.GetBlob(5), docId, cursor .GetString(4), false, cursor.GetLong(3), options.GetContentOptions()); } } QueryRow row = new QueryRow(docId, sequence, keyDoc.JsonObject(), valueDoc.JsonObject (), docContents); row.SetDatabase(database); rows.AddItem(row); cursor.MoveToNext(); } } } catch (SQLException e) { string errMsg = string.Format("Error querying view: %s", this); Log.E(Log.TagView, errMsg, e); throw new CouchbaseLiteException(errMsg, e, new Status(Status.DbError)); } finally { if (cursor != null) { cursor.Close(); } } return(rows); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestAllDocsQuery() { IList<RevisionInternal> docs = PutDocs(database); IList<QueryRow> expectedRow = new AList<QueryRow>(); foreach (RevisionInternal rev in docs) { IDictionary<string, object> value = new Dictionary<string, object>(); value.Put("rev", rev.GetRevId()); value.Put("_conflicts", new AList<string>()); QueryRow queryRow = new QueryRow(rev.GetDocId(), 0, rev.GetDocId(), value, null); queryRow.SetDatabase(database); expectedRow.AddItem(queryRow); } QueryOptions options = new QueryOptions(); IDictionary<string, object> allDocs = database.GetAllDocs(options); IList<QueryRow> expectedRows = new AList<QueryRow>(); expectedRows.AddItem(expectedRow[2]); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedRows.AddItem(expectedRow[1]); expectedRows.AddItem(expectedRow[4]); IDictionary<string, object> expectedQueryResult = CreateExpectedQueryResult(expectedRows , 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Start/end key query: options = new QueryOptions(); options.SetStartKey("2"); options.SetEndKey("44444"); allDocs = database.GetAllDocs(options); expectedRows = new AList<QueryRow>(); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedRows.AddItem(expectedRow[1]); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Start/end query without inclusive end: options.SetInclusiveEnd(false); allDocs = database.GetAllDocs(options); expectedRows = new AList<QueryRow>(); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Get all documents: with default QueryOptions options = new QueryOptions(); allDocs = database.GetAllDocs(options); expectedRows = new AList<QueryRow>(); expectedRows.AddItem(expectedRow[2]); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedRows.AddItem(expectedRow[1]); expectedRows.AddItem(expectedRow[4]); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Get specific documents: options = new QueryOptions(); IList<object> docIds = new AList<object>(); QueryRow expected2 = expectedRow[2]; docIds.AddItem(expected2.GetDocument().GetId()); options.SetKeys(docIds); allDocs = database.GetAllDocs(options); expectedRows = new AList<QueryRow>(); expectedRows.AddItem(expected2); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); }
/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception> public virtual void TestAllDocsQuery() { IList <RevisionInternal> docs = PutDocs(database); IList <QueryRow> expectedRow = new AList <QueryRow>(); foreach (RevisionInternal rev in docs) { IDictionary <string, object> value = new Dictionary <string, object>(); value.Put("rev", rev.GetRevId()); value.Put("_conflicts", new AList <string>()); QueryRow queryRow = new QueryRow(rev.GetDocId(), 0, rev.GetDocId(), value, null); queryRow.SetDatabase(database); expectedRow.AddItem(queryRow); } QueryOptions options = new QueryOptions(); IDictionary <string, object> allDocs = database.GetAllDocs(options); IList <QueryRow> expectedRows = new AList <QueryRow>(); expectedRows.AddItem(expectedRow[2]); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedRows.AddItem(expectedRow[1]); expectedRows.AddItem(expectedRow[4]); IDictionary <string, object> expectedQueryResult = CreateExpectedQueryResult(expectedRows , 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Start/end key query: options = new QueryOptions(); options.StartKey = "2"; options.EndKey = "44444"; allDocs = database.GetAllDocs(options); expectedRows = new AList <QueryRow>(); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedRows.AddItem(expectedRow[1]); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Start/end query without inclusive end: options.SetInclusiveEnd(false); allDocs = database.GetAllDocs(options); expectedRows = new AList <QueryRow>(); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Get all documents: with default QueryOptions options = new QueryOptions(); allDocs = database.GetAllDocs(options); expectedRows = new AList <QueryRow>(); expectedRows.AddItem(expectedRow[2]); expectedRows.AddItem(expectedRow[0]); expectedRows.AddItem(expectedRow[3]); expectedRows.AddItem(expectedRow[1]); expectedRows.AddItem(expectedRow[4]); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); // Get specific documents: options = new QueryOptions(); IList <object> docIds = new AList <object>(); QueryRow expected2 = expectedRow[2]; docIds.AddItem(expected2.Document.Id); options.SetKeys(docIds); allDocs = database.GetAllDocs(options); expectedRows = new AList <QueryRow>(); expectedRows.AddItem(expected2); expectedQueryResult = CreateExpectedQueryResult(expectedRows, 0); NUnit.Framework.Assert.AreEqual(expectedQueryResult, allDocs); }
public IList<QueryRow> QueryWithOptions(QueryOptions options) { if (options == null) { options = new QueryOptions(); } Cursor cursor = null; IList<QueryRow> rows = new AList<QueryRow>(); try { cursor = ResultSetWithOptions(options); int groupLevel = options.GetGroupLevel(); bool group = options.IsGroup() || (groupLevel > 0); bool reduce = options.IsReduce() || group; if (reduce && (reduceBlock == null) && !group) { Log.W(Log.TagView, "Cannot use reduce option in view %s which has no reduce block defined" , name); throw new CouchbaseLiteException(new Status(Status.BadRequest)); } if (reduce || group) { // Reduced or grouped query: rows = ReducedQuery(cursor, group, groupLevel); } else { // regular query cursor.MoveToNext(); while (!cursor.IsAfterLast()) { JsonDocument keyDoc = new JsonDocument(cursor.GetBlob(0)); JsonDocument valueDoc = new JsonDocument(cursor.GetBlob(1)); string docId = cursor.GetString(2); int sequence = Sharpen.Extensions.ValueOf(cursor.GetString(3)); IDictionary<string, object> docContents = null; if (options.IsIncludeDocs()) { object valueObject = valueDoc.JsonObject(); // http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents if (valueObject is IDictionary && ((IDictionary)valueObject).ContainsKey("_id")) { string linkedDocId = (string)((IDictionary)valueObject).Get("_id"); RevisionInternal linkedDoc = database.GetDocumentWithIDAndRev(linkedDocId, null, EnumSet.NoneOf<Database.TDContentOptions>()); docContents = linkedDoc.GetProperties(); } else { docContents = database.DocumentPropertiesFromJSON(cursor.GetBlob(5), docId, cursor .GetString(4), false, cursor.GetLong(3), options.GetContentOptions()); } } QueryRow row = new QueryRow(docId, sequence, keyDoc.JsonObject(), valueDoc.JsonObject (), docContents); row.SetDatabase(database); rows.AddItem(row); cursor.MoveToNext(); } } } catch (SQLException e) { string errMsg = string.Format("Error querying view: %s", this); Log.E(Log.TagView, errMsg, e); throw new CouchbaseLiteException(errMsg, e, new Status(Status.DbError)); } finally { if (cursor != null) { cursor.Close(); } } return rows; }
internal IList<QueryRow> ReducedQuery(Cursor cursor, bool group, int groupLevel) { IList<object> keysToReduce = null; IList<object> valuesToReduce = null; object lastKey = null; if (GetReduce() != null) { keysToReduce = new AList<object>(ReduceBatchSize); valuesToReduce = new AList<object>(ReduceBatchSize); } IList<QueryRow> rows = new AList<QueryRow>(); cursor.MoveToNext(); while (!cursor.IsAfterLast()) { JsonDocument keyDoc = new JsonDocument(cursor.GetBlob(0)); JsonDocument valueDoc = new JsonDocument(cursor.GetBlob(1)); System.Diagnostics.Debug.Assert((keyDoc != null)); object keyObject = keyDoc.JsonObject(); if (group && !GroupTogether(keyObject, lastKey, groupLevel)) { if (lastKey != null) { // This pair starts a new group, so reduce & record the last one: object reduced = (reduceBlock != null) ? reduceBlock.Reduce(keysToReduce, valuesToReduce , false) : null; object key = GroupKey(lastKey, groupLevel); QueryRow row = new QueryRow(null, 0, key, reduced, null); row.SetDatabase(database); rows.AddItem(row); keysToReduce.Clear(); valuesToReduce.Clear(); } lastKey = keyObject; } keysToReduce.AddItem(keyObject); valuesToReduce.AddItem(valueDoc.JsonObject()); cursor.MoveToNext(); } if (keysToReduce.Count > 0) { // Finish the last group (or the entire list, if no grouping): object key = group ? GroupKey(lastKey, groupLevel) : null; object reduced = (reduceBlock != null) ? reduceBlock.Reduce(keysToReduce, valuesToReduce , false) : null; QueryRow row = new QueryRow(null, 0, key, reduced, null); row.SetDatabase(database); rows.AddItem(row); } return rows; }