opencypher

package module
v1.0.0-beta-1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 22, 2022 License: Apache-2.0 Imports: 13 Imported by: 6

README

GoDoc Go Report Card Build Status

Embedded openCypher interpreter and labeled property graphs

This Go module contains a openCypher interpreter partial implementation and a labeled property graph implementation. The labeled property graph package can be used independently from the openCypher library.

This Go module is part of the Layered Schema Architecture.

openCypher

At this point, this library provides partial support for openCypher expressions. More support will be added as needed. unsupported features include:

  • MERGE
  • CALL
  • Transactions
  • CSV related functions

openCypher expressions are evaluated using an evaluation context.

Create Nodes

See examples/create directory.

import (
	"fmt"

	"github.com/cloudprivacylabs/opencypher"
	"github.com/cloudprivacylabs/opencypher/graph"
)

func main() {
	grph := graph.NewOCGraph()
	ectx := opencypher.NewEvalContext(grph)
	_, err := opencypher.ParseAndEvaluate(`CREATE (n:Person), (m)`, ectx)
	if err != nil {
		panic(err)
	}
	v, err := opencypher.ParseAndEvaluate(`MATCH (x:Person) return x as person`, ectx)
	if err != nil {
		panic(err)
	}
	fmt.Println(v.Get().(opencypher.ResultSet).Rows[0]["person"])
}
Evaluation Context

Variables defined in an expression will be in the evaluation context, and can be used to affect the results of subsequent expression.

See examples/context.

func main() {
	// Create an empty graph
	grph := graph.NewOCGraph()
	// Evaluation context knows the graph we are working on
	ectx := opencypher.NewEvalContext(grph)
	// CREATE a path
	_, err := opencypher.ParseAndEvaluate(`CREATE (andy {name:"Andy"})-[:KNOWS]-> (stephen {name:"Stephen"})`, ectx)
	if err != nil {
		panic(err)
	}
	// ectx knows andy and stephen. So this will only update stephen, and not andy
	v, err := opencypher.ParseAndEvaluate(`MATCH (stephen) SET stephen.age=34 return stephen`, ectx)
	if err != nil {
		panic(err)
	}
	age, _ := v.Get().(opencypher.ResultSet).Rows[0]["1"].Get().(graph.Node).GetProperty("age")
	fmt.Println(age) // This will print 34
}
Querying and result sets

Using the example in https://neo4j.com/docs/cypher-manual/current/clauses/match/

	// Get all nodes
	ectx = opencypher.NewEvalContext(grph)
	res, err := opencypher.ParseAndEvaluate(`match (n) return n`, ectx)
	fmt.Println("match (n) return n:", res.Get().(opencypher.ResultSet).Rows)

	// Get all movies
	ectx = opencypher.NewEvalContext(grph)
	res, err = opencypher.ParseAndEvaluate(`match (n:Movie) return n.title`, ectx)
	fmt.Println("match (n:Movie) return n.title:", res.Get().(opencypher.ResultSet).Rows)

	// Get related node
	ectx = opencypher.NewEvalContext(grph)
	res, err = opencypher.ParseAndEvaluate(`match (director {name: 'Oliver Stone'}) --(movie:Movie) return movie.title`, ectx)
	fmt.Println("match (director {name: 'Oliver Stone'}) --(movie:Movie) return movie.title:", res.Get().(opencypher.ResultSet).Rows)
	ectx = opencypher.NewEvalContext(grph)
	res, err = opencypher.ParseAndEvaluate(`match (director {name: 'Oliver Stone'}) --> (movie:Movie) return movie.title`, ectx)
	fmt.Println("match (director {name: 'Oliver Stone'}) --> (movie:Movie) return movie.title:", res.Get().(opencypher.ResultSet).Rows)

	// Get relationship type
	ectx = opencypher.NewEvalContext(grph)
	res, err = opencypher.ParseAndEvaluate(`match (:Person {name: 'Oliver Stone'}) -[r]->(movie) return r`, ectx)
	fmt.Println("match (:Person {name: 'Oliver Stone'}) -[r]->(movie) return r:", res.Get().(opencypher.ResultSet).Rows)
Values

Opencypher expressions return an object of type Value. Value.Get returns the value contained in the value object. For most queries, this value is of type opencypher.ResultSet. A ResultSet contains Rows that are map[string]Value objects. If the query explicitly names its columns, the map will contains those names as the keys. Otherwise, the columns will be "1", "2", etc.

The number of rows in the result set:

rs:=value.Get().(opencypher.ResultSet)
numResults:=len(rs.Rows)

Iterate the results:

for _,row := range resultSet.Rows {
   for colName, colValue := range row {
      // colName is the column name
      // colValue is an opencypher.Value object
      fmt.Println(colName,value.Get())
   }
}

Labeled Property Graph

This labeled property graph package implements the openCypher model of labeled property graphs. A labeled property graph (LPG) contains nodes and directed edges between those nodes. Every node contains:

  • Labels: Set of string tokens that usually identify the type of the node,
  • Properties: Key-value pairs.

Every edge contains:

  • A label: String token that identifies a relationship, and
  • Properties: Key-value pairs.

A graph indexes its nodes and edges, so finding a node, or a pattern usually does not involve iterating through all possibilities.

Create a graph using NewOCGraph function:

g := graph.NewOCGraph()
// Create two nodes
n1 := g.NewNode([]string{"label1"},map[string]interface{}{"prop": "value1" })
n2 := g.NewNode([]string{"label2"},map[string]interface{}{"prop": "value2" })
// Connect the two nodes with an edge
edge:=g.NewEdge(n1,n2,"relatedTo",nil)

The LPG library uses an iterator model to address nodes and edges because the underlying algorithm to collect nodes and edges mathcing a certain criteria may depend on the existence of indexes. Both incoming and outgoing edges of nodes are accessible:

for edges:=n1.GetEdges(graph.OutgoingEdge); edges.Next(); {
  edge:=edges.Edge()
  // edge.GetTo() and edge.GetFrom() are the adjacent nodes
}

The graph indexes nodes by label, so access to nodes using labels is fast. You can add additional indexes on properties:

g := graph.NewOCGraph()
// Index all nodes with property 'prop'
g.AddNodePropertyIndex("prop")

// This access should be fast
nodes := g.GetNodesWithProperty("prop")

// This will go through all nodes
slowNodes:= g.GetNodesWithProperty("propWithoutIndex")

Graph library supports searching patterns. The following example searches for the pattern that match

