Exemple #1
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        private RevisionInternal PutDoc(Database db, IDictionary <string, object> props)
        {
            RevisionInternal rev    = new RevisionInternal(props, db);
            Status           status = new Status();

            rev = db.PutRevision(rev, null, false, status);
            NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
            return(rev);
        }
Exemple #2
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/", StringCompare.IgnoreCase))
                        {
                            // 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());
                }
            }
        }
Exemple #3
0
		public RevisionInternal PutRevision(RevisionInternal oldRev, string prevRevId, bool
			 allowConflict, Status resultStatus)
		{
			// prevRevId is the rev ID being replaced, or nil if an insert
			string docId = oldRev.GetDocId();
			bool deleted = oldRev.IsDeleted();
			if ((oldRev == null) || ((prevRevId != null) && (docId == null)) || (deleted && (
				docId == null)) || ((docId != null) && !IsValidDocumentId(docId)))
			{
				throw new CouchbaseLiteException(Status.BadRequest);
			}
			BeginTransaction();
			Cursor cursor = null;
			bool inConflict = false;
			RevisionInternal winningRev = null;
			RevisionInternal newRev = null;
			//// PART I: In which are performed lookups and validations prior to the insert...
			long docNumericID = (docId != null) ? GetDocNumericID(docId) : 0;
			long parentSequence = 0;
			string oldWinningRevID = null;
			try
			{
				bool oldWinnerWasDeletion = false;
				bool wasConflicted = false;
				if (docNumericID > 0)
				{
					IList<bool> outIsDeleted = new AList<bool>();
					IList<bool> outIsConflict = new AList<bool>();
					try
					{
						oldWinningRevID = WinningRevIDOfDoc(docNumericID, outIsDeleted, outIsConflict);
						if (outIsDeleted.Count > 0)
						{
							oldWinnerWasDeletion = true;
						}
						if (outIsConflict.Count > 0)
						{
							wasConflicted = true;
						}
					}
					catch (Exception e)
					{
						Sharpen.Runtime.PrintStackTrace(e);
					}
				}
				if (prevRevId != null)
				{
					// Replacing: make sure given prevRevID is current & find its sequence number:
					if (docNumericID <= 0)
					{
						string msg = string.Format("No existing revision found with doc id: %s", docId);
						throw new CouchbaseLiteException(msg, Status.NotFound);
					}
					string[] args = new string[] { System.Convert.ToString(docNumericID), prevRevId };
					string additionalWhereClause = string.Empty;
					if (!allowConflict)
					{
						additionalWhereClause = "AND current=1";
					}
					cursor = database.RawQuery("SELECT sequence FROM revs WHERE doc_id=? AND revid=? "
						 + additionalWhereClause + " LIMIT 1", args);
					if (cursor.MoveToNext())
					{
						parentSequence = cursor.GetLong(0);
					}
					if (parentSequence == 0)
					{
						// Not found: either a 404 or a 409, depending on whether there is any current revision
						if (!allowConflict && ExistsDocumentWithIDAndRev(docId, null))
						{
							string msg = string.Format("Conflicts not allowed and there is already an existing doc with id: %s"
								, docId);
							throw new CouchbaseLiteException(msg, Status.Conflict);
						}
						else
						{
							string msg = string.Format("No existing revision found with doc id: %s", docId);
							throw new CouchbaseLiteException(msg, Status.NotFound);
						}
					}
					if (validations != null && validations.Count > 0)
					{
						// Fetch the previous revision and validate the new one against it:
						RevisionInternal prevRev = new RevisionInternal(docId, prevRevId, false, this);
						ValidateRevision(oldRev, prevRev);
					}
					// Make replaced rev non-current:
					ContentValues updateContent = new ContentValues();
					updateContent.Put("current", 0);
					database.Update("revs", updateContent, "sequence=" + parentSequence, null);
				}
				else
				{
					// Inserting first revision.
					if (deleted && (docId != null))
					{
						// Didn't specify a revision to delete: 404 or a 409, depending
						if (ExistsDocumentWithIDAndRev(docId, null))
						{
							throw new CouchbaseLiteException(Status.Conflict);
						}
						else
						{
							throw new CouchbaseLiteException(Status.NotFound);
						}
					}
					// Validate:
					ValidateRevision(oldRev, null);
					if (docId != null)
					{
						// Inserting first revision, with docID given (PUT):
						if (docNumericID <= 0)
						{
							// Doc doesn't exist at all; create it:
							docNumericID = InsertDocumentID(docId);
							if (docNumericID <= 0)
							{
								return null;
							}
						}
						else
						{
							// Doc ID exists; check whether current winning revision is deleted:
							if (oldWinnerWasDeletion == true)
							{
								prevRevId = oldWinningRevID;
								parentSequence = GetSequenceOfDocument(docNumericID, prevRevId, false);
							}
							else
							{
								if (oldWinningRevID != null)
								{
									// The current winning revision is not deleted, so this is a conflict
									throw new CouchbaseLiteException(Status.Conflict);
								}
							}
						}
					}
					else
					{
						// Inserting first revision, with no docID given (POST): generate a unique docID:
						docId = Database.GenerateDocumentId();
						docNumericID = InsertDocumentID(docId);
						if (docNumericID <= 0)
						{
							return null;
						}
					}
				}
				// There may be a conflict if (a) the document was already in conflict, or
				// (b) a conflict is created by adding a non-deletion child of a non-winning rev.
				inConflict = wasConflicted || (!deleted && prevRevId != null && oldWinningRevID !=
					 null && !prevRevId.Equals(oldWinningRevID));
				//// PART II: In which insertion occurs...
				// Get the attachments:
				IDictionary<string, AttachmentInternal> attachments = GetAttachmentsFromRevision(
					oldRev);
				// Bump the revID and update the JSON:
				string newRevId = GenerateNextRevisionID(prevRevId);
				byte[] data = null;
				if (!oldRev.IsDeleted())
				{
					data = EncodeDocumentJSON(oldRev);
					if (data == null)
					{
						// bad or missing json
						throw new CouchbaseLiteException(Status.BadRequest);
					}
				}
				newRev = oldRev.CopyWithDocID(docId, newRevId);
				StubOutAttachmentsInRevision(attachments, newRev);
				// Now insert the rev itself:
				long newSequence = InsertRevision(newRev, docNumericID, parentSequence, true, data
					);
				if (newSequence == 0)
				{
					return null;
				}
				// Store any attachments:
				if (attachments != null)
				{
					ProcessAttachmentsForRevision(attachments, newRev, parentSequence);
				}
				// Figure out what the new winning rev ID is:
				winningRev = Winner(docNumericID, oldWinningRevID, oldWinnerWasDeletion, newRev);
				// Success!
				if (deleted)
				{
					resultStatus.SetCode(Status.Ok);
				}
				else
				{
					resultStatus.SetCode(Status.Created);
				}
			}
			catch (SQLException e1)
			{
				Log.E(Database.Tag, "Error putting revision", e1);
				return null;
			}
			finally
			{
				if (cursor != null)
				{
					cursor.Close();
				}
				EndTransaction(resultStatus.IsSuccessful());
			}
			//// EPILOGUE: A change notification is sent...
			NotifyChange(newRev, winningRev, null, inConflict);
			return newRev;
		}
