opencypher

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

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

Go to latest
Published: Jul 30, 2022 License: Apache-2.0 Imports: 14 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.

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 IsNamedVar

func IsNamedVar(name string) bool

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 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 []string) (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) GetVarsNearestScope

func (ctx *EvalContext) GetVarsNearestScope() map[string]Value

func (*EvalContext) RemoveVar

func (ctx *EvalContext) RemoveVar(name string)

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) SetVars

func (ctx *EvalContext) SetVars(m map[string]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 Function

type Function func(*EvalContext, []Evaluatable) (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 Match

type Match struct {
	Optional bool
	Pattern  Pattern
	Where    Expression
}

func (Match) GetResults

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

type NodeLabels

type NodeLabels []schemaName

type Parameter

type Parameter string

func (Parameter) Evaluate

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

type Pattern

type Pattern struct {
	Parts []PatternPart
}

type PatternPart

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

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) (graph.Node, []graph.Edge, 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 Properties

type Properties struct {
	Map   *mapLiteral
	Param *Parameter
}

func (Properties) AsLiteral

func (properties Properties) AsLiteral(ctx *EvalContext) ([]mapKeyValue, 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 ReadingClause

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

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) Column

func (r *ResultSet) Column(key string) []Value

Column returns a column of results as a value

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 UpdatingClause

type UpdatingClause interface {
	Update(*EvalContext, ResultSet) (Value, error)
	TopLevelUpdate(*EvalContext) (Value, 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

Directories

Path Synopsis
examples
context command
create command
match command
merge command

Jump to

Keyboard shortcuts

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