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

org.neo4j.fabric.eval.Catalog.scala Maven / Gradle / Ivy

There is a newer version: 5.25.1
Show newest version
/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://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.fabric.eval

import java.util.UUID
import org.neo4j.configuration.helpers.NormalizedGraphName
import org.neo4j.configuration.helpers.RemoteUri
import org.neo4j.cypher.internal.ast.CatalogName
import org.neo4j.cypher.internal.util.InputPosition
import org.neo4j.fabric.util.Errors
import org.neo4j.fabric.util.Errors.show
import org.neo4j.kernel.database.NormalizedDatabaseName
import org.neo4j.values.AnyValue
import org.neo4j.values.storable.IntegralValue

object Catalog {

  sealed trait Entry

  sealed trait Graph extends Entry {
    def id: Long
    def uuid: UUID
    def name: Option[String]
  }

  sealed trait ConcreteGraph extends Graph
  sealed trait Alias extends Graph

  case class InternalGraph(
    id: Long,
    uuid: UUID,
    graphName: NormalizedGraphName,
    databaseName: NormalizedDatabaseName
  ) extends ConcreteGraph {
    def name: Option[String] = Some(graphName.name())
    override def toString: String = s"internal graph $id" + name.map(n => s" ($n)").getOrElse("")
  }

  case class ExternalGraph(
    id: Long,
    name: Option[String],
    uuid: UUID
  ) extends ConcreteGraph {
    override def toString: String = s"external graph $id" + name.map(n => s" ($n)").getOrElse("")
  }

  case class InternalAlias(
    id: Long,
    uuid: UUID,
    graphName: NormalizedGraphName,
    databaseName: NormalizedDatabaseName
  ) extends Alias {
    def name: Option[String] = Some(graphName.name())
    override def toString: String = s"graph alias $id" + name.map(n => s" ($n)").getOrElse("")
  }

  case class ExternalAlias(
    id: Long,
    uuid: UUID,
    graphName: NormalizedGraphName,
    localDatabaseName: NormalizedDatabaseName,
    remoteDatabaseName: NormalizedDatabaseName,
    uri: RemoteUri
  ) extends Alias {
    def name: Option[String] = Some(graphName.name())
    override def toString: String = s"graph alias $id" + name.map(n => s" ($n)").getOrElse("")
  }

  trait View extends Entry {
    val arity: Int
    val signature: Seq[Arg[_]]

    def eval(args: Seq[AnyValue]): Graph

    def checkArity(args: Seq[AnyValue]): Unit =
      if (args.size != arity) Errors.wrongArity(arity, args.size, InputPosition.NONE)

    def cast[T <: AnyValue](a: Arg[T], v: AnyValue, args: Seq[AnyValue]): T =
      try a.tpe.cast(v)
      catch {
        case _: ClassCastException => Errors.wrongType(show(signature), show(args))
      }
  }

  case class View1[A1 <: AnyValue](a1: Arg[A1])(f: A1 => Graph) extends View {
    val arity = 1
    val signature = Seq(a1)

    def eval(args: Seq[AnyValue]): Graph = {
      checkArity(args)
      f(cast(a1, args(0), args))
    }
  }

  case class Arg[T <: AnyValue](name: String, tpe: Class[T])

  def create(internalGraphs: Seq[Graph], externalGraphs: Seq[Graph], graphAliases: Seq[Graph], fabricNamespace: Option[String]): Catalog = {
    if (fabricNamespace.isEmpty) {
      val internalByName = byName(internalGraphs)
      val aliasesByName = byName(graphAliases)
      internalByName ++ aliasesByName
    } else {
      val allGraphs = externalGraphs ++ internalGraphs
      val byId = byIdView(allGraphs, fabricNamespace.get)
      val externalByName = byName(externalGraphs, fabricNamespace.get)
      val internalByName = byName(internalGraphs)
      val aliasesByName = byName(graphAliases)

      byId ++ externalByName ++ internalByName ++ aliasesByName
    }
  }

  private def byName(graphs: Seq[Catalog.Graph], namespace: String*): Catalog =
    Catalog((for {
      graph <- graphs
      name <- graph.name
      fqn = namespace :+ name
    } yield CatalogName(fqn.toList) -> graph).toMap)

  private def byIdView(lookupIn: Seq[Catalog.Graph], namespace: String): Catalog = {
    Catalog(Map(
      CatalogName(namespace, "graph") -> View1(Arg("gid", classOf[IntegralValue]))(gid =>
        lookupIn
          .collectFirst { case g: ConcreteGraph if g.id == gid.longValue() => g }
          .getOrElse(Errors.entityNotFound("Graph", show(gid)))
      )
    ))
  }
}

case class Catalog(entries: Map[CatalogName, Catalog.Entry]) {

  def resolve(name: CatalogName): Catalog.Graph =
    resolve(name, Seq())

  def resolve(name: CatalogName, args: Seq[AnyValue]): Catalog.Graph = {
    val normalizedName = CatalogName(name.parts.map(normalize))
    entries.get(normalizedName) match {
      case None => Errors.entityNotFound("Catalog entry", show(name))

      case Some(g: Catalog.Graph) =>
        if (args.nonEmpty) Errors.wrongArity(0, args.size, InputPosition.NONE)
        else g

      case Some(v: Catalog.View) => v.eval(args)
    }
  }

  private def normalize(name: String): String =
    new NormalizedGraphName(name).name

  def ++(that: Catalog): Catalog = Catalog(this.entries ++ that.entries)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy