/// <summary> /// Delete one dataBlock /// </summary> public DataBlock Delete(CollectionPage col, PageAddress blockAddress) { // get page and mark as dirty var page = _pager.GetPage <DataPage>(blockAddress.PageID); var block = page.GetBlock(blockAddress.Index); // if there a extended page, delete all if (block.ExtendPageID != uint.MaxValue) { _pager.DeletePage(block.ExtendPageID, true); } // delete block inside page page.DeleteBlock(block); // set page as dirty here _pager.SetDirty(page); // if there is no more datablocks, lets delete all page if (page.BlocksCount == 0) { // first, remove from free list _pager.AddOrRemoveToFreeList(false, page, col, ref col.FreeDataPageID); _pager.DeletePage(page.PageID); } else { // add or remove to free list _pager.AddOrRemoveToFreeList(page.FreeBytes > DataPage.DATA_RESERVED_BYTES, page, col, ref col.FreeDataPageID); } col.DocumentCount--; // mark collection page as dirty _pager.SetDirty(col); return(block); }
/// <summary> /// Create a new index and returns head page address (skip list) /// </summary> public CollectionIndex CreateIndex(CollectionPage col) { // get index slot var index = col.GetFreeIndex(); // get a new index page for first index page var page = _pager.NewPage <IndexPage>(); // create a empty node with full max level var head = new IndexNode(IndexNode.MAX_LEVEL_LENGTH) { Key = BsonValue.MinValue, KeyLength = (ushort)BsonValue.MinValue.GetBytesCount(false), Slot = (byte)index.Slot, Page = page }; // add as first node page.AddNode(head); // set index page as dirty _pager.SetDirty(index.Page); // add indexPage on freelist if has space _pager.AddOrRemoveToFreeList(true, page, index.Page, ref index.FreeIndexPageID); // point the head/tail node to this new node position index.HeadNode = head.Position; // insert tail node var tail = this.AddNode(index, BsonValue.MaxValue, IndexNode.MAX_LEVEL_LENGTH, null); index.TailNode = tail.Position; return(index); }
/// <summary> /// Internal implementation of insert a document /// </summary> private void InsertDocument(CollectionPage col, BsonDocument doc, BsonType autoId) { // collection Sequence was created after release current datafile version. // In this case, Sequence will be 0 but already has documents. Let's fix this // ** this code can be removed when datafile change from 7 (HeaderPage.FILE_VERSION) ** if (col.Sequence == 0 && col.DocumentCount > 0) { var max = this.Max(col.CollectionName, "_id"); // if max value is a number, convert to Sequence last value // if not, just set sequence as document count col.Sequence = (max.IsInt32 || max.IsInt64 || max.IsDouble || max.IsDecimal) ? Convert.ToInt64(max.RawValue) : Convert.ToInt64(col.DocumentCount); } // increase collection sequence _id col.Sequence++; _pager.SetDirty(col); // if no _id, add one if (!doc.RawValue.TryGetValue("_id", out var id)) { doc["_id"] = id = autoId == BsonType.ObjectId ? new BsonValue(ObjectId.NewObjectId()) : autoId == BsonType.Guid ? new BsonValue(Guid.NewGuid()) : autoId == BsonType.DateTime ? new BsonValue(DateTime.Now) : autoId == BsonType.Int32 ? new BsonValue((Int32)col.Sequence) : autoId == BsonType.Int64 ? new BsonValue(col.Sequence) : BsonValue.Null; } // create bubble in sequence number if _id is bigger than current sequence else if (autoId == BsonType.Int32 || autoId == BsonType.Int64) { var current = id.AsInt64; // if current id is bigger than sequence, jump sequence to this number. Other was, do not increse sequnce col.Sequence = current >= col.Sequence ? current : col.Sequence - 1; } // test if _id is a valid type if (id.IsNull || id.IsMinValue || id.IsMaxValue) { throw LiteException.InvalidDataType("_id", id); } _log.Write(Logger.COMMAND, "insert document on '{0}' :: _id = {1}", col.CollectionName, id.RawValue); // serialize object var bytes = _bsonWriter.Serialize(doc); // storage in data pages - returns dataBlock address var dataBlock = _data.Insert(col, bytes); // store id in a PK index [0 array] var pk = _indexer.AddNode(col.PK, id, null); // do link between index <-> data block pk.DataBlock = dataBlock.Position; // for each index, insert new IndexNode foreach (var index in col.GetIndexes(false)) { // for each index, get all keys (support now multi-key) - gets distinct values only // if index are unique, get single key only var expr = new BsonExpression(index.Expression); var keys = expr.Execute(doc, true); // do a loop with all keys (multi-key supported) foreach (var key in keys) { // insert node var node = _indexer.AddNode(index, key, pk); // link my index node to data block address node.DataBlock = dataBlock.Position; } } }
/// <summary> /// Implement internal update document /// </summary> private bool UpdateDocument(CollectionPage col, BsonDocument doc) { // normalize id before find var id = doc["_id"]; // validate id for null, min/max values if (id.IsNull || id.IsMinValue || id.IsMaxValue) { throw LiteException.InvalidDataType("_id", id); } _log.Write(Logger.COMMAND, "update document on '{0}' :: _id = {1}", col.CollectionName, id.RawValue); // find indexNode from pk index var pkNode = _indexer.Find(col.PK, id, false, Query.Ascending); // if not found document, no updates if (pkNode == null) { return(false); } // serialize document in bytes var bytes = _bsonWriter.Serialize(doc); // update data storage var dataBlock = _data.Update(col, pkNode.DataBlock, bytes); // get all non-pk index nodes from this data block var allNodes = _indexer.GetNodeList(pkNode, false).ToArray(); // delete/insert indexes - do not touch on PK foreach (var index in col.GetIndexes(false)) { var expr = new BsonExpression(index.Expression); // getting all keys do check var keys = expr.Execute(doc).ToArray(); // get a list of to delete nodes (using ToArray to resolve now) var toDelete = allNodes .Where(x => x.Slot == index.Slot && !keys.Any(k => k == x.Key)) .ToArray(); // get a list of to insert nodes (using ToArray to resolve now) var toInsert = keys .Where(x => !allNodes.Any(k => k.Slot == index.Slot && k.Key == x)) .ToArray(); // delete changed index nodes foreach (var node in toDelete) { _indexer.Delete(index, node.Position); } // insert new nodes foreach (var key in toInsert) { // and add a new one var node = _indexer.AddNode(index, key, pkNode); // link my node to data block node.DataBlock = dataBlock.Position; } } return(true); }