Ejemplo n.º 1
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal IList <QueryRow> ReducedQuery(Cursor cursor, Boolean group, Int32 groupLevel)
        {
            IList <object> keysToReduce   = null;
            IList <object> valuesToReduce = null;
            object         lastKey        = null;

            var reduce = Reduce;

            // FIXME: If reduce is null, then so are keysToReduce and ValuesToReduce, which can throw an NRE below.
            if (reduce != null)
            {
                keysToReduce   = new AList <Object>(ReduceBatchSize);
                valuesToReduce = new AList <Object>(ReduceBatchSize);
            }

            var rows = new AList <QueryRow>();

            cursor.MoveToNext();

            while (!cursor.IsAfterLast())
            {
                var keyData = FromJSON(cursor.GetBlob(0));
                var value   = FromJSON(cursor.GetBlob(1));
                System.Diagnostics.Debug.Assert((keyData != null));
                if (group && !GroupTogether(keyData, lastKey, groupLevel))
                {
                    if (lastKey != null)
                    {
                        // This pair starts a new group, so reduce & record the last one:
                        var reduced = (reduce != null) ? reduce(keysToReduce, valuesToReduce, false) : null;

                        var key = GroupKey(lastKey, groupLevel);
                        var row = new QueryRow(null, 0, key, reduced, null);
                        row.Database = Database;
                        rows.AddItem(row); // NOTE.ZJG: Change to `yield return row` to convert to a generator.

                        keysToReduce.Clear();
                        valuesToReduce.Clear();
                    }
                    lastKey = keyData;
                }
                keysToReduce.AddItem(keyData);
                valuesToReduce.AddItem(value);
                cursor.MoveToNext();
            }
            // NOTE.ZJG: Need to handle grouping differently if switching this to a generator.
            if (keysToReduce.Count > 0)
            {
                // Finish the last group (or the entire list, if no grouping):
                var key     = group ? GroupKey(lastKey, groupLevel) : null;
                var reduced = (reduce != null) ? reduce(keysToReduce, valuesToReduce, false) : null;
                var row     = new QueryRow(null, 0, key, reduced, null);
                row.Database = Database;
                rows.AddItem(row);
            }
            return(rows);
        }
Ejemplo n.º 2
0
        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);
        }
