/// <summary> /// Sort the documents of resultset in descending order according to a key (support only one OrderBy) /// </summary> public ILiteQueryableAsync <T> OrderByDescending(BsonExpression keySelector) { _wrappedQuery.OrderByDescending(keySelector); return(this); }
public void Expressions_Scalar_Path() { BsonDocument doc; BsonValue S(string s) { return(BsonExpression.Create(s).ExecuteScalar(doc)); } ; // direct path navigation doc = J("{ a: 1, b: null, c: true, d:[1,2], e:{d:4} }"); S("a").ExpectValue(1); S("b").ExpectValue(BsonValue.Null); S("c").ExpectValue(true); S("d").ExpectArray(1, 2); S("e").ExpectJson("{d:4}"); // dive into subdocumentos doc = J("{ a: { b: { c: { d: 1 } } } }"); S("a").ExpectJson("{ b: { c: { d: 1 } } }"); S("a.b").ExpectJson("{ c: { d: 1 } }"); S("a.b.c").ExpectJson("{ d: 1 }"); S("a.b.c.d").ExpectValue(1); // Missing field doc = J("{ a: { b: 1 } }"); S("b").ExpectValue(BsonValue.Null); S("a.c").ExpectValue(BsonValue.Null); S("x.j").ExpectValue(BsonValue.Null); // Array fixed position doc = J("{ a: [1, 2, 3] }"); S("a[0]").ExpectValue(1); S("a[1]").ExpectValue(2); S("a[-1]").ExpectValue(3); S("a[99]").ExpectValue(BsonValue.Null); // Root and Current in array doc = J("{ a: [ { b: 1, c: 2 }, { b: 2, c: 3 } ], i: 0 }"); S("FIRST(a[@.b = 1].c)").ExpectValue(2); S("FIRST(a[b = 2].c)").ExpectValue(3); // Complex field name doc = J("{ \"a b\": 1, \"c d\": { \"x y\": 2 }, x: { \"$y!z\\\"'\": 3 } }"); S("$.[\"a b\"]").ExpectValue(1); S("$.['c d'].['x y']").ExpectValue(2); S("$.x.[\"$y!z\\\"'\"]").ExpectValue(3); // Object creation S("'lite' + \"db\"").ExpectValue("litedb"); S("true").ExpectValue(true); S("123").ExpectValue(123); S("{ a: 1}").ExpectJson("{ a: 1}"); S("{ b: 1+1 }").ExpectJson("{ b: 2 }"); S("{'a-b':1, \"x+1\": 2, 'y': 3}").ExpectJson("{\"a-b\": 1, \"x+1\": 2, y: 3 }"); // Document simplified notation declaration doc = J("{ a: 1, b: 2, c: 3, d: {e: 4, f: 5 }}"); S("{ a }").ExpectJson("{ a: 1 }"); S("{ a, c }").ExpectJson("{ a: 1, c: 3 }"); S("{ d, z: d.e }").ExpectJson("{ d: { e: 4, f: 5 }, z: 4 }"); // Document simplified notation with complex key doc = J("{ \"a b\": 1, c: 2 }"); S("{ 'a b', z: null }").ExpectJson("{ \"a b\": 1, z: null }"); S("{ 'c' }").ExpectJson("{ c: 2 }"); }
public IEnumerable <T> Get(BsonExpression predicate, int skip = 0, int limit = int.MaxValue) { return(LiteDb.GetCollection <T>(CollectionName).Find(predicate, skip, limit)); }
/// <summary> /// INCLUDE: Do include in result document according path expression - Works only with DocumentLookup /// </summary> protected IEnumerable <BsonDocument> Include(IEnumerable <BsonDocument> source, BsonExpression path) { // cached services string last = null; Snapshot snapshot = null; IndexService indexer = null; DataService data = null; CollectionIndex index = null; IDocumentLookup lookup = null; foreach (var doc in source) { foreach (var value in path.Execute(doc, _pragmas.Collation) .Where(x => x.IsDocument || x.IsArray) .ToList()) { // if value is document, convert this ref document into full document (do another query) if (value.IsDocument) { DoInclude(value.AsDocument); } else { // if value is array, do same per item foreach (var item in value.AsArray .Where(x => x.IsDocument) .Select(x => x.AsDocument)) { DoInclude(item); } } } yield return(doc); } void DoInclude(BsonDocument value) { // works only if is a document var refId = value["$id"]; var refCol = value["$ref"]; // if has no reference, just go out if (refId.IsNull || !refCol.IsString) { return; } // do some cache re-using when is same $ref (almost always is the same $ref collection) if (last != refCol.AsString) { last = refCol.AsString; // initialize services snapshot = _transaction.CreateSnapshot(LockMode.Read, last, false); indexer = new IndexService(snapshot, _pragmas.Collation); data = new DataService(snapshot); lookup = new DatafileLookup(data, _pragmas.UtcDate, null); index = snapshot.CollectionPage?.PK; } // fill only if index and ref node exists if (index != null) { var node = indexer.Find(index, refId, false, Query.Ascending); if (node != null) { // load document based on dataBlock position var refDoc = lookup.Load(node); value.Remove("$id"); value.Remove("$ref"); refDoc.CopyTo(value); } else { // set in ref document that was not found value.Add("$missing", true); } } } }
private BsonExpression TestExpr <T>(Expression <Func <T, object> > expr, BsonExpression expect, params BsonValue[] args) { return(this.Test <T, object>(expr, expect, args)); }
/// <summary> /// Create a new index (or do nothing if already exists) to a collection/field /// </summary> public bool EnsureIndex(string collection, string name, BsonExpression expression, bool unique) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(collection)); } if (name.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(name)); } if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (expression.IsIndexable == false) { throw new ArgumentException("Index expressions must contains at least one document field. Used methods must be immutable. Parameters are not supported.", nameof(expression)); } if (name.Length > INDEX_NAME_MAX_LENGTH) { throw LiteException.InvalidIndexName(name, collection, "MaxLength = " + INDEX_NAME_MAX_LENGTH); } if (!name.IsWord()) { throw LiteException.InvalidIndexName(name, collection, "Use only [a-Z$_]"); } if (name.StartsWith("$")) { throw LiteException.InvalidIndexName(name, collection, "Index name can't starts with `$`"); } if (name == "_id") { return(false); // always exists } return(this.AutoTransaction(transaction => { var snapshot = transaction.CreateSnapshot(LockMode.Write, collection, true); var collectionPage = snapshot.CollectionPage; var indexer = new IndexService(snapshot, _header.Pragmas.Collation); var data = new DataService(snapshot); // check if index already exists var current = collectionPage.GetCollectionIndex(name); // if already exists, just exit if (current != null) { // but if expression are different, throw error if (current.Expression != expression.Source) { throw LiteException.IndexAlreadyExist(name); } return false; } LOG($"create index `{collection}.{name}`", "COMMAND"); // create index head var index = indexer.CreateIndex(name, expression.Source, unique); var count = 0u; // read all objects (read from PK index) foreach (var pkNode in new IndexAll("_id", LiteDB.Query.Ascending).Run(collectionPage, indexer)) { using (var reader = new BufferReader(data.Read(pkNode.DataBlock))) { var doc = reader.ReadDocument(expression.Fields); // first/last node in this document that will be added IndexNode last = null; IndexNode first = null; // get values from expression in document var keys = expression.Execute(doc, _header.Pragmas.Collation); // adding index node for each value foreach (var key in keys) { // when index key is an array, get items inside array. // valid only for first level (if this items are another array, this arrays will be indexed as array) if (key.IsArray) { var arr = key.AsArray; foreach (var itemKey in arr) { // insert new index node var node = indexer.AddNode(index, itemKey, pkNode.DataBlock, last); if (first == null) { first = node; } last = node; count++; } } else { // insert new index node var node = indexer.AddNode(index, key, pkNode.DataBlock, last); if (first == null) { first = node; } last = node; count++; } } // fix single linked-list in pkNode if (first != null) { last.SetNextNode(pkNode.NextNode); pkNode.SetNextNode(first.Position); } } transaction.Safepoint(); } return true; })); }
/// <summary> /// WHERE: Filter document according expression. Expression must be an Bool result /// </summary> protected IEnumerable <BsonDocument> Filter(IEnumerable <BsonDocument> source, BsonExpression expr) { foreach (var doc in source) { // checks if any result of expression is true var result = expr.ExecuteScalar(doc, _pragmas.Collation); if (result.IsBoolean && result.AsBoolean) { yield return(doc); } } }
/// <summary> /// Sort the documents of resultset in ascending (or descending) order according to a key (support only one OrderBy) /// </summary> public ILiteQueryableAsync <T> OrderBy(BsonExpression keySelector, int order = Query.Ascending) { _wrappedQuery.OrderBy(keySelector, order); return(this); }
public override Album GetById(long id) { return(repo.Include(BsonExpression.Create("$.Songs[*]")).FindById(id)); }
/// <summary> /// Load cross reference documents from path expression (DbRef reference) /// </summary> public ILiteQueryableAsync <T> Include(BsonExpression path) { _wrappedQuery.Include(path); return(this); }
/// <summary> /// Filters a sequence of documents based on a predicate expression /// </summary> public ILiteQueryableAsync <T> Where(BsonExpression predicate) { _wrappedQuery.Where(predicate); return(this); }
/// <summary> /// Transform input document into a new output document. Can be used with each document, group by or all source /// </summary> public ILiteQueryableAsyncResult <BsonDocument> Select(BsonExpression selector) { return(new LiteQueryableAsync <BsonDocument>((ILiteQueryable <BsonDocument>)_wrappedQuery.Select(selector), _liteDatabaseAsync)); }
/// <summary> /// Filter documents after group by pipe according to predicate expression (requires GroupBy and support only one Having) /// </summary> public ILiteQueryableAsync <T> Having(BsonExpression predicate) { _wrappedQuery.Having(predicate); return(this); }
/// <summary> /// Groups the documents of resultset according to a specified key selector expression (support only one GroupBy) /// </summary> public ILiteQueryableAsync <T> GroupBy(BsonExpression keySelector) { _wrappedQuery.GroupBy(keySelector); return(this); }
public override IEnumerable <Playlist> All() { return(repo .Include(BsonExpression.Create("$.Songs[*]")) .FindAll()); }
public IEnumerable <BsonValue> Execute(StringScanner s, LiteEngine engine) { var col = this.ReadCollection(engine, s); // single document update if (s.Match(@"\s*\{")) { var doc = JsonSerializer.Deserialize(s.ToString()).AsDocument; s.ThrowIfNotFinish(); yield return(engine.Update(col, doc)); } // query update else { // db.colName.update // field = value, // array += valueToAdd, // where _id = 1 // and ... var updates = new List <UpdateData>(); var query = Query.All(); while (!s.HasTerminated) { var path = BsonExpression.ReadExpression(s, true, true).Source; var action = s.Scan(@"\s*\+?=\s*").Trim().ThrowIfEmpty("Invalid operator (support = or +=)", s); var value = this.ReadBsonValue(s); var expr = value == null?BsonExpression.ReadExpression(s, true, false) : null; if (action != "+=" && action != "=") { throw LiteException.SyntaxError(s); } if (value == null && expr == null) { throw LiteException.SyntaxError(s); } updates.Add(new UpdateData { Path = path, Value = value, Expr = expr, Add = action == "+=" }); s.Scan(@"\s*"); if (s.Scan(@",\s*").Length > 0) { continue; } else if (s.Scan(@"where\s*").Length > 0 || s.HasTerminated) { break; } else { throw LiteException.SyntaxError(s); } } if (!s.HasTerminated) { query = this.ReadQuery(s, false); } s.ThrowIfNotFinish(); // fetch documents to update var count = engine.Update(col, this.FetchDocuments(engine, col, query, updates)); yield return(count); } }
/// <summary> /// Pipe: Run select expression over all recordset /// </summary> private IEnumerable <BsonDocument> SelectAll(IEnumerable <BsonDocument> source, BsonExpression select) { var defaultName = select.DefaultFieldName(); var result = select.Execute(source); //TODO: pode ter algum tipo de CACHE caso a expressão contenha mais de 1 "UseSource"... // evita executar todo pipe -- pior dos casos dá um ToArray() (ou usa um DocumentGroup) foreach (var value in result) { if (value.IsDocument) { yield return(value.AsDocument); } else { yield return(new BsonDocument { [defaultName] = value }); } } }
public Select(BsonExpression expression, bool all) { this.Expression = expression; this.All = all; }
/// <summary> /// Update documents using transform expression (must return a scalar/document value) using predicate as filter /// </summary> public int UpdateMany(string collection, BsonExpression transform, BsonExpression predicate) { if (collection.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(collection)); } if (transform == null) { throw new ArgumentNullException(nameof(transform)); } return(this.AutoTransaction(transaction => { return this.Update(collection, transformDocs()); IEnumerable <BsonDocument> transformDocs() { var q = new Query { Select = "$", ForUpdate = true }; if (predicate != null) { q.Where.Add(predicate); } using (var reader = this.Query(collection, q)) { while (reader.Read()) { var doc = reader.Current.AsDocument; var id = doc["_id"]; var value = transform.ExecuteScalar(doc); if (!value.IsDocument) { throw new ArgumentException("Extend expression must return a document", nameof(transform)); } var result = value.AsDocument; // be sure result document will contain same _id as current doc if (result.TryGetValue("_id", out var newId)) { if (newId != id) { throw LiteException.InvalidUpdateField("_id"); } } else { result["_id"] = id; } yield return result; } } } })); }
/// <summary> /// Delete all documents based on predicate expression. Returns how many documents was deleted /// </summary> public Task <int> DeleteManyAsync(BsonExpression predicate) { return(Database.EnqueueAsync( () => UnderlyingCollection.DeleteMany(predicate))); }
/// <summary> /// ORDER BY: Sort documents according orderby expression and order asc/desc /// </summary> protected IEnumerable <BsonDocument> OrderBy(IEnumerable <BsonDocument> source, BsonExpression expr, int order, int offset, int limit) { var keyValues = source .Select(x => new KeyValuePair <BsonValue, PageAddress>(expr.ExecuteScalar(x, _pragmas.Collation), x.RawId)); using (var sorter = new SortService(_tempDisk, order, _pragmas)) { sorter.Insert(keyValues); LOG($"sort {sorter.Count} keys in {sorter.Containers.Count} containers", "SORT"); var result = sorter.Sort().Skip(offset).Take(limit); foreach (var keyValue in result) { var doc = _lookup.Load(keyValue.Value); yield return(doc); } } }
private IEnumerable <BsonDocument> SelectMany(IEnumerable <BsonDocument> source, BsonExpression select) { var defaultName = select.DefaultFieldName(); foreach (var doc in source) { var values = select.Execute(doc, _pragmas.Collation); foreach (var value in values) { if (value.IsDocument) { yield return(value.AsDocument); } else { yield return(new BsonDocument { [defaultName] = value }); } } } }
public bool EnsureIndex(string collection, string name, BsonExpression expression, bool unique) { return(_engine.EnsureIndex(collection, name, expression, unique)); }
/// <summary> /// Pipe: Run select expression over all recordset /// </summary> private IEnumerable <BsonDocument> SelectAll(IEnumerable <BsonDocument> source, BsonExpression select) { var cached = new DocumentCacheEnumerable(source, _lookup); var defaultName = select.DefaultFieldName(); var result = select.Execute(cached, _pragmas.Collation); foreach (var value in result) { if (value.IsDocument) { yield return(value.AsDocument); } else { yield return(new BsonDocument { [defaultName] = value }); } } }
private BsonExpression TestPredicate <T>(Expression <Func <T, bool> > expr, BsonExpression expect, params BsonValue[] args) { return(this.Test <T, bool>(expr, expect, args)); }
public IEnumerable <T> Find(BsonExpression predicate) { return(Tablet.Find(predicate)); }
/// <summary> /// Run an include action in each document returned by Find(), FindById(), FindOne() and All() methods to load DbRef documents /// Returns a new Collection with this action included /// </summary> public ILiteCollectionAsync <T> Include(BsonExpression keySelector) { return(new LiteCollectionAsync <T>(UnderlyingCollection.Include(keySelector), Database)); }
public T FindObj(BsonExpression predicate) { return(Find(predicate).FirstOrDefault()); }
public T GetFirstOrDefault(BsonExpression predicate) { return(LiteDb.GetCollection <T>(CollectionName).FindOne(predicate)); }
/// <summary> /// Find all files that match with predicate expression. /// </summary> public Task <IEnumerable <LiteFileInfo <TFileId> > > FindAsync(BsonExpression predicate) { return(_liteDatabaseAsync.EnqueueAsync( () => _wrappedStorage.Find(predicate))); }