Exemple #4
0
        public void UpdateIndex()
        {
            Log.V(Log.TagView, "Re-indexing view: %s", 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)
                    Log.V(Log.TagView, "lastSequence (%s) == dbMaxSequence (%s), nothing to do", lastSequence
                          , dbMaxSequence);
                    result.SetCode(Status.NotModified);
                    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_428(this);
                //Log.v(Log.TAG_VIEW, "    emit(" + keyJson + ", "
                //        + valueJson + ")");
                // 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, no_attachments 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);
                long lastDocID = 0;
                bool keepGoing = cursor.MoveToNext();
                while (keepGoing)
                {
                    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!
                            keepGoing = cursor.MoveToNext();
                            continue;
                        }
                        string revId         = cursor.GetString(3);
                        byte[] json          = cursor.GetBlob(4);
                        bool   noAttachments = cursor.GetInt(5) > 0;
                        while ((keepGoing = cursor.MoveToNext()) && cursor.GetLong(0) == docID)
                        {
                        }
                        // Skip rows with the same doc_id -- these are losing conflicts.
                        if (lastSequence > 0)
                        {
                            // Find conflicts with documents from previous indexings.
                            string[] selectArgs2 = new string[] { System.Convert.ToString(docID), System.Convert.ToString
                                                                      (lastSequence) };
                            Cursor cursor2 = database.GetDatabase().RawQuery("SELECT revid, sequence FROM revs "
                                                                             + "WHERE doc_id=? AND sequence<=? AND current!=0 AND deleted=0 " + "ORDER BY revID DESC "
                                                                             + "LIMIT 1", selectArgs2);
                            if (cursor2.MoveToNext())
                            {
                                string oldRevId = cursor2.GetString(0);
                                // This is the revision that used to be the 'winner'.
                                // Remove its emitted rows:
                                long     oldSequence = cursor2.GetLong(1);
                                string[] args        = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                                                                          (oldSequence) };
                                database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence=?",
                                                               args);
                                if (RevisionInternal.CBLCompareRevIDs(oldRevId, revId) > 0)
                                {
                                    // It still 'wins' the conflict, so it's the one that
                                    // should be mapped [again], not the current revision!
                                    revId    = oldRevId;
                                    sequence = oldSequence;
                                    string[] selectArgs3 = new string[] { System.Convert.ToString(sequence) };
                                    json = Utils.ByteArrayResultForQuery(database.GetDatabase(), "SELECT json FROM revs WHERE sequence=?"
                                                                         , selectArgs3);
                                }
                            }
                        }
                        // Get the document properties, to pass to the map function:
                        EnumSet <Database.TDContentOptions> contentOptions = EnumSet.NoneOf <Database.TDContentOptions
                                                                                             >();
                        if (noAttachments)
                        {
                            contentOptions.AddItem(Database.TDContentOptions.TDNoAttachments);
                        }
                        IDictionary <string, object> properties = database.DocumentPropertiesFromJSON(json
                                                                                                      , docId, revId, false, sequence, contentOptions);
                        if (properties != null)
                        {
                            // Call the user-defined map() to emit new key/value
                            // pairs from this revision:
                            emitBlock.SetSequence(sequence);
                            mapBlock.Map(properties, emitBlock);
                        }
                    }
                }
                // 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(Log.TagView, "Finished re-indexing view: %s " + " up to sequence %s" + " (deleted %s added ?)"
                      , name, dbMaxSequence, deleted);
                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(Log.TagView, "Failed to rebuild view %s.  Result code: %d", name, result.GetCode
                              ());
                }
                if (database != null)
                {
                    database.EndTransaction(result.IsSuccessful());
                }
            }
        }
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		private RevisionInternal PutDoc(Database db, IDictionary<string, object> props)
		{
			RevisionInternal rev = new RevisionInternal(props, db);
			Status status = new Status();
			rev = db.PutRevision(rev, null, false, status);
			NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
			return rev;
		}
		/// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
		public virtual void TestViewIndex()
		{
			int numTimesMapFunctionInvoked = 0;
			IDictionary<string, object> dict1 = new Dictionary<string, object>();
			dict1.Put("key", "one");
			IDictionary<string, object> dict2 = new Dictionary<string, object>();
			dict2.Put("key", "two");
			IDictionary<string, object> dict3 = new Dictionary<string, object>();
			dict3.Put("key", "three");
			IDictionary<string, object> dictX = new Dictionary<string, object>();
			dictX.Put("clef", "quatre");
			RevisionInternal rev1 = PutDoc(database, dict1);
			RevisionInternal rev2 = PutDoc(database, dict2);
			RevisionInternal rev3 = PutDoc(database, dict3);
			PutDoc(database, dictX);
			View view = database.GetView("aview");
			_T1975167965 mapBlock = new _T1975167965(this);
			view.SetMap(mapBlock, "1");
			NUnit.Framework.Assert.AreEqual(1, view.GetViewId());
			NUnit.Framework.Assert.IsTrue(view.IsStale());
			view.UpdateIndex();
			IList<IDictionary<string, object>> dumpResult = view.Dump();
			Log.V(Tag, "View dump: " + dumpResult);
			NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
			NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[0].Get("key"));
			NUnit.Framework.Assert.AreEqual(1, dumpResult[0].Get("seq"));
			NUnit.Framework.Assert.AreEqual("\"two\"", dumpResult[2].Get("key"));
			NUnit.Framework.Assert.AreEqual(2, dumpResult[2].Get("seq"));
			NUnit.Framework.Assert.AreEqual("\"three\"", dumpResult[1].Get("key"));
			NUnit.Framework.Assert.AreEqual(3, dumpResult[1].Get("seq"));
			//no-op reindex
			NUnit.Framework.Assert.IsFalse(view.IsStale());
			view.UpdateIndex();
			// Now add a doc and update a doc:
			RevisionInternal threeUpdated = new RevisionInternal(rev3.GetDocId(), rev3.GetRevId
				(), false, database);
			numTimesMapFunctionInvoked = mapBlock.GetNumTimesInvoked();
			IDictionary<string, object> newdict3 = new Dictionary<string, object>();
			newdict3.Put("key", "3hree");
			threeUpdated.SetProperties(newdict3);
			Status status = new Status();
			rev3 = database.PutRevision(threeUpdated, rev3.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
			// Reindex again:
			NUnit.Framework.Assert.IsTrue(view.IsStale());
			view.UpdateIndex();
			// Make sure the map function was only invoked one more time (for the document that was added)
			NUnit.Framework.Assert.AreEqual(mapBlock.GetNumTimesInvoked(), numTimesMapFunctionInvoked
				 + 1);
			IDictionary<string, object> dict4 = new Dictionary<string, object>();
			dict4.Put("key", "four");
			RevisionInternal rev4 = PutDoc(database, dict4);
			RevisionInternal twoDeleted = new RevisionInternal(rev2.GetDocId(), rev2.GetRevId
				(), true, database);
			database.PutRevision(twoDeleted, rev2.GetRevId(), false, status);
			NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
			// Reindex again:
			NUnit.Framework.Assert.IsTrue(view.IsStale());
			view.UpdateIndex();
			dumpResult = view.Dump();
			Log.V(Tag, "View dump: " + dumpResult);
			NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
			NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[2].Get("key"));
			NUnit.Framework.Assert.AreEqual(1, dumpResult[2].Get("seq"));
			NUnit.Framework.Assert.AreEqual("\"3hree\"", dumpResult[0].Get("key"));
			NUnit.Framework.Assert.AreEqual(5, dumpResult[0].Get("seq"));
			NUnit.Framework.Assert.AreEqual("\"four\"", dumpResult[1].Get("key"));
			NUnit.Framework.Assert.AreEqual(6, dumpResult[1].Get("seq"));
			// Now do a real query:
			IList<QueryRow> rows = view.QueryWithOptions(null);
			NUnit.Framework.Assert.AreEqual(3, rows.Count);
			NUnit.Framework.Assert.AreEqual("one", rows[2].GetKey());
			NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rows[2].GetDocumentId());
			NUnit.Framework.Assert.AreEqual("3hree", rows[0].GetKey());
			NUnit.Framework.Assert.AreEqual(rev3.GetDocId(), rows[0].GetDocumentId());
			NUnit.Framework.Assert.AreEqual("four", rows[1].GetKey());
			NUnit.Framework.Assert.AreEqual(rev4.GetDocId(), rows[1].GetDocumentId());
			view.DeleteIndex();
		}
