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

org.apache.tinkerpop.gremlin.ogm.GraphMapper.kt Maven / Gradle / Ivy

There is a newer version: 0.21.0
Show newest version
@file:Suppress("unused")

package org.apache.tinkerpop.gremlin.ogm

import org.apache.tinkerpop.gremlin.ogm.elements.Edge
import org.apache.tinkerpop.gremlin.ogm.elements.Vertex
import org.apache.tinkerpop.gremlin.ogm.exceptions.UnregisteredClass
import org.apache.tinkerpop.gremlin.ogm.extensions.toMultiMap
import org.apache.tinkerpop.gremlin.ogm.extensions.toOptionalMap
import org.apache.tinkerpop.gremlin.ogm.extensions.toSingleMap
import org.apache.tinkerpop.gremlin.ogm.mappers.EdgeDeserializer
import org.apache.tinkerpop.gremlin.ogm.mappers.EdgeSerializer
import org.apache.tinkerpop.gremlin.ogm.mappers.VertexDeserializer
import org.apache.tinkerpop.gremlin.ogm.mappers.VertexSerializer
import org.apache.tinkerpop.gremlin.ogm.paths.Path
import org.apache.tinkerpop.gremlin.ogm.paths.bound.*
import org.apache.tinkerpop.gremlin.ogm.paths.steps.StepTraverser
import org.apache.tinkerpop.gremlin.ogm.reflection.GraphDescription
import org.apache.tinkerpop.gremlin.ogm.traversals.*
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversal
import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource
import org.slf4j.LoggerFactory
import kotlin.reflect.KClass
import kotlin.reflect.full.isSubclassOf

/**
 * The main object a client's application will interact with.
 * This interface provides the ability to map objects from a clients domain to the graph and back.
 * The default implementation of all methods are thread-safe.
 */
interface GraphMapper {

    val graphDescription: GraphDescription

    val traversal: GraphTraversalSource

    /**
     *
     * Vertices
     *
     */

    /**
     * Gets a graph traversal that emits a vertex with a given id.
     */
    fun  V(id: Any): GraphTraversalToOptional<*, V> = V(setOf(id)).toOptional()

    /**
     * Gets a graph traversal that emits vertices for given ids.
     * No exception is thrown for ids that don't correspond to a vertex, thus the number of vertices the traversal emits
     * may be less than the number of ids.
     */
    fun  V(vararg ids: Any): GraphTraversalToMany<*, V> = V(ids.toSet())

    /**
     * Gets a graph traversal that emits vertices for given ids.
     * No exception is thrown for ids that don't correspond to a vertex, thus the number of vertices the traversal emits
     * may be less than the number of ids.
     */
    fun  V(ids: Set): GraphTraversalToMany<*, V> {
        return if (ids.none()) {
            traversal.inject()
        } else {
            traversal.V(*ids.toTypedArray()).map { vertex ->
                deserialize(vertex.get())
            }
        }.toMany()
    }


    /**
     * Get a traversal that emits all vertices for class V (which has been registered as a vertex with this GraphMapper).
     * V may be a superclass of classes registered as a vertex.
     */
    fun  V(kClass: KClass, then: GraphTraversal<*, GraphVertex>.() -> GraphTraversal<*, GraphVertex> = { this }): GraphTraversalToMany<*, V> {
        val labels = graphDescription.vertexClasses.filter { vertexKClass ->
            vertexKClass.isSubclassOf(kClass)
        }.map { vertexClass ->
            graphDescription.getVertexDescription(vertexClass).label
        }
        if (labels.isEmpty()) throw UnregisteredClass(kClass)
        logger.debug("Will get all vertices with labels $labels")
        return labels.map { label ->
            traversal.V().hasLabel(label)
        }.reduce { traversal1, traversal2 ->
            traversal.V().union(traversal1, traversal2)
        }.then().map { vertex ->
            deserialize(vertex.get())
        }.toMany()
    }

    /**
     * Saves vertices to the graph. If the property annotated with @ID is null,
     * a new vertex will be created, otherwise this object will overwrite the current vertex with that id.
     * The returned object will always have a non-null @ID. If the property annotated with @ID is non-null,
     * but the vertex cannot be found, an exception is thrown.
     */
    fun  saveV(vararg objs: V): List = objs.map { saveV(it) }

