mirror of
https://github.com/ent/ent.git
synced 2026-05-22 09:31:45 +03:00
Summary: Pull Request resolved: https://github.com/facebookincubator/ent/pull/33 Reviewed By: noamsch Differential Revision: D17344992 fbshipit-source-id: 5336e9c5a4978e10923e26b5754942d92371a5cd
403 lines
13 KiB
Go
403 lines
13 KiB
Go
// Copyright 2019-present Facebook Inc. All rights reserved.
|
|
// This source code is licensed under the Apache 2.0 license found
|
|
// in the LICENSE file in the root directory of this source tree.
|
|
|
|
package dsl
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// Traversal mimics the TinkerPop graph traversal.
|
|
type Traversal struct {
|
|
// nodes holds the dsl nodes. first element is the reference name
|
|
// of the TinkerGraph. defaults to "g".
|
|
nodes []Node
|
|
}
|
|
|
|
// NewTraversal returns a new default traversal with "g" as a reference name to the Graph.
|
|
func NewTraversal() *Traversal {
|
|
return &Traversal{[]Node{G}}
|
|
}
|
|
|
|
// Group groups a list of traversals into one. all traversals are assigned into a temporary
|
|
// variables named by their index. The last variable functions as a return value of the query.
|
|
// Note that, this "temporary hack" is not perfect and may not work in some cases because of
|
|
// the limitation of evaluation order.
|
|
func Group(trs ...*Traversal) *Traversal {
|
|
var (
|
|
b = Block{}
|
|
names = make(map[*Traversal]Token)
|
|
)
|
|
for i, tr := range trs {
|
|
if _, ok := names[tr]; ok {
|
|
continue
|
|
}
|
|
v := &Var{Name: fmt.Sprintf("t%d", i), Elem: &Traversal{nodes: tr.nodes}}
|
|
b.Nodes = append(b.Nodes, v)
|
|
names[tr] = Token(v.Name)
|
|
}
|
|
for _, tr := range trs {
|
|
tr.nodes = []Node{names[tr]}
|
|
}
|
|
b.Nodes = append(b.Nodes, names[trs[len(trs)-1]])
|
|
return &Traversal{[]Node{b}}
|
|
}
|
|
|
|
// Join joins a list of traversals with a semicolon separator.
|
|
func Join(trs ...*Traversal) *Traversal {
|
|
b := Block{}
|
|
for _, tr := range trs {
|
|
b.Nodes = append(b.Nodes, &Traversal{nodes: tr.nodes})
|
|
}
|
|
return &Traversal{[]Node{b}}
|
|
}
|
|
|
|
// V step is usually used to start a traversal but it may also be used mid-traversal.
|
|
func (t *Traversal) V(args ...interface{}) *Traversal {
|
|
t.Add(Dot, NewFunc("V", args...))
|
|
return t
|
|
}
|
|
|
|
// OtherV maps the Edge to the incident vertex that was not just traversed from in the path history.
|
|
func (t *Traversal) OtherV() *Traversal {
|
|
t.Add(Dot, NewFunc("otherV"))
|
|
return t
|
|
}
|
|
|
|
// E step is usually used to start a traversal but it may also be used mid-traversal.
|
|
func (t *Traversal) E(args ...interface{}) *Traversal {
|
|
t.Add(Dot, NewFunc("E", args...))
|
|
return t
|
|
}
|
|
|
|
// AddV adds a vertex.
|
|
func (t *Traversal) AddV(args ...interface{}) *Traversal {
|
|
t.Add(Dot, NewFunc("addV", args...))
|
|
return t
|
|
}
|
|
|
|
// AddE adds an edge.
|
|
func (t *Traversal) AddE(args ...interface{}) *Traversal {
|
|
t.Add(Dot, NewFunc("addE", args...))
|
|
return t
|
|
}
|
|
|
|
// Next gets the next n-number of results from the traversal.
|
|
func (t *Traversal) Next() *Traversal {
|
|
return t.Add(Dot, NewFunc("next"))
|
|
}
|
|
|
|
// Drop removes elements and properties from the graph.
|
|
func (t *Traversal) Drop() *Traversal {
|
|
return t.Add(Dot, NewFunc("drop"))
|
|
}
|
|
|
|
// Property sets a Property value and related meta properties if supplied,
|
|
// if supported by the Graph and if the Element is a VertexProperty.
|
|
func (t *Traversal) Property(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("property", args...))
|
|
}
|
|
|
|
// Both maps the Vertex to its adjacent vertices given the edge labels.
|
|
func (t *Traversal) Both(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("both", args...))
|
|
}
|
|
|
|
// BothE maps the Vertex to its incident edges given the edge labels.
|
|
func (t *Traversal) BothE(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("bothE", args...))
|
|
}
|
|
|
|
// Has filters vertices, edges and vertex properties based on their properties.
|
|
// See: http://tinkerpop.apache.org/docs/current/reference/#has-step.
|
|
func (t *Traversal) Has(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("has", args...))
|
|
}
|
|
|
|
// HasNot filters vertices, edges and vertex properties based on the non-existence of properties.
|
|
// See: http://tinkerpop.apache.org/docs/current/reference/#has-step.
|
|
func (t *Traversal) HasNot(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("hasNot", args...))
|
|
}
|
|
|
|
// HasID filters vertices, edges and vertex properties based on their identifier.
|
|
func (t *Traversal) HasID(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("hasId", args...))
|
|
}
|
|
|
|
// HasLabel filters vertices, edges and vertex properties based on their label.
|
|
func (t *Traversal) HasLabel(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("hasLabel", args...))
|
|
}
|
|
|
|
// HasNext returns true if the iteration has more elements.
|
|
func (t *Traversal) HasNext() *Traversal {
|
|
return t.Add(Dot, NewFunc("hasNext"))
|
|
}
|
|
|
|
// Match maps the Traverser to a Map of bindings as specified by the provided match traversals.
|
|
func (t *Traversal) Match(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("match", args...))
|
|
}
|
|
|
|
// Choose routes the current traverser to a particular traversal branch option which allows the creation of if-then-else like semantics within a traversal.
|
|
func (t *Traversal) Choose(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("choose", args...))
|
|
}
|
|
|
|
// Select arbitrary values from the traversal.
|
|
func (t *Traversal) Select(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("select", args...))
|
|
}
|
|
|
|
// Group organizes objects in the stream into a Map.Calls to group() are typically accompanied with by() modulators which help specify how the grouping should occur.
|
|
func (t *Traversal) Group() *Traversal {
|
|
return t.Add(Dot, NewFunc("group"))
|
|
}
|
|
|
|
// Values maps the Element to the values of the associated properties given the provide property keys.
|
|
func (t *Traversal) Values(args ...string) *Traversal {
|
|
return t.Add(Dot, NewFunc("values", sface(args)...))
|
|
}
|
|
|
|
// ValueMap maps the Element to a Map of the property values key'd according to their Property.key().
|
|
func (t *Traversal) ValueMap(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("valueMap", args...))
|
|
}
|
|
|
|
// Properties maps the Element to its associated properties given the provide property keys.
|
|
func (t *Traversal) Properties(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("properties", args...))
|
|
}
|
|
|
|
// Range filters the objects in the traversal by the number of them to pass through the stream.
|
|
func (t *Traversal) Range(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("range", args...))
|
|
}
|
|
|
|
// Limit filters the objects in the traversal by the number of them to pass through the stream, where only the first n objects are allowed as defined by the limit argument.
|
|
func (t *Traversal) Limit(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("limit", args...))
|
|
}
|
|
|
|
// ID maps the Element to its Element.id().
|
|
func (t *Traversal) ID() *Traversal {
|
|
return t.Add(Dot, NewFunc("id"))
|
|
}
|
|
|
|
// Label maps the Element to its Element.label().
|
|
func (t *Traversal) Label() *Traversal {
|
|
return t.Add(Dot, NewFunc("label"))
|
|
}
|
|
|
|
// From provides from()-modulation to respective steps.
|
|
func (t *Traversal) From(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("from", args...))
|
|
}
|
|
|
|
// To used as a modifier to addE(String) this method specifies the traversal to use for selecting the incoming vertex of the newly added Edge.
|
|
func (t *Traversal) To(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("to", args...))
|
|
}
|
|
|
|
// As provides a label to the step that can be accessed later in the traversal by other steps.
|
|
func (t *Traversal) As(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("as", args...))
|
|
}
|
|
|
|
// Or ensures that at least one of the provided traversals yield a result.
|
|
func (t *Traversal) Or(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("or", args...))
|
|
}
|
|
|
|
// And ensures that all of the provided traversals yield a result.
|
|
func (t *Traversal) And(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("and", args...))
|
|
}
|
|
|
|
// Is filters the E object if it is not P.eq(V) to the provided value.
|
|
func (t *Traversal) Is(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("is", args...))
|
|
}
|
|
|
|
// Not removes objects from the traversal stream when the traversal provided as an argument does not return any objects.
|
|
func (t *Traversal) Not(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("not", args...))
|
|
}
|
|
|
|
// In maps the Vertex to its incoming adjacent vertices given the edge labels.
|
|
func (t *Traversal) In(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("in", args...))
|
|
}
|
|
|
|
// Where filters the current object based on the object itself or the path history.
|
|
func (t *Traversal) Where(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("where", args...))
|
|
}
|
|
|
|
// Out maps the Vertex to its outgoing adjacent vertices given the edge labels.
|
|
func (t *Traversal) Out(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("out", args...))
|
|
}
|
|
|
|
// OutE maps the Vertex to its outgoing incident edges given the edge labels.
|
|
func (t *Traversal) OutE(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("outE", args...))
|
|
}
|
|
|
|
// InE maps the Vertex to its incoming incident edges given the edge labels.
|
|
func (t *Traversal) InE(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("inE", args...))
|
|
}
|
|
|
|
// OutV maps the Edge to its outgoing/tail incident Vertex.
|
|
func (t *Traversal) OutV(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("outV", args...))
|
|
}
|
|
|
|
// InV maps the Edge to its incoming/head incident Vertex.
|
|
func (t *Traversal) InV(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("inV", args...))
|
|
}
|
|
|
|
// ToList puts all the results into a Groovy list.
|
|
func (t *Traversal) ToList() *Traversal {
|
|
return t.Add(Dot, NewFunc("toList"))
|
|
}
|
|
|
|
// Iterate iterates the traversal presumably for the generation of side-effects.
|
|
func (t *Traversal) Iterate() *Traversal {
|
|
return t.Add(Dot, NewFunc("iterate"))
|
|
}
|
|
|
|
// Count maps the traversal stream to its reduction as a sum of the Traverser.bulk() values
|
|
// (i.e. count the number of traversers up to this point).
|
|
func (t *Traversal) Count(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("count", args...))
|
|
}
|
|
|
|
// Order all the objects in the traversal up to this point and then emit them one-by-one in their ordered sequence.
|
|
func (t *Traversal) Order(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("order", args...))
|
|
}
|
|
|
|
// By can be applied to a number of different step to alter their behaviors.
|
|
// This form is essentially an identity() modulation.
|
|
func (t *Traversal) By(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("by", args...))
|
|
}
|
|
|
|
// Fold rolls up objects in the stream into an aggregate list..
|
|
func (t *Traversal) Fold() *Traversal {
|
|
return t.Add(Dot, NewFunc("fold"))
|
|
}
|
|
|
|
// Unfold unrolls a Iterator, Iterable or Map into a linear form or simply emits the object if it is not one of those types.
|
|
func (t *Traversal) Unfold() *Traversal {
|
|
return t.Add(Dot, NewFunc("unfold"))
|
|
}
|
|
|
|
// Sum maps the traversal stream to its reduction as a sum of the Traverser.get() values multiplied by their Traverser.bulk().
|
|
func (t *Traversal) Sum(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("sum", args...))
|
|
}
|
|
|
|
// Mean determines the mean value in the stream.
|
|
func (t *Traversal) Mean(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("mean", args...))
|
|
}
|
|
|
|
// Min determines the smallest value in the stream.
|
|
func (t *Traversal) Min(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("min", args...))
|
|
}
|
|
|
|
// Max determines the greatest value in the stream.
|
|
func (t *Traversal) Max(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("max", args...))
|
|
}
|
|
|
|
// Coalesce evaluates the provided traversals and returns the result of the first traversal to emit at least one object.
|
|
func (t *Traversal) Coalesce(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("coalesce", args...))
|
|
}
|
|
|
|
// Dedup removes all duplicates in the traversal stream up to this point.
|
|
func (t *Traversal) Dedup(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("dedup", args...))
|
|
}
|
|
|
|
// Constant maps any object to a fixed E value.
|
|
func (t *Traversal) Constant(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("constant", args...))
|
|
}
|
|
|
|
// Union merges the results of an arbitrary number of traversals.
|
|
func (t *Traversal) Union(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("union", args...))
|
|
}
|
|
|
|
// SideEffect allows the traverser to proceed unchanged, but yield some computational
|
|
// sideEffect in the process.
|
|
func (t *Traversal) SideEffect(args ...interface{}) *Traversal {
|
|
return t.Add(Dot, NewFunc("sideEffect", args...))
|
|
}
|
|
|
|
// Each is a Groovy each-loop function.
|
|
func Each(v interface{}, cb func(it *Traversal) *Traversal) *Traversal {
|
|
t := &Traversal{}
|
|
switch v := v.(type) {
|
|
case *Traversal:
|
|
t.Add(&Var{Elem: v})
|
|
case []interface{}:
|
|
t.Add(NewList(v...))
|
|
default:
|
|
t.Add(Token("undefined"))
|
|
}
|
|
t.Add(Dot, Token("each"), Token(" { "))
|
|
t.Add(cb(&Traversal{[]Node{Token("it")}}).nodes...)
|
|
t.Add(Token(" }"))
|
|
return t
|
|
}
|
|
|
|
// Add is the public API for adding new nodes to the traversal by its sub packages.
|
|
func (t *Traversal) Add(n ...Node) *Traversal {
|
|
t.nodes = append(t.nodes, n...)
|
|
return t
|
|
}
|
|
|
|
// Query returns the query-representation and its binding of this traversal object.
|
|
func (t *Traversal) Query() (string, Bindings) {
|
|
var (
|
|
names []interface{}
|
|
query strings.Builder
|
|
bindings = Bindings{}
|
|
)
|
|
for _, n := range t.nodes {
|
|
code, args := n.Code()
|
|
query.WriteString(code)
|
|
for _, arg := range args {
|
|
names = append(names, bindings.Add(arg))
|
|
}
|
|
}
|
|
return fmt.Sprintf(query.String(), names...), bindings
|
|
}
|
|
|
|
// Clone creates a deep copy of an existing traversal.
|
|
func (t *Traversal) Clone() *Traversal {
|
|
if t == nil {
|
|
return nil
|
|
}
|
|
return &Traversal{append(make([]Node, 0, len(t.nodes)), t.nodes...)}
|
|
}
|
|
|
|
// Undo reverts the last-step of the traversal.
|
|
func (t *Traversal) Undo() *Traversal {
|
|
if n := len(t.nodes); n > 2 {
|
|
t.nodes = t.nodes[:n-2]
|
|
}
|
|
return t
|
|
}
|