Exemple #7
0
        /// <exception cref="Couchbase.Lite.CouchbaseLiteException"></exception>
        public virtual void TestViewIndex()
        {
            int numTimesMapFunctionInvoked     = 0;
            IDictionary <string, object> dict1 = new Dictionary <string, object>();

            dict1["key"] = "one";
            IDictionary <string, object> dict2 = new Dictionary <string, object>();

            dict2["key"] = "two";
            IDictionary <string, object> dict3 = new Dictionary <string, object>();

            dict3["key"] = "three";
            IDictionary <string, object> dictX = new Dictionary <string, object>();

            dictX["clef"] = "quatre";
            RevisionInternal rev1 = PutDoc(database, dict1);
            RevisionInternal rev2 = PutDoc(database, dict2);
            RevisionInternal rev3 = PutDoc(database, dict3);

            PutDoc(database, dictX);
            View view            = database.GetView("aview");
            var  numTimesInvoked = 0;

            MapDelegate mapBlock = (IDictionary <string, object> document, EmitDelegate emitter) =>
            {
                numTimesInvoked += 1;
                NUnit.Framework.Assert.IsNotNull(document["_id"]);
                NUnit.Framework.Assert.IsNotNull(document["_rev"]);
                if (document["key"] != null)
                {
                    emitter(document["key"], null);
                }
            };

            view.SetMap(mapBlock, "1");
            NUnit.Framework.Assert.AreEqual(1, view.GetViewId());
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            IList <IDictionary <string, object> > dumpResult = view.Dump();

            Log.V(Tag, "View dump: " + dumpResult);
            NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
            NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[0]["key"]);
            NUnit.Framework.Assert.AreEqual(1, dumpResult[0]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"two\"", dumpResult[2]["key"]);
            NUnit.Framework.Assert.AreEqual(2, dumpResult[2]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"three\"", dumpResult[1]["key"]);
            NUnit.Framework.Assert.AreEqual(3, dumpResult[1]["seq"]);
            //no-op reindex
            NUnit.Framework.Assert.IsFalse(view.IsStale());
            view.UpdateIndex();
            // Now add a doc and update a doc:
            RevisionInternal threeUpdated = new RevisionInternal(rev3.GetDocId(), rev3.GetRevId
                                                                     (), false, database);

            numTimesMapFunctionInvoked = mapBlock.GetNumTimesInvoked();
            IDictionary <string, object> newdict3 = new Dictionary <string, object>();

            newdict3["key"] = "3hree";
            threeUpdated.SetProperties(newdict3);
            Status status = new Status();

            rev3 = database.PutRevision(threeUpdated, rev3.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
            // Reindex again:
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            // Make sure the map function was only invoked one more time (for the document that was added)
            NUnit.Framework.Assert.AreEqual(mapBlock.GetNumTimesInvoked(), numTimesMapFunctionInvoked
                                            + 1);
            IDictionary <string, object> dict4 = new Dictionary <string, object>();

            dict4["key"] = "four";
            RevisionInternal rev4       = PutDoc(database, dict4);
            RevisionInternal twoDeleted = new RevisionInternal(rev2.GetDocId(), rev2.GetRevId
                                                                   (), true, database);

            database.PutRevision(twoDeleted, rev2.GetRevId(), false, status);
            NUnit.Framework.Assert.IsTrue(status.IsSuccessful());
            // Reindex again:
            NUnit.Framework.Assert.IsTrue(view.IsStale());
            view.UpdateIndex();
            dumpResult = view.Dump();
            Log.V(Tag, "View dump: " + dumpResult);
            NUnit.Framework.Assert.AreEqual(3, dumpResult.Count);
            NUnit.Framework.Assert.AreEqual("\"one\"", dumpResult[2]["key"]);
            NUnit.Framework.Assert.AreEqual(1, dumpResult[2]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"3hree\"", dumpResult[0]["key"]);
            NUnit.Framework.Assert.AreEqual(5, dumpResult[0]["seq"]);
            NUnit.Framework.Assert.AreEqual("\"four\"", dumpResult[1]["key"]);
            NUnit.Framework.Assert.AreEqual(6, dumpResult[1]["seq"]);
            // Now do a real query:
            IList <QueryRow> rows = view.QueryWithOptions(null);

            NUnit.Framework.Assert.AreEqual(3, rows.Count);
            NUnit.Framework.Assert.AreEqual("one", rows[2].Key);
            NUnit.Framework.Assert.AreEqual(rev1.GetDocId(), rows[2].DocumentId);
            NUnit.Framework.Assert.AreEqual("3hree", rows[0].Key);
            NUnit.Framework.Assert.AreEqual(rev3.GetDocId(), rows[0].DocumentId);
            NUnit.Framework.Assert.AreEqual("four", rows[1].Key);
            NUnit.Framework.Assert.AreEqual(rev4.GetDocId(), rows[1].DocumentId);
            view.DeleteIndex();
        }