Ejemplo n.º 3
0
        public IList <IDictionary <string, object> > Dump()
        {
            if (GetViewId() < 0)
            {
                return(null);
            }
            string[] selectArgs = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
            Cursor   cursor     = null;
            IList <IDictionary <string, object> > result = null;

            try
            {
                cursor = database.GetDatabase().RawQuery("SELECT sequence, key, value FROM maps WHERE view_id=? ORDER BY key"
                                                         , selectArgs);
                cursor.MoveToNext();
                result = new AList <IDictionary <string, object> >();
                while (!cursor.IsAfterLast())
                {
                    IDictionary <string, object> row = new Dictionary <string, object>();
                    row.Put("seq", cursor.GetInt(0));
                    row.Put("key", cursor.GetString(1));
                    row.Put("value", cursor.GetString(2));
                    result.AddItem(row);
                    cursor.MoveToNext();
                }
            }
            catch (SQLException e)
            {
                Log.E(Log.TagView, "Error dumping view", e);
                return(null);
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            return(result);
        }
Ejemplo n.º 4
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal IList<QueryRow> ReducedQuery(Cursor cursor, Boolean group, Int32 groupLevel)
        {
            IList<object> keysToReduce = null;
            IList<object> valuesToReduce = null;
            object lastKey = null;

            var reduce = Reduce;
            // FIXME: If reduce is null, then so are keysToReduce and ValuesToReduce, which can throw an NRE below.
            if (reduce != null)
            {
                keysToReduce = new AList<Object>(ReduceBatchSize);
                valuesToReduce = new AList<Object>(ReduceBatchSize);
            }

            var rows = new AList<QueryRow>();
            cursor.MoveToNext();

            while (!cursor.IsAfterLast())
            {
                var keyData = FromJSON(cursor.GetBlob(0));
                var value = FromJSON(cursor.GetBlob(1));
                System.Diagnostics.Debug.Assert((keyData != null));
                if (group && !GroupTogether(keyData, lastKey, groupLevel))
                {
                    if (lastKey != null)
                    {
                        // This pair starts a new group, so reduce & record the last one:
                        var reduced = (reduce != null) ? reduce(keysToReduce, valuesToReduce, false) : null;

                        var key = GroupKey(lastKey, groupLevel);
                        var row = new QueryRow(null, 0, key, reduced, null);
                        row.Database = Database;
                        rows.AddItem(row); // NOTE.ZJG: Change to `yield return row` to convert to a generator.

                        keysToReduce.Clear();
                        valuesToReduce.Clear();
                    }
                    lastKey = keyData;
                }
                keysToReduce.AddItem(keyData);
                valuesToReduce.AddItem(value);
                cursor.MoveToNext();
            }
            // NOTE.ZJG: Need to handle grouping differently if switching this to a generator.
            if (keysToReduce.Count > 0)
            {
                // Finish the last group (or the entire list, if no grouping):
                var key = group ? GroupKey(lastKey, groupLevel) : null;
                var reduced = (reduce != null) ? reduce(keysToReduce, valuesToReduce, false) : null;
                var row = new QueryRow(null, 0, key, reduced, null);
                row.Database = Database;
                rows.AddItem(row);
            }
            return rows;
        }
Ejemplo n.º 5
0
        /// <summary>Queries the view.</summary>
        /// <remarks>Queries the view. Does NOT first update the index.</remarks>
        /// <param name="options">The options to use.</param>
        /// <returns>An array of QueryRow objects.</returns>
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        internal IEnumerable <QueryRow> QueryWithOptions(QueryOptions options)
        {
            if (options == null)
            {
                options = new QueryOptions();
            }

            Cursor           cursor = null;
            IList <QueryRow> rows   = new List <QueryRow>();

            try
            {
                cursor = ResultSetWithOptions(options);
                int groupLevel  = options.GetGroupLevel();
                var group       = options.IsGroup() || (groupLevel > 0);
                var reduceBlock = Reduce;
                var reduce      = GroupOrReduce(options);

                if (reduce && (reduceBlock == null) && !group)
                {
                    var msg = "Cannot use reduce option in view " + Name + " which has no reduce block defined";
                    Log.W(Database.Tag, msg);
                    throw new CouchbaseLiteException(StatusCode.BadRequest);
                }

                if (reduce || group)
                {
                    // Reduced or grouped query:
                    rows = ReducedQuery(cursor, group, groupLevel);
                }
                else
                {
                    // regular query
                    cursor.MoveToNext();
                    while (!cursor.IsAfterLast())
                    {
                        var key          = FromJSON(cursor.GetBlob(0));
                        var value        = FromJSON(cursor.GetBlob(1));
                        var docId        = cursor.GetString(2);
                        var sequenceLong = cursor.GetLong(3);
                        var sequence     = Convert.ToInt32(sequenceLong);


                        IDictionary <string, object> docContents = null;
                        if (options.IsIncludeDocs())
                        {
                            // http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views#Linked_documents
                            if (value is IDictionary <string, object> && ((IDictionary <string, object>)value).ContainsKey("_id"))
                            {
                                var linkedDocId = (string)((IDictionary <string, object>)value).Get("_id");
                                var linkedDoc   = Database.GetDocumentWithIDAndRev(linkedDocId, null, DocumentContentOptions.None);
                                docContents = linkedDoc.GetProperties();
                            }
                            else
                            {
                                var revId = cursor.GetString(4);
                                docContents = Database.DocumentPropertiesFromJSON(cursor.GetBlob(5), docId, revId, false, sequenceLong, options.GetContentOptions());
                            }
                        }
                        var row = new QueryRow(docId, sequence, key, value, docContents);
                        row.Database = Database;
                        rows.AddItem <QueryRow>(row);  // NOTE.ZJG: Change to `yield return row` to convert to a generator.
                        cursor.MoveToNext();
                    }
                }
            }
            catch (SQLException e)
            {
                var errMsg = string.Format("Error querying view: {0}", this);
                Log.E(Database.Tag, errMsg, e);
                throw new CouchbaseLiteException(errMsg, e, new Status(StatusCode.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
            }
            return(rows);
        }
Ejemplo n.º 6
0
        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);
        }
Ejemplo n.º 7
0
 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;
 }