    /**
     * Saves vertices to the graph. If the property annotated with @ID is null,
     * a new vertex will be created, otherwise this object will overwrite the current vertex with that id.
     * The returned object will always have a non-null @ID. If the property annotated with @ID is non-null,
     * but the vertex cannot be found, an exception is thrown.
     */
    fun  saveV(objs: Iterable): List = objs.map { saveV(it) }

    /**
     * Saves an object annotated with @Element to the graph. If property annotated with @ID is null,
     * a new vertex will be created, otherwise this object will overwrite the current vertex with that id.
     * The returned object will always have a non-null @ID. If the property annotated with @ID is non-null,
     * but the vertex cannot be found, an exception is thrown.
     */
    fun  saveV(deserialized: V): V {
        val serialized = serialize(deserialized)
        logger.debug("Saved ${serialized.label()} vertex with id: ${serialized.id()}\n")
        return deserialize(serialized)
    }

    /**
     *
     *  Edges
     *
     */

    /**
     * Gets a graph traversal that emits an edge with the given id.
     */
    fun > E(id: Any): GraphTraversalToOptional<*, E> = E(setOf(id)).toOptional()

    /**
     * Gets a graph traversal that emits edges for given ids.
     * No exception is thrown for ids that don't correspond to an edge, thus the number of edges the traversal emits
     * may be less than the number of ids.
     */
    fun > E(vararg ids: Any): GraphTraversalToMany<*, E> = E(ids.toSet())

    /**
     * Gets a graph traversal that emits edges for given ids.
     * No exception is thrown for ids that don't correspond to an edge, thus the number of edges the traversal emits
     * may be less than the number of ids.
     */
    fun > E(ids: Set): GraphTraversalToMany<*, E> {
        return if (ids.none()) {
            traversal.inject()
        } else {
            traversal.E(*ids.toTypedArray()).map { edge ->
                deserialize(edge.get())
            }
        }.toMany()
    }

    /**
     * Get a traversal that emits all edges for class E (which has been registered with a relationship as
     * an edge with this GraphMapper).
     */
    fun > E(
            kClass: KClass,
            then: GraphTraversal<*, GraphEdge>.() -> GraphTraversal<*, GraphEdge> = { this }
    ): GraphTraversalToMany<*, E> {
        val labels = graphDescription.edgeClasses.filter { edgeKClass ->
            edgeKClass.isSubclassOf(kClass)
        }.map { vertexClass ->
            graphDescription.getEdgeDescription(vertexClass).label
        }
        if (labels.isEmpty()) throw UnregisteredClass(kClass)
        logger.debug("Will get all edges with labels $labels")
        return labels.map { label ->
            traversal.E().hasLabel(label)
        }.reduce { traversal1, traversal2 ->
            traversal.E().union(traversal1, traversal2)
        }.then().map { edge ->
            deserialize(edge.get())
        }.toMany()
    }

    /**
     * Saves edges to the graph. If the property annotated with @ID is null,
     * a new edge will be created, otherwise this object will overwrite the current edge with that id.
     * The returned object will always have a non-null @ID.
     */
    fun > saveE(vararg edges: E): List = edges.map { saveE(it) }

    /**
     * Saves edges to the graph. If the property annotated with @ID is null,
     * a new edge will be created, otherwise this object will overwrite the current edge with that id.
     * The returned object will always have a non-null @ID.
     */
    fun > saveE(edges: Iterable): List = edges.map { saveE(it) }

    /**
     * Saves edges to the graph. If the property annotated with @ID is null,
     * a new edge will be created, otherwise this object will overwrite the current edge with that id.
     * The returned object will always have a non-null @ID.
     */
    fun > saveE(edge: E): E {
        val serialized = serialize(edge)
        logger.debug("Saved ${serialized.label()} edge with id ${serialized.id()}")
        return deserialize(serialized)
    }

    /**
     *
     * Traversals
     *
     */

    /**
     * Traverses from a vertex to the path's required destination object.
     */
    fun  traverse(boundPath: SingleBoundPath.ToSingle): GraphTraversalToSingle<*, TO> =
            traversePath(boundPath).traversal.map { it.get().second }.toSingle()

    /**
     * Traverses from a vertex to the path's optional destination object.
     */
    fun  traverse(boundPath: SingleBoundPath.ToOptional): GraphTraversalToOptional<*, TO> =
            traversePath(boundPath).traversal.map { it.get().second }.toOptional()