Exemple #8
0
 public void UpdateIndex()
 {
     Log.V(Log.TagView, "Re-indexing view: %s", 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)
             Log.V(Log.TagView, "lastSequence (%s) == dbMaxSequence (%s), nothing to do", lastSequence
                 , dbMaxSequence);
             result.SetCode(Status.NotModified);
             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_428(this);
         //Log.v(Log.TAG_VIEW, "    emit(" + keyJson + ", "
         //        + valueJson + ")");
         // 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, no_attachments 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);
         long lastDocID = 0;
         bool keepGoing = cursor.MoveToNext();
         while (keepGoing)
         {
             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!
                     keepGoing = cursor.MoveToNext();
                     continue;
                 }
                 string revId = cursor.GetString(3);
                 byte[] json = cursor.GetBlob(4);
                 bool noAttachments = cursor.GetInt(5) > 0;
                 while ((keepGoing = cursor.MoveToNext()) && cursor.GetLong(0) == docID)
                 {
                 }
                 // Skip rows with the same doc_id -- these are losing conflicts.
                 if (lastSequence > 0)
                 {
                     // Find conflicts with documents from previous indexings.
                     string[] selectArgs2 = new string[] { System.Convert.ToString(docID), System.Convert.ToString
                         (lastSequence) };
                     Cursor cursor2 = database.GetDatabase().RawQuery("SELECT revid, sequence FROM revs "
                          + "WHERE doc_id=? AND sequence<=? AND current!=0 AND deleted=0 " + "ORDER BY revID DESC "
                          + "LIMIT 1", selectArgs2);
                     if (cursor2.MoveToNext())
                     {
                         string oldRevId = cursor2.GetString(0);
                         // This is the revision that used to be the 'winner'.
                         // Remove its emitted rows:
                         long oldSequence = cursor2.GetLong(1);
                         string[] args = new string[] { Sharpen.Extensions.ToString(GetViewId()), System.Convert.ToString
                             (oldSequence) };
                         database.GetDatabase().ExecSQL("DELETE FROM maps WHERE view_id=? AND sequence=?", 
                             args);
                         if (RevisionInternal.CBLCompareRevIDs(oldRevId, revId) > 0)
                         {
                             // It still 'wins' the conflict, so it's the one that
                             // should be mapped [again], not the current revision!
                             revId = oldRevId;
                             sequence = oldSequence;
                             string[] selectArgs3 = new string[] { System.Convert.ToString(sequence) };
                             json = Utils.ByteArrayResultForQuery(database.GetDatabase(), "SELECT json FROM revs WHERE sequence=?"
                                 , selectArgs3);
                         }
                     }
                 }
                 // Get the document properties, to pass to the map function:
                 EnumSet<Database.TDContentOptions> contentOptions = EnumSet.NoneOf<Database.TDContentOptions
                     >();
                 if (noAttachments)
                 {
                     contentOptions.AddItem(Database.TDContentOptions.TDNoAttachments);
                 }
                 IDictionary<string, object> properties = database.DocumentPropertiesFromJSON(json
                     , docId, revId, false, sequence, contentOptions);
                 if (properties != null)
                 {
                     // Call the user-defined map() to emit new key/value
                     // pairs from this revision:
                     emitBlock.SetSequence(sequence);
                     mapBlock.Map(properties, emitBlock);
                 }
             }
         }
         // 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(Log.TagView, "Finished re-indexing view: %s " + " up to sequence %s" + " (deleted %s added ?)"
             , name, dbMaxSequence, deleted);
         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(Log.TagView, "Failed to rebuild view %s.  Result code: %d", name, result.GetCode
                 ());
         }
         if (database != null)
         {
             database.EndTransaction(result.IsSuccessful());
         }
     }
 }
        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());
                }
            }
        }
Exemple #10
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());
				}
			}
		}
Exemple #11
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());
                }
            }
        }