Incorrect mapping parents/children nodes (same relation, same node) of cypher results into structures
lomoval opened this issue · 3 comments
Bug Report:
Mapping results into structures adds created structures from relations into main result slice (checked on SCHEMA_LOAD_STRATEGY
only).
E.g.
we have such graph
and such structure
type Node struct {
gogm.BaseNode
Name string `gogm:"name=name"`
Children []*Node `gogm:"direction=outgoing;relationship=HAS_NODE"`
Parents []*Node `gogm:"direction=incoming;relationship=HAS_NODE"`
}
when loading 1 node by filter (name=n1
) and depth=1
(LoadAllDepthFilter(context.Background(), &nodes, 1, filter, nil)
) , you get all 3 nodes in the results - nodes
slice (see full example in the end).
Expected Behavior
nodes
slice contains 1 node with name=n1
and this node has other 2 nodes in Children
and Parents
slices.
nodes
should not contain nodes with other name
value.
Current Behavior
nodes
slice contains 3 node.
Steps to Reproduce
- Create Graph structure in the Neo4j
create (`14` :Node {name:'n1'}) ,
(`15` :Node {name:'n2'}) ,
(`16` :Node {name:'n3'}) ,
(`14`)-[:`HAS_NODE` ]->(`15`),
(`16`)-[:`HAS_NODE` ]->(`14`)
- Create a filter to get node with
name=n1
filter := gocypherdsl.
C(&gocypherdsl.ConditionConfig{Name: "n", Label: "Node"}).
And(&gocypherdsl.ConditionConfig{
Name: "n",
Field: "name",
ConditionOperator: gocypherdsl.EqualToOperator,
Check: "n1",
})
- Use
LoadAllDepthFilter(context.Background(), &nodes, 1, filter, nil)
to load nodes.
Possible Solution
I think some problem with mapping Cypher results into structures.
Query works fine (has 1 node with two relations), but mapping of results adds all Node
into main result slice.
MATCH (n:Node) WHERE n:Node AND n.name = 'n1' RETURN n , [[(n)-[r_H_1:HAS_NODE]->(n_N_1:Node) | [r_H_1, n_N_1]], [(n)<-[r_H_1:HAS_NODE]-(n_N_1:Node) | [r_H_1, n_N_1]]]
It is necessary to add structures from relations to the corresponding slices of the found structures, but not to the main results (If the structure does not match the filter).
Environment
Value | |
---|---|
Go Version | go1.18.3 windows/amd64 |
GoGM Version | v2.3.6 |
Neo4J Version | 4.4.3 community |
Operating System | Win 10 |
Would you be interested in tackling this issue
No
Additional info
Code example:
package main
import (
"context"
"fmt"
gocypherdsl "github.com/mindstand/go-cypherdsl"
"github.com/mindstand/gogm/v2"
"github.com/rs/zerolog/log"
)
func testSameRef() {
type Node struct {
gogm.BaseNode
Name string `gogm:"name=name"`
Children []*Node `gogm:"direction=outgoing;relationship=HAS_NODE"`
Parents []*Node `gogm:"direction=incoming;relationship=HAS_NODE"`
}
g, err := gogm.New(
&gogm.Config{
Host: "127.0.0.1",
Port: 7688,
Username: "neo4j",
Password: "neo4j1",
PoolSize: 50,
Logger: gogm.GetDefaultLogger(),
LogLevel: "DEBUG",
IndexStrategy: gogm.IGNORE_INDEX,
LoadStrategy: gogm.SCHEMA_LOAD_STRATEGY,
},
gogm.DefaultPrimaryKeyStrategy,
&Node{},
)
if err != nil {
log.Err(err).Msg("")
return
}
sess, err := g.NewSessionV2(gogm.SessionConfig{AccessMode: gogm.AccessModeRead})
if err != nil {
log.Err(err).Msg("")
return
}
filter := gocypherdsl.
C(&gocypherdsl.ConditionConfig{Name: "n", Label: "Node"}).
And(&gocypherdsl.ConditionConfig{
Name: "n",
Field: "name",
ConditionOperator: gocypherdsl.EqualToOperator,
Check: "n1",
})
if err != nil {
log.Err(err).Msg("")
return
}
var nodes []*Node
err = sess.LoadAllDepthFilter(context.Background(), &nodes, 1, filter, nil)
if err != nil {
log.Err(err).Msg("")
return
}
// Gives
// 1 name: n1
// 2 name: n2
// 3 name: n3
for i, node := range nodes {
fmt.Printf("%d name: %s\n", i+1, node.Name)
}
}
Hi @lomoval, thank you for the thorough bug report and apologies for the delayed reply. I'm going to reproduce and debug this issue with the examples you provided. I will get back to you here with updates. Expect to hear something in the next couple of days!
@erictg This worked fine for me with children of different type, but I got the same problem with Query method.
i.e. if you do smth like this:
user := new(model.User)
sess.Query(ctx,
MATCH (u {user_id: $id})-[:OWNS]->(p:Product) RETURN u, COLLECT(p) as products, map[string]interface{}{"id": userID}, user)
user.Products are nil, though it returns products as separate nodes, just doesn't map them as relations.
While it's critical for me to have such behaviour, as I don't want to load all user's relations
@atymkiv
To map results of Cypher query into GOGM structures with relations you need to have info about relations in a response.
Collect
only creates an array with nodes.
With SCHEMA_LOAD_STRATEGY
you can try such query:
MATCH (u {user_id: $id}) RETURN u, [[(u)-[r:OWNS]->(p:Product) | [r, p]]]
In this case you will have u
node and array with relation and nodes. Based on this info and internal config (struct fields - relation) GOGM will fill user.Products
.
GOGM generates same queries but for all relations and for needed depth.