Hi Pieter,
When considering the graph representation of the schema, I suppose the
equivalent to your EdgeRoles would be the actual from and to edges between
EdgeType and NodeType vertices:
(created:edgeType) --from--> (person:vertexType)
(created:edgeType) --to--> (software:vertexType)
I was considering these as simple edges without properties while drafting the
proposal, but you're right that if we want to allow A loves B and A loves C to
have distinct multiplicity constraints, those should be modeled as properties
on the from/to edge (equivalent to the EdgeRole triple). We could add an
equivalent EdgeRole class to the structure API which maps to these edges in the
schema-graph, just like how EdgeType and VertexType map to nodes in the
schema-graph. Each EdgeRole would be as you suggest a triple of a NodeType,
EdgeType, and Direction. This would expand GraphSchema itself to essentially be
a Collection of NodeTypes, EdgeTypes, and EdgeRoles. I believe providers should
be free to define custom constraints on either the EdgeTypes or EdgeRoles
arbitrarily as they wish.
Regarding the transaction synchronizing, I agree that TinkerPop generally has
no business imposing restrictions on providers in this manner. We do need to
make some choices for the TinkerGraph reference implementation, which is where
I think it's likely simplest to force a 1:1 binding between transactions in the
schema and data graphs. I suppose my position on this is really that
TinkerGraph should do what's easiest for its purposes, and providers should be
free to implement transaction semantics as they see fit.
Regarding the lock/unlock, I agree with your suggested methods. The inclusion
of graph.tx().unlock() as a convenience/sugar seems simple enough and should
prove useful.
Thanks,
Cole
On 2026/04/13 07:31:53 pieter wrote:
> Hi,
>
> Regarding EdgeRole:
>
> In Sqlg, 'EdgeRole' is purely a in memory object to represent the
> triplet '[from]edgeLabel[to]'. The actual information is stored in the
> meta graph on the edge between VertexType and EdgeType. I don't see any
> way of not directly dealing with the triplet as it needs to carry its
> own information describing the triplet's properties like multiplicity,
> uniqueness and order.
>
> e.g.
> A loves[multiplicity:0..1, unique:false, ordered: false] B
> A loves[multiplicity:0..*, unique:true, ordered: true ] C
>
> This information needs to be captured on the triplet. Just having a
> 'from/toNodeTypes' Set will not be powerful enough.
>
> Regarding 'coordinating transactions'
>
> graph.tx().begin();
>
> GraphSchema schema = graph.schema();
>
> VertexType personVertexType =
> schema.traversal().addVType("person").next();
> personVertexType.propertyType("name").constraint("type",
> String.class).constraint("required", true).constraint("unique",
> true).next();
> personVertexType.propertyType("age").constraint("type",
> Integer.class).next();
>
> EdgeType knowsEdgeType =
> schema.traversal().addEType("knows").from("person").to("person").next()
> ;
> knowsEdgeType.propertyType("weight").constraint("type",
> Double.class).next()
>
> Vertex marco = graph.addVertex(T.label, "person", "name", "marco");
> Vertex vadas = graph.addVertex(T.label, "person", "name", "vadas");
> marco.addEdge("knows", vadas);
>
> graph.tx().commit();
>
> What coordinating or synchronizing does TinkerPop need to do?
> Storing(creating,updating,deleting) the schema and associated meta
> data(graph) is the job of the provider in its underlying datastore.
> Each provider will have its own way of managing its schema including
> the persistence and transaction semantics. Afterall, the core business
> of a provider is precisely laying out the graph on disc which inturn
> implies accute knowledge and management of the schema. Of course
> TinkerGraph will have its own reference implemtation, but its
> implentation will be largely irrelevant to providers.
>
> Regarding lock/unlock.
>
> The way I see it 3 methods are needed.
>
> Schema.lock()
>
> Schema.unlock()
> graph.tx().unlock()
>
> On graph.tx().commit() and rollback() the transactional unlock will
> automatically lock again if the schema itself is locked.
>
> Thanks
> Pieter
>
> On Fri, 2026-04-10 at 23:41 +0000, Cole Greer wrote:
> >
> >
> > >
> > > Hi Pieter,
> > > Thanks for the feedback.
> >
> >
> > >
> > >
> > > >
> > > >
> > > > >
> > > > > Will we not need an additional type to represent the
> > > > > 'EdgeLabel:VertexLabel:Direction'?
> > > >
> > >
> >
> >
> > >
> > > Regarding #1, I hadn't considered extracting something like an
> > > EdgeRole into a standalone object. The closest equivalent in the
> > > model I proposed is that each edge type would contain a list of
> > > fromNodeTypes and toNodeTypes. In the example you provided, the
> > > "loves" edge type would have fromNodeTypes of ["A"] and toNodeTypes
> > > of ["B", "C"]. The EdgeType in my proposal should map 1:1 to an
> > > EdgeType + a set of EdgeRoles in the sqlg model.
> >
> >
> > >
> > >
> > > >
> > > >
> > > > >
> > > > > Some backends will support transactions for schema and data
> > > > > changes. Some will support transactions only for data changes.
> > > > > Some
> > > > > databases like Mariadb will silently commit the current
> > > > > transaction when a
> > > > > schema change occurs.
> > > > > Probabaly there are variations on this theme, so it should be
> > > > > up to
> > > > > the provider to specify the granularity of support.
> > > >
> > >
> >
> >
> > >
> > > I agree that providers need flexibility to vary transaction support
> > > according to their specific backends and needs. There is also a bit
> > > of an open question as to how to coordinate transactions between
> > > the
> > > schema graph and data graph. My current thoughts is that the
> > > default
> > > behaviour should be to sync begins, commits, and rollbacks across
> > > the
> > > data and schema graphs as the default behaviour. I would think
> > > providers should be free to manage these independently if they
> > > prefer.
> >
> >
> > >
> > >
> > > >
> > > >
> > > > >
> > > > > Further I suggest adding schema.lock/unlock. If locked no
> > > > > schema changes are allowed. There can also be a transaction
> > > > > scoped
> > > > > unlock() which will just for the current transaction unlock the
> > > > > schema.
> > > > > This will add in a safety net for graphs with a well defined
> > > > > schema where no code should be able to make schema changes.
> > > >
> > >
> >
> >
> > >
> > > I like this suggestion, I think this would make sense as lock() and
> > > unlock() methods on the GraphSchema structure API class. I think it
> > > would be fine to exclude this from the gremlin language and remote
> > > APIs for now, as this could always be added later if it demand
> > > arises. Any thoughts on this approach?
> >
> >
> > >
> > >
> > > >
> > > >
> > > > >
> > > > > Regarding File IO.
> > > > > I am not sure why this is needed. We can simply export/import
> > > > > some gremlin to manage the schema. We already have the language
> > > > > that
> > > > > does this, why is a translation to json needed?
> > > >
> > >
> >
> >
> > >
> > > I would say that File IO isn't strictly necessary, but I also see
> > > little downside in including it. I believe it needs relatively
> > > little
> > > effort to include it, and I don't expect it to be an ongoing
> > > maintenance burden. There's some demand for it as a feature, I
> > > would
> > > use it as a compact and convenient way of persisting and loading my
> > > schemas. Josh has expressed interest in it as well. We have similar
> > > precedent in the data graph side with the io() step existing to
> > > facilitate bulk reads/writes instead of relying on gremlin for all
> > > ingest. I would also see no issue in any providers choosing not to
> > > support the file io in their schema implementations if they so
> > > choose.
> > > Please let me know your thoughts on these matters.
> > > Thanks, Cole
> > > On 2026/04/04 10:24:20 pieter wrote:
> >
> >
> > >
> > >
> > > >
> > > >
> > > > >
> > > > > Hi,
> > > > > This looks good to me.
> > > > > #1
> > > > > Will we not need an additional type to represent the
> > > > > 'EdgeLabel:VertexLabel:Direction'?
> > > > > In Sqlg this is called 'EdgeRole'.
> > > > > class EdgeRole { private EdgeType; private VertexType;
> > > > > private Direction; }
> > > > > Consider the following example
> > > > > VertexType aVertexType = schema.traversal().addVType("A");
> > > > > VertexType bVertexType = schema.traversal().addVType("B");
> > > > > VertexType cVertexType = schema.traversal().addVType("C");
> > > > > EdgeType lovesEdgeType = schema.traversal().addEType("loves")
> > > > > lovesEdgeType.from("A").to("B");
> > > > > lovesEdgeType.from("A").to("C");
> > > > > In this case there are 3 EdgesRoles
> > > > > loves:A:OUT loves:B:IN loves:C:IN
> > > > > Later the user needs to manipulate the 'loves:C:IN' EdgeRole
> > > > > like deleting it or changing the multiplicities... For this the
> > > > > user needs to be able to access the EdgeRole.
> > > > > EdgeRole cLovesEdgeRole = lovesEdgeType.edgeRole(cVertexType);
> > > > > cLovesEdgeRole.drop();
> > > > > #2
> > > > > Regarding transactions.
> > > > > Some backends will support transactions for schema and data
> > > > > changes. Some will support transactions only for data changes.
> > > > > Some
> > > > > databases like Mariadb will silently commit the current
> > > > > transaction when a
> > > > > schema change occurs.
> > > > > Probabaly there are variations on this theme, so it should be
> > > > > up to
> > > > > the provider to specify the granularity of support.
> > > > > Further I suggest adding schema.lock/unlock. If locked no
> > > > > schema changes are allowed. There can also be a transaction
> > > > > scoped
> > > > > unlock() which will just for the current transaction unlock the
> > > > > schema.
> > > > > This will add in a safety net for graphs with a well defined
> > > > > schema where no code should be able to make schema changes.
> > > > > #3
> > > > > Regarding File IO.
> > > > > I am not sure why this is needed. We can simply export/import
> > > > > some gremlin to manage the schema. We already have the language
> > > > > that
> > > > > does this, why is a translation to json needed?
> > > > > Regards Pieter
> > > > > On Fri, 2026-04-03 at 02:16 +0000, Cole Greer via dev wrote:
> > > >
> > >
> > >
> > > >
> > > >
> > > > >
> > > > >
> > > > > >
> > > > > >
> > > > > > >
> > > > > > > Hi Everyone,
> > > > > > > The topic of Graph Schema has been discussed extensively in
> > > > > > > recent TInkerPop Gatherings, and the following proposal has
> > > > > > > emerged from these gatherings. I believe it is now ready
> > > > > > > for broad
> > > > > > > consideration and discussions. I’ve done my best to
> > > > > > > incorporate initial
> > > > > > > feedback from Josh, Pieter, Valentyn, Stephen, Kris and
> > > > > > > others into this proposal, however I won’t claim that it
> > > > > > > accurately represents the views of anyone other than myself
> > > > > > > at this time. This is a broad
> > > > > > > topic and I’m deliberately excluding critical topics to
> > > > > > > focus this
> > > > > > > thread on standardizing interfaces for gremlin users and
> > > > > > > providers to interact with schema (see assumptions for more
> > > > > > > details).
> > > > > > > OverviewThis proposal introduces graph schema interfaces
> > > > > > > for TinkerPop: a
> > > > > > > way to define vertex types, edge types, and property types
> > > > > > > as a meta- graph that is itself traversable with Gremlin.
> > > > > > > The schema
> > > > > > > describes the structure of a data graph; what kinds of
> > > > > > > vertices and edges exist, what properties they carry, and
> > > > > > > how they connect..
> > > > > > > Assumptions * Type keys are element labels: there is a 1-
> > > > > > > to-1 mapping between
> > > > > > > a label and a type definition. A vertex labeled "person"
> > > > > > > corresponds to exactly one VertexType, and an edge
> > > > > > > labeled "knows" corresponds
> > > > > > > to exactly one EdgeType.
> > > > > > > * Java classes are used as a type system: This proposal
> > > > > > > uses Java classes to define property type constraints. This
> > > > > > > is intended as
> > > > > > > a placeholder to be replaced by a proper type system to
> > > > > > > be defined
> > > > > > > via a later discussion.
> > > > > > > * This proposal makes very little consideration of
> > > > > > > if/when/where/how validation and enforcement of schema
> > > > > > > takes place. I believe it is important for us to ship
> > > > > > > something which is flexible and useful
> > > > > > > to providers out of the box as well as leaving space for
> > > > > > > providers
> > > > > > > to plugin existing implementations or build their own if
> > > > > > > they
> > > > > > > desire. I’ve left this out of scope for this proposal to
> > > > > > > focus first on interfaces which give providers the
> > > > > > > appropriate access to schema.
> > > > > > > Design Points 1. Schema-as-GraphGraphSchema extends Graph.
> > > > > > > Providers implement a familiar interface, and users
> > > > > > > traverse the schema with schema.traversal(). This avoids
> > > > > > > inventing a parallel API surface. The schema is just
> > > > > > > another graph.
> > > > > > > A data graph exposes its schema via Graph.schema(), which
> > > > > > > returns
> > > > > > > the GraphSchema instance. Providers that don't support
> > > > > > > schema return UnsupportedOperationException by default.
> > > > > > > 2. All type definitions are verticesVertexType, EdgeType,
> > > > > > > and PropertyType are all vertices in the
> > > > > > > schema meta-graph.
> > > > > > > * A VertexType vertex represents a vertex label definition
> > > > > > > (e.g. "person", "software").
> > > > > > > * An EdgeType vertex represents an edge label definition
> > > > > > > (e.g. "knows", "created"). Even though it describes edges
> > > > > > > in the data graph, it is itself a vertex in the schema
> > > > > > > graph, connected to
> > > > > > > its endpoint VertexType vertices via from/to edges.
> > > > > > > * A PropertyType vertex represents a property on a type,
> > > > > > > connected to its parent type vertex via a “hasProperty"
> > > > > > > edge.
> > > > > > > Property definitions are independent per type, no sharing
> > > > > > > across types.
> > > > > > > Schema graph example for the classic TinkerPop modern
> > > > > > > graph:
> > > > > > > (person:vertexType) --hasProperty--> (name:propertyType)
> > > > > > > (person:vertexType) --hasProperty--> (age:propertyType)
> > > > > > > (software:vertexType) --hasProperty--> (name:propertyType)
> > > > > > > (software:vertexType) --hasProperty--> (lang:propertyType)
> > > > > > > (knows:edgeType) --from--> (person:vertexType)
> > > > > > > (knows:edgeType) --to--> (person:vertexType)
> > > > > > > (knows:edgeType) --hasProperty--> (weight:propertyType)
> > > > > > > (created:edgeType) --from--> (person:vertexType)
> > > > > > > (created:edgeType) --to--> (software:vertexType)
> > > > > > > (created:edgeType) --hasProperty--> (weight:propertyType)
> > > > > > > 3. Constraints are properties on type verticesRather than a
> > > > > > > fixed constraint taxonomy, constraints are regular
> > > > > > > properties on type vertices, keyed by string via
> > > > > > > constraint(key, value). This keeps the model extensible
> > > > > > > such that providers can define their own constraints
> > > > > > > without changes to the core API.
> > > > > > > Constraints can be added to VertexType, EdgeType, and
> > > > > > > PropertyType vertices directly. The most common constraints
> > > > > > > such as property
> > > > > > > types and required properties would apply to PropertyTypes,
> > > > > > > while edge multiplicity constraints (e.g. one-to-many, one-
> > > > > > > to-one) are
> > > > > > > naturally expressed as constraints on the EdgeType itself
> > > > > > > rather than on
> > > > > > > any property.
> > > > > > > While constraint keys are arbitrary strings and providers
> > > > > > > are
> > > > > > > free to implement any constraints they like, TinkerPop
> > > > > > > should standardize
> > > > > > > a set of core constraint keys representing the most common
> > > > > > > constraints. Examples include “type", “required", “unique",
> > > > > > > “minValue", “maxValue", etc. Providers that support
> > > > > > > equivalent constraints
> > > > > > > are encouraged to follow these conventional names for
> > > > > > > interoperability.
> > > > > > > Non-core constraints (custom to a provider) are encouraged
> > > > > > > to
> > > > > > > follow a namespaced key convention to avoid collisions,
> > > > > > > e.g. "tinkergraph:notNull". Core constraint keys are
> > > > > > > unnamespaced.
> > > > > > > 4. Schema traversal steps in core GremlinNew steps for
> > > > > > > schema manipulation live directly in
> > > > > > > GraphTraversal/GraphTraversalSource, not in a separate DSL:
> > > > > > > * addVType(label) — creates a VertexType vertex
> > > > > > > * addEType(label) — creates an EdgeType vertex
> > > > > > > * propertyType(name) — creates a PropertyType vertex and
> > > > > > > connects
> > > > > > > it via hasProperty
> > > > > > > * constraint(key, value) — adds a constraint property to
> > > > > > > the
> > > > > > > current type vertex
> > > > > > > Example: defining a vertex type with properties:
> > > > > > > schema.traversal().addVType("person")
> > > > > > > .propertyType("name").constraint("type",
> > > > > > > String.class).constraint("required",
> > > > > > > true).constraint("unique", true)
> > > > > > > .propertyType("age").constraint("type", Integer.class)
> > > > > > >
> > > > > > > Example: defining an edge type with endpoint types and a
> > > > > > > property:
> > > > > > > schema.traversal().addEType("knows")
> > > > > > > .from("person").to("person")
> > > > > > > .propertyType("weight").constraint("type", Double.class)
> > > > > > >
> > > > > > > This mirrors the addE().from().to() pattern from the data-
> > > > > > > graph.
> > > > > > > Here from() and to() take vertex type labels (strings) and
> > > > > > > create
> > > > > > > from/to edges in the schema graph connecting the EdgeType
> > > > > > > to the
> > > > > > > referenced VertexType vertices.
> > > > > > > 5. Convenience methods for direct accessThe schema-as-graph
> > > > > > > model is the source of truth, but traversing
> > > > > > > it for simple lookups isn’t always convenient. Direct
> > > > > > > methods
> > > > > > > provide compact access:
> > > > > > > GraphSchema methods:
> > > > > > > * vertexTypes() → Collection
> > > > > > > * vertexType(String label) → Optional
> > > > > > > * edgeTypes() → Collection
> > > > > > > * edgeType(String label) → Optional
> > > > > > > * addVertexType(String label) → VertexType
> > > > > > > * addEdgeType(String label) → EdgeType
> > > > > > > * store(OutputStream): serialize the schema to a compact
> > > > > > > JSON representation
> > > > > > > * load(InputStream): deserialize and merge a schema from
> > > > > > > JSON
> > > > > > > into this schema graph
> > > > > > > EdgeType methods:
> > > > > > > * fromVertexTypes() → Collection
> > > > > > > * toVertexTypes() → Collection
> > > > > > > Example:
> > > > > > > GraphSchema schema = graph.schema(); // Look up a vertex
> > > > > > > type VertexType person =
> > > > > > > schema.vertexType("person").orElseThrow(); // Inspect its
> > > > > > > properties for (PropertyType pd : person.propertyTypes()) {
> > > > > > > System.out.println(pd.name() + " : " +
> > > > > > > pd.constraint("type")); } // Look up an edge type and its
> > > > > > > connectivity EdgeType knows =
> > > > > > > schema.edgeType("knows").orElseThrow();
> > > > > > > Collection<VertexType> fromTypes = knows.fromVertexTypes();
> > > > > > > Collection<VertexType> toTypes = knows.toVertexTypes();
> > > > > > > 6. Cross-graph jumpsTwo steps bridge the data graph and
> > > > > > > schema graph:
> > > > > > > * type(): from a data traversal, jump to the element's
> > > > > > > type definition in the schema graph.
> > > > > > > * instances(): from a schema traversal, jump to all
> > > > > > > matching
> > > > > > > elements in the data graph.
> > > > > > > These compose for round-trip traversals:
> > > > > > > // Get the type definition for "person" vertices
> > > > > > > g.V().hasLabel("person").type() // Get all instances of a
> > > > > > > schema type
> > > > > > > schema.traversal().vertexType("person").instances() //
> > > > > > > Round-trip: find marko's type, then get all instances of
> > > > > > > that type g.V().has("person", "name",
> > > > > > > "marko").type().instances()
> > > > > > > 7. Schema restriction strategyThere are some steps we will
> > > > > > > want to restrict in both the data
> > > > > > > graph and the schema-graph. addVType() wouldn’t make sense
> > > > > > > in the data- graph, nor would addV() be sensible in the
> > > > > > > schema-graph. A TraversalStrategy can restrict schema
> > > > > > > traversals to a safe subset
> > > > > > > of Gremlin steps (allowlist-based). This prevents
> > > > > > > accidentally
> > > > > > > running data element insertions, OLAP computations, complex
> > > > > > > control flow,
> > > > > > > or side-effect steps against the schema graph. The strategy
> > > > > > > should
> > > > > > > be auto-registered when traversing a GraphSchema instance.
> > > > > > > The exact allowlist should be a topic for later discussion.
> > > > > > > 8. Instance counts on type
> > > > > > > verticesVertexType.instanceCount() and
> > > > > > > EdgeType.instanceCount() return
> > > > > > > the count of data graph elements matching each type. This
> > > > > > > is a method rather than a property on the type vertex,
> > > > > > > keeping the schema
> > > > > > > graph definitional (not statistical) and giving providers
> > > > > > > full implementation flexibility.
> > > > > > > Approximate counts are likely acceptable and preferable for
> > > > > > > performance in most cases. However, TinkerPop should not
> > > > > > > stand in
> > > > > > > the way of providers that prefer exact counts, and should
> > > > > > > ensure that appropriate hooks are in place in reference
> > > > > > > implementations so
> > > > > > > that providers can maintain exact counts if they so desire.
> > > > > > > Transactional implications need additional consideration.
> > > > > > > Maintaining accurate counts across concurrent writes,
> > > > > > > rollbacks, and
> > > > > > > transaction isolation levels adds significant complexity.
> > > > > > > This interacts with
> > > > > > > the broader schema transactions question (see transactions
> > > > > > > below) and should be addressed alongside it.
> > > > > > > 9. GLV SupportEach GLV (Python, JavaScript, .NET, Go)
> > > > > > > needs:
> > > > > > > * Schema data classes: Parallel classes to the 4 core Java
> > > > > > > interfaces, following the same pattern as existing Vertex
> > > > > > > and
> > > > > > > Edge classes. These are data containers representing
> > > > > > > schema objects returned from the server: - GraphSchema:
> > > > > > > holds collections of VertexTypes and EdgeTypes -
> > > > > > > VertexType: label, full constraints map, and collection of
> > > > > > > PropertyTypes - EdgeType: label, full constraints map,
> > > > > > > from/to VertexType references (same pattern as
> > > > > > > Edge.outV/Edge.inV), and collection
> > > > > > > of PropertyTypes - PropertyType: name and full
> > > > > > > constraints map (including data
> > > > > > > type as a constraint)
> > > > > > > * All new gremlin steps are supported from each GLV
> > > > > > > Future Questions Schema validationProviders will need lots
> > > > > > > of flexibility regarding validation
> > > > > > > modes. Some providers may choose to have write-time
> > > > > > > validation for all inserts, others may choose validate an
> > > > > > > entire graph against a
> > > > > > > schema as a batch job, while others may choose to validate
> > > > > > > on-commit.
> > > > > > > For our purposes, we need to provide a viable reference
> > > > > > > implementation, as well as ensuring sufficient extension
> > > > > > > points exist for
> > > > > > > providers to fulfill their needs.
> > > > > > > Dynamic schema updates from data writesIt would be useful
> > > > > > > to auto-update the schema graph when data
> > > > > > > writes introduce new labels or properties (e.g.
> > > > > > > addV("newLabel”) automatically creates a VertexType).
> > > > > > > Keeping the schema exactly
> > > > > > > in- sync with such operations may introduce too much
> > > > > > > overhead for
> > > > > > > many purposes. We should provide appropriate hooks for
> > > > > > > providers to implement such behaviour if desired, or to
> > > > > > > help providers
> > > > > > > aggregate changes and perform incremental batch updates to
> > > > > > > the schema.
> > > > > > > TransactionsThe schema graph will need to be transactional
> > > > > > > if the data
> > > > > > > File IOIt is often useful to persist and load schemas
> > > > > > > to/from files.
> > > > > > > This capability should be build into the GraphSchema class
> > > > > > > via simple store() and load() methods, using a custom
> > > > > > > compact JSON representation of the schema. The specifics of
> > > > > > > this format are deferred to later discussion.
> > > > > > > GraphSchema exposes file IO directly:
> > > > > > > * store(OutputStream): serialize the schema to a compact
> > > > > > > JSON representation
> > > > > > > * load(InputStream): deserialize a schema from JSON and
> > > > > > > merge it
> > > > > > > into the current schema graph
> > > > > > > Schema file IO should be implemented across all GLVs.
> > > > > > > Reference ImplementationTinkerGraph serves as the reference
> > > > > > > implementation:
> > > > > > > * TinkerGraphSchema extends TinkerGraph implements
> > > > > > > GraphSchema
> > > > > > > * TinkerVertexType extends TinkerVertex implements
> > > > > > > VertexType
> > > > > > > * TinkerPropertyType extends TinkerVertex implements
> > > > > > > PropertyType
> > > > > > > * TinkerEdgeType extends TinkerVertex implements EdgeType
> > > > > > > * Recursion guard prevents schema-of-schema
> > > > > > > (TinkerGraphSchema overrides initSchema())
> > > > > > > Please let me know any thoughts you may have on the
> > > > > > > approach. I intend to move this into a proposal PR soon,
> > > > > > > unless there are any major disagreements over the design.
> > > > > > > Thanks, Cole
> > > > > >
> > > > >
> > > >
> > >
> > >
> > > >
> > > >
> > > > >
> > > >
> > >
>