From 6e369869dca4de2d2f9fa5bca77b9905cd1d6ef9 Mon Sep 17 00:00:00 2001 From: Eric Solender Date: Tue, 21 Jul 2020 15:32:05 -0400 Subject: [PATCH 1/5] started replacing driver --- config.go | 6 +- decoder.go | 169 ++++++++++++++++++++++----------------------------- go.mod | 1 + go.sum | 31 ++++++++++ test/main.go | 42 +++++++++++++ 5 files changed, 149 insertions(+), 100 deletions(-) create mode 100644 test/main.go diff --git a/config.go b/config.go index 352a093..ec164ee 100644 --- a/config.go +++ b/config.go @@ -22,12 +22,12 @@ package gogm import ( "errors" "fmt" - "net/url" - "reflect" - "github.com/cornelk/hashmap" goBolt "github.com/mindstand/go-bolt" + _ "github.com/neo4j/neo4j-go-driver/neo4j" "github.com/sirupsen/logrus" + "net/url" + "reflect" ) var externalLog *logrus.Entry diff --git a/decoder.go b/decoder.go index 8019348..ab4b7fa 100644 --- a/decoder.go +++ b/decoder.go @@ -22,22 +22,17 @@ package gogm import ( "errors" "fmt" - "github.com/mindstand/go-bolt/structures/graph" + "github.com/neo4j/neo4j-go-driver/neo4j" "reflect" "strings" ) //decodes raw path response from driver //example query `match p=(n)-[*0..5]-() return p` -func decode(rawArr [][]interface{}, respObj interface{}) (err error) { +func decode(result neo4j.Result, respObj interface{}) (err error) { //check nil params - if rawArr == nil { - return fmt.Errorf("rawArr can not be nil, %w", ErrInvalidParams) - } - - //check empty - if len(rawArr) == 0 { - return fmt.Errorf("nothing returned from driver, %w", ErrNotFound) + if result == nil { + return fmt.Errorf("result can not be nil, %w", ErrInvalidParams) } //we're doing reflection now, lets set up a panic recovery @@ -61,24 +56,21 @@ func decode(rawArr [][]interface{}, respObj interface{}) (err error) { } //todo optimize with set array size - var paths []*graph.Path - var strictRels []*graph.Relationship - var isolatedNodes []*graph.Node + var paths []neo4j.Path + var strictRels []neo4j.Relationship + var isolatedNodes []neo4j.Node - for _, arr := range rawArr { - for _, graphType := range arr { + for result.Next() { + for _, graphType := range result.Record().Values() { switch graphType.(type) { - case graph.Path: - convP := graphType.(graph.Path) - paths = append(paths, &convP) + case neo4j.Path: + paths = append(paths, graphType.(neo4j.Path)) break - case graph.Relationship: - convR := graphType.(graph.Relationship) - strictRels = append(strictRels, &convR) + case neo4j.Relationship: + strictRels = append(strictRels, graphType.(neo4j.Relationship)) break - case graph.Node: - convN := graphType.(graph.Node) - isolatedNodes = append(isolatedNodes, &convN) + case neo4j.Node: + isolatedNodes = append(isolatedNodes, graphType.(neo4j.Node)) break default: continue @@ -344,7 +336,7 @@ func getPrimaryLabel(rt reflect.Type) string { } // sortIsolatedNodes process nodes that are returned individually from bolt driver -func sortIsolatedNodes(isolatedNodes []*graph.Node, labelLookup *map[int64]string, nodeLookup *map[int64]*reflect.Value, pks *[]int64, pkLabel string, relMaps *map[int64]map[string]*RelationConfig) error { +func sortIsolatedNodes(isolatedNodes []neo4j.Node, labelLookup *map[int64]string, nodeLookup *map[int64]*reflect.Value, pks *[]int64, pkLabel string, relMaps *map[int64]map[string]*RelationConfig) error { if isolatedNodes == nil { return fmt.Errorf("isolatedNodes can not be nil, %w", ErrInternal) } @@ -355,24 +347,24 @@ func sortIsolatedNodes(isolatedNodes []*graph.Node, labelLookup *map[int64]strin } //check if node has already been found by another process - if _, ok := (*nodeLookup)[node.NodeIdentity]; !ok { + if _, ok := (*nodeLookup)[node.Id()]; !ok { //if it hasn't, map it - val, err := convertNodeToValue(*node) + val, err := convertNodeToValue(node) if err != nil { return err } - (*nodeLookup)[node.NodeIdentity] = val - (*relMaps)[node.NodeIdentity] = map[string]*RelationConfig{} + (*nodeLookup)[node.Id()] = val + (*relMaps)[node.Id()] = map[string]*RelationConfig{} //primary to return - if node.Labels != nil && len(node.Labels) != 0 && node.Labels[0] == pkLabel { - *pks = append(*pks, node.NodeIdentity) + if node.Labels != nil && len(node.Labels()) != 0 && node.Labels()[0] == pkLabel { + *pks = append(*pks, node.Id()) } //set label map - if _, ok := (*labelLookup)[node.NodeIdentity]; !ok && len(node.Labels) != 0 && node.Labels[0] == pkLabel { - (*labelLookup)[node.NodeIdentity] = node.Labels[0] + if _, ok := (*labelLookup)[node.Id()]; !ok && len(node.Labels()) != 0 && node.Labels()[0] == pkLabel { + (*labelLookup)[node.Id()] = node.Labels()[0] } } } @@ -381,7 +373,7 @@ func sortIsolatedNodes(isolatedNodes []*graph.Node, labelLookup *map[int64]strin } // sortStrictRels sorts relationships that are strictly defined (i.e direction is pre defined) from the bolt driver -func sortStrictRels(strictRels []*graph.Relationship, labelLookup *map[int64]string, rels *map[int64]*neoEdgeConfig) error { +func sortStrictRels(strictRels []neo4j.Relationship, labelLookup *map[int64]string, rels *map[int64]*neoEdgeConfig) error { if strictRels == nil { return fmt.Errorf("paths is empty, that shouldn't have happened, %w", ErrInternal) } @@ -391,25 +383,25 @@ func sortStrictRels(strictRels []*graph.Relationship, labelLookup *map[int64]str return errors.New("path can not be nil") } - if _, ok := (*rels)[rel.RelIdentity]; !ok { - startLabel, ok := (*labelLookup)[rel.StartNodeIdentity] + if _, ok := (*rels)[rel.Id()]; !ok { + startLabel, ok := (*labelLookup)[rel.StartId()] if !ok { - return fmt.Errorf("label not found for node [%v], %w", rel.StartNodeIdentity, ErrInternal) + return fmt.Errorf("label not found for node [%v], %w", rel.Id(), ErrInternal) } - endLabel, ok := (*labelLookup)[rel.EndNodeIdentity] + endLabel, ok := (*labelLookup)[rel.EndId()] if !ok { - return fmt.Errorf("label not found for node [%v], %w", rel.EndNodeIdentity, ErrInternal) + return fmt.Errorf("label not found for node [%v], %w", rel.EndId(), ErrInternal) } - (*rels)[rel.RelIdentity] = &neoEdgeConfig{ - Id: rel.RelIdentity, - StartNodeId: rel.StartNodeIdentity, + (*rels)[rel.Id()] = &neoEdgeConfig{ + Id: rel.Id(), + StartNodeId: rel.StartId(), StartNodeType: startLabel, - EndNodeId: rel.StartNodeIdentity, + EndNodeId: rel.EndId(), EndNodeType: endLabel, - Obj: rel.Properties, - Type: rel.Type, + Obj: rel.Props(), + Type: rel.Type(), } } } @@ -418,7 +410,7 @@ func sortStrictRels(strictRels []*graph.Relationship, labelLookup *map[int64]str } // sortPaths sorts nodes and relationships from bolt driver that dont specify the direction explicitly, instead uses the bolt spec to determine direction -func sortPaths(paths []*graph.Path, nodeLookup *map[int64]*reflect.Value, rels *map[int64]*neoEdgeConfig, pks *[]int64, pkLabel string, relMaps *map[int64]map[string]*RelationConfig) error { +func sortPaths(paths []neo4j.Path, nodeLookup *map[int64]*reflect.Value, rels *map[int64]*neoEdgeConfig, pks *[]int64, pkLabel string, relMaps *map[int64]map[string]*RelationConfig) error { if paths == nil { return fmt.Errorf("paths is empty, that shouldn't have happened, %w", ErrInternal) } @@ -428,70 +420,53 @@ func sortPaths(paths []*graph.Path, nodeLookup *map[int64]*reflect.Value, rels * return errors.New("path can not be nil") } - if path.Nodes == nil || len(path.Nodes) == 0 { + if path.Nodes() == nil || len(path.Nodes()) == 0 { return fmt.Errorf("no nodes found, %w", ErrNotFound) } - for _, node := range path.Nodes { + labelLookup := make(map[int64]string, len(path.Nodes())) - if _, ok := (*nodeLookup)[node.NodeIdentity]; !ok { + for _, node := range path.Nodes() { + if _, ok := labelLookup[node.Id()]; !ok && len(node.Labels()) != 0 { + labelLookup[node.Id()] = node.Labels()[0] + } + if _, ok := (*nodeLookup)[node.Id()]; !ok { //we haven't parsed this one yet, lets do that now val, err := convertNodeToValue(node) if err != nil { return err } - (*nodeLookup)[node.NodeIdentity] = val - (*relMaps)[node.NodeIdentity] = map[string]*RelationConfig{} + (*nodeLookup)[node.Id()] = val + (*relMaps)[node.Id()] = map[string]*RelationConfig{} //primary to return - if node.Labels != nil && len(node.Labels) != 0 && node.Labels[0] == pkLabel { - *pks = append(*pks, node.NodeIdentity) + if node.Labels != nil && len(node.Labels()) != 0 && node.Labels()[0] == pkLabel { + *pks = append(*pks, node.Id()) } } } - //handle the relationships - //sequence is [node_id, edge_id (negative if its in the wrong direction), repeat....] - if path.Sequence != nil && len(path.Sequence) != 0 && path.Relationships != nil && len(path.Relationships) == (len(path.Sequence)/2) { - seqPrime := append([]int{0}, path.Sequence...) - seqPrimeLen := len(seqPrime) - - for i := 0; i+2 < seqPrimeLen; i += 2 { - startPosIndex := seqPrime[i] - edgeIndex := seqPrime[i+1] - endPosIndex := seqPrime[i+2] - - var startId int - var endId int - var edgeId int - - //keep order - if edgeIndex >= 0 { - startId = startPosIndex - endId = endPosIndex - edgeId = edgeIndex - } else { - //reverse - startId = endPosIndex - endId = startPosIndex - edgeId = -edgeIndex - } + for _, rel := range path.Relationships() { + startLabel, ok := labelLookup[rel.StartId()] + if !ok { + return fmt.Errorf("label not found for node with graphId [%v], %w", rel.StartId(), ErrInternal) + } + + endLabel, ok := labelLookup[rel.EndId()] + if !ok { + return fmt.Errorf("label not found for node with graphId [%v], %w", rel.EndId(), ErrInternal) + } - startNode := path.Nodes[startId] - endNode := path.Nodes[endId] - rel := path.Relationships[edgeId-1] //offset for the array - - if _, ok := (*rels)[rel.RelIdentity]; !ok { - (*rels)[rel.RelIdentity] = &neoEdgeConfig{ - Id: rel.RelIdentity, - StartNodeId: startNode.NodeIdentity, - StartNodeType: startNode.Labels[0], - EndNodeId: endNode.NodeIdentity, - EndNodeType: endNode.Labels[0], - Obj: rel.Properties, - Type: rel.Type, - } + if _, ok := (*rels)[rel.Id()]; !ok { + (*rels)[rel.Id()] = &neoEdgeConfig{ + Id: rel.Id(), + StartNodeId: rel.StartId(), + StartNodeType: startLabel, + EndNodeId: rel.EndId(), + EndNodeType: endLabel, + Obj: rel.Props(), + Type: rel.Type(), } } } @@ -617,17 +592,17 @@ func convertToValue(graphId int64, conf structDecoratorConfig, props map[string] } // convertNodeToValue converts raw bolt node to reflect value -func convertNodeToValue(boltNode graph.Node) (*reflect.Value, error) { +func convertNodeToValue(boltNode neo4j.Node) (*reflect.Value, error) { - if boltNode.Labels == nil || len(boltNode.Labels) == 0 { + if boltNode.Labels == nil || len(boltNode.Labels()) == 0 { return nil, errors.New("boltNode has no labels") } var typeConfig structDecoratorConfig - temp, ok := mappedTypes.Get(boltNode.Labels[0]) // mappedTypes[boltNode.Labels[0]] + temp, ok := mappedTypes.Get(boltNode.Labels()[0]) // mappedTypes[boltNode.Labels[0]] if !ok { - return nil, fmt.Errorf("can not find mapping for node with label %s", boltNode.Labels[0]) + return nil, fmt.Errorf("can not find mapping for node with label %s", boltNode.Labels()[0]) } typeConfig, ok = temp.(structDecoratorConfig) @@ -635,5 +610,5 @@ func convertNodeToValue(boltNode graph.Node) (*reflect.Value, error) { return nil, errors.New("unable to cast to struct decorator config") } - return convertToValue(boltNode.NodeIdentity, typeConfig, boltNode.Properties, typeConfig.Type) + return convertToValue(boltNode.Id(), typeConfig, boltNode.Props(), typeConfig.Type) } diff --git a/go.mod b/go.mod index 5072e84..7d148ef 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,7 @@ require ( github.com/kr/pretty v0.1.0 // indirect github.com/mindstand/go-bolt v0.2.1-0.20200414154543-dd7499698d09 github.com/mindstand/go-cypherdsl v0.1.1 + github.com/neo4j/neo4j-go-driver v1.8.1-0.20200622090208-4295b59525c9 github.com/sirupsen/logrus v1.6.0 github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.5.1 diff --git a/go.sum b/go.sum index 568c7a0..398bb14 100644 --- a/go.sum +++ b/go.sum @@ -16,8 +16,12 @@ github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jolestar/go-commons-pool v2.0.0+incompatible h1:uHn5uRKsLLQSf9f1J5QPY2xREWx/YH+e4bIIXcAuAaE= github.com/jolestar/go-commons-pool v2.0.0+incompatible/go.mod h1:ChJYIbIch0DMCSU6VU0t0xhPoWDR2mMFIQek3XWU0s8= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -36,6 +40,15 @@ github.com/mindstand/go-cypherdsl v0.1.1 h1:2DofP2gnBxP0aS9Dh7aHjxURQRouc3vR/YBO github.com/mindstand/go-cypherdsl v0.1.1/go.mod h1:dxU6sCuwaCdfWiLKDMGC1umeCPpSZk45jiJvEpLuO8I= github.com/mindstand/gotime v0.0.0-20200414142228-237d9416b724 h1:SOnJCXp+mG8BQjx02AinZBhtVHT4ElVwiJZbDXKcy8s= github.com/mindstand/gotime v0.0.0-20200414142228-237d9416b724/go.mod h1:DzECeSxMVcU5J1CTiyIjdI/+Q8TJWhdo96Vb1OpY41Q= +github.com/neo4j/neo4j-go-driver v1.8.0 h1:YRp9jsFcF9k/AnvbcqFCN9OMeIT2XTJgxOpp2Puq7OE= +github.com/neo4j/neo4j-go-driver v1.8.0/go.mod h1:0A49wIv0oP3uQdnbceK7Kc+snlY5B0F6dmtYArM0ltk= +github.com/neo4j/neo4j-go-driver v1.8.1-0.20200622090208-4295b59525c9 h1:amJ2VBG5eDK/r9pUjo6/h2np0kYyla5FLWWLyg29H0w= +github.com/neo4j/neo4j-go-driver v1.8.1-0.20200622090208-4295b59525c9/go.mod h1:0A49wIv0oP3uQdnbceK7Kc+snlY5B0F6dmtYArM0ltk= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= @@ -58,13 +71,31 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/urfave/cli/v2 v2.0.0 h1:+HU9SCbu8GnEUFtIBfuUNXN39ofWViIEJIp6SURMpCg= github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/test/main.go b/test/main.go new file mode 100644 index 0000000..6fdaa76 --- /dev/null +++ b/test/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "fmt" + "github.com/neo4j/neo4j-go-driver/neo4j" +) + +func main() { + configForNeo4j40 := func(conf *neo4j.Config) { + conf.Encrypted = false + } + + driver, err := neo4j.NewDriver("bolt://0.0.0.0:7687", neo4j.BasicAuth("neo4j", "password", ""), configForNeo4j40) + if err != nil { + panic(err) + } + + // handle driver lifetime based on your application lifetime requirements + // driver's lifetime is usually bound by the application lifetime, which usually implies one driver instance per application + defer driver.Close() + + // For multidatabase support, set sessionConfig.DatabaseName to requested database + sessionConfig := neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite} + session, err := driver.NewSession(sessionConfig) + if err != nil { + panic(err) + } + defer session.Close() + + result, err := session.Run("CREATE (n:Item { id: $id, name: $name }) RETURN n", map[string]interface{}{ + "id": 1, + "name": "Item 1", + }) + if err != nil { + panic(err) + } + + for result.Next() { + + fmt.Printf("Created Item with Id = '%d' and Name = '%s'\n", result.Record().GetByIndex(0).(int64), result.Record().GetByIndex(1).(string)) + } +} From a844cc3e64dd0d35d355117ce2d9ef681fc160c0 Mon Sep 17 00:00:00 2001 From: Eric Solender Date: Wed, 22 Jul 2020 12:20:52 -0400 Subject: [PATCH 2/5] more migration to new driver --- config.go | 5 +- decoder_test.go | 1 + delete.go | 36 +++++---- delete_test.go | 4 +- index.go | 60 +++++++++++---- index_test.go | 4 +- integration_test.go | 2 +- save.go | 76 +++++++++++-------- session.go | 173 ++++++++++++++++++++++++-------------------- test/main.go | 2 + 10 files changed, 220 insertions(+), 143 deletions(-) diff --git a/config.go b/config.go index ec164ee..17cedb5 100644 --- a/config.go +++ b/config.go @@ -24,6 +24,7 @@ import ( "fmt" "github.com/cornelk/hashmap" goBolt "github.com/mindstand/go-bolt" + "github.com/neo4j/neo4j-go-driver/neo4j" _ "github.com/neo4j/neo4j-go-driver/neo4j" "github.com/sirupsen/logrus" "net/url" @@ -106,7 +107,7 @@ const ( var mappedTypes = &hashmap.HashMap{} //thread pool -var driverPool goBolt.IDriverPool +var driver neo4j.Driver //relationship + label var mappedRelations = &relationConfigs{} @@ -170,7 +171,7 @@ func setupInit(isTest bool, conf *Config, mapTypes ...interface{}) error { return err } - driverPool, err = client.NewDriverPool(conf.PoolSize) + driver, err = client.NewDriverPool(conf.PoolSize) if err != nil { return err } diff --git a/decoder_test.go b/decoder_test.go index dacd974..e2527fe 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -23,6 +23,7 @@ import ( "errors" "github.com/cornelk/hashmap" "github.com/mindstand/go-bolt/structures/graph" + "github.com/neo4j/neo4j-go-driver/neo4j" "github.com/stretchr/testify/require" "reflect" "testing" diff --git a/delete.go b/delete.go index c8ce9f5..3b7a3d3 100644 --- a/delete.go +++ b/delete.go @@ -21,13 +21,12 @@ package gogm import ( "errors" - "github.com/mindstand/go-bolt/connection" dsl "github.com/mindstand/go-cypherdsl" "reflect" ) // deleteNode is used to remove nodes from the database -func deleteNode(conn connection.IQuery, deleteObj interface{}) error { +func deleteNode(runFunc neoRunFunc, deleteObj interface{}) error { rawType := reflect.TypeOf(deleteObj) if rawType.Kind() != reflect.Ptr && rawType.Kind() != reflect.Slice { @@ -72,12 +71,12 @@ func deleteNode(conn connection.IQuery, deleteObj interface{}) error { } } - return deleteByIds(conn, ids...) + return deleteByIds(runFunc, ids...) } // deleteByIds deletes node by graph ids -func deleteByIds(conn connection.IQuery, ids ...int64) error { - _, err := dsl.QB(). +func deleteByIds(runFunc neoRunFunc, ids ...int64) error { + cyp, err := dsl.QB(). Cypher("UNWIND {rows} as row"). Match(dsl.Path().V(dsl.V{Name: "n"}).Build()). Where(dsl.C(&dsl.ConditionConfig{ @@ -87,10 +86,14 @@ func deleteByIds(conn connection.IQuery, ids ...int64) error { Check: dsl.ParamString("row"), })). Delete(true, "n"). - WithNeo(conn). - Exec(map[string]interface{}{ - "rows": ids, - }) + ToCypher() + if err != nil { + return err + } + + _, err = runFunc(cyp, map[string]interface{}{ + "rows": ids, + }) if err != nil { return err } @@ -99,8 +102,8 @@ func deleteByIds(conn connection.IQuery, ids ...int64) error { } // deleteByUuids deletes nodes by uuids -func deleteByUuids(conn connection.IQuery, ids ...string) error { - _, err := dsl.QB(). +func deleteByUuids(runFunc neoRunFunc, ids ...string) error { + cyp, err := dsl.QB(). Cypher("UNWIND {rows} as row"). Match(dsl.Path().V(dsl.V{Name: "n"}).Build()). Where(dsl.C(&dsl.ConditionConfig{ @@ -110,10 +113,13 @@ func deleteByUuids(conn connection.IQuery, ids ...string) error { Check: dsl.ParamString("row"), })). Delete(true, "n"). - WithNeo(conn). - Exec(map[string]interface{}{ - "rows": ids, - }) + ToCypher() + if err != nil { + return err + } + _, err = runFunc(cyp, map[string]interface{}{ + "rows": ids, + }) if err != nil { return err } diff --git a/delete_test.go b/delete_test.go index f96bbf4..83f4546 100644 --- a/delete_test.go +++ b/delete_test.go @@ -25,11 +25,11 @@ import ( ) func testDelete(req *require.Assertions) { - conn, err := driverPool.Open(bolt_mode.WriteMode) + conn, err := driver.Open(bolt_mode.WriteMode) if err != nil { req.Nil(err) } - defer driverPool.Reclaim(conn) + defer driver.Reclaim(conn) del := a{ BaseNode: BaseNode{ diff --git a/index.go b/index.go index 0f5feae..f4955cd 100644 --- a/index.go +++ b/index.go @@ -24,38 +24,63 @@ import ( "fmt" "github.com/adam-hanna/arrayOperations" "github.com/cornelk/hashmap" - "github.com/mindstand/go-bolt/bolt_mode" dsl "github.com/mindstand/go-cypherdsl" + "github.com/neo4j/neo4j-go-driver/neo4j" ) +func resultToStringArr(res neo4j.Result) ([]string, error) { + if res == nil { + return nil, errors.New("result is nil") + } + + var result []string + + for res.Next() { + val := res.Record().Values() + // nothing to parse + if val == nil || len(val) == 0 { + continue + } + + str, ok := val[0].(string) + if !ok { + return nil, fmt.Errorf("unable to parse [%T] to string, %w", val[0], ErrInternal) + } + + result = append(result, str) + } + + return result, nil +} + //drops all known indexes func dropAllIndexesAndConstraints() error { - conn, err := driverPool.Open(bolt_mode.WriteMode) + sess, err := driver.Session(neo4j.AccessModeWrite) if err != nil { return err } - defer driverPool.Reclaim(conn) + defer sess.Close() - constraintRows, err := dsl.QB().Cypher("CALL db.constraints").WithNeo(conn).Query(nil) + res, err := sess.Run("CALL db.constraints", nil) if err != nil { return err } - constraints, err := dsl.RowsToStringArray(constraintRows) + constraints, err := resultToStringArr(res) if err != nil { return err } //if there is anything, get rid of it if len(constraints) != 0 { - tx, err := conn.Begin() + tx, err := sess.BeginTransaction() if err != nil { return err } for _, constraint := range constraints { log.Debugf("dropping constraint '%s'", constraint) - _, err := dsl.QB().Cypher(fmt.Sprintf("DROP %s", constraint)).WithNeo(tx).Exec(nil) + _, err := tx.Run(fmt.Sprintf("DROP %s", constraint), nil) if err != nil { oerr := err err = tx.Rollback() @@ -73,14 +98,19 @@ func dropAllIndexesAndConstraints() error { } } - indexes, err := dsl.QB().Cypher("CALL db.indexes()").WithNeo(conn).Query(nil) + res, err = sess.Run("CALL db.indexes()", nil) + if err != nil { + return err + } + + indexes, err := resultToStringArr(res) if err != nil { return err } //if there is anything, get rid of it if len(indexes) != 0 { - tx, err := conn.Begin() + tx, err := sess.BeginTransaction() if err != nil { return err } @@ -90,7 +120,7 @@ func dropAllIndexesAndConstraints() error { return errors.New("invalid index config") } - _, err := dsl.QB().Cypher(fmt.Sprintf("DROP %s", index[0].(string))).WithNeo(tx).Exec(nil) + _, err := tx.Run(fmt.Sprintf("DROP %s", index), nil) if err != nil { oerr := err err = tx.Rollback() @@ -110,11 +140,11 @@ func dropAllIndexesAndConstraints() error { //creates all indexes func createAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { - conn, err := driverPool.Open(bolt_mode.WriteMode) + sess, err := driver.Session(neo4j.AccessModeWrite) if err != nil { return err } - defer driverPool.Reclaim(conn) + defer sess.Close() //validate that we have to do anything if mappedTypes == nil || mappedTypes.Len() == 0 { @@ -123,7 +153,7 @@ func createAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { numIndexCreated := 0 - tx, err := conn.Begin() + tx, err := sess.BeginTransaction() if err != nil { return err } @@ -190,11 +220,11 @@ func createAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { //verifies all indexes func verifyAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { - conn, err := driverPool.Open(bolt_mode.WriteMode) + sess, err := driver.Session(neo4j.AccessModeWrite) if err != nil { return err } - defer driverPool.Reclaim(conn) + defer sess.Close() //validate that we have to do anything if mappedTypes == nil || mappedTypes.Len() == 0 { diff --git a/index_test.go b/index_test.go index 531ef6c..975024d 100644 --- a/index_test.go +++ b/index_test.go @@ -29,10 +29,10 @@ func testIndexManagement(req *require.Assertions) { //delete everything req.Nil(dropAllIndexesAndConstraints()) - conn, err := driverPool.Open(bolt_mode.WriteMode) + conn, err := driver.Open(bolt_mode.WriteMode) req.Nil(err) - defer driverPool.Reclaim(conn) + defer driver.Reclaim(conn) req.Nil(err) //setup structure diff --git a/integration_test.go b/integration_test.go index fe2c68a..e0ab890 100644 --- a/integration_test.go +++ b/integration_test.go @@ -61,7 +61,7 @@ func TestIntegration(t *testing.T) { req.Nil(sess.PurgeDatabase()) req.Nil(sess.Close()) - req.Nil(driverPool.Close()) + req.Nil(driver.Close()) } // runs with integration test diff --git a/save.go b/save.go index 0e864af..8747861 100644 --- a/save.go +++ b/save.go @@ -22,8 +22,8 @@ package gogm import ( "errors" "fmt" - "github.com/mindstand/go-bolt/connection" dsl "github.com/mindstand/go-cypherdsl" + "github.com/neo4j/neo4j-go-driver/neo4j" "reflect" ) @@ -31,6 +31,19 @@ import ( const maxSaveDepth = 10 const defaultSaveDepth = 1 +// neoRunFunc typedefs function signature from neo4j driver +type neoRunFunc func(cypher string, params map[string]interface{}) (neo4j.Result, error) + +// runWrap is used to wrap the session run function into a consistent function signature +func runWrap(sess neo4j.Session) neoRunFunc { + return func(cypher string, params map[string]interface{}) (neo4j.Result, error) { + if sess == nil { + return nil, fmt.Errorf("session can not be nil, %w", ErrInternal) + } + return sess.Run(cypher, params) + } +} + // nodeCreateConf holds configuration for creating new nodes type nodeCreateConf struct { // params to save @@ -54,8 +67,8 @@ type relCreateConf struct { } // saves target node and connected node to specified depth -func saveDepth(sess connection.IQuery, obj interface{}, depth int) error { - if sess == nil { +func saveDepth(runFunc neoRunFunc, obj interface{}, depth int) error { + if runFunc == nil { return errors.New("session can not be nil") } @@ -108,7 +121,7 @@ func saveDepth(sess connection.IQuery, obj interface{}, depth int) error { return err } - ids, err := createNodes(sess, nodes, &nodeRef) + ids, err := createNodes(runFunc, nodes, &nodeRef) if err != nil { return err } @@ -139,14 +152,14 @@ func saveDepth(sess connection.IQuery, obj interface{}, depth int) error { //calculate dels if len(dels) != 0 { - err := removeRelations(sess, dels) + err := removeRelations(runFunc, dels) if err != nil { return err } } if len(relations) != 0 { - err := relateNodes(sess, relations, ids) + err := relateNodes(runFunc, relations, ids) if err != nil { return err } @@ -204,13 +217,13 @@ func calculateDels(oldRels, curRels map[string]map[string]*RelationConfig) map[s } // removes relationships between specified nodes -func removeRelations(conn connection.IQuery, dels map[string][]int64) error { +func removeRelations(runFunc neoRunFunc, dels map[string][]int64) error { if dels == nil || len(dels) == 0 { return nil } - if conn == nil { - return fmt.Errorf("connection can not be nil, %w", ErrInternal) + if runFunc == nil { + return fmt.Errorf("runFunc can not be nil, %w", ErrInternal) } var params []interface{} @@ -229,7 +242,7 @@ func removeRelations(conn connection.IQuery, dels map[string][]int64) error { return fmt.Errorf("%s, %w", err.Error(), ErrInternal) } - _, err = dsl.QB(). + cyq, err := dsl.QB(). Cypher("UNWIND {rows} as row"). Match(dsl.Path(). V(dsl.V{ @@ -242,22 +255,24 @@ func removeRelations(conn connection.IQuery, dels map[string][]int64) error { }).Build()). Cypher("WHERE id(end) IN row.endNodeIds"). Delete(false, "e"). - WithNeo(conn). - Exec(map[string]interface{}{ - "rows": params, - }, - ) + ToCypher() if err != nil { - return fmt.Errorf("%s, %w", err.Error(), ErrInternal) + return err } + _, err = runFunc(cyq, map[string]interface{}{ + "rows": params, + }) + if err != nil { + return fmt.Errorf("%s, %w", err.Error(), ErrInternal) + } //todo sanity check to make sure the affects worked return nil } // creates nodes -func createNodes(conn connection.IQuery, crNodes map[string]map[string]nodeCreateConf, nodeRef *map[string]*reflect.Value) (map[string]int64, error) { +func createNodes(runFunc neoRunFunc, crNodes map[string]map[string]nodeCreateConf, nodeRef *map[string]*reflect.Value) (map[string]int64, error) { idMap := map[string]int64{} for label, nodes := range crNodes { @@ -284,7 +299,7 @@ func createNodes(conn connection.IQuery, crNodes map[string]map[string]nodeCreat } //todo replace once unwind is fixed and path - resRows, err := dsl.QB(). + cyp, err := dsl.QB(). Cypher("UNWIND {rows} as row"). Merge(&dsl.MergeConfig{ Path: path, @@ -300,15 +315,17 @@ func createNodes(conn connection.IQuery, crNodes map[string]map[string]nodeCreat }, Alias: "id", }). - WithNeo(conn). - Query(map[string]interface{}{ - "rows": rows, - }) + ToCypher() + + res, err := runFunc(cyp, map[string]interface{}{ + "rows": rows, + }) if err != nil { return nil, err } - for _, row := range resRows { + for res.Next() { + row := res.Record().Values() if len(row) != 2 { continue } @@ -338,7 +355,7 @@ func createNodes(conn connection.IQuery, crNodes map[string]map[string]nodeCreat } // relateNodes connects nodes together using edge config -func relateNodes(conn connection.IQuery, relations map[string][]relCreateConf, ids map[string]int64) error { +func relateNodes(runFunc neoRunFunc, relations map[string][]relCreateConf, ids map[string]int64) error { if relations == nil || len(relations) == 0 { return errors.New("relations can not be nil or empty") } @@ -396,7 +413,7 @@ func relateNodes(conn connection.IQuery, relations map[string][]relCreateConf, i return err } - _, err = dsl.QB(). + cyp, err := dsl.QB(). Cypher("UNWIND {rows} as row"). Match(dsl.Path().V(dsl.V{Name: "startNode"}).Build()). Where(dsl.C(&dsl.ConditionConfig{ @@ -426,10 +443,11 @@ func relateNodes(conn connection.IQuery, relations map[string][]relCreateConf, i Path: mergePath, }). Cypher("SET rel += row.props"). - WithNeo(conn). - Exec(map[string]interface{}{ - "rows": params, - }) + ToCypher() + + _, err = runFunc(cyp, map[string]interface{}{ + "rows": params, + }) if err != nil { return err } diff --git a/session.go b/session.go index 03b28f9..6b07d89 100644 --- a/session.go +++ b/session.go @@ -22,42 +22,41 @@ package gogm import ( "errors" "fmt" - "github.com/mindstand/go-bolt/bolt_mode" - "github.com/mindstand/go-bolt/connection" dsl "github.com/mindstand/go-cypherdsl" + "github.com/neo4j/neo4j-go-driver/neo4j" "reflect" ) const defaultDepth = 1 type Session struct { - conn connection.IConnection - tx connection.ITransaction + neoSess neo4j.Session + tx neo4j.Transaction DefaultDepth int LoadStrategy LoadStrategy } func NewSession(readonly bool) (*Session, error) { - if driverPool == nil { - return nil, errors.New("driverPool cannot be nil") + if driver == nil { + return nil, errors.New("driver cannot be nil") } session := new(Session) - var mode bolt_mode.AccessMode + var mode neo4j.AccessMode if readonly { - mode = bolt_mode.ReadMode + mode = neo4j.AccessModeRead } else { - mode = bolt_mode.WriteMode + mode = neo4j.AccessModeWrite } - conn, err := driverPool.Open(mode) + neoSess, err := driver.Session(mode) if err != nil { return nil, err } - session.conn = conn + session.neoSess = neoSess session.DefaultDepth = defaultDepth @@ -65,7 +64,7 @@ func NewSession(readonly bool) (*Session, error) { } func (s *Session) Begin() error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } @@ -75,7 +74,7 @@ func (s *Session) Begin() error { var err error - s.tx, err = s.conn.Begin() + s.tx, err = s.neoSess.BeginTransaction() if err != nil { return err } @@ -84,7 +83,7 @@ func (s *Session) Begin() error { } func (s *Session) Rollback() error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } @@ -111,7 +110,7 @@ func (s *Session) RollbackWithError(originalError error) error { } func (s *Session) Commit() error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } @@ -199,19 +198,24 @@ func (s *Session) LoadDepthFilterPagination(respObj interface{}, id string, dept } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - rows, err := query.WithNeo(conn).Query(params) + cyp, err := query.ToCypher() if err != nil { return err } - return decode(rows, respObj) + result, err := rf(cyp, params) + if err != nil { + return err + } + + return decode(result, respObj) } func (s *Session) LoadAll(respObj interface{}) error { @@ -289,19 +293,24 @@ func (s *Session) LoadAllDepthFilterPagination(respObj interface{}, depth int, f } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) + } + + cyp, err := query.ToCypher() + if err != nil { + return err } - rows, err := query.WithNeo(conn).Query(params) + result, err := rf(cyp, params) if err != nil { return err } - return decode(rows, respObj) + return decode(result, respObj) } func (s *Session) LoadAllEdgeConstraint(respObj interface{}, endNodeType, endNodeField string, edgeConstraint interface{}, minJumps, maxJumps, depth int, filter dsl.ConditionOperator) error { @@ -350,21 +359,26 @@ func (s *Session) LoadAllEdgeConstraint(respObj interface{}, endNodeType, endNod } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - rows, err := query.WithNeo(conn).Query(map[string]interface{}{ + cyp, err := query.ToCypher() + if err != nil { + return err + } + + result, err := rf(cyp, map[string]interface{}{ endNodeField: edgeConstraint, }) if err != nil { return err } - return decode(rows, respObj) + return decode(result, respObj) } func (s *Session) Save(saveObj interface{}) error { @@ -372,23 +386,23 @@ func (s *Session) Save(saveObj interface{}) error { } func (s *Session) SaveDepth(saveObj interface{}, depth int) error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - return saveDepth(conn, saveObj, depth) + return saveDepth(rf, saveObj, depth) } func (s *Session) Delete(deleteObj interface{}) error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } @@ -397,111 +411,116 @@ func (s *Session) Delete(deleteObj interface{}) error { } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - return deleteNode(conn, deleteObj) + return deleteNode(rf, deleteObj) } func (s *Session) DeleteUUID(uuid string) error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - return deleteByUuids(conn, uuid) + return deleteByUuids(rf, uuid) } func (s *Session) Query(query string, properties map[string]interface{}, respObj interface{}) error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - rows, err := dsl.QB().Cypher(query).WithNeo(conn).Query(properties) + res, err := rf(query, properties) if err != nil { return err } - return decode(rows, respObj) + return decode(res, respObj) } func (s *Session) QueryRaw(query string, properties map[string]interface{}) ([][]interface{}, error) { - if s.conn == nil { + if s.neoSess == nil { return nil, errors.New("neo4j connection not initialized") } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - data, err := dsl.QB().Cypher(query).WithNeo(conn).Query(properties) + res, err := rf(query, properties) if err != nil { return nil, err } - return data, nil + var result [][]interface{} + + for res.Next() { + result = append(result, res.Record().Values()) + } + + return result, nil } func (s *Session) PurgeDatabase() error { - if s.conn == nil { + if s.neoSess == nil { return errors.New("neo4j connection not initialized") } // handle if in transaction - var conn connection.IQuery + var rf neoRunFunc if s.tx != nil { - conn = s.tx + rf = s.tx.Run } else { - conn = s.conn + rf = runWrap(s.neoSess) } - _, err := dsl.QB().Match(dsl.Path().V(dsl.V{Name: "n"}).Build()).Delete(true, "n").WithNeo(conn).Exec(nil) + cyp, err := dsl.QB().Match(dsl.Path().V(dsl.V{Name: "n"}).Build()).Delete(true, "n").ToCypher() + if err != nil { + return err + } + + _, err = rf(cyp, nil) return err } func (s *Session) Close() error { - if s.conn == nil { - return errors.New("neo4j connection not initialized") - } - - if s.conn == nil { + if s.neoSess == nil { return fmt.Errorf("cannot close nil connection: %w", ErrInternal) } + // handle tx if s.tx != nil { - log.Warn("attempting to close a session with a pending transaction") - return fmt.Errorf("cannot close a session with a pending transaction: %w", ErrTransaction) - } - - err := driverPool.Reclaim(s.conn) - if err != nil { - return err + log.Warn("attempting to close a session with a pending transaction. Tx is being rolled back") + err := s.tx.Rollback() + if err != nil { + return err + } + s.tx = nil } - s.conn = nil - - return nil + return s.neoSess.Close() } diff --git a/test/main.go b/test/main.go index 6fdaa76..6980545 100644 --- a/test/main.go +++ b/test/main.go @@ -35,6 +35,8 @@ func main() { panic(err) } + session.WriteTransaction() + for result.Next() { fmt.Printf("Created Item with Id = '%d' and Name = '%s'\n", result.Record().GetByIndex(0).(int64), result.Record().GetByIndex(1).(string)) From 29df7deb8516bddcc86fb2f9e5fc787070950abd Mon Sep 17 00:00:00 2001 From: Eric Solender Date: Wed, 22 Jul 2020 18:16:32 -0400 Subject: [PATCH 3/5] switched to new driver --- config.go | 23 ++-- decoder.go | 24 ++-- decoder_test.go | 283 ++++++++++++++++++++++++------------------------ defaults.go | 2 +- delete_test.go | 8 +- index.go | 217 +++++++++++++++++++------------------ index_test.go | 7 -- session.go | 4 +- test/main.go | 44 -------- test_util.go | 77 +++++++++++++ 10 files changed, 369 insertions(+), 320 deletions(-) delete mode 100644 test/main.go create mode 100644 test_util.go diff --git a/config.go b/config.go index 17cedb5..c3c08eb 100644 --- a/config.go +++ b/config.go @@ -23,11 +23,8 @@ import ( "errors" "fmt" "github.com/cornelk/hashmap" - goBolt "github.com/mindstand/go-bolt" "github.com/neo4j/neo4j-go-driver/neo4j" - _ "github.com/neo4j/neo4j-go-driver/neo4j" "github.com/sirupsen/logrus" - "net/url" "reflect" ) @@ -73,6 +70,10 @@ type Config struct { // PoolSize is the size of the connection pool for GoGM PoolSize int `yaml:"pool_size" json:"pool_size"` + Realm string `yaml:"realm" json:"realm"` + + Encrypted bool `yaml:"encrypted" json:"encrypted"` + // Index Strategy defines the index strategy for GoGM IndexStrategy IndexStrategy `yaml:"index_strategy" json:"index_strategy"` } @@ -87,8 +88,8 @@ func (c *Config) ConnectionString() string { protocol = "bolt" } // In case of special characters in password string - password := url.QueryEscape(c.Password) - return fmt.Sprintf("%s://%s:%s@%s:%v", protocol, c.Username, password, c.Host, c.Port) + //password := url.QueryEscape(c.Password) + return fmt.Sprintf("%s://%s:%v", protocol, c.Host, c.Port) } // Index Strategy typedefs int to define different index approaches @@ -165,13 +166,13 @@ func setupInit(isTest bool, conf *Config, mapTypes ...interface{}) error { if !isTest { log.Debug("opening connection to neo4j") - - client, err := goBolt.NewClient(goBolt.WithConnectionString(conf.ConnectionString())) - if err != nil { - return err + // todo tls support + config := func(neoConf *neo4j.Config) { + neoConf.Encrypted = conf.Encrypted + neoConf.MaxConnectionPoolSize = conf.PoolSize } - - driver, err = client.NewDriverPool(conf.PoolSize) + var err error + driver, err = neo4j.NewDriver(conf.ConnectionString(), neo4j.BasicAuth(conf.Username, conf.Password, conf.Realm), config) if err != nil { return err } diff --git a/decoder.go b/decoder.go index ab4b7fa..b350512 100644 --- a/decoder.go +++ b/decoder.go @@ -27,9 +27,19 @@ import ( "strings" ) +func decode(result neo4j.Result, respObj interface{}) (err error) { + var rows [][]interface{} + + for result.Next() { + rows = append(rows, result.Record().Values()) + } + + return innerDecode(rows, respObj) +} + //decodes raw path response from driver //example query `match p=(n)-[*0..5]-() return p` -func decode(result neo4j.Result, respObj interface{}) (err error) { +func innerDecode(result [][]interface{}, respObj interface{}) (err error) { //check nil params if result == nil { return fmt.Errorf("result can not be nil, %w", ErrInvalidParams) @@ -60,8 +70,8 @@ func decode(result neo4j.Result, respObj interface{}) (err error) { var strictRels []neo4j.Relationship var isolatedNodes []neo4j.Node - for result.Next() { - for _, graphType := range result.Record().Values() { + for _, row := range result { + for _, graphType := range row { switch graphType.(type) { case neo4j.Path: paths = append(paths, graphType.(neo4j.Path)) @@ -358,7 +368,7 @@ func sortIsolatedNodes(isolatedNodes []neo4j.Node, labelLookup *map[int64]string (*relMaps)[node.Id()] = map[string]*RelationConfig{} //primary to return - if node.Labels != nil && len(node.Labels()) != 0 && node.Labels()[0] == pkLabel { + if node.Labels() != nil && len(node.Labels()) != 0 && node.Labels()[0] == pkLabel { *pks = append(*pks, node.Id()) } @@ -441,7 +451,7 @@ func sortPaths(paths []neo4j.Path, nodeLookup *map[int64]*reflect.Value, rels *m (*relMaps)[node.Id()] = map[string]*RelationConfig{} //primary to return - if node.Labels != nil && len(node.Labels()) != 0 && node.Labels()[0] == pkLabel { + if node.Labels() != nil && len(node.Labels()) != 0 && node.Labels()[0] == pkLabel { *pks = append(*pks, node.Id()) } } @@ -461,7 +471,7 @@ func sortPaths(paths []neo4j.Path, nodeLookup *map[int64]*reflect.Value, rels *m if _, ok := (*rels)[rel.Id()]; !ok { (*rels)[rel.Id()] = &neoEdgeConfig{ Id: rel.Id(), - StartNodeId: rel.StartId(), + StartNodeId: rel.StartId(), StartNodeType: startLabel, EndNodeId: rel.EndId(), EndNodeType: endLabel, @@ -594,7 +604,7 @@ func convertToValue(graphId int64, conf structDecoratorConfig, props map[string] // convertNodeToValue converts raw bolt node to reflect value func convertNodeToValue(boltNode neo4j.Node) (*reflect.Value, error) { - if boltNode.Labels == nil || len(boltNode.Labels()) == 0 { + if boltNode.Labels() == nil || len(boltNode.Labels()) == 0 { return nil, errors.New("boltNode has no labels") } diff --git a/decoder_test.go b/decoder_test.go index e2527fe..8b21d92 100644 --- a/decoder_test.go +++ b/decoder_test.go @@ -22,8 +22,6 @@ package gogm import ( "errors" "github.com/cornelk/hashmap" - "github.com/mindstand/go-bolt/structures/graph" - "github.com/neo4j/neo4j-go-driver/neo4j" "github.com/stretchr/testify/require" "reflect" "testing" @@ -83,13 +81,13 @@ func TestConvertNodeToValue(t *testing.T) { }, }) - bn := graph.Node{ - NodeIdentity: 10, - Properties: map[string]interface{}{ + bn := testNode{ + id: 10, + props: map[string]interface{}{ "uuid": "dadfasdfasdf", "other_field": "dafsdfasd", }, - Labels: []string{"TestStruct"}, + labels: []string{"TestStruct"}, } val, err := convertNodeToValue(bn) @@ -101,14 +99,14 @@ func TestConvertNodeToValue(t *testing.T) { OtherField: "dafsdfasd", }, val.Interface().(TestStruct)) - bn = graph.Node{ - NodeIdentity: 10, - Properties: map[string]interface{}{ + bn = testNode{ + id: 10, + props: map[string]interface{}{ "uuid": "dadfasdfasdf", "other_field": "dafsdfasd", "t": "dadfasdf", }, - Labels: []string{"TestStruct"}, + labels: []string{"TestStruct"}, } var te structDecoratorConfig @@ -219,41 +217,46 @@ func TestDecoder(t *testing.T) { vars10 := [][]interface{}{ { - graph.Path{ - Nodes: []graph.Node{ - graph.Node{ - Labels: []string{"f"}, - Properties: map[string]interface{}{ + testPath{ + nodes: []*testNode{ + { + labels: []string{"f"}, + props: map[string]interface{}{ "uuid": "0", }, - NodeIdentity: 0, + id: 0, }, - graph.Node{ - Labels: []string{"f"}, - Properties: map[string]interface{}{ + { + labels: []string{"f"}, + props: map[string]interface{}{ "uuid": "1", }, - NodeIdentity: 1, + id: 1, }, - graph.Node{ - Labels: []string{"f"}, - Properties: map[string]interface{}{ + { + labels: []string{"f"}, + props: map[string]interface{}{ "uuid": "2", }, - NodeIdentity: 2, + id: 2, }, }, - Relationships: []graph.UnboundRelationship{ - graph.UnboundRelationship{ - RelIdentity: 3, - Type: "test", + relNodes: []*testRelationship{ + { + id: 3, + startId: 0, + endId: 1, + _type: "test", + props: nil, }, - graph.UnboundRelationship{ - RelIdentity: 4, - Type: "test", + { + id: 4, + startId: 1, + endId: 2, + _type: "test", + props: nil, }, }, - Sequence: []int{1, 1 /*#*/, 2, 2}, }, }, } @@ -285,7 +288,7 @@ func TestDecoder(t *testing.T) { f2.Children = []*f{&f1} var readin10 []*f - req.Nil(decode(vars10, &readin10)) + req.Nil(innerDecode(vars10, &readin10)) req.True(len(readin10) == 3) for _, r := range readin10 { if r.Id == 0 { @@ -310,34 +313,35 @@ func TestDecoder(t *testing.T) { vars := [][]interface{}{ { - graph.Path{ - Nodes: []graph.Node{ - graph.Node{ - Labels: []string{"b"}, - Properties: map[string]interface{}{ + testPath{ + nodes: []*testNode{ + { + labels: []string{"b"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfas", "test_time": fTime, }, - NodeIdentity: 2, + id: 2, }, - graph.Node{ - Labels: []string{"a"}, - Properties: map[string]interface{}{ + { + labels: []string{"a"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfasd", }, - NodeIdentity: 1, + id: 1, }, }, - Relationships: []graph.UnboundRelationship{ - graph.UnboundRelationship{ - RelIdentity: 1, - Type: "test_rel", - Properties: nil, + relNodes: []*testRelationship{ + { + id: 1, + startId: 1, + endId: 2, + _type: "test_rel", + props: nil, }, }, - Sequence: []int{1, 1}, }, }, } @@ -366,7 +370,7 @@ func TestDecoder(t *testing.T) { comp.SingleA = comp22 comp22.Single = comp - req.Nil(decode(vars, &readin)) + req.Nil(innerDecode(vars, &readin)) req.EqualValues(comp.TestField, readin.TestField) req.EqualValues(comp.UUID, readin.UUID) req.EqualValues(comp.Id, readin.Id) @@ -376,7 +380,7 @@ func TestDecoder(t *testing.T) { var readinSlicePtr []*a - req.Nil(decode(vars, &readinSlicePtr)) + req.Nil(innerDecode(vars, &readinSlicePtr)) req.EqualValues(comp.TestField, readinSlicePtr[0].TestField) req.EqualValues(comp.UUID, readinSlicePtr[0].UUID) req.EqualValues(comp.Id, readinSlicePtr[0].Id) @@ -386,7 +390,7 @@ func TestDecoder(t *testing.T) { var readinSlice []a - req.Nil(decode(vars, &readinSlice)) + req.Nil(innerDecode(vars, &readinSlice)) req.EqualValues(comp.TestField, readinSlice[0].TestField) req.EqualValues(comp.UUID, readinSlice[0].UUID) req.EqualValues(comp.Id, readinSlice[0].Id) @@ -396,37 +400,38 @@ func TestDecoder(t *testing.T) { vars2 := [][]interface{}{ { - graph.Path{ - Nodes: []graph.Node{ - graph.Node{ - Labels: []string{"a"}, - Properties: map[string]interface{}{ + testPath{ + nodes: []*testNode{ + { + labels: []string{"a"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfasd", }, - NodeIdentity: 1, + id: 1, }, - graph.Node{ - Labels: []string{"b"}, - Properties: map[string]interface{}{ + { + labels: []string{"b"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfas", "test_time": fTime, }, - NodeIdentity: 2, + id: 2, }, }, - Relationships: []graph.UnboundRelationship{ - graph.UnboundRelationship{ - RelIdentity: 5, - Type: "special_single", - Properties: map[string]interface{}{ + relNodes: []*testRelationship{ + { + id: 5, + startId: 1, + endId: 2, + _type: "special_single", + props: map[string]interface{}{ "test": "testing", "uuid": "asdfasdafsd", }, }, }, - Sequence: []int{1, 1}, }, }, } @@ -463,7 +468,7 @@ func TestDecoder(t *testing.T) { comp2.SingleSpecA = c1 b2.SingleSpec = c1 - req.Nil(decode(vars2, &readin2)) + req.Nil(innerDecode(vars2, &readin2)) req.EqualValues(comp2.TestField, readin2.TestField) req.EqualValues(comp2.UUID, readin2.UUID) req.EqualValues(comp2.Id, readin2.Id) @@ -473,34 +478,35 @@ func TestDecoder(t *testing.T) { vars3 := [][]interface{}{ { - graph.Path{ - Nodes: []graph.Node{ - graph.Node{ - Labels: []string{"a"}, - Properties: map[string]interface{}{ + testPath{ + nodes: []*testNode{ + { + labels: []string{"a"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfasd", }, - NodeIdentity: 1, + id: 1, }, - graph.Node{ - Labels: []string{"b"}, - Properties: map[string]interface{}{ + { + labels: []string{"b"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfas", "test_time": fTime, }, - NodeIdentity: 2, + id: 2, }, }, - Relationships: []graph.UnboundRelationship{ - graph.UnboundRelationship{ - RelIdentity: 5, - Type: "multib", - Properties: nil, + relNodes: []*testRelationship{ + { + id: 5, + startId: 1, + endId: 2, + _type: "multib", + props: nil, }, }, - Sequence: []int{1, 1}, }, }, } @@ -525,7 +531,7 @@ func TestDecoder(t *testing.T) { }, } - req.Nil(decode(vars3, &readin3)) + req.Nil(innerDecode(vars3, &readin3)) req.EqualValues(comp3.TestField, readin3.TestField) req.EqualValues(comp3.UUID, readin3.UUID) req.EqualValues(comp3.Id, readin3.Id) @@ -537,37 +543,38 @@ func TestDecoder(t *testing.T) { vars4 := [][]interface{}{ { - graph.Path{ - Nodes: []graph.Node{ - graph.Node{ - Labels: []string{"a"}, - Properties: map[string]interface{}{ + testPath{ + nodes: []*testNode{ + { + labels: []string{"a"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfasd", }, - NodeIdentity: 1, + id: 1, }, - graph.Node{ - Labels: []string{"b"}, - Properties: map[string]interface{}{ + { + labels: []string{"b"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfas", "test_time": fTime, }, - NodeIdentity: 2, + id: 2, }, }, - Relationships: []graph.UnboundRelationship{ - graph.UnboundRelationship{ - RelIdentity: 5, - Type: "special_multi", - Properties: map[string]interface{}{ + relNodes: []*testRelationship{ + { + id: 5, + startId: 1, + endId: 2, + _type: "special_multi", + props: map[string]interface{}{ "test": "testing", "uuid": "asdfasdafsd", }, }, }, - Sequence: []int{1, 1}, }, }, } @@ -603,7 +610,7 @@ func TestDecoder(t *testing.T) { comp4.MultiSpecA = append(comp4.MultiSpecA, &c4) b3.MultiSpec = append(b3.MultiSpec, &c4) - req.Nil(decode(vars4, &readin4)) + req.Nil(innerDecode(vars4, &readin4)) req.EqualValues(b3.TestField, readin4.TestField) req.EqualValues(b3.UUID, readin4.UUID) req.EqualValues(b3.Id, readin4.Id) @@ -617,12 +624,12 @@ func TestDecoder(t *testing.T) { vars5 := [][]interface{}{ { - graph.Path{ - Nodes: []graph.Node{ - graph.Node{ - NodeIdentity: 1, - Labels: []string{"propsTest"}, - Properties: map[string]interface{}{ + testPath{ + nodes: []*testNode{ + { + id: 1, + labels: []string{"propsTest"}, + props: map[string]interface{}{ "uuid": var5uuid, "props.test.test": "test", "props.test2": "test2", @@ -630,8 +637,6 @@ func TestDecoder(t *testing.T) { }, }, }, - Relationships: nil, - Sequence: nil, }, }, } @@ -648,7 +653,7 @@ func TestDecoder(t *testing.T) { }, } - req.Nil(decode(vars5, &readin5)) + req.Nil(innerDecode(vars5, &readin5)) req.EqualValues(r.Id, readin5.Id) req.EqualValues(r.UUID, readin5.UUID) req.EqualValues(r.Props["test"], readin5.Props["test"]) @@ -658,29 +663,27 @@ func TestDecoder(t *testing.T) { //multi single vars6 := [][]interface{}{ { - graph.Path{ - Nodes: []graph.Node{ - graph.Node{ - Labels: []string{"b"}, - Properties: map[string]interface{}{ + testPath{ + nodes: []*testNode{ + { + labels: []string{"b"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfas", "test_time": fTime, }, - NodeIdentity: 2, + id: 2, }, - graph.Node{ - Labels: []string{"b"}, - Properties: map[string]interface{}{ + { + labels: []string{"b"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfas", "test_time": fTime, }, - NodeIdentity: 3, + id: 3, }, }, - Relationships: nil, - Sequence: nil, }, }, } @@ -700,22 +703,22 @@ func TestDecoder(t *testing.T) { // Id: 3, //} - req.Nil(decode(vars6, &readin6)) + req.Nil(innerDecode(vars6, &readin6)) req.True(len(readin6) == 2) vars7 := [][]interface{}{ { - graph.Path{ - Nodes: nil, - Relationships: nil, - Sequence: nil, + testPath{ + nodes: nil, + relNodes: nil, + indexes: nil, }, }, } var readin7 []*b - emptyErr := decode(vars7, &readin7) + emptyErr := innerDecode(vars7, &readin7) req.NotNil(emptyErr) req.True(errors.As(emptyErr, &ErrNotFound)) @@ -723,17 +726,17 @@ func TestDecoder(t *testing.T) { vars8 := [][]interface{}{ { - graph.Path{ - Nodes: nil, - Relationships: nil, - Sequence: nil, + testPath{ + nodes: nil, + relNodes: nil, + indexes: nil, }, }, } var readin8 b - emptyErr = decode(vars8, &readin8) + emptyErr = innerDecode(vars8, &readin8) req.NotNil(emptyErr) req.True(errors.As(emptyErr, &ErrNotFound)) @@ -741,10 +744,10 @@ func TestDecoder(t *testing.T) { vars9 := [][]interface{}{ { - graph.Node{ - NodeIdentity: 55, - Labels: []string{"b"}, - Properties: map[string]interface{}{ + testNode{ + id: 55, + labels: []string{"b"}, + props: map[string]interface{}{ "test_field": "test", "uuid": "dasdfas", "test_time": fTime, @@ -753,7 +756,7 @@ func TestDecoder(t *testing.T) { }, } var readin9 b - req.Nil(decode(vars9, &readin9)) + req.Nil(innerDecode(vars9, &readin9)) req.Equal("test", readin9.TestField) req.Equal(int64(55), readin9.Id) req.Equal("dasdfas", readin9.UUID) diff --git a/defaults.go b/defaults.go index 61a183c..e737354 100644 --- a/defaults.go +++ b/defaults.go @@ -34,7 +34,7 @@ type BaseNode struct { LoadMap map[string]*RelationConfig `json:"-" gogm:"-"` } -// Specifies Type of Relationship +// Specifies Type of testRelationship type RelationType int const ( diff --git a/delete_test.go b/delete_test.go index 83f4546..1f9ce4b 100644 --- a/delete_test.go +++ b/delete_test.go @@ -20,16 +20,16 @@ package gogm import ( - "github.com/mindstand/go-bolt/bolt_mode" + "github.com/neo4j/neo4j-go-driver/neo4j" "github.com/stretchr/testify/require" ) func testDelete(req *require.Assertions) { - conn, err := driver.Open(bolt_mode.WriteMode) + conn, err := driver.Session(neo4j.AccessModeWrite) if err != nil { req.Nil(err) } - defer driver.Reclaim(conn) + defer conn.Close() del := a{ BaseNode: BaseNode{ @@ -38,6 +38,6 @@ func testDelete(req *require.Assertions) { }, } - err = deleteNode(conn, &del) + err = deleteNode(runWrap(conn), &del) req.Nil(err) } diff --git a/index.go b/index.go index f4955cd..1038a10 100644 --- a/index.go +++ b/index.go @@ -22,7 +22,6 @@ package gogm import ( "errors" "fmt" - "github.com/adam-hanna/arrayOperations" "github.com/cornelk/hashmap" dsl "github.com/mindstand/go-cypherdsl" "github.com/neo4j/neo4j-go-driver/neo4j" @@ -174,12 +173,17 @@ func createAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { if config.PrimaryKey || config.Unique { numIndexCreated++ - _, err := dsl.QB().WithNeo(conn).Create(dsl.NewConstraint(&dsl.ConstraintConfig{ + cyp, err := dsl.QB().Create(dsl.NewConstraint(&dsl.ConstraintConfig{ Unique: true, Name: node, Type: structConfig.Label, Field: config.Name, - })).Exec(nil) + })).ToCypher() + if err != nil { + return err + } + + _, err = tx.Run(cyp, nil) if err != nil { oerr := err err = tx.Rollback() @@ -197,10 +201,15 @@ func createAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { //create composite index if len(indexFields) > 0 { numIndexCreated++ - _, err := dsl.QB().WithNeo(conn).Create(dsl.NewIndex(&dsl.IndexConfig{ + cyp, err := dsl.QB().Create(dsl.NewIndex(&dsl.IndexConfig{ Type: structConfig.Label, Fields: indexFields, - })).Exec(nil) + })).ToCypher() + if err != nil { + return err + } + + _, err = tx.Run(cyp, nil) if err != nil { oerr := err err = tx.Rollback() @@ -220,105 +229,105 @@ func createAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { //verifies all indexes func verifyAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { - sess, err := driver.Session(neo4j.AccessModeWrite) - if err != nil { - return err - } - defer sess.Close() - - //validate that we have to do anything - if mappedTypes == nil || mappedTypes.Len() == 0 { - return errors.New("must have types to map") - } - - var constraints []string - var indexes []string - - //build constraint strings - for nodes := range mappedTypes.Iter() { - node := nodes.Key.(string) - structConfig := nodes.Value.(structDecoratorConfig) - - if structConfig.Fields == nil || len(structConfig.Fields) == 0 { - continue - } - - fields := []string{} - - for _, config := range structConfig.Fields { - - if config.PrimaryKey || config.Unique { - t := fmt.Sprintf("CONSTRAINT ON (%s:%s) ASSERT %s.%s IS UNIQUE", node, structConfig.Label, node, config.Name) - constraints = append(constraints, t) - - indexes = append(indexes, fmt.Sprintf("INDEX ON :%s(%s)", structConfig.Label, config.Name)) - - } else if config.Index { - fields = append(fields, config.Name) - } - } - - f := "(" - for _, field := range fields { - f += field - } - - f += ")" - - indexes = append(indexes, fmt.Sprintf("INDEX ON :%s%s", structConfig.Label, f)) - - } - - //get whats there now - foundConstraints, err := dsl.QB().WithNeo(conn).Cypher("CALL db.constraints").Query(nil) - if err != nil { - return err - } - - var foundIndexes []string - - findexes, err := dsl.QB().WithNeo(conn).Cypher("CALL db.indexes()").Query(nil) - if err != nil { - return err - } - - if len(findexes) != 0 { - for _, index := range findexes { - if len(index) == 0 { - return errors.New("invalid index config") - } - - foundIndexes = append(foundIndexes, index[0].(string)) - } - } - - //verify from there - delta, found := arrayOperations.Difference(foundIndexes, indexes) - if !found { - return fmt.Errorf("found differences in remote vs ogm for found indexes, %v", delta) - } - - log.Debug(delta) - - var founds []string - - for _, constraint := range foundConstraints { - if len(constraint) != 0 { - val, ok := constraint[0].(string) - if !ok { - return fmt.Errorf("unable to convert [%T] to [string]", val) - } - - founds = append(founds, val) - } - } - - delta, found = arrayOperations.Difference(founds, constraints) - if !found { - return fmt.Errorf("found differences in remote vs ogm for found constraints, %v", delta) - } - - log.Debug(delta) + //sess, err := driver.Session(neo4j.AccessModeWrite) + //if err != nil { + // return err + //} + //defer sess.Close() + // + ////validate that we have to do anything + //if mappedTypes == nil || mappedTypes.Len() == 0 { + // return errors.New("must have types to map") + //} + // + //var constraints []string + //var indexes []string + // + ////build constraint strings + //for nodes := range mappedTypes.Iter() { + // node := nodes.Key.(string) + // structConfig := nodes.Value.(structDecoratorConfig) + // + // if structConfig.Fields == nil || len(structConfig.Fields) == 0 { + // continue + // } + // + // fields := []string{} + // + // for _, config := range structConfig.Fields { + // + // if config.PrimaryKey || config.Unique { + // t := fmt.Sprintf("CONSTRAINT ON (%s:%s) ASSERT %s.%s IS UNIQUE", node, structConfig.Label, node, config.Name) + // constraints = append(constraints, t) + // + // indexes = append(indexes, fmt.Sprintf("INDEX ON :%s(%s)", structConfig.Label, config.Name)) + // + // } else if config.Index { + // fields = append(fields, config.Name) + // } + // } + // + // f := "(" + // for _, field := range fields { + // f += field + // } + // + // f += ")" + // + // indexes = append(indexes, fmt.Sprintf("INDEX ON :%s%s", structConfig.Label, f)) + // + //} + // + ////get whats there now + //foundConstraints, err := dsl.QB().WithNeo(conn).Cypher("CALL db.constraints").Query(nil) + //if err != nil { + // return err + //} + // + //var foundIndexes []string + // + //findexes, err := dsl.QB().WithNeo(conn).Cypher("CALL db.indexes()").Query(nil) + //if err != nil { + // return err + //} + // + //if len(findexes) != 0 { + // for _, index := range findexes { + // if len(index) == 0 { + // return errors.New("invalid index config") + // } + // + // foundIndexes = append(foundIndexes, index[0].(string)) + // } + //} + // + ////verify from there + //delta, found := arrayOperations.Difference(foundIndexes, indexes) + //if !found { + // return fmt.Errorf("found differences in remote vs ogm for found indexes, %v", delta) + //} + // + //log.Debug(delta) + // + //var founds []string + // + //for _, constraint := range foundConstraints { + // if len(constraint) != 0 { + // val, ok := constraint[0].(string) + // if !ok { + // return fmt.Errorf("unable to convert [%T] to [string]", val) + // } + // + // founds = append(founds, val) + // } + //} + // + //delta, found = arrayOperations.Difference(founds, constraints) + //if !found { + // return fmt.Errorf("found differences in remote vs ogm for found constraints, %v", delta) + //} + // + //log.Debug(delta) return nil } diff --git a/index_test.go b/index_test.go index 975024d..95f051b 100644 --- a/index_test.go +++ b/index_test.go @@ -20,7 +20,6 @@ package gogm import ( - "github.com/mindstand/go-bolt/bolt_mode" "github.com/stretchr/testify/require" "reflect" ) @@ -29,12 +28,6 @@ func testIndexManagement(req *require.Assertions) { //delete everything req.Nil(dropAllIndexesAndConstraints()) - conn, err := driver.Open(bolt_mode.WriteMode) - req.Nil(err) - - defer driver.Reclaim(conn) - req.Nil(err) - //setup structure mapp := toHashmapStructdecconf(map[string]structDecoratorConfig{ "TEST1": { diff --git a/session.go b/session.go index 6b07d89..39b515b 100644 --- a/session.go +++ b/session.go @@ -30,8 +30,8 @@ import ( const defaultDepth = 1 type Session struct { - neoSess neo4j.Session - tx neo4j.Transaction + neoSess neo4j.Session + tx neo4j.Transaction DefaultDepth int LoadStrategy LoadStrategy } diff --git a/test/main.go b/test/main.go deleted file mode 100644 index 6980545..0000000 --- a/test/main.go +++ /dev/null @@ -1,44 +0,0 @@ -package main - -import ( - "fmt" - "github.com/neo4j/neo4j-go-driver/neo4j" -) - -func main() { - configForNeo4j40 := func(conf *neo4j.Config) { - conf.Encrypted = false - } - - driver, err := neo4j.NewDriver("bolt://0.0.0.0:7687", neo4j.BasicAuth("neo4j", "password", ""), configForNeo4j40) - if err != nil { - panic(err) - } - - // handle driver lifetime based on your application lifetime requirements - // driver's lifetime is usually bound by the application lifetime, which usually implies one driver instance per application - defer driver.Close() - - // For multidatabase support, set sessionConfig.DatabaseName to requested database - sessionConfig := neo4j.SessionConfig{AccessMode: neo4j.AccessModeWrite} - session, err := driver.NewSession(sessionConfig) - if err != nil { - panic(err) - } - defer session.Close() - - result, err := session.Run("CREATE (n:Item { id: $id, name: $name }) RETURN n", map[string]interface{}{ - "id": 1, - "name": "Item 1", - }) - if err != nil { - panic(err) - } - - session.WriteTransaction() - - for result.Next() { - - fmt.Printf("Created Item with Id = '%d' and Name = '%s'\n", result.Record().GetByIndex(0).(int64), result.Record().GetByIndex(1).(string)) - } -} diff --git a/test_util.go b/test_util.go new file mode 100644 index 0000000..b9033b2 --- /dev/null +++ b/test_util.go @@ -0,0 +1,77 @@ +package gogm + +import "github.com/neo4j/neo4j-go-driver/neo4j" + +type testNode struct { + id int64 + labels []string + props map[string]interface{} +} + +func (n testNode) Id() int64 { + return n.id +} + +func (n testNode) Labels() []string { + return n.labels +} + +func (n testNode) Props() map[string]interface{} { + return n.props +} + +type testRelationship struct { + id int64 + startId int64 + endId int64 + _type string + props map[string]interface{} +} + +func (r testRelationship) Id() int64 { + return r.id +} + +func (r testRelationship) StartId() int64 { + return r.startId +} + +func (r testRelationship) EndId() int64 { + return r.endId +} + +func (r testRelationship) Type() string { + return r._type +} + +func (r testRelationship) Props() map[string]interface{} { + return r.props +} + +type testRelNode struct { + id int64 + _type string + props map[string]interface{} +} + +type testPath struct { + nodes []*testNode + relNodes []*testRelationship + indexes []int +} + +func (t testPath) Nodes() []neo4j.Node { + nodes := make([]neo4j.Node, len(t.nodes)) + for i, n := range t.nodes { + nodes[i] = n + } + return nodes +} + +func (t testPath) Relationships() []neo4j.Relationship { + nodes := make([]neo4j.Relationship, len(t.relNodes)) + for i, n := range t.relNodes { + nodes[i] = n + } + return nodes +} From b3a20a1cb39d09f4c0f5592848ed1dd1beafdec5 Mon Sep 17 00:00:00 2001 From: Eric Solender Date: Thu, 23 Jul 2020 10:04:14 -0400 Subject: [PATCH 4/5] updated dsl dep and fixed index stuff --- go.mod | 8 +- go.sum | 35 ++++---- index.go | 190 +++++++++++++++++++++----------------------- integration_test.go | 3 - 4 files changed, 111 insertions(+), 125 deletions(-) diff --git a/go.mod b/go.mod index 7d148ef..89a9366 100644 --- a/go.mod +++ b/go.mod @@ -3,20 +3,18 @@ module github.com/mindstand/gogm go 1.13 require ( - github.com/adam-hanna/arrayOperations v0.2.5 + github.com/adam-hanna/arrayOperations v0.2.6 github.com/cornelk/hashmap v1.0.0 github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect github.com/dchest/siphash v1.2.1 // indirect - github.com/fortytw2/leaktest v1.3.0 // indirect github.com/google/uuid v1.1.1 github.com/kr/pretty v0.1.0 // indirect - github.com/mindstand/go-bolt v0.2.1-0.20200414154543-dd7499698d09 - github.com/mindstand/go-cypherdsl v0.1.1 + github.com/mindstand/go-cypherdsl v0.2.0 github.com/neo4j/neo4j-go-driver v1.8.1-0.20200622090208-4295b59525c9 github.com/sirupsen/logrus v1.6.0 github.com/stretchr/objx v0.2.0 // indirect github.com/stretchr/testify v1.5.1 github.com/urfave/cli/v2 v2.0.0 - golang.org/x/sys v0.0.0-20200523222454-059865788121 // indirect + golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 // indirect gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect ) diff --git a/go.sum b/go.sum index 398bb14..73485fc 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/adam-hanna/arrayOperations v0.2.5 h1:zphKpB5HGhHDkztF2oLcvnqIAu/L/YU3FB/9UghdsO0= -github.com/adam-hanna/arrayOperations v0.2.5/go.mod h1:PhqKQzzPMRjFcC4Heh+kxha3nMvJ6lQNKuVEgoyimgU= +github.com/adam-hanna/arrayOperations v0.2.6 h1:QZC99xC8MgUawXnav7bFMejs/dm7YySnDpMx3oZzz2Y= +github.com/adam-hanna/arrayOperations v0.2.6/go.mod h1:iIzkSjP91FnE66cUFNAjjUJVmjyAwCH0SXnWsx2nbdk= github.com/cornelk/hashmap v1.0.0 h1:jNHWycAM10SO5Ig76HppMQ69jnbqaziRpqVTNvAxdJQ= github.com/cornelk/hashmap v1.0.0/go.mod h1:8wbysTUDnwJGrPZ1Iwsou3m+An6sldFrJItjRhfegCw= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= @@ -14,16 +14,15 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/dchest/siphash v1.1.0/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= github.com/dchest/siphash v1.2.1 h1:4cLinnzVJDKxTCl9B01807Yiy+W7ZzVHj/KIroQRvT4= github.com/dchest/siphash v1.2.1/go.mod h1:q+IRvb2gOSrUnYoPqHiyHXS0FOBBOdl6tONBlVnOnt4= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jolestar/go-commons-pool v2.0.0+incompatible h1:uHn5uRKsLLQSf9f1J5QPY2xREWx/YH+e4bIIXcAuAaE= -github.com/jolestar/go-commons-pool v2.0.0+incompatible/go.mod h1:ChJYIbIch0DMCSU6VU0t0xhPoWDR2mMFIQek3XWU0s8= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= @@ -34,19 +33,15 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/mindstand/go-bolt v0.2.1-0.20200414154543-dd7499698d09 h1:mHyx0Fi0XYb3WXio8Og3w2gyWk1DKWrcyIELPmoIljM= -github.com/mindstand/go-bolt v0.2.1-0.20200414154543-dd7499698d09/go.mod h1:QHuPo43WBka6lBRuQv/ZsAxIHXgzuL+tOt1X9AL3WOw= -github.com/mindstand/go-cypherdsl v0.1.1 h1:2DofP2gnBxP0aS9Dh7aHjxURQRouc3vR/YBOz3rbiH4= -github.com/mindstand/go-cypherdsl v0.1.1/go.mod h1:dxU6sCuwaCdfWiLKDMGC1umeCPpSZk45jiJvEpLuO8I= -github.com/mindstand/gotime v0.0.0-20200414142228-237d9416b724 h1:SOnJCXp+mG8BQjx02AinZBhtVHT4ElVwiJZbDXKcy8s= -github.com/mindstand/gotime v0.0.0-20200414142228-237d9416b724/go.mod h1:DzECeSxMVcU5J1CTiyIjdI/+Q8TJWhdo96Vb1OpY41Q= -github.com/neo4j/neo4j-go-driver v1.8.0 h1:YRp9jsFcF9k/AnvbcqFCN9OMeIT2XTJgxOpp2Puq7OE= -github.com/neo4j/neo4j-go-driver v1.8.0/go.mod h1:0A49wIv0oP3uQdnbceK7Kc+snlY5B0F6dmtYArM0ltk= +github.com/mindstand/go-cypherdsl v0.2.0 h1:/B6A8DhWk2RksdJxruy3+ii3Hvrr5JU+2vL3/oJMLrI= +github.com/mindstand/go-cypherdsl v0.2.0/go.mod h1:swzbrSTuq3CRgFglg3aVThG9GBQmHXz6AY81q9mRMto= github.com/neo4j/neo4j-go-driver v1.8.1-0.20200622090208-4295b59525c9 h1:amJ2VBG5eDK/r9pUjo6/h2np0kYyla5FLWWLyg29H0w= github.com/neo4j/neo4j-go-driver v1.8.1-0.20200622090208-4295b59525c9/go.mod h1:0A49wIv0oP3uQdnbceK7Kc+snlY5B0F6dmtYArM0ltk= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg= github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -73,25 +68,29 @@ github.com/urfave/cli/v2 v2.0.0 h1:+HU9SCbu8GnEUFtIBfuUNXN39ofWViIEJIp6SURMpCg= github.com/urfave/cli/v2 v2.0.0/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0= -golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zrpzXdb/voyeOuVKS46o= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6 h1:X9xIZ1YU8bLZA3l6gqDUHSFiD0GFI9S548h6C8nDtOY= +golang.org/x/sys v0.0.0-20200722175500-76b94024e4b6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/index.go b/index.go index 1038a10..19011de 100644 --- a/index.go +++ b/index.go @@ -22,6 +22,7 @@ package gogm import ( "errors" "fmt" + "github.com/adam-hanna/arrayOperations" "github.com/cornelk/hashmap" dsl "github.com/mindstand/go-cypherdsl" "github.com/neo4j/neo4j-go-driver/neo4j" @@ -229,105 +230,96 @@ func createAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { //verifies all indexes func verifyAllIndexesAndConstraints(mappedTypes *hashmap.HashMap) error { - //sess, err := driver.Session(neo4j.AccessModeWrite) - //if err != nil { - // return err - //} - //defer sess.Close() - // - ////validate that we have to do anything - //if mappedTypes == nil || mappedTypes.Len() == 0 { - // return errors.New("must have types to map") - //} - // - //var constraints []string - //var indexes []string - // - ////build constraint strings - //for nodes := range mappedTypes.Iter() { - // node := nodes.Key.(string) - // structConfig := nodes.Value.(structDecoratorConfig) - // - // if structConfig.Fields == nil || len(structConfig.Fields) == 0 { - // continue - // } - // - // fields := []string{} - // - // for _, config := range structConfig.Fields { - // - // if config.PrimaryKey || config.Unique { - // t := fmt.Sprintf("CONSTRAINT ON (%s:%s) ASSERT %s.%s IS UNIQUE", node, structConfig.Label, node, config.Name) - // constraints = append(constraints, t) - // - // indexes = append(indexes, fmt.Sprintf("INDEX ON :%s(%s)", structConfig.Label, config.Name)) - // - // } else if config.Index { - // fields = append(fields, config.Name) - // } - // } - // - // f := "(" - // for _, field := range fields { - // f += field - // } - // - // f += ")" - // - // indexes = append(indexes, fmt.Sprintf("INDEX ON :%s%s", structConfig.Label, f)) - // - //} - // - ////get whats there now - //foundConstraints, err := dsl.QB().WithNeo(conn).Cypher("CALL db.constraints").Query(nil) - //if err != nil { - // return err - //} - // - //var foundIndexes []string - // - //findexes, err := dsl.QB().WithNeo(conn).Cypher("CALL db.indexes()").Query(nil) - //if err != nil { - // return err - //} - // - //if len(findexes) != 0 { - // for _, index := range findexes { - // if len(index) == 0 { - // return errors.New("invalid index config") - // } - // - // foundIndexes = append(foundIndexes, index[0].(string)) - // } - //} - // - ////verify from there - //delta, found := arrayOperations.Difference(foundIndexes, indexes) - //if !found { - // return fmt.Errorf("found differences in remote vs ogm for found indexes, %v", delta) - //} - // - //log.Debug(delta) - // - //var founds []string - // - //for _, constraint := range foundConstraints { - // if len(constraint) != 0 { - // val, ok := constraint[0].(string) - // if !ok { - // return fmt.Errorf("unable to convert [%T] to [string]", val) - // } - // - // founds = append(founds, val) - // } - //} - // - //delta, found = arrayOperations.Difference(founds, constraints) - //if !found { - // return fmt.Errorf("found differences in remote vs ogm for found constraints, %v", delta) - //} - // - //log.Debug(delta) + sess, err := driver.Session(neo4j.AccessModeWrite) + if err != nil { + return err + } + defer sess.Close() + + //validate that we have to do anything + if mappedTypes == nil || mappedTypes.Len() == 0 { + return errors.New("must have types to map") + } + + var constraints []string + var indexes []string + + //build constraint strings + for nodes := range mappedTypes.Iter() { + node := nodes.Key.(string) + structConfig := nodes.Value.(structDecoratorConfig) + + if structConfig.Fields == nil || len(structConfig.Fields) == 0 { + continue + } + + fields := []string{} + + for _, config := range structConfig.Fields { + + if config.PrimaryKey || config.Unique { + t := fmt.Sprintf("CONSTRAINT ON (%s:%s) ASSERT %s.%s IS UNIQUE", node, structConfig.Label, node, config.Name) + constraints = append(constraints, t) + + indexes = append(indexes, fmt.Sprintf("INDEX ON :%s(%s)", structConfig.Label, config.Name)) + + } else if config.Index { + fields = append(fields, config.Name) + } + } + + f := "(" + for _, field := range fields { + f += field + } + + f += ")" + + indexes = append(indexes, fmt.Sprintf("INDEX ON :%s%s", structConfig.Label, f)) + + } + + //get whats there now + foundResult, err := sess.Run("CALL db.constraints", nil) + if err != nil { + return err + } + + foundConstraints, err := resultToStringArr(foundResult) + if err != nil { + return err + } + + foundInxdexResult, err := sess.Run("CALL db.indexes()", nil) + if err != nil { + return err + } + + foundIndexes, err := resultToStringArr(foundInxdexResult) + if err != nil { + return err + } + + //verify from there + delta, found := arrayOperations.Difference(foundIndexes, indexes) + if !found { + return fmt.Errorf("found differences in remote vs ogm for found indexes, %v", delta) + } + + log.Debug(delta) + + var founds []string + + for _, constraint := range foundConstraints { + founds = append(founds, constraint) + } + + delta, found = arrayOperations.Difference(founds, constraints) + if !found { + return fmt.Errorf("found differences in remote vs ogm for found constraints, %v", delta) + } + + log.Debug(delta) return nil } diff --git a/integration_test.go b/integration_test.go index e0ab890..ce236c3 100644 --- a/integration_test.go +++ b/integration_test.go @@ -20,7 +20,6 @@ package gogm import ( - _log "github.com/mindstand/go-bolt/log" "github.com/stretchr/testify/require" "testing" "time" @@ -31,8 +30,6 @@ func TestIntegration(t *testing.T) { t.Skip() } - _log.SetLevel("trace") - req := require.New(t) conf := Config{ From 4c0d962cafb7b18ae81a146e5efe86dd5a5b224f Mon Sep 17 00:00:00 2001 From: Eric Solender Date: Thu, 23 Jul 2020 10:10:02 -0400 Subject: [PATCH 5/5] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 238a074..97e22b0 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ go get -u github.com/mindstand/gogm - Struct Mapping through the `gogm` struct decorator - Full support for ACID transactions - Underlying connection pooling -- Support for HA Casual Clusters using `bolt+routing` through [MindStand's bolt driver](https://github.com/mindstand/go-bolt) +- Support for HA Casual Clusters using `bolt+routing` through the [Official Neo4j Go Driver](https://github.com/neo4j/neo4j-go-driver) - Custom queries in addition to built in functionality - Builder pattern cypher queries using [MindStand's cypher dsl package](https://github.com/mindstand/go-cypherdsl) - CLI to generate link and unlink functions for gogm structs.