All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencypher.spark.schema.CAPSSchema.scala Maven / Gradle / Ivy

There is a newer version: 1.0.0-beta7
Show newest version
/*
 * Copyright (c) 2016-2018 "Neo4j, Inc." [https://neo4j.com]
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.opencypher.spark.schema

import org.opencypher.okapi.api.schema.PropertyKeys.PropertyKeys
import org.opencypher.okapi.api.schema.{LabelPropertyMap, RelTypePropertyMap, Schema}
import org.opencypher.okapi.api.types.{CTRelationship, CypherType}
import org.opencypher.okapi.impl.exception.SchemaException
import org.opencypher.okapi.impl.schema.SchemaUtils._
import org.opencypher.okapi.impl.schema.{ImpliedLabels, LabelCombinations}
import org.opencypher.spark.impl.convert.CAPSCypherType._

object CAPSSchema {
  val empty: CAPSSchema = Schema.empty.asCaps

  implicit class CAPSSchemaConverter(schema: Schema) {

    /**
      * Converts a given schema into a CAPS specific schema. The conversion fails if the schema specifies property types
      * that cannot be represented in CAPS and throws a [[org.opencypher.okapi.impl.exception.SchemaException]].
      */
    def asCaps: CAPSSchema = {
      schema match {
        case s: CAPSSchema => s
        case _ =>
          val combosByLabel = schema.foldAndProduce(Map.empty[String, Set[Set[String]]])(
            (set, combos, _) => set + combos,
            (combos, _) => Set(combos))

          combosByLabel.foreach {
            case (_, combos) =>
              val keysForAllCombosOfLabel = combos.map(combo => combo -> schema.nodeKeys(combo))
              for {
                (combo1, keys1) <- keysForAllCombosOfLabel
                (combo2, keys2) <- keysForAllCombosOfLabel
              } yield {
                (keys1.keySet intersect keys2.keySet).foreach { k =>
                  val t1 = keys1(k)
                  val t2 = keys2(k)
                  val join = t1.join(t2)
                  if (!join.isSparkCompatible) {
                    val explanation = if (combo1 == combo2) {
                      s"The unsupported type is specified on label combination ${combo1.mkString("[", ", ", "]")}."
                    } else {
                      s"The conflict appears between label combinations ${combo1.mkString("[", ", ", "]")} and ${combo2.mkString("[", ", ", "]")}."
                    }
                    throw SchemaException(s"The property type '$join' for property '$k' can not be stored in a Spark column. " + explanation)
                  }
                }
              }
          }

          new CAPSSchema(schema)
      }
    }
  }

}

case class CAPSSchema private[schema](schema: Schema) extends Schema {
  override def labels: Set[String] = schema.labels

  override def relationshipTypes: Set[String] = schema.relationshipTypes

  override def labelPropertyMap: LabelPropertyMap = schema.labelPropertyMap

  override def relTypePropertyMap: RelTypePropertyMap = schema.relTypePropertyMap

  override def impliedLabels: ImpliedLabels = schema.impliedLabels

  override def labelCombinations: LabelCombinations = schema.labelCombinations

  override def impliedLabels(knownLabels: Set[String]): Set[String] = schema.impliedLabels(knownLabels)

  override def nodeKeys(labels: Set[String]): PropertyKeys = schema.nodeKeys(labels)

  override def allNodeKeys: PropertyKeys = schema.allNodeKeys

  override def allLabelCombinations: Set[Set[String]] = schema.allLabelCombinations

  override def combinationsFor(knownLabels: Set[String]): Set[Set[String]] = schema.combinationsFor(knownLabels)

  override def nodeKeyType(labels: Set[String], key: String): Option[CypherType] = schema.nodeKeyType(labels, key)

  override def keysFor(labelCombinations: Set[Set[String]]): PropertyKeys = schema.keysFor(labelCombinations)

  override def relationshipKeyType(types: Set[String], key: String): Option[CypherType] = schema.relationshipKeyType(types, key)

  override def relationshipKeys(typ: String): PropertyKeys = schema.relationshipKeys(typ)

  override def withNodePropertyKeys(nodeLabels: Set[String], keys: PropertyKeys): Schema = schema.withNodePropertyKeys(nodeLabels, keys)

  override def withRelationshipPropertyKeys(typ: String, keys: PropertyKeys): Schema = schema.withRelationshipPropertyKeys(typ, keys)

  override def ++(other: Schema): Schema = schema ++ other

  override def fromNodeEntity(labels: Set[String]): Schema = schema.fromNodeEntity(labels)

  override def pretty: String = schema.pretty

  override def isEmpty: Boolean = schema.isEmpty

  override def forNodeScan(labelConstraints: Set[String]): Schema = schema.forNodeScan(labelConstraints)

  override def forRelationship(relType: CTRelationship): Schema = schema.forRelationship(relType)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy