Esempio n. 1
0
        private bool UpdateDoc(BsonValue id, BsonDocument doc)
        {
            // serialize object
            var bytes = BsonSerializer.Serialize(doc);

            // start transaction
            this.Database.Transaction.Begin();

            try
            {
                var col = this.GetCollectionPage(false);

                // if no collection, no updates
                if (col == null)
                {
                    this.Database.Transaction.Abort();
                    return(false);
                }

                // normalize id before find
                var value = id.Normalize(col.PK.Options);

                // find indexNode from pk index
                var indexNode = this.Database.Indexer.Find(col.PK, value, false, Query.Ascending);

                // if not found document, no updates
                if (indexNode == null)
                {
                    this.Database.Transaction.Abort();
                    return(false);
                }

                // update data storage
                var dataBlock = this.Database.Data.Update(col, indexNode.DataBlock, bytes);

                // delete/insert indexes - do not touch on PK
                foreach (var index in col.GetIndexes(false))
                {
                    var key = doc.Get(index.Field);

                    var node = this.Database.Indexer.GetNode(dataBlock.IndexRef[index.Slot]);

                    // check if my index node was changed
                    if (node.Key.CompareTo(key) != 0)
                    {
                        // remove old index node
                        this.Database.Indexer.Delete(index, node.Position);

                        // and add a new one
                        var newNode = this.Database.Indexer.AddNode(index, key);

                        // point my index to data object
                        newNode.DataBlock = dataBlock.Position;

                        // point my dataBlock
                        dataBlock.IndexRef[index.Slot] = newNode.Position;

                        dataBlock.Page.IsDirty = true;
                    }
                }

                this.Database.Transaction.Commit();

                return(true);
            }
            catch
            {
                this.Database.Transaction.Rollback();
                throw;
            }
        }
Esempio n. 2
0
        /// <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);

            // 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 = BsonSerializer.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))
            {
                // using Distinct/ToArray because I will do many queries
                var keys = doc.GetValues(index.Field, true, index.Unique).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);
        }
Esempio n. 3
0
        /// <summary>
        /// Internal implementation of insert a document
        /// </summary>
        private void InsertDocument(CollectionPage col, BsonDocument doc, BsonType autoId)
        {
            BsonValue id;

            // 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 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 = BsonSerializer.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;
                }
            }
        }
Esempio n. 4
0
        /// <summary>
        /// Implement update command to a document inside a collection
        /// </summary>
        public int Update(string colName, IEnumerable <BsonDocument> docs)
        {
            return(this.WriteTransaction <int>(colName, false, (col) =>
            {
                // no collection, no updates
                if (col == null)
                {
                    return 0;
                }

                var count = 0;

                foreach (var doc in docs)
                {
                    // normalize id before find
                    var id = doc["_id"].Normalize(col.PK.Options);

                    // 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}", colName, id);

                    // find indexNode from pk index
                    var indexNode = _indexer.Find(col.PK, id, false, Query.Ascending);

                    // if not found document, no updates
                    if (indexNode == null)
                    {
                        continue;
                    }

                    // serialize document in bytes
                    var bytes = BsonSerializer.Serialize(doc);

                    // update data storage
                    var dataBlock = _data.Update(col, indexNode.DataBlock, bytes);

                    // delete/insert indexes - do not touch on PK
                    foreach (var index in col.GetIndexes(false))
                    {
                        var key = doc.Get(index.Field);

                        var node = _indexer.GetNode(dataBlock.IndexRef[index.Slot]);

                        // check if my index node was changed
                        if (node.Key.CompareTo(key) != 0)
                        {
                            // remove old index node
                            _indexer.Delete(index, node.Position);

                            // and add a new one
                            var newNode = _indexer.AddNode(index, key);

                            // point my index to data object
                            newNode.DataBlock = dataBlock.Position;

                            // set my block page as dirty before change
                            _pager.SetDirty(dataBlock.Page);

                            // point my dataBlock
                            dataBlock.IndexRef[index.Slot] = newNode.Position;
                        }
                    }

                    _cache.CheckPoint();

                    count++;
                }

                return count;
            }));
        }
Esempio n. 5
0
        /// <summary>
        /// Insert a new document to this collection. Document Id must be a new value in collection
        /// </summary>
        public virtual void Insert(T doc)
        {
            if (doc == null)
            {
                throw new ArgumentNullException("doc");
            }

            // gets document Id
            var id = BsonSerializer.GetIdValue(doc);

            if (id == null)
            {
                throw new LiteException("Document Id can't be null");
            }

            // serialize object
            var bytes = BsonSerializer.Serialize(doc);

            _engine.Transaction.Begin();

            try
            {
                var col = this.GetCollectionPage(true);

                // storage in data pages - returns dataBlock address
                var dataBlock = _engine.Data.Insert(col, new IndexKey(id), bytes);

                // store id in a PK index [0 array]
                var pk = _engine.Indexer.AddNode(col.PK, id);

                // do links between index <-> data block
                pk.DataBlock          = dataBlock.Position;
                dataBlock.IndexRef[0] = pk.Position;

                // for each index, insert new IndexNode
                for (byte i = 1; i < col.Indexes.Length; i++)
                {
                    var index = col.Indexes[i];

                    if (!index.IsEmpty)
                    {
                        var key = BsonSerializer.GetFieldValue(doc, index.Field);

                        var node = _engine.Indexer.AddNode(index, key);

                        // point my index to data object
                        node.DataBlock = dataBlock.Position;

                        // point my dataBlock
                        dataBlock.IndexRef[i] = node.Position;
                    }
                }

                _engine.Transaction.Commit();
            }
            catch (Exception ex)
            {
                _engine.Transaction.Rollback();
                throw ex;
            }
        }
Esempio n. 6
0
        /// <summary>
        /// Insert a new document to this collection. Document Id must be a new value in collection - Returns document Id
        /// </summary>
        public virtual BsonValue Insert(T document)
        {
            if (document == null)
            {
                throw new ArgumentNullException("document");
            }

            // set an id value if document object needs
            this.Database.Mapper.SetAutoId(document, this.GetBsonCollection());

            var doc = this.Database.Mapper.ToDocument(document);

            BsonValue id;

            // add ObjectId to _id if _id not found
            if (!doc.RawValue.TryGetValue("_id", out id))
            {
                id = doc["_id"] = ObjectId.NewObjectId();
            }

            // test if _id is a valid type
            if (id.IsNull || id.IsMinValue || id.IsMaxValue)
            {
                throw LiteException.InvalidDataType("_id", id);
            }

            // serialize object
            var bytes = BsonSerializer.Serialize(doc);

            this.Database.Transaction.Begin();

            try
            {
                var col = this.GetCollectionPage(true);

                // storage in data pages - returns dataBlock address
                var dataBlock = this.Database.Data.Insert(col, bytes);

                // store id in a PK index [0 array]
                var pk = this.Database.Indexer.AddNode(col.PK, id);

                // do links between index <-> data block
                pk.DataBlock          = dataBlock.Position;
                dataBlock.IndexRef[0] = pk.Position;

                // for each index, insert new IndexNode
                foreach (var index in col.GetIndexes(false))
                {
                    var key = doc.Get(index.Field);

                    var node = this.Database.Indexer.AddNode(index, key);

                    // point my index to data object
                    node.DataBlock = dataBlock.Position;

                    // point my dataBlock
                    dataBlock.IndexRef[index.Slot] = node.Position;
                }

                this.Database.Transaction.Commit();

                return(id);
            }
            catch
            {
                this.Database.Transaction.Rollback();
                throw;
            }
        }