Ejemplo n.º 8
0
        internal void UpdateIndex()
        {
            Log.V(Database.Tag, "Re-indexing view " + Name + " ...");
            System.Diagnostics.Debug.Assert((Map != null));

            if (Id < 0)
            {
                var msg = string.Format("View.Id < 0");
                throw new CouchbaseLiteException(msg, new Status(StatusCode.NotFound));
            }

            Database.BeginTransaction();

            var    result = new Status(StatusCode.InternalServerError);
            Cursor cursor = null;

            try
            {
                var lastSequence  = LastSequenceIndexed;
                var dbMaxSequence = Database.LastSequenceNumber;

                if (lastSequence == dbMaxSequence)
                {
                    // nothing to do (eg,  kCBLStatusNotModified)
                    var msg = String.Format("lastSequence ({0}) == dbMaxSequence ({1}), nothing to do", lastSequence, dbMaxSequence);
                    Log.D(Database.Tag, msg);
                    result.SetCode(StatusCode.Ok);
                    return;
                }

                // First remove obsolete emitted results from the 'maps' table:
                var sequence = lastSequence;
                if (lastSequence < 0)
                {
                    var msg = string.Format("lastSequence < 0 ({0})", lastSequence);
                    throw new CouchbaseLiteException(msg, new Status(StatusCode.InternalServerError));
                }
                if (lastSequence == 0)
                {
                    // If the lastSequence has been reset to 0, make sure to remove
                    // any leftover rows:
                    var whereArgs = new string[] { Sharpen.Extensions.ToString(Id) };
                    Database.StorageEngine.Delete("maps", "view_id=@", whereArgs);
                }
                else
                {
                    // Delete all obsolete map results (ones from since-replaced
                    // revisions):
                    var args = new [] {
                        Id.ToString(),
                        lastSequence.ToString(),
                        lastSequence.ToString()
                    };

                    Database.StorageEngine.ExecSQL(
                        "DELETE FROM maps WHERE view_id=@ AND sequence IN ("
                        + "SELECT parent FROM revs WHERE sequence>@ " + "AND parent>0 AND parent<=@)",
                        args);
                }
                var deleted = 0;
                cursor = Database.StorageEngine.RawQuery("SELECT changes()", null); // TODO: Convert to ADO params.
                cursor.MoveToNext();
                deleted = cursor.GetInt(0);
                cursor.Close();

                // find a better way to propagate this back
                // Now scan every revision added since the last time the view was
                // indexed:
                var selectArgs = new[] { Convert.ToString(lastSequence) };
                cursor = Database.StorageEngine.RawQuery("SELECT revs.doc_id, sequence, docid, revid, json FROM revs, docs "
                                                         + "WHERE sequence>@ AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
                                                         + "ORDER BY revs.doc_id, revid DESC", CommandBehavior.SequentialAccess, selectArgs);
                cursor.MoveToNext();

                var lastDocID = 0L;
                while (!cursor.IsAfterLast())
                {
                    long docID = cursor.GetLong(0);
                    if (docID != lastDocID)
                    {
                        // Only look at the first-iterated revision of any document,
                        // because this is the
                        // one with the highest revid, hence the "winning" revision
                        // of a conflict.
                        lastDocID = docID;
                        // Reconstitute the document as a dictionary:
                        sequence = cursor.GetLong(1);
                        string docId = cursor.GetString(2);
                        if (docId.StartsWith("_design/", StringComparison.InvariantCultureIgnoreCase))
                        {
                            // design docs don't get indexed!
                            cursor.MoveToNext();
                            continue;
                        }
                        var revId      = cursor.GetString(3);
                        var json       = cursor.GetBlob(4);
                        var properties = Database.DocumentPropertiesFromJSON(
                            json, docId, revId, false, sequence, EnumSet.NoneOf <TDContentOptions>()
                            );
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:
                            Log.V(Database.Tag, "  call map for sequence=" + System.Convert.ToString(sequence
                                                                                                     ));
                            // This is the emit() block, which gets called from within the
                            // user-defined map() block
                            // that's called down below.

                            var enclosingView = this;
                            var thisSequence  = sequence;
                            var map           = Map;

                            if (map == null)
                            {
                                throw new CouchbaseLiteException("Map function is missing.");
                            }

                            EmitDelegate emitBlock = (key, value) =>
                            {
                                // TODO: Do we need to do any null checks on key or value?
                                try
                                {
                                    var keyJson   = Manager.GetObjectMapper().WriteValueAsString(key);
                                    var valueJson = value == null ? null : Manager.GetObjectMapper().WriteValueAsString(value);
                                    Log.V(Database.Tag, String.Format("    emit({0}, {1})", keyJson, valueJson));

                                    var insertValues = new ContentValues();
                                    insertValues.Put("view_id", enclosingView.Id);
                                    insertValues["sequence"] = thisSequence;
                                    insertValues["key"]      = keyJson;
                                    insertValues["value"]    = valueJson;

                                    enclosingView.Database.StorageEngine.Insert("maps", null, insertValues);
                                }
                                catch (Exception e)
                                {
                                    Log.E(Database.Tag, "Error emitting", e);
                                }
                            };

                            map(properties, emitBlock);
                        }
                    }
                    cursor.MoveToNext();
                }
                // Finally, record the last revision sequence number that was
                // indexed:
                ContentValues updateValues = new ContentValues();
                updateValues["lastSequence"] = dbMaxSequence;
                var whereArgs_1 = new string[] { Sharpen.Extensions.ToString(Id) };
                Database.StorageEngine.Update("views", updateValues, "view_id=@", whereArgs_1);
                // FIXME actually count number added :)
                Log.V(Database.Tag, "...Finished re-indexing view " + Name + " up to sequence " +
                      System.Convert.ToString(dbMaxSequence) + " (deleted " + deleted + " added " + "?" + ")");
                result.SetCode(StatusCode.Ok);
            }
            catch (SQLException e)
            {
                throw new CouchbaseLiteException(e, new Status(StatusCode.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
                if (!result.IsSuccessful())
                {
                    Log.W(Database.Tag, "Failed to rebuild view " + Name + ": " + result.GetCode());
                }
                if (Database != null)
                {
                    Database.EndTransaction(result.IsSuccessful());
                }
            }
        }
Ejemplo n.º 9
0
        public void UpdateIndex()
        {
            Log.V(Database.Tag, "Re-indexing view " + name + " ...");
            System.Diagnostics.Debug.Assert((mapBlock != null));
            if (GetViewId() < 0)
            {
                string msg = string.Format("getViewId() < 0");
                throw new CouchbaseLiteException(msg, new Status(Status.NotFound));
            }
            database.BeginTransaction();
            Status result = new Status(Status.InternalServerError);
            Cursor cursor = null;

            try
            {
                long lastSequence  = GetLastSequenceIndexed();
                long dbMaxSequence = database.GetLastSequenceNumber();
                if (lastSequence == dbMaxSequence)
                {
                    // nothing to do (eg,  kCBLStatusNotModified)
                    string msg = string.Format("lastSequence (%d) == dbMaxSequence (%d), nothing to do"
                                               , lastSequence, dbMaxSequence);
                    Log.D(Database.Tag, msg);
                    result.SetCode(Status.Ok);
                    return;
                }
                // First remove obsolete emitted results from the 'maps' table:
                long sequence = lastSequence;
                if (lastSequence < 0)
                {
                    string msg = string.Format("lastSequence < 0 (%s)", lastSequence);
                    throw new CouchbaseLiteException(msg, new Status(Status.InternalServerError));
                }
                if (lastSequence == 0)
                {
                    // If the lastSequence has been reset to 0, make sure to remove
                    // any leftover rows:
                    string[] whereArgs = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
                    database.GetDatabase().Delete("maps", "view_id=?", whereArgs);
                }
                else
                {
                    // Delete all obsolete map results (ones from since-replaced
                    // revisions):
                    string[] args = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                                                       (lastSequence), System.Convert.ToString(lastSequence) };
                    database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence IN ("
                                                   + "SELECT parent FROM revs WHERE sequence>? " + "AND parent>0 AND parent<=?)",
                                                   args);
                }
                int deleted = 0;
                cursor = database.GetDatabase().RawQuery("SELECT changes()", null);
                cursor.MoveToNext();
                deleted = cursor.GetInt(0);
                cursor.Close();
                // This is the emit() block, which gets called from within the
                // user-defined map() block
                // that's called down below.
                AbstractTouchMapEmitBlock emitBlock = new _AbstractTouchMapEmitBlock_446(this);
                // find a better way to propagate this back
                // Now scan every revision added since the last time the view was
                // indexed:
                string[] selectArgs = new string[] { System.Convert.ToString(lastSequence) };
                cursor = database.GetDatabase().RawQuery("SELECT revs.doc_id, sequence, docid, revid, json FROM revs, docs "
                                                         + "WHERE sequence>? AND current!=0 AND deleted=0 " + "AND revs.doc_id = docs.doc_id "
                                                         + "ORDER BY revs.doc_id, revid DESC", selectArgs);
                cursor.MoveToNext();
                long lastDocID = 0;
                while (!cursor.IsAfterLast())
                {
                    long docID = cursor.GetLong(0);
                    if (docID != lastDocID)
                    {
                        // Only look at the first-iterated revision of any document,
                        // because this is the
                        // one with the highest revid, hence the "winning" revision
                        // of a conflict.
                        lastDocID = docID;
                        // Reconstitute the document as a dictionary:
                        sequence = cursor.GetLong(1);
                        string docId = cursor.GetString(2);
                        if (docId.StartsWith("_design/"))
                        {
                            // design docs don't get indexed!
                            cursor.MoveToNext();
                            continue;
                        }
                        string revId = cursor.GetString(3);
                        byte[] json  = cursor.GetBlob(4);
                        IDictionary <string, object> properties = database.DocumentPropertiesFromJSON(json
                                                                                                      , docId, revId, false, sequence, EnumSet.NoneOf <Database.TDContentOptions>());
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:
                            Log.V(Database.Tag, "  call map for sequence=" + System.Convert.ToString(sequence
                                                                                                     ));
                            emitBlock.SetSequence(sequence);
                            mapBlock.Map(properties, emitBlock);
                        }
                    }
                    cursor.MoveToNext();
                }
                // Finally, record the last revision sequence number that was
                // indexed:
                ContentValues updateValues = new ContentValues();
                updateValues.Put("lastSequence", dbMaxSequence);
                string[] whereArgs_1 = new string[] { Sharpen.Extensions.ToString(GetViewId()) };
                database.GetDatabase().Update("views", updateValues, "view_id=?", whereArgs_1);
                // FIXME actually count number added :)
                Log.V(Database.Tag, "...Finished re-indexing view " + name + " up to sequence " +
                      System.Convert.ToString(dbMaxSequence) + " (deleted " + deleted + " added " + "?"
                      + ")");
                result.SetCode(Status.Ok);
            }
            catch (SQLException e)
            {
                throw new CouchbaseLiteException(e, new Status(Status.DbError));
            }
            finally
            {
                if (cursor != null)
                {
                    cursor.Close();
                }
                if (!result.IsSuccessful())
                {
                    Log.W(Database.Tag, "Failed to rebuild view " + name + ": " + result.GetCode());
                }
                if (database != null)
                {
                    database.EndTransaction(result.IsSuccessful());
                }
            }
        }