    /**
     * Traverses from vertex to the path's destination objects.
     */
    fun  traverse(boundPath: SingleBoundPath.ToMany): GraphTraversalToMany<*, TO> =
            traversePath(boundPath).traversal.map { it.get().second }.toMany()

    /**
     * Traverses from multiple vertices to the path's required destination object for each origin vertex.
     */
    fun  traverse(boundPath: BoundPath.ToSingle): Map =
            traversePath(boundPath).toSingleMap(boundPath.froms)

    /**
     * Traverses from multiple vertices to the path's optional destination object for each origin vertex.
     */
    fun  traverse(boundPath: BoundPath.ToOptional): Map =
            traversePath(boundPath).toOptionalMap(boundPath.froms)

    /**
     * Traverses from multiple vertices to the path's destination objects for each origin vertex.
     */
    fun  traverse(boundPath: BoundPath.ToMany): Map> =
            traversePath(boundPath).toMultiMap(boundPath.froms)

    /**
     * Traverses from any number of vertices to the path's destination object(s) for each origin vertex.
     */
    fun  traversePath(boundPath: BoundPath): GraphTraversalToMany<*, Pair> {
        val froms = boundPath.froms
        val path = boundPath.path
        if (froms.none()) {
            return traversal.inject>().toMany()
        }
        val traversalStart = froms.fold(initial = traversal.inject()) { traversal, from ->
            traversal.inject(from).`as`(fromKey)
        }
        @Suppress("UNCHECKED_CAST")
        val traversed = path.path().fold(initial = traversalStart as GraphTraversal) { traversal, step ->
            step as Path
            step(StepTraverser(traversal, this)) as GraphTraversal
        }
        @Suppress("UNCHECKED_CAST")
        return traversed.`as`(toKey).select(fromKey, toKey).map {
            val map = it.get()
            val from = map[fromKey] as FROM
            val to = map[toKey] as TO
            logger.debug("Traversed from $from to $to")
            from to to
        }.toMany()
    }

    fun > serialize(edge: E): GraphEdge =
            EdgeSerializer(graphDescription, traversal)(edge)

    fun > deserialize(graphEdge: GraphEdge): E =
            EdgeDeserializer(graphDescription)(graphEdge)

    fun  serialize(vertex: V): GraphVertex =
            VertexSerializer(graphDescription, traversal)(vertex)

    fun  deserialize(graphVertex: GraphVertex): V =
            VertexDeserializer(graphDescription)(graphVertex)

    fun  vertexID(vertex: V): Any? {
        if (graphDescription.vertexClasses.contains(vertex::class)) {
            val vertexDescription = graphDescription.getVertexDescription(vertex::class)
            return vertexDescription.id.property.get(vertex)
        }
        return null
    }

    fun > edgeID(edge: E): Any? {
        if (graphDescription.edgeClasses.contains(edge::class)) {
            val edgeDescription = graphDescription.getEdgeDescription(edge::class)
            return edgeDescription.id.property.get(edge)
        }
        return null
    }

    companion object {

        private const val fromKey = "from"

        private const val toKey = "to"

        private val logger = LoggerFactory.getLogger(GraphMapper::class.java)
    }
}

typealias GraphVertex = org.apache.tinkerpop.gremlin.structure.Vertex

typealias GraphEdge = org.apache.tinkerpop.gremlin.structure.Edge

/**
 * Get a traversal that emits all vertices for class V (which has been registered as a vertex with this GraphMapper).
 * V may be a superclass of classes registered as a vertex.
 */
inline fun  GraphMapper.allV(
        noinline then: GraphTraversal<*, GraphVertex>.() -> GraphTraversal<*, GraphVertex> = { this }
): GraphTraversalToMany<*, V> = V(V::class, then)

/**
 * Get a traversal that emits all edges for class E (which has been registered with a relationship as
 * an edge with this GraphMapper).
 */
inline fun > GraphMapper.allE(
        noinline then: GraphTraversal<*, GraphEdge>.() -> GraphTraversal<*, GraphEdge> = { this }
): GraphTraversalToMany<*, E> = E(E::class, then)




© 2015 - 2024 Weber Informatics LLC | Privacy Policy