1 /***********************************************************************
2  * Copyright (c) 2013-2024 Commonwealth Computer Research, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Apache License, Version 2.0
5  * which accompanies this distribution and is available at
6  * http://www.opensource.org/licenses/apache2.0.php.
7  ***********************************************************************/
8 
9 package org.locationtech.geomesa.filter
10 
11 import scala.collection.GenTraversableOnce
12 
13 /**
14   * Holds values extracted from a filter. Values may be empty, in which case nothing was extracted from
15   * the filter. May be marked as 'disjoint', which means that mutually exclusive values were extracted
16   * from the filter. This may be checked to short-circuit queries that will not result in any hits.
17   *
18   * @param values values extracted from the filter. If nothing was extracted, will be empty
19   * @param precise values exactly match the filter, or may return false positives
20   * @param disjoint mutually exclusive values were extracted, e.g. 'a < 1 && a > 2'
21   * @tparam T type parameter
22   */
23 case class FilterValues[+T](values: Seq[T], precise: Boolean = true, disjoint: Boolean = false) {
24   def map[U](f: T => U): FilterValues[U] = FilterValues(values.map(f), precise, disjoint)
25   def flatMap[U](f: T => GenTraversableOnce[U]): FilterValues[U] = FilterValues(values.flatMap(f), precise, disjoint)
26   def foreach[U](f: T => U): Unit = values.foreach(f)
27   def forall(p: T => Boolean): Boolean = values.forall(p)
28   def exists(p: T => Boolean): Boolean = values.exists(p)
29   def filter(f: T => Boolean): FilterValues[T] = FilterValues(values.filter(f), precise, disjoint)
30   def nonEmpty: Boolean = values.nonEmpty || disjoint
31   def isEmpty: Boolean = !nonEmpty
32 }
33 
34 object FilterValues {
35 
36   def empty[T]: FilterValues[T] = FilterValues[T](Seq.empty)
37 
38   def disjoint[T]: FilterValues[T] = FilterValues[T](Seq.empty, disjoint = true)
39 
40   def or[T](join: (Seq[T], Seq[T]) => Seq[T])(left: FilterValues[T], right: FilterValues[T]): FilterValues[T] = {
41     (left.disjoint, right.disjoint) match {
42       case (false, false) => FilterValues(join(left.values, right.values), left.precise && right.precise)
43       case (false, true)  => left
44       case (true,  false) => right
45       case (true,  true)  => FilterValues.disjoint
46     }
47   }
48 
49   def and[T](intersect: (T, T) => Option[T])(left: FilterValues[T], right: FilterValues[T]): FilterValues[T] = {
50     if (left.disjoint || right.disjoint) {
51       FilterValues.disjoint
52     } else if (left.isEmpty) {
53       right
54     } else if (right.isEmpty) {
55       left
56     } else {
57       val intersections = left.values.flatMap(v => right.values.flatMap(intersect(_, v)))
58       if (intersections.isEmpty) {
59         FilterValues.disjoint
60       } else {
61         FilterValues(intersections.distinct, left.precise && right.precise)
62       }
63     }
64   }
65 }
Line Stmt Id Pos Tree Symbol Tests Code
24 25117 1321 - 1321 TypeApply scala.collection.Seq.canBuildFrom collection.this.Seq.canBuildFrom[U]
24 25118 1311 - 1324 ApplyToImplicitArgs scala.collection.TraversableLike.map FilterValues.this.values.map[U, Seq[U]](f)(collection.this.Seq.canBuildFrom[U])
24 25119 1326 - 1333 Select org.locationtech.geomesa.filter.FilterValues.precise FilterValues.this.precise
24 25120 1335 - 1343 Select org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.this.disjoint
24 25121 1298 - 1344 Apply org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[U](FilterValues.this.values.map[U, Seq[U]](f)(collection.this.Seq.canBuildFrom[U]), FilterValues.this.precise, FilterValues.this.disjoint)
25 25122 1439 - 1439 TypeApply scala.collection.Seq.canBuildFrom collection.this.Seq.canBuildFrom[U]
25 25123 1425 - 1442 ApplyToImplicitArgs scala.collection.TraversableLike.flatMap FilterValues.this.values.flatMap[U, Seq[U]](f)(collection.this.Seq.canBuildFrom[U])
25 25124 1444 - 1451 Select org.locationtech.geomesa.filter.FilterValues.precise FilterValues.this.precise
25 25125 1453 - 1461 Select org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.this.disjoint
25 25126 1412 - 1462 Apply org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[U](FilterValues.this.values.flatMap[U, Seq[U]](f)(collection.this.Seq.canBuildFrom[U]), FilterValues.this.precise, FilterValues.this.disjoint)
26 25127 1499 - 1516 Apply scala.collection.IterableLike.foreach FilterValues.this.values.foreach[U](f)
27 25128 1558 - 1574 Apply scala.collection.IterableLike.forall FilterValues.this.values.forall(p)
28 25129 1616 - 1632 Apply scala.collection.IterableLike.exists FilterValues.this.values.exists(p)
29 25130 1695 - 1711 Apply scala.collection.TraversableLike.filter FilterValues.this.values.filter(f)
29 25131 1713 - 1720 Select org.locationtech.geomesa.filter.FilterValues.precise FilterValues.this.precise
29 25132 1722 - 1730 Select org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.this.disjoint
29 25133 1682 - 1731 Apply org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[T](FilterValues.this.values.filter(f), FilterValues.this.precise, FilterValues.this.disjoint)
30 25134 1777 - 1785 Select org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.this.disjoint
30 25135 1758 - 1785 Apply scala.Boolean.|| FilterValues.this.values.nonEmpty.||(FilterValues.this.disjoint)
31 25136 1811 - 1820 Select scala.Boolean.unary_! FilterValues.this.nonEmpty.unary_!
36 25137 1897 - 1906 TypeApply scala.collection.generic.GenericCompanion.empty scala.collection.Seq.empty[Nothing]
36 25138 1893 - 1893 TypeApply org.locationtech.geomesa.filter.FilterValues.apply$default$2 FilterValues.apply$default$2[T]
36 25139 1893 - 1893 TypeApply org.locationtech.geomesa.filter.FilterValues.apply$default$3 FilterValues.apply$default$3[T]
36 25140 1881 - 1907 Apply org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[T](scala.collection.Seq.empty[Nothing], FilterValues.apply$default$2[T], FilterValues.apply$default$3[T])
38 25141 1962 - 1971 TypeApply scala.collection.generic.GenericCompanion.empty scala.collection.Seq.empty[Nothing]
38 25142 1984 - 1988 Literal <nosymbol> true
38 25143 1958 - 1958 TypeApply org.locationtech.geomesa.filter.FilterValues.apply$default$2 FilterValues.apply$default$2[T]
38 25144 1946 - 1989 Apply org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[T](x$1, x$3, x$2)
42 25145 2196 - 2207 Select org.locationtech.geomesa.filter.FilterValues.values left.values
42 25146 2209 - 2221 Select org.locationtech.geomesa.filter.FilterValues.values right.values
42 25147 2191 - 2222 Apply scala.Function2.apply join.apply(left.values, right.values)
42 25148 2240 - 2253 Select org.locationtech.geomesa.filter.FilterValues.precise right.precise
42 25149 2224 - 2253 Apply scala.Boolean.&& left.precise.&&(right.precise)
42 25150 2178 - 2178 TypeApply org.locationtech.geomesa.filter.FilterValues.apply$default$3 FilterValues.apply$default$3[Nothing]
42 25151 2178 - 2254 Apply org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[T](join.apply(left.values, right.values), left.precise.&&(right.precise), FilterValues.apply$default$3[Nothing])
45 25152 2353 - 2374 TypeApply org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.disjoint[Nothing]
50 25153 2524 - 2538 Select org.locationtech.geomesa.filter.FilterValues.disjoint right.disjoint
50 25154 2507 - 2538 Apply scala.Boolean.|| left.disjoint.||(right.disjoint)
51 25155 2548 - 2569 TypeApply org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.disjoint[Nothing]
51 25156 2548 - 2569 Block org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.disjoint[Nothing]
52 25157 2585 - 2597 Select org.locationtech.geomesa.filter.FilterValues.isEmpty left.isEmpty
52 25178 2581 - 2928 If <nosymbol> if (left.isEmpty) right else if (right.isEmpty) left else { val intersections: Seq[T] = left.values.flatMap[T, Seq[T]](((v: T) => right.values.flatMap[T, Seq[T]](((x$1: T) => scala.this.Option.option2Iterable[T](intersect.apply(x$1, v))))(collection.this.Seq.canBuildFrom[T])))(collection.this.Seq.canBuildFrom[T]); if (intersections.isEmpty) FilterValues.disjoint[Nothing] else FilterValues.apply[T](intersections.distinct, left.precise.&&(right.precise), FilterValues.apply$default$3[Nothing]) }
53 25158 2607 - 2612 Ident org.locationtech.geomesa.filter.FilterValues.right right
54 25159 2628 - 2641 Select org.locationtech.geomesa.filter.FilterValues.isEmpty right.isEmpty
54 25177 2624 - 2928 If <nosymbol> if (right.isEmpty) left else { val intersections: Seq[T] = left.values.flatMap[T, Seq[T]](((v: T) => right.values.flatMap[T, Seq[T]](((x$1: T) => scala.this.Option.option2Iterable[T](intersect.apply(x$1, v))))(collection.this.Seq.canBuildFrom[T])))(collection.this.Seq.canBuildFrom[T]); if (intersections.isEmpty) FilterValues.disjoint[Nothing] else FilterValues.apply[T](intersections.distinct, left.precise.&&(right.precise), FilterValues.apply$default$3[Nothing]) }
55 25160 2651 - 2655 Ident org.locationtech.geomesa.filter.FilterValues.left left
56 25176 2667 - 2928 Block <nosymbol> { val intersections: Seq[T] = left.values.flatMap[T, Seq[T]](((v: T) => right.values.flatMap[T, Seq[T]](((x$1: T) => scala.this.Option.option2Iterable[T](intersect.apply(x$1, v))))(collection.this.Seq.canBuildFrom[T])))(collection.this.Seq.canBuildFrom[T]); if (intersections.isEmpty) FilterValues.disjoint[Nothing] else FilterValues.apply[T](intersections.distinct, left.precise.&&(right.precise), FilterValues.apply$default$3[Nothing]) }
57 25161 2741 - 2756 Apply scala.Function2.apply intersect.apply(x$1, v)
57 25162 2741 - 2756 ApplyImplicitView scala.Option.option2Iterable scala.this.Option.option2Iterable[T](intersect.apply(x$1, v))
57 25163 2740 - 2740 TypeApply scala.collection.Seq.canBuildFrom collection.this.Seq.canBuildFrom[T]
57 25164 2720 - 2757 ApplyToImplicitArgs scala.collection.TraversableLike.flatMap right.values.flatMap[T, Seq[T]](((x$1: T) => scala.this.Option.option2Iterable[T](intersect.apply(x$1, v))))(collection.this.Seq.canBuildFrom[T])
57 25165 2714 - 2714 TypeApply scala.collection.Seq.canBuildFrom collection.this.Seq.canBuildFrom[T]
57 25166 2695 - 2758 ApplyToImplicitArgs scala.collection.TraversableLike.flatMap left.values.flatMap[T, Seq[T]](((v: T) => right.values.flatMap[T, Seq[T]](((x$1: T) => scala.this.Option.option2Iterable[T](intersect.apply(x$1, v))))(collection.this.Seq.canBuildFrom[T])))(collection.this.Seq.canBuildFrom[T])
58 25167 2769 - 2790 Select scala.collection.SeqLike.isEmpty intersections.isEmpty
59 25168 2802 - 2823 TypeApply org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.disjoint[Nothing]
59 25169 2802 - 2823 Block org.locationtech.geomesa.filter.FilterValues.disjoint FilterValues.disjoint[Nothing]
61 25170 2860 - 2882 Select scala.collection.SeqLike.distinct intersections.distinct
61 25171 2900 - 2913 Select org.locationtech.geomesa.filter.FilterValues.precise right.precise
61 25172 2884 - 2913 Apply scala.Boolean.&& left.precise.&&(right.precise)
61 25173 2847 - 2847 TypeApply org.locationtech.geomesa.filter.FilterValues.apply$default$3 FilterValues.apply$default$3[Nothing]
61 25174 2847 - 2914 Apply org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[T](intersections.distinct, left.precise.&&(right.precise), FilterValues.apply$default$3[Nothing])
61 25175 2847 - 2914 Block org.locationtech.geomesa.filter.FilterValues.apply FilterValues.apply[T](intersections.distinct, left.precise.&&(right.precise), FilterValues.apply$default$3[Nothing])