Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added path support #20

Merged
merged 3 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
# Golang CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-go/ for more details
version: 2
version: 2.1
commands:
early_return_for_forked_pull_requests:
description: >-
If this build is from a fork, stop executing the current job and return success.
This is useful to avoid steps that will fail due to missing credentials.
steps:
- run:
name: Early return if this build is from a forked PR
command: |
if [ -n "$CIRCLE_PR_NUMBER" ]; then
echo "Nothing to do for forked PRs, so marking this step successful"
circleci step halt
fi
jobs:
build:
docker:
- image: circleci/golang:1.9

- image: redislabs/redisgraph:edge
port: 6379:6379

working_directory: /go/src/github.com/RedisGraph/redisgraph-go
steps:
- checkout
- run: go get -v -t -d ./...
- run: go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
- early_return_for_forked_pull_requests
- run: bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN}

workflows:
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ func main() {
p_age, _ := r.Get("p.age")
fmt.Printf("\nAge: %d\n", p_age)
}

// Path matching example.
query = "MATCH p = (:Person)-[:Visited]->(:Country) RETURN p"
result, _ = graph.Query(query)
res.Next()
r := res.Record()
p, ok := r.GetByIndex(0).(Path)
fmt.Printf("%s", p)
}
```

Expand Down
41 changes: 41 additions & 0 deletions client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,44 @@ func TestArray(t *testing.T) {
assert.Equal(t, b.GetProperty("age"), resB.GetProperty("age"), "Unexpected property value.")
assert.Equal(t, b.GetProperty("array"), resB.GetProperty("array"), "Unexpected property value.")
}

func TestPath(t *testing.T) {
createGraph()
q := "MATCH p = (:Person)-[:Visited]->(:Country) RETURN p"
res, err := graph.Query(q)
if err != nil {
t.Error(err)
}

assert.Equal(t, len(res.results), 1, "expecting 1 result record")

res.Next()
r := res.Record()

p, ok := r.GetByIndex(0).(Path)
assert.True(t, ok, "First column should contain path.")

assert.Equal(t, 2, p.NodesCount(), "Path should contain two nodes")
assert.Equal(t, 1, p.EdgeCount(), "Path should contain one edge")

s := p.FirstNode()
e := p.GetEdge(0)
d := p.LastNode()

assert.Equal(t, s.Label, "Person", "Node should be of type 'Person'")
assert.Equal(t, e.Relation, "Visited", "Edge should be of relation type 'Visited'")
assert.Equal(t, d.Label, "Country", "Node should be of type 'Country'")

assert.Equal(t, len(s.Properties), 4, "Person node should have 4 properties")

assert.Equal(t, s.GetProperty("name"), "John Doe", "Unexpected property value.")
assert.Equal(t, s.GetProperty("age"), 33, "Unexpected property value.")
assert.Equal(t, s.GetProperty("gender"), "male", "Unexpected property value.")
assert.Equal(t, s.GetProperty("status"), "single", "Unexpected property value.")

assert.Equal(t, e.GetProperty("year"), 2017, "Unexpected property value.")

assert.Equal(t, d.GetProperty("name"), "Japan", "Unexpected property value.")
assert.Equal(t, d.GetProperty("population"), 126800000, "Unexpected property value.")

}
78 changes: 78 additions & 0 deletions path.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package redisgraph

import (
"fmt"
"strings"
)

type Path struct {
Nodes []*Node
Edges []*Edge
}

func PathNew(nodes []interface{}, edges []interface{}) Path {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func PathNew(nodes []Node, edges []Edge) Path {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the constructor will convert from array of interface to array of node/edge

Nodes := make([]*Node, len(nodes))
for i := 0; i < len(nodes); i++ {
Nodes[i] = nodes[i].(*Node)
}
Edges := make([]*Edge, len(edges))
for i := 0; i < len(edges); i++ {
Edges[i] = edges[i].(*Edge)
}

return Path{
Edges : Edges,
Nodes : Nodes,
}
}

func (p Path) GetNodes() []*Node {
return p.Nodes
}

func (p Path) GetEdges() []*Edge {
return p.Edges
}

func (p Path) GetNode(index int) *Node {
return p.Nodes[index]
}

func (p Path) GetEdge(index int) *Edge{
return p.Edges[index]
}

func (p Path) FirstNode() *Node {
return p.GetNode(0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the Path is empty?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

execption

}

func (p Path) LastNode() *Node {
return p.GetNode(p.NodesCount() - 1)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if NodeCount is 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exception

}

func (p Path) NodesCount() int {
return len(p.Nodes)
}

func (p Path) EdgeCount() int {
return len(p.Edges)
}

func (p Path) String() string {
s := []string{"<"}
edgeCount := p.EdgeCount()
for i := 0; i < edgeCount; i++ {
var node = p.GetNode(i)
s = append(s, "(" , fmt.Sprintf("%v", node.ID) , ")")
var edge = p.GetEdge(i)
if node.ID == edge.srcNodeID {
s = append(s, "-[" , fmt.Sprintf("%v", edge.ID) , "]->")
} else {
s= append(s, "<-[" , fmt.Sprintf("%v", edge.ID) , "]-")
}
}
s = append(s, "(" , fmt.Sprintf("%v", p.GetNode(edgeCount).ID) , ")")
s = append(s, ">")

return strings.Join(s, "")
}
11 changes: 11 additions & 0 deletions query_result.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const (
VALUE_ARRAY
VALUE_EDGE
VALUE_NODE
VALUE_PATH
)

type QueryResultHeader struct {
Expand Down Expand Up @@ -217,6 +218,13 @@ func (qr *QueryResult) parseArray(cell interface{}) []interface{} {
return array
}

func (qr *QueryResult) parsePath(cell interface{}) Path {
arrays := cell.([]interface{})
nodes := qr.parseScalar(arrays[0].([]interface{}))
edges := qr.parseScalar(arrays[1].([]interface{}))
return PathNew(nodes.([]interface{}), edges.([]interface{}))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return PathNew(nodes, edges)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be an array

}

func (qr *QueryResult) parseScalar(cell []interface{}) interface{} {
t, _ := redis.Int(cell[0], nil)
v := cell[1]
Expand Down Expand Up @@ -246,6 +254,9 @@ func (qr *QueryResult) parseScalar(cell []interface{}) interface{} {
case VALUE_NODE:
s = qr.parseNode(v)

case VALUE_PATH:
s = qr.parsePath(v)

case VALUE_UNKNOWN:
panic("Unknown scalar type\n")
}
Expand Down