(:label1) -[]->({prop:value})`

and returns the head nodes for every matching path:

pattern := graph.Pattern{ 
 // Node containing label 'label1'
 {
   Labels: graph.NewStringSet("label1"),
 },
 // Edge of length 1
 {
   Min: 1, 
   Max: 1,
 },
 // Node with property prop=value
 {
   Properties: map[string]interface{} {"prop":"value"},
 }}
nodes, err:=pattern.FindNodes(g,nil)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrDivideByZero                   = errors.New("Divide by zero")
	ErrInvalidUnaryOperation          = errors.New("Invalid unary operation")
	ErrInvalidPowerOperation          = errors.New("Invalid power operation")
	ErrInvalidMultiplicativeOperation = errors.New("Invalid multiplicative operation")
	ErrInvalidDurationOperation       = errors.New("Invalid duration operation")
	ErrOperationWithNull              = errors.New("Operation with null")
	ErrInvalidStringOperation         = errors.New("Invalid string operation")
	ErrInvalidDateOperation           = errors.New("Invalid date operation")
	ErrInvalidAdditiveOperation       = errors.New("Invalid additive operation")
	ErrInvalidComparison              = errors.New("Invalid comparison")
	ErrInvalidListIndex               = errors.New("Invalid list index")
	ErrNotAList                       = errors.New("Not a list")
	ErrNotABooleanExpression          = errors.New("Not a boolean expression")
	ErrMapKeyNotString                = errors.New("Map key is not a string")
	ErrInvalidMapKey                  = errors.New("Invalid map key")
	ErrNotAStringSet                  = errors.New("Not a string set")
	ErrIntValueRequired               = errors.New("Int value required")
	ErrExpectingResultSet             = errors.New("Expecting a result set")
	ErrPropertiesParameterExpected    = errors.New("Parameter value cannot be used for properties")
	ErrPropertiesExpected             = errors.New("Value cannot be used for properties")
	ErrValueDoesNotHaveProperties     = errors.New("Value does not have properties")
	ErrNotAnLValue                    = errors.New("Not and lvalue")
)
View Source
var ErrIncompatibleCells = errors.New("Incompatible result set cells")
View Source
var ErrRowsHaveDifferentSizes = errors.New("Rows have different sizes")

Functions

func BuildPatternSymbols

func BuildPatternSymbols(ctx *EvalContext, pattern graph.Pattern) (map[string]*graph.PatternSymbol, error)

BuildPatternSymbols copies all the symbols referenced in the pattern from the context, and puts them in a map

func EscapeLabelLiteral

func EscapeLabelLiteral(s string) string

EscapeLabelLiteral escape a literal that can be used as a label. It returns `s`

func EscapePropertyKeyLiteral

func EscapePropertyKeyLiteral(s string) string

EscapePropertyKeyLiteral escapes a literal that can be used as a property key. Returns `s`

func EscapeStringLiteral

func EscapeStringLiteral(s string) string

EscapeStringLiteral returns "s" where backslashes and quotes in s are escaped

func GetParser

func GetParser(input string) *parser.CypherParser

GetParser returns a parser that will parse the input string

func IsValuePrimitive

func IsValuePrimitive(v Value) bool

IsValuePrimitive returns true if the value is int, float64, bool, string, duration, date, datetime, localDateTime, or localTime

func IsValueSame

func IsValueSame(v, v2 Value) bool

IsValueSame compares two values and decides if the two are the same

func ValueAsBool

func ValueAsBool(v Value) (bool, bool)

ValueAsBool returns the bool value, or if it is not bool, false,false

Types

type Atom

type Atom interface {
	Evaluatable
}

type BooleanLiteral

type BooleanLiteral bool

func (BooleanLiteral) Evaluate

func (literal BooleanLiteral) Evaluate(ctx *EvalContext) (Value, error)

type Case

type Case struct {
	Test         Expression
	Alternatives []CaseAlternative
	Default      Expression
}

func (Case) Evaluate

func (cs Case) Evaluate(ctx *EvalContext) (Value, error)

type CaseAlternative

type CaseAlternative struct {
	When Expression
	Then Expression
}

type CountAtom

type CountAtom struct{}

func (CountAtom) Evaluate

func (cnt CountAtom) Evaluate(ctx *EvalContext) (Value, error)

type DoubleLiteral

type DoubleLiteral float64

func (DoubleLiteral) Evaluate

func (literal DoubleLiteral) Evaluate(ctx *EvalContext) (Value, error)

type ErrInvalidAssignment

type ErrInvalidAssignment string

func (ErrInvalidAssignment) Error

func (e ErrInvalidAssignment) Error() string

type ErrInvalidExpression

type ErrInvalidExpression string

func (ErrInvalidExpression) Error

func (e ErrInvalidExpression) Error() string

type ErrInvalidFunctionCall

type ErrInvalidFunctionCall struct {
	Msg string
}

func (ErrInvalidFunctionCall) Error

func (e ErrInvalidFunctionCall) Error() string

type ErrInvalidIndirection

type ErrInvalidIndirection string

func (ErrInvalidIndirection) Error

func (e ErrInvalidIndirection) Error() string

type ErrInvalidValueReferenceInPattern

type ErrInvalidValueReferenceInPattern struct {
	Symbol string
}

func (ErrInvalidValueReferenceInPattern) Error

type ErrSyntax

type ErrSyntax string

func (ErrSyntax) Error

func (e ErrSyntax) Error() string

type ErrUnknownFunction

type ErrUnknownFunction struct {
	Name string
}

func (ErrUnknownFunction) Error

func (e ErrUnknownFunction) Error() string

type ErrUnknownParameter

type ErrUnknownParameter struct {
	Key string
}

func (ErrUnknownParameter) Error

func (e ErrUnknownParameter) Error() string

type ErrUnknownVariable

type ErrUnknownVariable struct {
	Name string
}

func (ErrUnknownVariable) Error

func (e ErrUnknownVariable) Error() string

type EvalContext

type EvalContext struct {
	// contains filtered or unexported fields
}

func NewEvalContext

func NewEvalContext(graph graph.Graph) *EvalContext

func (*EvalContext) GetFunction

func (ctx *EvalContext) GetFunction(name []SymbolicName) (Function, error)

func (*EvalContext) GetParameter

func (ctx *EvalContext) GetParameter(key string) (Value, error)

func (*EvalContext) GetVar

func (ctx *EvalContext) GetVar(name string) (Value, error)

func (*EvalContext) SetParameter

func (ctx *EvalContext) SetParameter(key string, value Value) *EvalContext

SetParameter sets a parameter to be used in expressions

func (*EvalContext) SetVar

func (ctx *EvalContext) SetVar(name string, value Value)

func (*EvalContext) SubContext

func (ctx *EvalContext) SubContext() *EvalContext

SubContext creates a new subcontext with a new variable set

type Evaluatable

type Evaluatable interface {
	Evaluate(*EvalContext) (Value, error)
}

func Parse

func Parse(input string) (Evaluatable, error)

GetEvaluatable returns an evaluatable object

type Expression

type Expression interface {
	Evaluatable
}

type Filter

type Filter struct {
}

type FilterAtom

type FilterAtom struct {
	Op     string
	Filter FilterExpression
}

func (FilterAtom) Evaluate

func (flt FilterAtom) Evaluate(ctx *EvalContext) (Value, error)

type FilterExpression

type FilterExpression struct {
	Variable Variable
	InExpr   Expression
	Where    Expression
}

type Function

type Function func(*EvalContext, []Evaluatable) (Value, error)

type FunctionInvocation

type FunctionInvocation struct {
	Name     []SymbolicName
	Distinct bool
	Args     []Expression
	// contains filtered or unexported fields
}

func (*FunctionInvocation) Evaluate

func (f *FunctionInvocation) Evaluate(ctx *EvalContext) (Value, error)

type IntLiteral

type IntLiteral int

func DecimalInteger

func DecimalInteger(ctx antlr.TerminalNode) IntLiteral

func (IntLiteral) Evaluate

func (literal IntLiteral) Evaluate(ctx *EvalContext) (Value, error)

type LValue

type LValue struct {
	// contains filtered or unexported fields
}

LValue is a pointer to a value

func NewLValue

func NewLValue(v Value) LValue

NewLValue returns an LValue from the given value

func (LValue) Evaluate

func (v LValue) Evaluate(*EvalContext) (Value, error)

func (LValue) Get

func (v LValue) Get() interface{}

func (LValue) IsConst

func (LValue) IsConst() bool

func (LValue) Set

func (v LValue) Set(val interface{})

type ListComprehension

type ListComprehension struct {
	Filter FilterExpression
	Expr   Expression
}

func (ListComprehension) Evaluate

func (ls ListComprehension) Evaluate(ctx *EvalContext) (Value, error)

type ListLiteral

type ListLiteral struct {
	Values []Expression
	// contains filtered or unexported fields
}

func (*ListLiteral) Evaluate

func (lst *ListLiteral) Evaluate(ctx *EvalContext) (Value, error)

type ListRangeExpression

type ListRangeExpression struct {
	First  Expression
	Second Expression
}

type MapKeyValue

type MapKeyValue struct {
	Key   string
	Value Expression
}

type MapLiteral

type MapLiteral struct {
	KeyValues []MapKeyValue
	// contains filtered or unexported fields
}

func (*MapLiteral) Evaluate

func (mp *MapLiteral) Evaluate(ctx *EvalContext) (Value, error)

type Match

type Match struct {
	Optional bool
	Pattern  Pattern
	Where    Expression
}

func (Match) GetResults

func (match Match) GetResults(ctx *EvalContext) (ResultSet, error)

type MultiplyDivideModuloExpression

type MultiplyDivideModuloExpression struct {
	Parts []MultiplyDivideModuloExpressionPart
	// contains filtered or unexported fields
}

func (*MultiplyDivideModuloExpression) Evaluate

func (expr *MultiplyDivideModuloExpression) Evaluate(ctx *EvalContext) (Value, error)

type MultiplyDivideModuloExpressionPart

type MultiplyDivideModuloExpressionPart struct {
	// For the first element of parts, Op=0
	Op   rune
	Expr Evaluatable
}

type NodeLabels

type NodeLabels []SchemaName

type NodePattern

type NodePattern struct {
	Var        *Variable
	Labels     *NodeLabels
	Properties *Properties
}

func (NodePattern) Create

func (np NodePattern) Create(ctx *EvalContext) (string, graph.Node, error)

type NullLiteral

type NullLiteral struct{}

func (NullLiteral) Evaluate

func (literal NullLiteral) Evaluate(ctx *EvalContext) (Value, error)

type Order

type Order struct {
	Items []SortItem
}

type Parameter

type Parameter string

func (Parameter) Evaluate

func (expr Parameter) Evaluate(ctx *EvalContext) (Value, error)

type Pattern

type Pattern struct {
	Parts []PatternPart
}

type PatternChain

type PatternChain struct {
	Rel  RelationshipPattern
	Node NodePattern
}

type PatternComprehension

type PatternComprehension struct {
	Var   *Variable
	Rel   RelationshipsPattern
	Where Expression
	Expr  Expression
}

func (PatternComprehension) Evaluate

func (p PatternComprehension) Evaluate(ctx *EvalContext) (Value, error)

type PatternPart

type PatternPart struct {
	Var   *Variable
	Start NodePattern
	Path  []PatternChain
}

func ParsePatternExpr

func ParsePatternExpr(expr string) (PatternPart, error)

ParsePatternExpr parses the pattern expression that starts at the current node named 'this', and describes a path reaching one or more nodes named 'target'. For instance:

(this)-[]->(target)

will return all nodes reachable from the current node by one step.

This expression:

(this)<[a]-()-[]->(target :x)

will start from the current node, go back one nore following an edge with label `a`, and then move to a node with label `x`

func (PatternPart) Create

func (part PatternPart) Create(ctx *EvalContext) error

func (PatternPart) FindRelative

func (p PatternPart) FindRelative(this graph.Node) ([]graph.Node, error)

FindRelative evaluates a pattern expression starting at the given node. It may return zero or more nodes reached from the node

type PowerOfExpression

type PowerOfExpression struct {
	Parts []Evaluatable
	// contains filtered or unexported fields
}

func (*PowerOfExpression) Evaluate

func (expr *PowerOfExpression) Evaluate(ctx *EvalContext) (Value, error)

type ProjectionBody

type ProjectionBody struct {
	Distinct bool
	Items    ProjectionItems
	Order    *Order
	Skip     Expression
	Limit    Expression
}

type ProjectionItem

type ProjectionItem struct {
	Var  *Variable
	Expr Expression
}

type ProjectionItems

type ProjectionItems struct {
	All   bool
	Items []ProjectionItem
}

func (ProjectionItems) Project

func (prj ProjectionItems) Project(ctx *EvalContext, values map[string]Value) (map[string]Value, error)

type Properties

type Properties struct {
	Map   *MapLiteral
	Param *Parameter
}

func (Properties) AsLiteral

func (properties Properties) AsLiteral(ctx *EvalContext) ([]MapKeyValue, error)

type PropertyOrLabelsExpression

type PropertyOrLabelsExpression struct {
	Atom           Atom
	PropertyLookup []SchemaName
	NodeLabels     *NodeLabels
}

func (PropertyOrLabelsExpression) Evaluate

func (pl PropertyOrLabelsExpression) Evaluate(ctx *EvalContext) (Value, error)

type RValue

type RValue struct {
	Value interface{}
	Const bool
}

RValue is a value

func (RValue) Evaluate

func (v RValue) Evaluate(*EvalContext) (Value, error)

func (RValue) Get

func (v RValue) Get() interface{}

func (RValue) IsConst

func (v RValue) IsConst() bool

func (RValue) String

func (v RValue) String() string

type RangeLiteral

type RangeLiteral struct {
	From, To *IntLiteral
}

func (*RangeLiteral) Evaluate

func (r *RangeLiteral) Evaluate(ctx *EvalContext) (from, to *int, err error)

type ReadingClause

type ReadingClause interface {
	GetResults(*EvalContext) (ResultSet, error)
}

type RegularQuery

type RegularQuery struct {
	SingleQuery Evaluatable
	Unions      []union
}

func (RegularQuery) Evaluate

func (query RegularQuery) Evaluate(ctx *EvalContext) (Value, error)

Evaluate a regular query, which is a single query with an optional union list

type RelationshipPattern

type RelationshipPattern struct {
	ToLeft     bool
	ToRight    bool
	Var        *Variable
	RelTypes   *RelationshipTypes
	Range      *RangeLiteral
	Properties *Properties
}

func (RelationshipPattern) Create

func (rel RelationshipPattern) Create(ctx *EvalContext, from, to graph.Node) (graph.Edge, error)

type RelationshipTypes

type RelationshipTypes struct {
	Rel []SchemaName
}

type RelationshipsPattern

type RelationshipsPattern struct {
	Start NodePattern
	Chain []PatternChain
}

func (RelationshipsPattern) Evaluate

func (rel RelationshipsPattern) Evaluate(ctx *EvalContext) (Value, error)

type ReservedWord

type ReservedWord string

type ResultSet

type ResultSet struct {
	Nodes graph.NodeSet
	Edges graph.EdgeSet

	Rows []map[string]Value
}

ResultSet is a table of values

func CartesianProduct

func CartesianProduct(resultsets []ResultSet, all bool, filter func(map[string]Value) bool) ResultSet

CartesianProuduct builds the product of all the resultsets

func (*ResultSet) Add

func (r *ResultSet) Add(rs ResultSet)

func (*ResultSet) AddPath

func (r *ResultSet) AddPath(node graph.Node, edges []graph.Edge)

func (*ResultSet) Append

func (r *ResultSet) Append(row map[string]Value) error

Append the row to the resultset.

func (ResultSet) CartesianProduct

func (r ResultSet) CartesianProduct(f func(map[string]Value) bool) bool

CartesianProduct calls f with all permutations of rows until f returns false. The map passed to f is reused, so copy if you need a copy of it.

func (ResultSet) String

func (r ResultSet) String() string

func (*ResultSet) Union

func (r *ResultSet) Union(src ResultSet, all bool) error

Union adds the src resultset to this. If all is set, it adds all rows, otherwise, it adds unique rows

type ReturnClause

type ReturnClause struct {
	Projection ProjectionBody
}

type SchemaName

type SchemaName struct {
	*SymbolicName
	*ReservedWord
}

func (SchemaName) String

func (s SchemaName) String() string

type SortItem

type SortItem struct {
	Asc  bool
	Expr Expression
}

type StringListNullOperatorExpression

type StringListNullOperatorExpression struct {
	PropertyOrLabels PropertyOrLabelsExpression
	Parts            []StringListNullOperatorExpressionPart
}

func (StringListNullOperatorExpression) Evaluate

type StringListNullOperatorExpressionPart

type StringListNullOperatorExpressionPart struct {
	String    *StringOperatorExpression
	ListIn    Expression
	ListIndex Expression
	ListRange *ListRangeExpression
	IsNull    *bool
}

type StringLiteral

type StringLiteral string

func (StringLiteral) Evaluate

func (literal StringLiteral) Evaluate(ctx *EvalContext) (Value, error)

type StringOperatorExpression

type StringOperatorExpression struct {
	Operator string
	Expr     Expression
}

type SymbolicName

type SymbolicName string

type UnaryAddOrSubtractExpression

type UnaryAddOrSubtractExpression struct {
	Neg  bool
	Expr StringListNullOperatorExpression
	// contains filtered or unexported fields
}

func (*UnaryAddOrSubtractExpression) Evaluate

func (expr *UnaryAddOrSubtractExpression) Evaluate(ctx *EvalContext) (Value, error)

type UpdatingClause

type UpdatingClause interface {
	Update(*EvalContext, ResultSet) (Value, error)
	TopLevelUpdate(*EvalContext) error
}

type Value

type Value interface {
	Evaluatable
	Get() interface{}
	IsConst() bool
}

func ParseAndEvaluate

func ParseAndEvaluate(input string, ctx *EvalContext) (Value, error)

func ValueOf

func ValueOf(in interface{}) Value

type Variable

type Variable SymbolicName

func (Variable) Evaluate

func (v Variable) Evaluate(ctx *EvalContext) (Value, error)

Directories

Path Synopsis
examples
context command
create command
match command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL