/// <summary> /// Build a simple atomic query (one value and unary operator) /// </summary> /// <param name="metadata">The metadata of the value</param> /// <param name="value"></param> /// <param name="oper"></param> public AtomicQuery([NotNull] KeyInfo metadata, [NotNull] KeyValue value, QueryOperator oper = QueryOperator.Eq) { if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } if (value == null) { throw new ArgumentNullException(nameof(value)); } // only scalar operators can be used here if (oper == QueryOperator.In || oper == QueryOperator.NotIn || oper.IsRangeOperator()) { throw new ArgumentException("invalid operator"); } if (value.KeyName != metadata.Name) { throw new ArgumentException("Mismatch between value and metadata"); } Metadata = metadata; Value = value; Operator = oper; }
/// <summary> /// Check if the operators are compatible to define a subset relationship /// Non trivial compatibility cases: /// a < b is subset of a <= b /// a > b is subset of a >= b /// a = b is subset of a oper c if b oper c /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> private static bool AreOperatorsCompatibleWithSubset(QueryOperator left, QueryOperator right) { if (left == right) { return(true); } if (left.IsRangeOperator() && right.IsRangeOperator()) { return(true); } if (left == QueryOperator.NotContains || left == QueryOperator.NotEq || left == QueryOperator.StrContains || left == QueryOperator.StrEndsWith || left == QueryOperator.StrStartsWith) { return(false); } if (right == QueryOperator.NotContains || right == QueryOperator.NotEq || right == QueryOperator.StrContains || right == QueryOperator.StrEndsWith || right == QueryOperator.StrStartsWith) { return(false); } if (right == QueryOperator.Eq) { return(false); } if (left == QueryOperator.Eq) { return(true); } if (left == QueryOperator.Gt && right == QueryOperator.Ge) { return(true); } if (left == QueryOperator.Lt && right == QueryOperator.Le) { return(true); } return(false); }
/// <summary> /// Build a query of the type BETWEEN value value2 /// </summary> /// <param name="metadata"></param> /// <param name="value"></param> /// <param name="value2"></param> /// <param name="oper">A range operator like <see cref="QueryOperator.GeLe"/></param> public AtomicQuery([NotNull] KeyInfo metadata, [NotNull] KeyValue value, [NotNull] KeyValue value2, QueryOperator oper = QueryOperator.GeLe) { Metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); Value = value ?? throw new ArgumentNullException(nameof(value)); Value2 = value2 ?? throw new ArgumentNullException(nameof(value2)); if (!oper.IsRangeOperator()) { throw new ArgumentException("Only range operators can be used with two values"); } if (value.KeyName != metadata.Name) { throw new ArgumentException("Mismatch between value and metadata"); } if (value2.KeyName != metadata.Name) { throw new ArgumentException("Mismatch between value and metadata"); } Operator = oper; //the one and only binary operator }
public override ISet <PackedObject> GetMany(IList <KeyValue> values, QueryOperator op = QueryOperator.Eq) { if (_insideFeedSession) { throw new InvalidOperationException( "Illegal operation during a feed session (GetMany was called)."); } if (values.Count == 0) { throw new ArgumentException("Empty list of keys passed to GetMany on index " + Name); } if (values.Count > 2 && op != QueryOperator.In) { throw new ArgumentException("More than two keys passed to GetMany on ordered index " + Name); } if (values.Count == 2 && !op.IsRangeOperator()) { throw new ArgumentException("Two keys passed to GetMany on ordered index " + Name + " for operator " + op); } var result = new HashSet <PackedObject>(); if (op == QueryOperator.In) { foreach (var value in values) { FindAllEq(value, result); } return(result); } switch (op) { case QueryOperator.Le: FindAllLe(values[0], result); break; case QueryOperator.Lt: FindAllLs(values[0], result); break; case QueryOperator.Eq: FindAllEq(values[0], result); break; case QueryOperator.Ge: FindAllGe(values[0], result); break; case QueryOperator.Gt: FindAllGt(values[0], result); break; //range operators case QueryOperator.GeLe: FindAllGeLe(values[0], values[1], result); break; case QueryOperator.GeLt: FindAllGeLt(values[0], values[1], result); break; case QueryOperator.GtLt: FindAllGtLt(values[0], values[1], result); break; case QueryOperator.GtLe: FindAllGtLe(values[0], values[1], result); break; } return(result); }
public override int GetCount(IList <KeyValue> values, QueryOperator op = QueryOperator.Eq) { if (values.Count == 0) { throw new ArgumentException("Empty list of keys passed to GetCount on index " + Name); } if (values.Count > 2) { throw new ArgumentException("More than two keys passed to GetCount on ordered index " + Name); } if (values.Count == 2 && !op.IsRangeOperator()) { throw new ArgumentException("Two keys passed to GetCount on ordered index " + Name + " for operator " + op); } if (op == QueryOperator.In) { return(int.MaxValue); // can not count efficiently so do not use me as index } var result = 0; switch (op) { case QueryOperator.Le: result = CountAllLe(values[0]); break; case QueryOperator.Lt: result = CountAllLs(values[0]); break; case QueryOperator.Eq: result = CountAllEq(values[0]); break; case QueryOperator.Ge: result = CountAllGe(values[0]); break; case QueryOperator.Gt: result = CountAllGt(values[0]); break; case QueryOperator.GeLe: result = CountAllGeLe(values[0], values[1]); break; case QueryOperator.GeLt: result = CountAllGeLt(values[0], values[1]); break; case QueryOperator.GtLe: result = CountAllGtLe(values[0], values[1]); break; case QueryOperator.GtLt: result = CountAllGtLt(values[0], values[1]); break; } return(result); }
public static void OptimizeQuery(OrQuery rootExpression) { // convert < and > into BETWEEN operator. Much more efficient foreach (var andQuery in rootExpression.Elements) { var multipleTests = andQuery.Elements.Where(q => q.IsComparison).GroupBy(q => q.PropertyName).Where(g => g.Count() > 1).ToList(); if (multipleTests.Count > 0) { // these ones will not be changed var atomicQueries = andQuery.Elements.Where(q => multipleTests.All(mt => mt.Key != q.PropertyName)) .ToList(); foreach (var multipleTest in multipleTests) { if (multipleTest.Count() == 2) { var q1 = multipleTest.First(); var q2 = multipleTest.Last(); // multiple atomic queries for the same index do not make sense if (q1.Operator == QueryOperator.Eq) { throw new CacheException($"Inconsistent query on index {multipleTest.Key}"); } if (q2.Operator == QueryOperator.Eq) { throw new CacheException($"Inconsistent query on index {multipleTest.Key}"); } var optimized = false; // a >= x && a <=y will be converted to "a BETWEEN x, y" if (q1.Operator != QueryOperator.In && q1.Operator != QueryOperator.In) { if (q1.Value < q2.Value) { QueryOperator oper = QueryOperator.Eq; if (q1.Operator == QueryOperator.Ge) { if (q2.Operator == QueryOperator.Le) { oper = QueryOperator.GeLe; } } if (q1.Operator == QueryOperator.Gt) { if (q2.Operator == QueryOperator.Le) { oper = QueryOperator.GtLe; } } if (q1.Operator == QueryOperator.Gt) { if (q2.Operator == QueryOperator.Lt) { oper = QueryOperator.GtLt; } } if (q1.Operator == QueryOperator.Ge) { if (q2.Operator == QueryOperator.Lt) { oper = QueryOperator.GeLt; } } if (oper.IsRangeOperator()) { var between = new AtomicQuery(q1.Metadata, q1.Value, q2.Value, oper); atomicQueries.Add(between); optimized = true; } } else if (q1.Value > q2.Value) { QueryOperator oper = QueryOperator.Eq; if (q1.Operator == QueryOperator.Le) { if (q2.Operator == QueryOperator.Ge) { oper = QueryOperator.GeLe; } } if (q1.Operator == QueryOperator.Lt) { if (q2.Operator == QueryOperator.Ge) { oper = QueryOperator.GeLt; } } if (q1.Operator == QueryOperator.Le) { if (q2.Operator == QueryOperator.Gt) { oper = QueryOperator.GtLe; } } if (q1.Operator == QueryOperator.Lt) { if (q2.Operator == QueryOperator.Gt) { oper = QueryOperator.GtLt; } } var between = new AtomicQuery(q1.Metadata, q2.Value, q1.Value, oper); atomicQueries.Add(between); optimized = true; } } if (!optimized) { // keep the original expressions atomicQueries.Add(q1); atomicQueries.Add(q2); } } } andQuery.Elements.Clear(); andQuery.Elements.AddRange(atomicQueries); } } }