Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [https://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
package org.neo4j.cypher.internal.logical.plans
import org.neo4j.cypher.internal.logical.plans.MinMaxOrdering.NullOrdering
import org.neo4j.cypher.internal.util.NonEmptyList
/*
Seek ranges describe intervals. In practice they are used to summarize all inequalities over the
same node and property (n.prop) during planning, esp. for generating index seek by range plans.
*/
sealed trait SeekRange[+V] {
/**
* The [[V]]s that are used to define this [[SeekRange]].
*/
def arguments: Seq[V]
}
/*
An inequality seek range can either only have lower bounds (RangeGreaterThan),
or upper bounds (RangeLessThan),or both (RangeBetween). Bounds can either be inclusive or exclusive
to differentiate between< and <= or > and >= respectively. Multiple bounds are needed since
it is not known at compile time which ast expression is the maximum lower or the minimum upper bound.
*/
object InequalitySeekRange {
/*
Construct an inequality seek range given either some lower bounds and optionally some upper bounds - or -
optionally some lower bounds and some upper bounds. Such an input may be obtained by partitioning
inequality expressions. See usages of this method for examples.
*/
def fromPartitionedBounds[V](bounds: Either[(Bounds[V], Option[Bounds[V]]), (Option[Bounds[V]], Bounds[V])])
: InequalitySeekRange[V] =
bounds match {
case Left((lefts, None)) => RangeGreaterThan(lefts)
case Left((lefts, Some(rights))) => RangeBetween(RangeGreaterThan(lefts), RangeLessThan(rights))
case Right((None, rights)) => RangeLessThan(rights)
case Right((Some(lefts), rights)) => RangeBetween(RangeGreaterThan(lefts), RangeLessThan(rights))
}
}
sealed trait InequalitySeekRange[+V] extends SeekRange[V] {
def mapBounds[P](f: V => P): InequalitySeekRange[P]
def flatMapBounds[P](f: V => Option[P]): Option[InequalitySeekRange[P]]
def groupBy[K](f: Bound[V] => K): Map[K, InequalitySeekRange[V]]
}
sealed trait HalfOpenSeekRange[+V] extends InequalitySeekRange[V] {
def bounds: Bounds[V]
override def arguments: Seq[V] = bounds.map(_.endPoint).toIndexedSeq
override def mapBounds[P](f: V => P): HalfOpenSeekRange[P]
override def flatMapBounds[P](f: V => Option[P]): Option[HalfOpenSeekRange[P]]
// returns the limit of this half open seek range, i.e.
// the greatest bound if this is a RangeGreaterThan and
// the smallest bound if this is a RangeLessThan
//
def limit[X >: V](implicit ordering: MinMaxOrdering[X]): Option[Bound[X]] =
boundLimit(boundOrdering(ordering))
protected def boundLimit[X >: V](implicit ordering: Ordering[Bound[X]]): Option[Bound[X]]
protected def boundOrdering[X >: V](implicit ordering: MinMaxOrdering[X]): Ordering[Bound[X]]
}
final case class RangeBetween[+V](greaterThan: RangeGreaterThan[V], lessThan: RangeLessThan[V])
extends InequalitySeekRange[V] {
override def arguments: Seq[V] = greaterThan.arguments ++ lessThan.arguments
override def mapBounds[P](f: V => P): RangeBetween[P] =
copy(greaterThan = greaterThan.mapBounds(f), lessThan = lessThan.mapBounds(f))
def flatMapBounds[P](f: V => Option[P]): Option[RangeBetween[P]] = {
for (
g <- greaterThan.flatMapBounds(f);
l <- lessThan.flatMapBounds(f)
) yield copy(greaterThan = g, lessThan = l)
}
override def groupBy[K](f: Bound[V] => K): Map[K, InequalitySeekRange[V]] = {
val greaterThanBounds = greaterThan.bounds.map(Left(_))
val lessThanBounds = lessThan.bounds.map(Right(_))
val allBounds = greaterThanBounds ++ lessThanBounds
val groupedBounds = allBounds.groupBy[Either[Bound[V], Bound[V]], K] {
case Left(bound) => f(bound)
case Right(bound) => f(bound)
}
groupedBounds.view.mapValues[InequalitySeekRange[V]] { bounds =>
InequalitySeekRange.fromPartitionedBounds(bounds.partition(identity))
}
}.toMap
}
final case class RangeGreaterThan[+V](bounds: Bounds[V]) extends HalfOpenSeekRange[V] {
override def mapBounds[P](f: V => P): RangeGreaterThan[P] =
copy(bounds = bounds.map(_.map(f)))
override def flatMapBounds[P](f: V => Option[P]): Option[RangeGreaterThan[P]] = {
val newBounds = bounds.map(_.flatMap(f)).foldLeft[Option[Seq[Bound[P]]]](Some(Seq.empty[Bound[P]]))({
case (None, _) => None
case (_, None) => None
case (Some(aggregated), Some(item)) => Some(aggregated :+ item)
})
newBounds.map(b => copy(bounds = NonEmptyList.from(b)))
}
override def groupBy[K](f: Bound[V] => K): Map[K, RangeGreaterThan[V]] =
bounds.groupBy(f).view.mapValues(bounds => RangeGreaterThan(bounds)).toMap
protected def boundLimit[X >: V](implicit ordering: Ordering[Bound[X]]): Option[Bound[X]] = {
val limit = bounds.max[Bound[X]](ordering)
if (limit.endPoint == null) None else Some(limit)
}
protected def boundOrdering[X >: V](implicit ordering: MinMaxOrdering[X]): Ordering[Bound[X]] =
MaxBoundOrdering(ordering.forMax)
}
final case class RangeLessThan[+V](bounds: Bounds[V]) extends HalfOpenSeekRange[V] {
override def mapBounds[P](f: V => P): RangeLessThan[P] =
copy(bounds = bounds.map(_.map(f)))
override def flatMapBounds[P](f: V => Option[P]): Option[RangeLessThan[P]] = {
val newBounds = bounds.map(_.flatMap(f)).foldLeft[Option[Seq[Bound[P]]]](Some(Seq.empty[Bound[P]]))({
case (None, _) => None
case (_, None) => None
case (Some(aggregated), Some(item)) => Some(aggregated :+ item)
})
newBounds.map(b => copy(bounds = NonEmptyList.from(b)))
}
override def groupBy[K](f: Bound[V] => K): Map[K, RangeLessThan[V]] =
bounds.groupBy(f).view.mapValues(bounds => RangeLessThan(bounds)).toMap
protected def boundLimit[X >: V](implicit ordering: Ordering[Bound[X]]): Option[Bound[X]] = {
val limit = bounds.min[Bound[X]](ordering)
if (limit.endPoint == null) None else Some(limit)
}
protected def boundOrdering[X >: V](implicit ordering: MinMaxOrdering[X]): Ordering[Bound[X]] =
MinBoundOrdering(ordering.forMin)
}
/*
PrefixRange is used to describe intervals on string values for prefix search.
This is practical for two reasons:
- It directly maps on prefix queries of index implementations
- It removes the need to construct a proper upper bound value for an interval that
would describe the prefix search (which can be difficult due to unicode issues)
*/
final case class PrefixRange[T](prefix: T) extends SeekRange[T] {
override def arguments: Seq[T] = Seq(prefix)
def map[X](f: T => X): PrefixRange[X] = copy(f(prefix))
override def toString: String = s"STARTS WITH ${if (prefix == null) "null" else prefix.toString}"
}
final case class PointDistanceRange[T](point: T, distance: T, inclusive: Boolean) extends SeekRange[T] {
override def arguments: Seq[T] = Seq(point, distance)
def map[X](f: T => X): PointDistanceRange[X] = copy(f(point), f(distance), inclusive)
}
final case class PointBoundingBoxRange[T](lowerLeft: T, upperRight: T) extends SeekRange[T] {
override def arguments: Seq[T] = Seq(lowerLeft, upperRight)
def map[X](f: T => X): PointBoundingBoxRange[X] = copy(f(lowerLeft), f(upperRight))
}
final case class MinBoundOrdering[T](inner: Ordering[T]) extends Ordering[Bound[T]] {
override def compare(x: Bound[T], y: Bound[T]): Int = {
val cmp = inner.compare(x.endPoint, y.endPoint)
if (cmp == 0)
Ordering.Boolean.compare(x.isInclusive, y.isInclusive)
else
cmp
}
}
final case class MaxBoundOrdering[T](inner: Ordering[T]) extends Ordering[Bound[T]] {
override def compare(x: Bound[T], y: Bound[T]): Int = {
val cmp = inner.compare(x.endPoint, y.endPoint)
if (cmp == 0)
Ordering.Boolean.compare(y.isInclusive, x.isInclusive)
else
cmp
}
}
case class MinMaxOrdering[T](ordering: Ordering[T]) {
val forMin: Ordering[T] = ordering.withNullsFirst
val forMax: Ordering[T] = ordering.withNullsLast
}
object MinMaxOrdering {
implicit class NullOrdering[T](ordering: Ordering[T]) {
def withNullsFirst: Ordering[T] = (x: T, y: T) => {
if (x == null) {
if (y == null) 0 else -1
} else if (y == null) {
+1
} else {
ordering.compare(x, y)
}
}
def withNullsLast: Ordering[T] = (x: T, y: T) => {
if (x == null) {
if (y == null) 0 else +1
} else if (y == null) {
-1
} else {
ordering.compare(x, y)
}
}
}
}