/// <summary> /// Implements delete based on IDs enumerable /// </summary> public int Delete(string collection, IEnumerable <BsonValue> ids) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(collection)); } if (ids == null) { throw new ArgumentNullException(nameof(ids)); } return(this.AutoTransaction(transaction => { var snapshot = transaction.CreateSnapshot(LockMode.Write, collection, false); var collectionPage = snapshot.CollectionPage; var data = new DataService(snapshot); var indexer = new IndexService(snapshot, _header.Pragmas.Collation); if (collectionPage == null) { return 0; } var count = 0; var pk = collectionPage.PK; foreach (var id in ids) { var pkNode = indexer.Find(pk, id, false, LiteDB.Query.Ascending); // if pk not found, continue if (pkNode == null) { continue; } // remove object data data.Delete(pkNode.DataBlock); if (pkNode.NextNode.IsEmpty) { ; } // delete all nodes (start in pk node) indexer.DeleteAll(pkNode.Position); transaction.Safepoint(); count++; } return count; })); }
public override async IAsyncEnumerable <IndexNode> Execute(IndexService indexer, CollectionIndex index) { var node = await indexer.Find(index, _value, false, Query.Ascending); if (node == null) { yield break; } yield return(node); if (index.Unique == false) { // navigate in both sides to return all nodes found var first = node; // first go forward while (!node.Next[0].IsEmpty && ((node = await indexer.GetNode(node.Next[0])).Key.CompareTo(_value, indexer.Collation) == 0)) { if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } yield return(node); } node = first; // and than, go backward while (!node.Prev[0].IsEmpty && ((node = await indexer.GetNode(node.Prev[0])).Key.CompareTo(_value, indexer.Collation) == 0)) { if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } yield return(node); } } }
private IEnumerable <IndexNode> ExecuteStartsWith(IndexService indexer, CollectionIndex index) { // find first indexNode var first = indexer.Find(index, _startsWith, true, this.Order); var node = first; // if collection exists but are empty if (first == null) { yield break; } // first, go backward to get all same values while (node != null) { // if current node are edges exit while if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } var valueString = node.Key.IsString ? node.Key.AsString : node.Key.ToString(); if (_equals ? valueString.Equals(_startsWith, StringComparison.OrdinalIgnoreCase) : valueString.StartsWith(_startsWith, StringComparison.OrdinalIgnoreCase)) { // must still testing SqlLike method for rest of pattern - only if exists more to test (avoid slow SqlLike test) if ((_testSqlLike == false) || (_testSqlLike == true && valueString.SqlLike(_pattern) == true)) { yield return(node); } } else { break; } node = indexer.GetNode(node.GetNextPrev(0, -this.Order)); } // move fordward node = indexer.GetNode(first.GetNextPrev(0, this.Order)); while (node != null) { // if current node are edges exit while if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } var valueString = node.Key.IsString ? node.Key.AsString : node.Key.ToString(); if (_equals ? valueString.Equals(_pattern, StringComparison.OrdinalIgnoreCase) : valueString.StartsWith(_startsWith, StringComparison.OrdinalIgnoreCase)) { // must still testing SqlLike method for rest of pattern - only if exists more to test (avoid slow SqlLike test) if (node.DataBlock.IsEmpty == false && ((_testSqlLike == false) || (_testSqlLike == true && valueString.SqlLike(_pattern) == true))) { yield return(node); } } else { break; } // first, go backward to get all same values node = indexer.GetNode(node.GetNextPrev(0, this.Order)); } }
/// <summary> /// Implement internal update document /// </summary> private bool UpdateDocument(Snapshot snapshot, CollectionPage col, BsonDocument doc, IndexService indexer, DataService data) { // 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); } // find indexNode from pk index var pkNode = indexer.Find(col.PK, id, false, LiteDB.Query.Ascending); // if not found document, no updates if (pkNode == null) { return(false); } // update data storage data.Update(col, pkNode.DataBlock, doc); // get all current non-pk index nodes from this data block (slot, key, nodePosition) var oldKeys = indexer.GetNodeList(pkNode.NextNode) .Select(x => new Tuple <byte, BsonValue, PageAddress>(x.Slot, x.Key, x.Position)) .ToArray(); // build a list of all new key index keys var newKeys = new List <Tuple <byte, BsonValue, string> >(); foreach (var index in col.GetCollectionIndexes().Where(x => x.Name != "_id")) { // getting all keys from expression over document var keys = index.BsonExpr.Execute(doc, _header.Pragmas.Collation); foreach (var key in keys) { newKeys.Add(new Tuple <byte, BsonValue, string>(index.Slot, key, index.Name)); } } if (oldKeys.Length == 0 && newKeys.Count == 0) { return(true); } // get a list of all nodes that are in oldKeys but not in newKeys (must delete) var toDelete = new HashSet <PageAddress>(oldKeys .Where(x => newKeys.Any(n => n.Item1 == x.Item1 && n.Item2 == x.Item2) == false) .Select(x => x.Item3)); // get a list of all keys that are not in oldKeys (must insert) var toInsert = newKeys .Where(x => oldKeys.Any(o => o.Item1 == x.Item1 && o.Item2 == x.Item2) == false) .ToArray(); // if nothing to change, just exit if (toDelete.Count == 0 && toInsert.Length == 0) { return(true); } // delete nodes and return last keeped node in list var last = indexer.DeleteList(pkNode.Position, toDelete); // now, insert all new nodes foreach (var elem in toInsert) { var index = col.GetCollectionIndex(elem.Item3); last = indexer.AddNode(index, elem.Item2, pkNode.DataBlock, last); } return(true); }
public override IEnumerable <IndexNode> Execute(IndexService indexer, CollectionIndex index) { // if order are desc, swap start/end values var start = this.Order == Query.Ascending ? _start : _end; var end = this.Order == Query.Ascending ? _end : _start; var startEquals = this.Order == Query.Ascending ? _startEquals : _endEquals; var endEquals = this.Order == Query.Ascending ? _endEquals : _startEquals; // find first indexNode (or get from head/tail if Min/Max value) var first = start.Type == BsonType.MinValue ? indexer.GetNode(index.Head) : start.Type == BsonType.MaxValue ? indexer.GetNode(index.Tail) : indexer.Find(index, start, true, this.Order); var node = first; // if startsEquals, return all equals value from start linked list if (startEquals && node != null) { // going backward in same value list to get first value while (!node.GetNextPrev(0, -this.Order).IsEmpty&& ((node = indexer.GetNode(node.GetNextPrev(0, -this.Order))).Key.CompareTo(start) == 0)) { if (node.Key.IsMinValue || node.Key.IsMaxValue) { break; } yield return(node); } node = first; } // returns (or not) equals start value while (node != null) { var diff = node.Key.CompareTo(start); // if current value are not equals start, go out this loop if (diff != 0) { break; } if (startEquals && !(node.Key.IsMinValue || node.Key.IsMaxValue)) { yield return(node); } node = indexer.GetNode(node.GetNextPrev(0, this.Order)); } // navigate using next[0] do next node - if less or equals returns while (node != null) { var diff = node.Key.CompareTo(end); if (endEquals && diff == 0 && !(node.Key.IsMinValue || node.Key.IsMaxValue)) { yield return(node); } else if (diff == -this.Order && !(node.Key.IsMinValue || node.Key.IsMaxValue)) { yield return(node); } else { break; } node = indexer.GetNode(node.GetNextPrev(0, this.Order)); } }