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.
/**
* FILE: JoinQueryDetector
* PATH: org.apache.spark.sql.geosparksql.strategy.join.JoinQueryDetector
* Copyright (c) GeoSpark Development Team
*
* MIT License
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.apache.spark.sql.geosparksql.strategy.join
import org.apache.spark.sql.Strategy
import org.apache.spark.sql.catalyst.expressions.{Expression, LessThan, LessThanOrEqual}
import org.apache.spark.sql.catalyst.plans.Inner
import org.apache.spark.sql.catalyst.plans.logical.{Join, LogicalPlan}
import org.apache.spark.sql.execution.SparkPlan
import org.apache.spark.sql.geosparksql.expressions.{ST_Contains, ST_Distance, ST_Intersects, ST_Within}
/**
* Plans `RangeJoinExec` for inner joins on spatial relationships ST_Contains(a, b)
* and ST_Intersects(a, b).
*
* Plans `DistanceJoinExec` for inner joins on spatial relationship ST_Distance(a, b) < r.
*/
object JoinQueryDetector extends Strategy {
/**
* Returns true if specified expression has at least one reference and all its references
* map to the output of the specified plan.
*/
private def matches(expr: Expression, plan: LogicalPlan): Boolean =
expr.references.find(plan.outputSet.contains(_)).isDefined &&
expr.references.find(!plan.outputSet.contains(_)).isEmpty
private def matchExpressionsToPlans(exprA: Expression,
exprB: Expression,
planA: LogicalPlan,
planB: LogicalPlan): Option[(LogicalPlan, LogicalPlan)] =
if (matches(exprA, planA) && matches(exprB, planB)) {
Some((planA, planB))
} else if (matches(exprA, planB) && matches(exprB, planA)) {
Some((planB, planA))
} else {
None
}
def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
// ST_Contains(a, b) - a contains b
case Join(left, right, Inner, Some(ST_Contains(Seq(leftShape, rightShape)))) =>
planSpatialJoin(left, right, Seq(leftShape,rightShape), false)
// ST_Intersects(a, b) - a intersects b
case Join(left, right, Inner, Some(ST_Intersects(Seq(leftShape, rightShape)))) =>
planSpatialJoin(left, right, Seq(leftShape,rightShape), true)
// ST_WITHIN(a, b) - a is within b
case Join(left, right, Inner, Some(ST_Within(Seq(leftShape, rightShape)))) =>
planSpatialJoin(right, left, Seq(rightShape,leftShape), false)
// ST_Distance(a, b) <= radius consider boundary intersection
case Join(left, right, Inner, Some(LessThanOrEqual(ST_Distance(Seq(leftShape, rightShape)), radius))) =>
planDistanceJoin(left, right, Seq(leftShape,rightShape), radius, true)
// ST_Distance(a, b) < radius don't consider boundary intersection
case Join(left, right, Inner, Some(LessThan(ST_Distance(Seq(leftShape, rightShape)), radius))) =>
planDistanceJoin(left, right, Seq(leftShape,rightShape), radius, false)
case _ =>
Nil
}
private def planSpatialJoin(left: LogicalPlan,
right: LogicalPlan,
children: Seq[Expression],
intersects: Boolean,
extraCondition: Option[Expression] = None): Seq[SparkPlan] = {
val a = children.head
val b = children.tail.head
val relationship = if (intersects) "ST_Intersects" else "ST_Contains";
matchExpressionsToPlans(a, b, left, right) match {
case Some((planA, planB)) =>
logInfo(s"Planning spatial join for $relationship relationship")
RangeJoinExec(planLater(planA), planLater(planB), a, b, intersects, extraCondition) :: Nil
case None =>
logInfo(
s"Spatial join for $relationship with arguments not aligned " +
"with join relations is not supported")
Nil
}
}
private def planDistanceJoin(left: LogicalPlan,
right: LogicalPlan,
children: Seq[Expression],
radius: Expression,
intersects: Boolean,
extraCondition: Option[Expression] = None): Seq[SparkPlan] = {
val a = children.head
val b = children.tail.head
val relationship = if (intersects) "ST_Distance <=" else "ST_Distance <";
matchExpressionsToPlans(a, b, left, right) match {
case Some((planA, planB)) =>
if (radius.references.isEmpty || matches(radius, planA)) {
logInfo("Planning spatial distance join")
DistanceJoinExec(planLater(planA), planLater(planB), a, b, radius, intersects, extraCondition) :: Nil
} else if (matches(radius, planB)) {
logInfo("Planning spatial distance join")
DistanceJoinExec(planLater(planB), planLater(planA), b, a, radius, intersects, extraCondition) :: Nil
} else {
logInfo(
"Spatial distance join for ST_Distance with non-scalar radius " +
"that is not a computation over just one side of the join is not supported")
Nil
}
case None =>
logInfo(
"Spatial distance join for ST_Distance with arguments not " +
"aligned with join relations is not supported")
Nil
}
}
}