From d526ab640e269d11c329c6723cbc87e273ea3c0a Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Fri, 9 Feb 2024 08:52:08 -0800 Subject: [PATCH 1/9] Starting to refactor mongo data storage, to eliminate using the `data` element and put all data at the top level, with key fields as underscores (_label, _to, _from) --- mongo/compile.go | 227 +++++++++++++++++++----------------------- mongo/compile_test.go | 2 +- mongo/convert.go | 61 +++++++----- mongo/graph.go | 70 ++++++------- mongo/index.go | 6 +- mongo/processor.go | 37 ++++--- 6 files changed, 200 insertions(+), 203 deletions(-) diff --git a/mongo/compile.go b/mongo/compile.go index 1b83c1fd..206643d4 100644 --- a/mongo/compile.go +++ b/mongo/compile.go @@ -99,15 +99,13 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile startCollection = vertCol ids := protoutil.AsStringList(stmt.V) if len(ids) > 0 { - query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{"_id": bson.M{"$in": ids}}}}) + query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{FIELD_ID: bson.M{"$in": ids}}}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$_id", - "label": "$label", - "data": "$data", - "marks": "$marks", - "path": []interface{}{bson.M{"vertex": "$_id"}}, + FIELD_CURRENT: "$$CURRENT", + "marks": "$marks", + "path": []interface{}{bson.M{"vertex": "$_id"}}, }, }}) lastType = gdbi.VertexData @@ -119,19 +117,16 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile startCollection = edgeCol ids := protoutil.AsStringList(stmt.E) if len(ids) > 0 { - query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{"_id": bson.M{"$in": ids}}}}) + query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{FIELD_ID: bson.M{"$in": ids}}}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$_id", - "to": "$to", - "from": "$from", - "label": "$label", - "data": "$data", - "marks": "$marks", - "path": []interface{}{bson.M{"edge": "$_id"}}, + FIELD_CURRENT: "$$CURRENT", + "marks": "$marks", + "path": []interface{}{bson.M{"edge": FIELD_ID}}, }, }}) + lastType = gdbi.EdgeData case *gripql.GraphStatement_In, *gripql.GraphStatement_InNull: @@ -149,9 +144,9 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": edgeCol, - "localField": "_id", - "foreignField": "to", - "as": "dst", + "localField": FIELD_CURRENT_ID, + "foreignField": FIELD_TO, + "as": FIELD_DST, }, }}, ) @@ -159,12 +154,12 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ "marks": "$marks", "path": "$path", - "dst": bson.M{ + FIELD_DST: bson.M{ "$filter": bson.M{ "input": "$dst", "as": "d", "cond": bson.M{ - "$in": bson.A{"$$d.label", labels}, + "$in": bson.A{"$$d._label", labels}, }, }, }, @@ -176,12 +171,9 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "to": "$dst.to", - "from": "$dst.from", - "marks": "$marks", - "path": "$path", + FIELD_CURRENT: "$dst", + "marks": "$marks", + "path": "$path", }}}) } else { if len(labels) > 0 { @@ -192,8 +184,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": vertCol, - "localField": "from", - "foreignField": "_id", + "localField": FIELD_CURRENT_FROM, + "foreignField": FIELD_ID, "as": "dst", }, }}, @@ -204,11 +196,9 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "marks": "$marks", - "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"vertex": "$dst._id"}}}}, + FIELD_CURRENT: "$dst", + "marks": "$marks", + "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"vertex": "$dst._id"}}}}, }}}) lastType = gdbi.VertexData @@ -227,8 +217,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": edgeCol, - "localField": "_id", - "foreignField": "from", + "localField": FIELD_CURRENT_ID, + "foreignField": FIELD_FROM, "as": "dst", }, }}, @@ -242,7 +232,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile "input": "$dst", "as": "d", "cond": bson.M{ - "$in": bson.A{"$$d.label", labels}, + "$in": bson.A{"$$d._label", labels}, }, }, }, @@ -254,16 +244,13 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "to": "$dst.to", - "from": "$dst.from", - "marks": "$marks", - "path": "$path", + FIELD_CURRENT: "$dst", + "marks": "$marks", + "path": "$path", }}}) } else { if len(labels) > 0 { - query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{"label": bson.M{"$in": labels}}}}) + query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{FIELD_LABEL: bson.M{"$in": labels}}}}) } } @@ -271,8 +258,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": vertCol, - "localField": "to", - "foreignField": "_id", + "localField": FIELD_CURRENT_TO, + "foreignField": FIELD_ID, "as": "dst", }, }}, @@ -283,9 +270,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", + "data": "$dst", "marks": "$marks", "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"vertex": "$dst._id"}}}}, }}}) @@ -301,14 +286,14 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": edgeCol, - "let": bson.M{"vid": "$_id", "marks": "$marks"}, + "let": bson.M{"vid": "$data._id", "marks": "$marks"}, "pipeline": []bson.M{ { "$match": bson.M{ "$expr": bson.M{ "$or": []bson.M{ - {"$eq": []string{"$to", "$$vid"}}, - {"$eq": []string{"$from", "$$vid"}}, + {"$eq": []string{"$_to", "$$vid"}}, + {"$eq": []string{"$_from", "$$vid"}}, }, }, }, @@ -323,24 +308,22 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile ) query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "to": "$dst.to", - "from": "$dst.from", - "marks": "$marks", - "vid": "$_id", - "path": "$path", + FIELD_CURRENT: "$dst", + "marks": "$marks", + "vid": "$data._id", + "path": "$path", }}}) } + // filter outgoing edges by label is needed if len(labels) > 0 { - query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{"label": bson.M{"$in": labels}}}}) + query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{"data._label": bson.M{"$in": labels}}}}) } + // lookup the vertex on the other end of that edge query = append(query, bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": vertCol, - "let": bson.M{"to": "$to", "from": "$from", "marks": "$marks", "vid": "$vid"}, + "let": bson.M{"to": "$data._to", "from": "$data._from", "marks": "$marks", "vid": "$vid"}, "pipeline": []bson.M{ { "$match": bson.M{ @@ -366,11 +349,9 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile ) query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "marks": "$marks", - "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"vertex": "$dst._id"}}}}, + FIELD_CURRENT: "$dst", + "marks": "$marks", + "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"vertex": "$dst._id"}}}}, }}}) lastType = gdbi.VertexData @@ -388,8 +369,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": edgeCol, - "localField": "_id", - "foreignField": "to", + "localField": FIELD_ID, + "foreignField": FIELD_TO, "as": "dst", }, }}, @@ -403,7 +384,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile "input": "$dst", "as": "d", "cond": bson.M{ - "$in": bson.A{"$$d.label", labels}, + "$in": bson.A{"$$d._label", labels}, }, }, }, @@ -415,13 +396,13 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "to": "$dst.to", - "from": "$dst.from", - "marks": "$marks", - "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"edge": "$dst._id"}}}}, + FIELD_ID: "$dst._id", + FIELD_LABEL: "$dst._label", + FIELD_TO: "$dst._to", + FIELD_FROM: "$dst._from", + "data": "$dst", + "marks": "$marks", + "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"edge": "$dst._id"}}}}, }}}) lastType = gdbi.EdgeData @@ -440,8 +421,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile bson.D{primitive.E{ Key: "$lookup", Value: bson.M{ "from": edgeCol, - "localField": "_id", - "foreignField": "from", + "localField": FIELD_ID, + "foreignField": FIELD_FROM, "as": "dst", }, }}, @@ -455,7 +436,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile "input": "$dst", "as": "d", "cond": bson.M{ - "$in": bson.A{"$$d.label", labels}, + "$in": bson.A{"$$d._label", labels}, }, }, }, @@ -467,13 +448,13 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) } query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "to": "$dst.to", - "from": "$dst.from", - "marks": "$marks", - "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"edge": "$dst._id"}}}}, + FIELD_ID: "$dst._id", + FIELD_LABEL: "$dst._label", + FIELD_TO: "$dst._to", + FIELD_FROM: "$dst._from", + "data": "$dst", + "marks": "$marks", + "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"edge": "$dst._id"}}}}, }}}) lastType = gdbi.EdgeData @@ -491,8 +472,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile "$match": bson.M{ "$expr": bson.M{ "$or": []bson.M{ - {"$eq": []string{"$to", "$$vid"}}, - {"$eq": []string{"$from", "$$vid"}}, + {"$eq": []string{"$_to", "$$vid"}}, + {"$eq": []string{"$_from", "$$vid"}}, }, }, }, @@ -507,17 +488,17 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile ) query = append(query, bson.D{primitive.E{Key: "$unwind", Value: "$dst"}}) query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "to": "$dst.to", - "from": "$dst.from", - "marks": "$marks", - "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"edge": "$dst._id"}}}}, + FIELD_ID: "$dst._id", + FIELD_LABEL: "$dst._label", + FIELD_TO: "$dst._to", + FIELD_FROM: "$dst._from", + "data": "$dst.data", + "marks": "$marks", + "path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"edge": "$dst._id"}}}}, }}}) labels := protoutil.AsStringList(stmt.BothE) if len(labels) > 0 { - query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{"label": bson.M{"$in": labels}}}}) + query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{FIELD_LABEL: bson.M{"$in": labels}}}}) } lastType = gdbi.EdgeData @@ -538,7 +519,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile for i, v := range labels { ilabels[i] = v } - has := gripql.Within("_label", ilabels...) + has := gripql.Within(FIELD_LABEL, ilabels...) whereExpr := convertHasExpression(has, false) matchStmt := bson.D{primitive.E{Key: "$match", Value: whereExpr}} query = append(query, matchStmt) @@ -552,7 +533,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile for i, v := range ids { iids[i] = v } - has := gripql.Within("_gid", iids...) + has := gripql.Within(FIELD_ID, iids...) whereExpr := convertHasExpression(has, false) matchStmt := bson.D{primitive.E{Key: "$match", Value: whereExpr}} query = append(query, matchStmt) @@ -597,7 +578,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile } fields := protoutil.AsStringList(stmt.Distinct) if len(fields) == 0 { - fields = append(fields, "_gid") + fields = append(fields, FIELD_ID) } keys := bson.M{} match := bson.M{} @@ -628,21 +609,21 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile switch lastType { case gdbi.VertexData: query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "marks": "$dst.marks", - "path": "$dst.path", + FIELD_ID: "$dst._id", + FIELD_LABEL: "$dst._label", + "data": "$dst.data", + "marks": "$dst.marks", + "path": "$dst.path", }}}) case gdbi.EdgeData: query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": "$dst._id", - "label": "$dst.label", - "data": "$dst.data", - "to": "$dst.to", - "from": "$dst.from", - "marks": "$dst.marks", - "path": "$dst.path", + FIELD_ID: "$dst._id", + FIELD_LABEL: "$dst.label", + FIELD_TO: "$dst.to", + FIELD_FROM: "$dst.from", + "data": "$dst.data", + "marks": "$dst.marks", + "path": "$dst.path", }}}) } @@ -670,23 +651,23 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile switch markTypes[stmt.Select] { case gdbi.VertexData: query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": mark + "._id", - "label": mark + ".label", - "data": mark + ".data", - "marks": 1, - "path": "$path", + "_id": mark + "._id", + "_label": mark + "._label", + "data": mark + ".data", + "marks": 1, + "path": "$path", //"path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"vertex": mark + "._id"}}}}, }}}) lastType = gdbi.VertexData case gdbi.EdgeData: query = append(query, bson.D{primitive.E{Key: "$project", Value: bson.M{ - "_id": mark + "._id", - "label": mark + ".label", - "from": mark + ".from", - "to": mark + ".to", - "data": mark + ".data", - "marks": 1, - "path": "$path", + "_id": mark + "._id", + "_label": mark + "._label", + "_from": mark + "._from", + "_to": mark + "._to", + "data": mark + ".data", + "marks": 1, + "path": "$path", //"path": bson.M{"$concatArrays": []interface{}{"$path", []bson.M{{"edge": mark + "._id"}}}}, }}}) lastType = gdbi.EdgeData @@ -744,7 +725,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile } if len(includeFields) > 0 || len(excludeFields) == 0 { - fieldSelect = bson.M{"_id": 1, "label": 1, "from": 1, "to": 1, "marks": 1} + fieldSelect = bson.M{"_id": 1, "_label": 1, "_from": 1, "_to": 1, "marks": 1} for _, v := range excludeFields { switch v { case "gid": diff --git a/mongo/compile_test.go b/mongo/compile_test.go index 3ea0cba2..b3bf2c6f 100644 --- a/mongo/compile_test.go +++ b/mongo/compile_test.go @@ -42,7 +42,7 @@ func TestDistinctPathing(t *testing.T) { f = travelerpath.GetJSONPath(f) f = strings.TrimPrefix(f, "$.") if f == "gid" { - f = "_id" + f = FIELD_ID } if namespace != travelerpath.Current { f = fmt.Sprintf("marks.%s.%s", namespace, f) diff --git a/mongo/convert.go b/mongo/convert.go index 8abf0926..33301b2d 100644 --- a/mongo/convert.go +++ b/mongo/convert.go @@ -12,11 +12,14 @@ func PackVertex(v *gdbi.Vertex) map[string]interface{} { if v.Data != nil { p = v.Data } - return map[string]interface{}{ - "_id": v.ID, - "label": v.Label, - "data": p, + out := map[string]interface{}{ + "_id": v.ID, + "_label": v.Label, } + for k, v := range p { + out[k] = v + } + return out } // PackEdge takes a GRIP edge and converts it to a mongo doc @@ -25,13 +28,16 @@ func PackEdge(e *gdbi.Edge) map[string]interface{} { if e.Data != nil { p = e.Data } - return map[string]interface{}{ - "_id": e.ID, - "from": e.From, - "to": e.To, - "label": e.Label, - "data": p, + out := map[string]interface{}{ + "_id": e.ID, + "_from": e.From, + "_to": e.To, + "_label": e.Label, + } + for k, v := range p { + out[k] = v } + return out } type pair struct { @@ -44,15 +50,15 @@ type pair struct { func UnpackVertex(i map[string]interface{}) *gdbi.Vertex { o := &gdbi.Vertex{} o.ID = i["_id"].(string) - o.Label = i["label"].(string) - if d, ok := i["data"]; ok { - d = removePrimatives(d) - o.Data = d.(map[string]interface{}) - o.Loaded = true - } else { - o.Loaded = false - o.Data = map[string]interface{}{} + o.Label = i["_label"].(string) + d := removePrimatives(i).(map[string]any) + o.Data = map[string]any{} + for k, v := range d { + if k != "_id" && k != "_label" { + o.Data[k] = v + } } + o.Loaded = true return o } @@ -61,16 +67,17 @@ func UnpackEdge(i map[string]interface{}) *gdbi.Edge { o := &gdbi.Edge{} id := i["_id"] o.ID = id.(string) - o.Label = i["label"].(string) - o.From = i["from"].(string) - o.To = i["to"].(string) - if d, ok := i["data"]; ok { - o.Data = d.(map[string]interface{}) - o.Loaded = true - } else { - o.Loaded = false - o.Data = map[string]interface{}{} + o.Label = i["_label"].(string) + o.From = i["_from"].(string) + o.To = i["_to"].(string) + o.Data = map[string]any{} + d := removePrimatives(i).(map[string]any) + for k, v := range d { + if k != "_id" && k != "_label" && k != "_to" && k != "from" { + o.Data[k] = v + } } + o.Loaded = true return o } diff --git a/mongo/graph.go b/mongo/graph.go index 4554ae5b..eab9957e 100644 --- a/mongo/graph.go +++ b/mongo/graph.go @@ -45,9 +45,9 @@ func (mg *Graph) GetTimestamp() string { func (mg *Graph) GetVertex(id string, load bool) *gdbi.Vertex { opts := options.FindOne() if !load { - opts.SetProjection(map[string]interface{}{"_id": 1, "label": 1}) + opts.SetProjection(map[string]interface{}{FIELD_ID: 1, FIELD_LABEL: 1}) } - result := mg.ar.VertexCollection(mg.graph).FindOne(context.Background(), bson.M{"_id": id}, opts) + result := mg.ar.VertexCollection(mg.graph).FindOne(context.Background(), bson.M{FIELD_ID: id}, opts) if result.Err() != nil { return nil } @@ -63,9 +63,9 @@ func (mg *Graph) GetVertex(id string, load bool) *gdbi.Vertex { func (mg *Graph) GetEdge(id string, load bool) *gdbi.Edge { opts := options.FindOne() if !load { - opts.SetProjection(map[string]interface{}{"_id": 1, "label": 1, "from": 1, "to": 1}) + opts.SetProjection(map[string]interface{}{FIELD_ID: 1, FIELD_LABEL: 1, FIELD_FROM: 1, FIELD_TO: 1}) } - result := mg.ar.EdgeCollection(mg.graph).FindOne(context.TODO(), bson.M{"_id": id}, opts) + result := mg.ar.EdgeCollection(mg.graph).FindOne(context.TODO(), bson.M{FIELD_ID: id}, opts) if result.Err() != nil { return nil } @@ -84,7 +84,7 @@ func (mg *Graph) AddVertex(vertices []*gdbi.Vertex) error { var err error docBatch := make([]mongo.WriteModel, 0, len(vertices)) for _, v := range vertices { - i := mongo.NewReplaceOneModel().SetUpsert(true).SetFilter(bson.M{"_id": v.ID}) + i := mongo.NewReplaceOneModel().SetUpsert(true).SetFilter(bson.M{FIELD_ID: v.ID}) ent := PackVertex(v) i.SetReplacement(ent) docBatch = append(docBatch, i) @@ -105,7 +105,7 @@ func (mg *Graph) AddEdge(edges []*gdbi.Edge) error { var err error docBatch := make([]mongo.WriteModel, 0, len(edges)) for _, edge := range edges { - i := mongo.NewReplaceOneModel().SetUpsert(true).SetFilter(bson.M{"_id": edge.ID}) + i := mongo.NewReplaceOneModel().SetUpsert(true).SetFilter(bson.M{FIELD_ID: edge.ID}) ent := PackEdge(edge) i.SetReplacement(ent) docBatch = append(docBatch, i) @@ -123,7 +123,7 @@ func (mg *Graph) BulkAdd(stream <-chan *gdbi.GraphElement) error { // deleteConnectedEdges deletes edges where `from` or `to` equal `key` func (mg *Graph) deleteConnectedEdges(key string) error { eCol := mg.ar.EdgeCollection(mg.graph) - _, err := eCol.DeleteMany(context.TODO(), bson.M{"$or": []bson.M{{"from": key}, {"to": key}}}) + _, err := eCol.DeleteMany(context.TODO(), bson.M{"$or": []bson.M{{FIELD_FROM: key}, {FIELD_TO: key}}}) if err != nil { return fmt.Errorf("failed to delete edge(s): %s", err) } @@ -134,7 +134,7 @@ func (mg *Graph) deleteConnectedEdges(key string) error { // DelVertex deletes vertex with id `key` func (mg *Graph) DelVertex(key string) error { vCol := mg.ar.VertexCollection(mg.graph) - _, err := vCol.DeleteOne(context.TODO(), bson.M{"_id": key}) + _, err := vCol.DeleteOne(context.TODO(), bson.M{FIELD_ID: key}) if err != nil { return fmt.Errorf("failed to delete vertex %s: %s", key, err) } @@ -149,7 +149,7 @@ func (mg *Graph) DelVertex(key string) error { // DelEdge deletes edge with id `key` func (mg *Graph) DelEdge(key string) error { eCol := mg.ar.EdgeCollection(mg.graph) - _, err := eCol.DeleteOne(context.TODO(), bson.M{"_id": key}) + _, err := eCol.DeleteOne(context.TODO(), bson.M{FIELD_ID: key}) if err != nil { return fmt.Errorf("failed to delete edge %s: %s", key, err) } @@ -166,7 +166,7 @@ func (mg *Graph) GetVertexList(ctx context.Context, load bool) <-chan *gdbi.Vert vCol := mg.ar.VertexCollection(mg.graph) opts := options.Find() if !load { - opts.SetProjection(bson.M{"_id": 1, "label": 1}) + opts.SetProjection(bson.M{FIELD_ID: 1, FIELD_LABEL: 1}) } query, err := vCol.Find(ctx, bson.M{}, opts) if err != nil { @@ -201,7 +201,7 @@ func (mg *Graph) GetEdgeList(ctx context.Context, loadProp bool) <-chan *gdbi.Ed eCol := mg.ar.EdgeCollection(mg.graph) opts := options.Find() if !loadProp { - opts.SetProjection(bson.M{"_id": 1, "to": 1, "from": 1, "label": 1}) + opts.SetProjection(bson.M{FIELD_ID: 1, FIELD_TO: 1, FIELD_FROM: 1, FIELD_LABEL: 1}) } query, err := eCol.Find(ctx, bson.M{}, opts) if err != nil { @@ -216,7 +216,7 @@ func (mg *Graph) GetEdgeList(ctx context.Context, loadProp bool) <-chan *gdbi.Ed default: } if err := query.Decode(&result); err == nil { - if _, ok := result["to"]; ok { + if _, ok := result[FIELD_TO]; ok { e := UnpackEdge(result) o <- e } @@ -248,10 +248,10 @@ func (mg *Graph) GetVertexChannel(ctx context.Context, ids chan gdbi.ElementLook idBatch = append(idBatch, batch[i].ID) } } - query := bson.M{"_id": bson.M{"$in": idBatch}} + query := bson.M{FIELD_ID: bson.M{"$in": idBatch}} opts := options.Find() if !load { - opts.SetProjection(bson.M{"_id": 1, "label": 1}) + opts.SetProjection(bson.M{FIELD_ID: 1, FIELD_LABEL: 1}) } cursor, err := vCol.Find(context.TODO(), query, opts) if err != nil { @@ -305,17 +305,18 @@ func (mg *Graph) GetOutChannel(ctx context.Context, reqChan chan gdbi.ElementLoo batchMapReturnCount[batch[i].ID] = 0 } } - query := []bson.M{{"$match": bson.M{"from": bson.M{"$in": idBatch}}}} + query := []bson.M{{"$match": bson.M{FIELD_FROM: bson.M{"$in": idBatch}}}} if len(edgeLabels) > 0 { - query = append(query, bson.M{"$match": bson.M{"label": bson.M{"$in": edgeLabels}}}) + query = append(query, bson.M{"$match": bson.M{FIELD_LABEL: bson.M{"$in": edgeLabels}}}) } vertCol := fmt.Sprintf("%s_vertices", mg.graph) - query = append(query, bson.M{"$lookup": bson.M{"from": vertCol, "localField": "to", "foreignField": "_id", "as": "dst"}}) + query = append(query, bson.M{"$lookup": bson.M{FIELD_FROM: vertCol, "localField": FIELD_TO, "foreignField": FIELD_ID, "as": "dst"}}) query = append(query, bson.M{"$unwind": "$dst"}) if load { - query = append(query, bson.M{"$project": bson.M{"from": true, "dst._id": true, "dst.label": true, "dst.data": true}}) + //query = append(query, bson.M{"$project": bson.M{FIELD_FROM: true, "dst._id": true, "dst._label": true, "dst.data": true}}) + query = append(query, bson.M{"$project": bson.M{FIELD_FROM: true, "dst": true}}) } else { - query = append(query, bson.M{"$project": bson.M{"from": true, "dst._id": true, "dst.label": true}}) + query = append(query, bson.M{"$project": bson.M{FIELD_FROM: true, "dst._id": true, "dst._label": true}}) } eCol := mg.ar.EdgeCollection(mg.graph) @@ -326,7 +327,7 @@ func (mg *Graph) GetOutChannel(ctx context.Context, reqChan chan gdbi.ElementLoo if err := cursor.Decode(&result); err == nil { if dst, ok := result["dst"].(map[string]interface{}); ok { v := UnpackVertex(dst) - fromID := result["from"].(string) + fromID := result[FIELD_FROM].(string) r := batchMap[fromID] batchMapReturnCount[fromID]++ for _, ri := range r { @@ -384,17 +385,18 @@ func (mg *Graph) GetInChannel(ctx context.Context, reqChan chan gdbi.ElementLook batchMapReturnCount[batch[i].ID] = 0 } } - query := []bson.M{{"$match": bson.M{"to": bson.M{"$in": idBatch}}}} + query := []bson.M{{"$match": bson.M{FIELD_TO: bson.M{"$in": idBatch}}}} if len(edgeLabels) > 0 { - query = append(query, bson.M{"$match": bson.M{"label": bson.M{"$in": edgeLabels}}}) + query = append(query, bson.M{"$match": bson.M{FIELD_LABEL: bson.M{"$in": edgeLabels}}}) } vertCol := fmt.Sprintf("%s_vertices", mg.graph) - query = append(query, bson.M{"$lookup": bson.M{"from": vertCol, "localField": "from", "foreignField": "_id", "as": "src"}}) + query = append(query, bson.M{"$lookup": bson.M{FIELD_FROM: vertCol, "localField": FIELD_FROM, "foreignField": FIELD_ID, "as": "src"}}) query = append(query, bson.M{"$unwind": "$src"}) if load { - query = append(query, bson.M{"$project": bson.M{"to": true, "src._id": true, "src.label": true, "src.data": true}}) + //query = append(query, bson.M{"$project": bson.M{FIELD_TO: true, "src._id": true, "src._label": true, "src.data": true}}) //FIX: .data no longer used + query = append(query, bson.M{"$project": bson.M{FIELD_TO: true, "src._id": true, "src": true}}) } else { - query = append(query, bson.M{"$project": bson.M{"to": true, "src._id": true, "src.label": true}}) + query = append(query, bson.M{"$project": bson.M{FIELD_TO: true, "src._id": true, "src._label": true}}) } eCol := mg.ar.EdgeCollection(mg.graph) @@ -405,7 +407,7 @@ func (mg *Graph) GetInChannel(ctx context.Context, reqChan chan gdbi.ElementLook if err := cursor.Decode(&result); err == nil { if src, ok := result["src"].(map[string]interface{}); ok { v := UnpackVertex(src) - toID := result["to"].(string) + toID := result[FIELD_TO].(string) r := batchMap[toID] batchMapReturnCount[toID]++ for _, ri := range r { @@ -463,9 +465,9 @@ func (mg *Graph) GetOutEdgeChannel(ctx context.Context, reqChan chan gdbi.Elemen batchMapReturnCount[batch[i].ID] = 0 } } - query := []bson.M{{"$match": bson.M{"from": bson.M{"$in": idBatch}}}} + query := []bson.M{{"$match": bson.M{FIELD_FROM: bson.M{"$in": idBatch}}}} if len(edgeLabels) > 0 { - query = append(query, bson.M{"$match": bson.M{"label": bson.M{"$in": edgeLabels}}}) + query = append(query, bson.M{"$match": bson.M{FIELD_LABEL: bson.M{"$in": edgeLabels}}}) } eCol := mg.ar.EdgeCollection(mg.graph) cursor, err := eCol.Aggregate(context.TODO(), query) @@ -474,7 +476,7 @@ func (mg *Graph) GetOutEdgeChannel(ctx context.Context, reqChan chan gdbi.Elemen for cursor.Next(context.TODO()) { if err := cursor.Decode(&result); err == nil { e := UnpackEdge(result) - fromID := result["from"].(string) + fromID := result[FIELD_FROM].(string) r := batchMap[fromID] batchMapReturnCount[fromID]++ for _, ri := range r { @@ -530,9 +532,9 @@ func (mg *Graph) GetInEdgeChannel(ctx context.Context, reqChan chan gdbi.Element batchMapReturnCount[batch[i].ID] = 0 } } - query := []bson.M{{"$match": bson.M{"to": bson.M{"$in": idBatch}}}} + query := []bson.M{{"$match": bson.M{FIELD_TO: bson.M{"$in": idBatch}}}} if len(edgeLabels) > 0 { - query = append(query, bson.M{"$match": bson.M{"label": bson.M{"$in": edgeLabels}}}) + query = append(query, bson.M{"$match": bson.M{FIELD_LABEL: bson.M{"$in": edgeLabels}}}) } eCol := mg.ar.EdgeCollection(mg.graph) cursor, err := eCol.Aggregate(context.TODO(), query) @@ -541,7 +543,7 @@ func (mg *Graph) GetInEdgeChannel(ctx context.Context, reqChan chan gdbi.Element for cursor.Next(context.TODO()) { if err := cursor.Decode(&result); err == nil { e := UnpackEdge(result) - toID := result["to"].(string) + toID := result[FIELD_TO].(string) r := batchMap[toID] batchMapReturnCount[toID]++ for _, ri := range r { @@ -579,7 +581,7 @@ func (mg *Graph) GetInEdgeChannel(ctx context.Context, reqChan chan gdbi.Element // ListVertexLabels returns a list of vertex types in the graph func (mg *Graph) ListVertexLabels() ([]string, error) { v := mg.ar.VertexCollection(mg.graph) - out, err := v.Distinct(context.TODO(), "label", bson.M{}) + out, err := v.Distinct(context.TODO(), FIELD_LABEL, bson.M{}) if err != nil { return nil, err } @@ -593,7 +595,7 @@ func (mg *Graph) ListVertexLabels() ([]string, error) { // ListEdgeLabels returns a list of edge types in the graph func (mg *Graph) ListEdgeLabels() ([]string, error) { e := mg.ar.EdgeCollection(mg.graph) - out, err := e.Distinct(context.TODO(), "label", bson.M{}) + out, err := e.Distinct(context.TODO(), FIELD_LABEL, bson.M{}) if err != nil { return nil, err } diff --git a/mongo/index.go b/mongo/index.go index bd9a3db0..55e7a9ff 100644 --- a/mongo/index.go +++ b/mongo/index.go @@ -110,11 +110,11 @@ func (mg *Graph) VertexLabelScan(ctx context.Context, label string) chan string go func() { defer close(out) selection := map[string]interface{}{ - "label": label, + FIELD_LABEL: label, } vcol := mg.ar.VertexCollection(mg.graph) opts := options.Find() - opts.SetProjection(map[string]interface{}{"_id": 1, "label": 1}) + opts.SetProjection(map[string]interface{}{FIELD_ID: 1, FIELD_LABEL: 1}) cursor, err := vcol.Find(context.TODO(), selection, opts) if err == nil { @@ -127,7 +127,7 @@ func (mg *Graph) VertexLabelScan(ctx context.Context, label string) chan string default: } if nil == cursor.Decode(&result) { - out <- result["_id"].(string) + out <- result[FIELD_ID].(string) } } if err := cursor.Close(context.TODO()); err != nil { diff --git a/mongo/processor.go b/mongo/processor.go index 4f2b72dd..8b36b38c 100644 --- a/mongo/processor.go +++ b/mongo/processor.go @@ -28,20 +28,23 @@ type Processor struct { func getDataElement(result map[string]interface{}) *gdbi.DataElement { de := &gdbi.DataElement{} - if x, ok := result["_id"]; ok { + if x, ok := result[FIELD_ID]; ok { de.ID = x.(string) } - if x, ok := result["label"]; ok { + if x, ok := result[FIELD_LABEL]; ok { de.Label = x.(string) } - if x, ok := result["data"]; ok { - de.Data = removePrimatives(x).(map[string]interface{}) - de.Loaded = true + de.Data = map[string]any{} + for k, v := range removePrimatives(result).(map[string]any) { + if !IsNodeField(k) { + de.Data[k] = v + } } - if x, ok := result["to"]; ok { + de.Loaded = true + if x, ok := result[FIELD_TO]; ok { de.To = x.(string) } - if x, ok := result["from"]; ok { + if x, ok := result[FIELD_FROM]; ok { de.From = x.(string) } return de @@ -176,6 +179,8 @@ func (proc *Processor) Process(ctx context.Context, man gdbi.Manager, in gdbi.In } default: + //Reconstruct the traveler + //Extract the path if path, ok := result["path"]; ok { if pathA, ok := path.(bson.A); ok { o := make([]gdbi.DataElementID, len(pathA)) @@ -192,6 +197,7 @@ func (proc *Processor) Process(ctx context.Context, man gdbi.Manager, in gdbi.In t = &gdbi.BaseTraveler{Path: o} } } + //Extract marks if marks, ok := result["marks"]; ok { if marks, ok := marks.(map[string]interface{}); ok { for k, v := range marks { @@ -204,20 +210,21 @@ func (proc *Processor) Process(ctx context.Context, man gdbi.Manager, in gdbi.In } de := &gdbi.DataElement{} - if x, ok := result["_id"]; ok { + data := removePrimatives(result["data"]).(map[string]any) + if x, ok := data[FIELD_ID]; ok { de.ID = removePrimatives(x).(string) } - if x, ok := result["label"]; ok { + if x, ok := data[FIELD_LABEL]; ok { de.Label = x.(string) } - if x, ok := result["data"]; ok { - de.Data = removePrimatives(x).(map[string]interface{}) - de.Loaded = true - } - if x, ok := result["to"]; ok { + //if x, ok := result["data"]; ok { + de.Data = RemoveKeyFields(data) //removePrimatives(x).(map[string]interface{}) + de.Loaded = true + //} + if x, ok := data[FIELD_TO]; ok { de.To = x.(string) } - if x, ok := result["from"]; ok { + if x, ok := data[FIELD_FROM]; ok { de.From = x.(string) } out <- t.AddCurrent(de) From e4ba0b1e93e41937eb8677f08587f417730d8174 Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Fri, 9 Feb 2024 09:31:01 -0800 Subject: [PATCH 2/9] Fixing hasLabel bug --- conformance/tests/ot_aggregations.py | 4 ++-- conformance/tests/ot_repeat.py | 8 ++++---- gripql/inspect/haslabel.go | 3 +++ gripql/javascript/gripql.js | 7 +++++++ mongo/compile.go | 11 ++--------- 5 files changed, 18 insertions(+), 15 deletions(-) diff --git a/conformance/tests/ot_aggregations.py b/conformance/tests/ot_aggregations.py index d0553c79..97f0b629 100644 --- a/conformance/tests/ot_aggregations.py +++ b/conformance/tests/ot_aggregations.py @@ -122,7 +122,7 @@ def getMinMax(input_data, percent, accuracy=0.15): if count != len(percents): errors.append( "Unexpected number of terms: %d != %d" % - (len(row["buckets"]), len(percents)) + (len(res["buckets"]), len(percents)) ) return errors @@ -194,7 +194,7 @@ def test_field_aggregation(man): G = man.setGraph("swapi") count = 0 - for row in G.query().V().hasLabel("Planet").aggregate(gripql.field("gid-agg", "$._data")): + for row in G.query().V().hasLabel("Planet").aggregate(gripql.field("gid-agg", "$")): if row["key"] not in fields: errors.append("unknown field returned: %s" % (row['key'])) if row["value"] != 3: diff --git a/conformance/tests/ot_repeat.py b/conformance/tests/ot_repeat.py index 91630765..dcc8abe5 100644 --- a/conformance/tests/ot_repeat.py +++ b/conformance/tests/ot_repeat.py @@ -79,20 +79,20 @@ def test_set(man): G = man.setGraph("swapi") q = G.query().V("Character:1").set("count", 0) - q = q.as_("start").render("$start._data") + q = q.as_("start").render("$start") for row in q: if row['count'] != 0: errors.append("Incorrect increment value") q = G.query().V("Character:1").set("count", 0).as_("start").out().increment("$start.count") - q = q.render("$start._data") + q = q.render("$start") for row in q: if row['count'] != 1: errors.append("Incorrect increment value") q = G.query().V("Character:1").set("count", 0).as_("start").out().increment("$start.count") q = q.increment("$start.count").has(gripql.gt("$start.count", 1.0)) - q = q.render("$start._data") + q = q.render("$start") count = 0 for row in q: count += 1 @@ -102,7 +102,7 @@ def test_set(man): errors.append("Incorrect number of rows returned") q = G.query().V("Character:1").set("count", 0).increment("count",2).as_("start").out().increment("$start.count") - q = q.render("$start._data") + q = q.render("$start") for row in q: if row['count'] != 3: errors.append("Incorrect increment value") diff --git a/gripql/inspect/haslabel.go b/gripql/inspect/haslabel.go index fa9abdcc..01723e55 100644 --- a/gripql/inspect/haslabel.go +++ b/gripql/inspect/haslabel.go @@ -5,6 +5,9 @@ import ( "github.com/bmeg/grip/util/protoutil" ) +// Determine if a pipeline starts with 'V().hasLabel(x)' and trim it out +// This can be used to optimize pipelines that start with looking up vertex labels +// using an index func FindVertexHasLabelStart(pipe []*gripql.GraphStatement) ([]string, []*gripql.GraphStatement) { hasLabelLen := 0 labels := []string{} diff --git a/gripql/javascript/gripql.js b/gripql/javascript/gripql.js index f1d1e212..1a4279c5 100644 --- a/gripql/javascript/gripql.js +++ b/gripql/javascript/gripql.js @@ -256,6 +256,13 @@ function histogram(name, field, interval) { } } +function count(name) { + return { + "name": name, + "count": {} + } +} + function V(id) { return query().V(id) } diff --git a/mongo/compile.go b/mongo/compile.go index 206643d4..770770e5 100644 --- a/mongo/compile.go +++ b/mongo/compile.go @@ -515,14 +515,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile return &Pipeline{}, fmt.Errorf(`"hasLabel" statement is only valid for edge or vertex types not: %s`, lastType.String()) } labels := protoutil.AsStringList(stmt.HasLabel) - ilabels := make([]interface{}, len(labels)) - for i, v := range labels { - ilabels[i] = v - } - has := gripql.Within(FIELD_LABEL, ilabels...) - whereExpr := convertHasExpression(has, false) - matchStmt := bson.D{primitive.E{Key: "$match", Value: whereExpr}} - query = append(query, matchStmt) + query = append(query, bson.D{primitive.E{Key: "$match", Value: bson.M{FIELD_CURRENT_LABEL: bson.M{"$in": labels}}}}) case *gripql.GraphStatement_HasId: if lastType != gdbi.VertexData && lastType != gdbi.EdgeData { @@ -533,7 +526,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile for i, v := range ids { iids[i] = v } - has := gripql.Within(FIELD_ID, iids...) + has := gripql.Within(FIELD_CURRENT_ID, iids...) whereExpr := convertHasExpression(has, false) matchStmt := bson.D{primitive.E{Key: "$match", Value: whereExpr}} query = append(query, matchStmt) From d75e125807ac29a72ad6822fb389629dfdb8eb0b Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Fri, 9 Feb 2024 10:04:06 -0800 Subject: [PATCH 3/9] Fixing schema sampling --- mongo/schema.go | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/mongo/schema.go b/mongo/schema.go index 261a9f06..80d77998 100644 --- a/mongo/schema.go +++ b/mongo/schema.go @@ -73,7 +73,7 @@ func (ma *GraphDB) getVertexSchema(ctx context.Context, graph string, n uint32, pipe := []bson.M{ { "$match": bson.M{ - "label": bson.M{"$eq": label}, + FIELD_LABEL: bson.M{"$eq": label}, }, }, } @@ -84,7 +84,10 @@ func (ma *GraphDB) getVertexSchema(ctx context.Context, graph string, n uint32, pipe = append(pipe, bson.M{"$limit": n}) } - cursor, _ := ma.VertexCollection(graph).Aggregate(context.TODO(), pipe) + cursor, err := ma.VertexCollection(graph).Aggregate(context.TODO(), pipe) + if err != nil { + log.Errorf("Vertex schema scan error: %s", err) + } result := make(map[string]interface{}) schema := make(map[string]interface{}) for cursor.Next(context.TODO()) { @@ -94,8 +97,8 @@ func (ma *GraphDB) getVertexSchema(ctx context.Context, graph string, n uint32, default: if err := cursor.Decode(&result); err == nil { - if result["data"] != nil { - ds := gripql.GetDataFieldTypes(result["data"].(map[string]interface{})) + if result != nil { + ds := gripql.GetDataFieldTypes(result) util.MergeMaps(schema, ds) } } else { @@ -153,7 +156,7 @@ func (ma *GraphDB) getEdgeSchema(ctx context.Context, graph string, n uint32, ra pipe := []bson.M{ { "$match": bson.M{ - "label": bson.M{"$eq": label}, + FIELD_LABEL: bson.M{"$eq": label}, }, }, } @@ -177,9 +180,9 @@ func (ma *GraphDB) getEdgeSchema(ctx context.Context, graph string, n uint32, ra default: if err := cursor.Decode(&result); err == nil { - fromToPairs.Add(fromtokey{result["from"].(string), result["to"].(string)}) - if result["data"] != nil { - ds := gripql.GetDataFieldTypes(result["data"].(map[string]interface{})) + fromToPairs.Add(fromtokey{result[FIELD_FROM].(string), result[FIELD_TO].(string)}) + if result != nil { + ds := gripql.GetDataFieldTypes(result) util.MergeMaps(schema, ds) } } else { @@ -277,18 +280,18 @@ func (ma *GraphDB) resolveLabels(graph string, ft fromto) fromto { to := "" result := map[string]string{} opts := options.FindOne() - opts.SetProjection(bson.M{"_id": -1, "label": 1}) + opts.SetProjection(bson.M{"_id": -1, "_label": 1}) cursor := v.FindOne(context.TODO(), bson.M{"_id": fromID}, opts) if cursor.Err() == nil { if nil == cursor.Decode(&result) { - from = result["label"] + from = result["_label"] } } result = map[string]string{} cursor = v.FindOne(context.TODO(), bson.M{"_id": toID}, opts) if cursor.Err() == nil { if nil == cursor.Decode(&result) { - to = result["label"] + to = result["_label"] } } if from != "" && to != "" { From e5bb94cbc8662a44d6b9850c235083d365bcf9d5 Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Fri, 9 Feb 2024 10:08:08 -0800 Subject: [PATCH 4/9] Adding missing file --- mongo/fields.go | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 mongo/fields.go diff --git a/mongo/fields.go b/mongo/fields.go new file mode 100644 index 00000000..1eacc990 --- /dev/null +++ b/mongo/fields.go @@ -0,0 +1,32 @@ +package mongo + +const FIELD_ID = "_id" +const FIELD_LABEL = "_label" +const FIELD_TO = "_to" +const FIELD_FROM = "_from" + +const FIELD_CURRENT = "data" +const FIELD_CURRENT_ID = "data._id" +const FIELD_CURRENT_TO = "data._to" +const FIELD_CURRENT_FROM = "data._from" +const FIELD_CURRENT_LABEL = "data._label" + +const FIELD_DST = "dst" +const FIELD_DST_ID = "dst._id" +const FIELD_DST_TO = "dst._to" +const FIELD_DST_FROM = "dst._from" +const FIELD_DST_LABEL = "dst._label" + +func IsNodeField(f string) bool { + return f == FIELD_ID || f == FIELD_LABEL || f == FIELD_TO || f == FIELD_FROM +} + +func RemoveKeyFields(x map[string]any) map[string]any { + out := map[string]any{} + for k, v := range x { + if !IsNodeField(k) { + out[k] = v + } + } + return out +} From 0ab3ae177878767cb8d491fe25862ba30a29e617 Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Fri, 9 Feb 2024 11:43:58 -0800 Subject: [PATCH 5/9] Fixing various issues in the engines --- conformance/tests/ot_update.py | 2 +- mongo/convert.go | 27 +++++++++------------------ mongo/graph.go | 6 +++--- 3 files changed, 13 insertions(+), 22 deletions(-) diff --git a/conformance/tests/ot_update.py b/conformance/tests/ot_update.py index 8d0ac997..8b214771 100644 --- a/conformance/tests/ot_update.py +++ b/conformance/tests/ot_update.py @@ -44,7 +44,7 @@ def test_replace(man): errors.append("vertex has unexpected data") if G.getEdge("edge1")["data"] != {"weight": 5}: - errors.append("edge is missing expected data") + errors.append("edge is missing expected data: %s" % (G.getEdge("edge1"))) return errors diff --git a/mongo/convert.go b/mongo/convert.go index 33301b2d..d7c63858 100644 --- a/mongo/convert.go +++ b/mongo/convert.go @@ -3,7 +3,6 @@ package mongo import ( "github.com/bmeg/grip/gdbi" "go.mongodb.org/mongo-driver/bson/primitive" - "google.golang.org/protobuf/types/known/structpb" ) // PackVertex take a GRIP vertex and convert it to a mongo doc @@ -12,13 +11,12 @@ func PackVertex(v *gdbi.Vertex) map[string]interface{} { if v.Data != nil { p = v.Data } - out := map[string]interface{}{ - "_id": v.ID, - "_label": v.Label, - } + out := map[string]interface{}{} for k, v := range p { out[k] = v } + out["_id"] = v.ID + out["_label"] = v.Label return out } @@ -28,24 +26,17 @@ func PackEdge(e *gdbi.Edge) map[string]interface{} { if e.Data != nil { p = e.Data } - out := map[string]interface{}{ - "_id": e.ID, - "_from": e.From, - "_to": e.To, - "_label": e.Label, - } + out := map[string]interface{}{} for k, v := range p { out[k] = v } + out["_id"] = e.ID + out["_from"] = e.From + out["_to"] = e.To + out["_label"] = e.Label return out } -type pair struct { - key string - valueMap interface{} - valueStruct *structpb.Struct -} - // UnpackVertex takes a mongo doc and converts it into an gripql.Vertex func UnpackVertex(i map[string]interface{}) *gdbi.Vertex { o := &gdbi.Vertex{} @@ -73,7 +64,7 @@ func UnpackEdge(i map[string]interface{}) *gdbi.Edge { o.Data = map[string]any{} d := removePrimatives(i).(map[string]any) for k, v := range d { - if k != "_id" && k != "_label" && k != "_to" && k != "from" { + if k != "_id" && k != "_label" && k != "_to" && k != "_from" { o.Data[k] = v } } diff --git a/mongo/graph.go b/mongo/graph.go index eab9957e..1d32cb84 100644 --- a/mongo/graph.go +++ b/mongo/graph.go @@ -310,7 +310,7 @@ func (mg *Graph) GetOutChannel(ctx context.Context, reqChan chan gdbi.ElementLoo query = append(query, bson.M{"$match": bson.M{FIELD_LABEL: bson.M{"$in": edgeLabels}}}) } vertCol := fmt.Sprintf("%s_vertices", mg.graph) - query = append(query, bson.M{"$lookup": bson.M{FIELD_FROM: vertCol, "localField": FIELD_TO, "foreignField": FIELD_ID, "as": "dst"}}) + query = append(query, bson.M{"$lookup": bson.M{"from": vertCol, "localField": FIELD_TO, "foreignField": FIELD_ID, "as": "dst"}}) query = append(query, bson.M{"$unwind": "$dst"}) if load { //query = append(query, bson.M{"$project": bson.M{FIELD_FROM: true, "dst._id": true, "dst._label": true, "dst.data": true}}) @@ -390,11 +390,11 @@ func (mg *Graph) GetInChannel(ctx context.Context, reqChan chan gdbi.ElementLook query = append(query, bson.M{"$match": bson.M{FIELD_LABEL: bson.M{"$in": edgeLabels}}}) } vertCol := fmt.Sprintf("%s_vertices", mg.graph) - query = append(query, bson.M{"$lookup": bson.M{FIELD_FROM: vertCol, "localField": FIELD_FROM, "foreignField": FIELD_ID, "as": "src"}}) + query = append(query, bson.M{"$lookup": bson.M{"from": vertCol, "localField": FIELD_FROM, "foreignField": FIELD_ID, "as": "src"}}) query = append(query, bson.M{"$unwind": "$src"}) if load { //query = append(query, bson.M{"$project": bson.M{FIELD_TO: true, "src._id": true, "src._label": true, "src.data": true}}) //FIX: .data no longer used - query = append(query, bson.M{"$project": bson.M{FIELD_TO: true, "src._id": true, "src": true}}) + query = append(query, bson.M{"$project": bson.M{FIELD_TO: true, "src": true}}) } else { query = append(query, bson.M{"$project": bson.M{FIELD_TO: true, "src._id": true, "src._label": true}}) } From 5cabbd135e751fd365b648a8c767e4aa0e6ae300 Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Thu, 15 Feb 2024 22:38:59 -0800 Subject: [PATCH 6/9] Working on issues conformance test issues --- conformance/tests/ot_aggregations.py | 2 +- conformance/tests/ot_mark.py | 18 +-- engine/core/optimize.go | 8 +- engine/core/processors.go | 24 +-- engine/core/processors_extra.go | 20 +-- engine/core/statement_compiler.go | 6 +- engine/logic/match.go | 3 + gdbi/statement_processor.go | 6 +- .../jsonpath.go => gdbi/tpath/namepath.go | 43 +++-- gdbi/tpath/namepath_test.go | 22 +++ gdbi/tpath/render.go | 37 +++++ gdbi/traveler.go | 12 +- gdbi/{jsonpath.go => traveler_doc.go} | 153 +++++++----------- ...{jsonpath_test.go => traveler_doc_test.go} | 53 +++--- gripql/inspect/inspect.go | 12 +- gripql/javascript/gripql.js | 17 ++ kvgraph/index.go | 3 +- mongo/compile.go | 42 ++--- mongo/compile_test.go | 8 +- mongo/has_evaluator.go | 6 +- mongo/index.go | 10 +- 21 files changed, 280 insertions(+), 225 deletions(-) rename travelerpath/jsonpath.go => gdbi/tpath/namepath.go (68%) create mode 100644 gdbi/tpath/namepath_test.go create mode 100644 gdbi/tpath/render.go rename gdbi/{jsonpath.go => traveler_doc.go} (69%) rename gdbi/{jsonpath_test.go => traveler_doc_test.go} (85%) diff --git a/conformance/tests/ot_aggregations.py b/conformance/tests/ot_aggregations.py index 97f0b629..e5809f53 100644 --- a/conformance/tests/ot_aggregations.py +++ b/conformance/tests/ot_aggregations.py @@ -190,7 +190,7 @@ def test_traversal_gid_aggregation(man): def test_field_aggregation(man): errors = [] - fields = [ "id", 'orbital_period', 'gravity', 'terrain', 'name','climate', 'system', 'diameter', 'rotation_period', 'url', 'population', 'surface_water'] + fields = [ "_gid", "_label", 'orbital_period', 'gravity', 'terrain', 'name','climate', 'system', 'diameter', 'rotation_period', 'url', 'population', 'surface_water'] G = man.setGraph("swapi") count = 0 diff --git a/conformance/tests/ot_mark.py b/conformance/tests/ot_mark.py index 667b53de..e9bddd8b 100644 --- a/conformance/tests/ot_mark.py +++ b/conformance/tests/ot_mark.py @@ -13,9 +13,9 @@ def test_mark_select_label_filter(man): count += 1 if len(row) != 2: errors.append("Incorrect number of marks returned") - if row["a"]["gid"] != "Film:1": + if row["a"]["_gid"] != "Film:1": errors.append("Incorrect vertex returned for 'a': %s" % row["a"]) - if row["b"]["label"] not in ["Vehicle", "Starship", "Species", "Planet", "Character"]: + if row["b"]["_label"] not in ["Vehicle", "Starship", "Species", "Planet", "Character"]: errors.append("Incorrect vertex returned for 'b': %s" % row["b"]) if count != 38: @@ -36,11 +36,11 @@ def test_mark_select(man): count += 1 if len(row) != 3: errors.append("Incorrect number of marks returned") - if row["a"]["gid"] != "Character:1": + if row["a"]["_gid"] != "Character:1": errors.append("Incorrect vertex returned for 'a': %s" % row["a"]) - if row["a"]["data"]["height"] != 172: + if row["a"]["height"] != 172: errors.append("Missing data for 'a'") - if row["b"]["label"] not in ["Starship", "Planet", "Species", "Film"]: + if row["b"]["_label"] not in ["Starship", "Planet", "Species", "Film"]: errors.append("Incorrect vertex returned for 'b': %s" % row["b"]) if count != 64: @@ -61,13 +61,13 @@ def test_mark_edge_select(man): count += 1 if len(row) != 3: errors.append("Incorrect number of marks returned") - if row["a"]["gid"] != "Film:1": + if row["a"]["_gid"] != "Film:1": errors.append("Incorrect as selection") - if row["b"]["label"] != "planets": + if row["b"]["_label"] != "planets": errors.append("Incorrect as edge selection: %s" % row["b"]) - if "scene_count" not in row["b"]["data"]: + if "scene_count" not in row["b"]: errors.append("Data not returned") - if row["c"]["label"] != "Planet": + if row["c"]["_label"] != "Planet": errors.append("Incorrect element returned") if count != 3: diff --git a/engine/core/optimize.go b/engine/core/optimize.go index afac7981..5ff07215 100644 --- a/engine/core/optimize.go +++ b/engine/core/optimize.go @@ -1,8 +1,8 @@ package core import ( + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" - "github.com/bmeg/grip/travelerpath" "github.com/bmeg/grip/util/protoutil" ) @@ -47,11 +47,11 @@ func IndexStartOptimize(pipe []*gripql.GraphStatement) []*gripql.GraphStatement return IndexStartOptimize(newPipe) } if cond := s.Has.GetCondition(); cond != nil { - path := travelerpath.GetJSONPath(cond.Key) + path := tpath.NormalizePath(cond.Key) switch path { - case "$.gid": + case "$_current._gid": hasIDIdx = append(hasIDIdx, i) - case "$.label": + case "$_current._label": hasLabelIdx = append(hasLabelIdx, i) default: // do nothing diff --git a/engine/core/processors.go b/engine/core/processors.go index 695e99f9..8aead1f2 100644 --- a/engine/core/processors.go +++ b/engine/core/processors.go @@ -1053,19 +1053,21 @@ func (agg *aggregate) Process(ctx context.Context, man gdbi.Manager, in gdbi.InP c++ } } - sort.Float64s(fieldValues) - min := fieldValues[0] - max := fieldValues[len(fieldValues)-1] - - for bucket := math.Floor(min/i) * i; bucket <= max; bucket += i { - var count float64 - for _, v := range fieldValues { - if v >= bucket && v < (bucket+i) { - count++ + if len(fieldValues) > 0 { + sort.Float64s(fieldValues) + min := fieldValues[0] + max := fieldValues[len(fieldValues)-1] + + for bucket := math.Floor(min/i) * i; bucket <= max; bucket += i { + var count float64 + for _, v := range fieldValues { + if v >= bucket && v < (bucket+i) { + count++ + } } + //sBucket, _ := structpb.NewValue(bucket) + out <- &gdbi.BaseTraveler{Aggregation: &gdbi.Aggregate{Name: a.Name, Key: bucket, Value: float64(count)}} } - //sBucket, _ := structpb.NewValue(bucket) - out <- &gdbi.BaseTraveler{Aggregation: &gdbi.Aggregate{Name: a.Name, Key: bucket, Value: float64(count)}} } return outErr }) diff --git a/engine/core/processors_extra.go b/engine/core/processors_extra.go index ac85838a..e78dd549 100644 --- a/engine/core/processors_extra.go +++ b/engine/core/processors_extra.go @@ -6,10 +6,10 @@ import ( "math" "strings" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" "github.com/bmeg/grip/kvi" "github.com/bmeg/grip/kvindex" - "github.com/bmeg/grip/travelerpath" "github.com/influxdata/tdigest" "golang.org/x/sync/errgroup" @@ -61,8 +61,8 @@ func (agg *aggregateDisk) Process(ctx context.Context, man gdbi.Manager, in gdbi kv := man.GetTempKV() idx := kvindex.NewIndex(kv) - namespace := travelerpath.GetNamespace(tagg.Field) - field := travelerpath.GetJSONPath(tagg.Field) + namespace := tpath.GetNamespace(tagg.Field) + field := tpath.NormalizePath(tagg.Field) field = strings.TrimPrefix(field, "$.") idx.AddField(field) @@ -70,7 +70,7 @@ func (agg *aggregateDisk) Process(ctx context.Context, man gdbi.Manager, in gdbi for batch := range aChans[a.Name] { err := kv.Update(func(tx kvi.KVTransaction) error { for _, t := range batch { - doc := gdbi.GetDoc(t, namespace) + doc := gdbi.TravelerGetDoc(t, namespace) err := idx.AddDocTx(tx, fmt.Sprintf("%d", tid), doc) tid++ if err != nil { @@ -107,8 +107,8 @@ func (agg *aggregateDisk) Process(ctx context.Context, man gdbi.Manager, in gdbi kv := man.GetTempKV() idx := kvindex.NewIndex(kv) - namespace := travelerpath.GetNamespace(hagg.Field) - field := travelerpath.GetJSONPath(hagg.Field) + namespace := tpath.GetNamespace(hagg.Field) + field := tpath.NormalizePath(hagg.Field) field = strings.TrimPrefix(field, "$.") idx.AddField(field) @@ -116,7 +116,7 @@ func (agg *aggregateDisk) Process(ctx context.Context, man gdbi.Manager, in gdbi for batch := range aChans[a.Name] { err := kv.Update(func(tx kvi.KVTransaction) error { for _, t := range batch { - doc := gdbi.GetDoc(t, namespace) + doc := gdbi.TravelerGetDoc(t, namespace) err := idx.AddDocTx(tx, fmt.Sprintf("%d", tid), doc) tid++ if err != nil { @@ -152,8 +152,8 @@ func (agg *aggregateDisk) Process(ctx context.Context, man gdbi.Manager, in gdbi kv := man.GetTempKV() idx := kvindex.NewIndex(kv) - namespace := travelerpath.GetNamespace(pagg.Field) - field := travelerpath.GetJSONPath(pagg.Field) + namespace := tpath.GetNamespace(pagg.Field) + field := tpath.NormalizePath(pagg.Field) field = strings.TrimPrefix(field, "$.") idx.AddField(field) @@ -161,7 +161,7 @@ func (agg *aggregateDisk) Process(ctx context.Context, man gdbi.Manager, in gdbi for batch := range aChans[a.Name] { err := kv.Update(func(tx kvi.KVTransaction) error { for _, t := range batch { - doc := gdbi.GetDoc(t, namespace) + doc := gdbi.TravelerGetDoc(t, namespace) err := idx.AddDocTx(tx, fmt.Sprintf("%d", tid), doc) tid++ if err != nil { diff --git a/engine/core/statement_compiler.go b/engine/core/statement_compiler.go index 30333d5f..7aab4ce3 100644 --- a/engine/core/statement_compiler.go +++ b/engine/core/statement_compiler.go @@ -5,8 +5,8 @@ import ( "github.com/bmeg/grip/engine/logic" "github.com/bmeg/grip/gdbi" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" - "github.com/bmeg/grip/travelerpath" "github.com/bmeg/grip/util/protoutil" ) @@ -166,8 +166,8 @@ func (sc *DefaultStmtCompiler) As(stmt *gripql.GraphStatement_As, ps *gdbi.State if err := gripql.ValidateFieldName(stmt.As); err != nil { return nil, fmt.Errorf(`"mark" statement invalid; %v`, err) } - if stmt.As == travelerpath.Current { - return nil, fmt.Errorf(`"mark" statement invalid; uses reserved name %s`, travelerpath.Current) + if stmt.As == tpath.CURRENT { + return nil, fmt.Errorf(`"mark" statement invalid; uses reserved name %s`, tpath.CURRENT) } ps.MarkTypes[stmt.As] = ps.LastType return &Marker{stmt.As}, nil diff --git a/engine/logic/match.go b/engine/logic/match.go index 88a6be6e..9acd6529 100644 --- a/engine/logic/match.go +++ b/engine/logic/match.go @@ -13,9 +13,12 @@ import ( func MatchesCondition(trav gdbi.Traveler, cond *gripql.HasCondition) bool { var val interface{} var condVal interface{} + val = gdbi.TravelerPathLookup(trav, cond.Key) condVal = cond.Value.AsInterface() + log.Debugf("match: %s %s %s", condVal, val, cond.Key) + switch cond.Condition { case gripql.Condition_EQ: return reflect.DeepEqual(val, condVal) diff --git a/gdbi/statement_processor.go b/gdbi/statement_processor.go index 38730e3e..ba4c024a 100644 --- a/gdbi/statement_processor.go +++ b/gdbi/statement_processor.go @@ -3,8 +3,8 @@ package gdbi import ( "fmt" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" - "github.com/bmeg/grip/travelerpath" ) func StatementProcessor( @@ -171,8 +171,8 @@ func StatementProcessor( if err := gripql.ValidateFieldName(stmt.As); err != nil { return nil, fmt.Errorf(`"mark" statement invalid; %v`, err) } - if stmt.As == travelerpath.Current { - return nil, fmt.Errorf(`"mark" statement invalid; uses reserved name %s`, travelerpath.Current) + if stmt.As == tpath.CURRENT { + return nil, fmt.Errorf(`"mark" statement invalid; uses reserved name %s`, tpath.CURRENT) } ps.MarkTypes[stmt.As] = ps.LastType return sc.As(stmt, ps) diff --git a/travelerpath/jsonpath.go b/gdbi/tpath/namepath.go similarity index 68% rename from travelerpath/jsonpath.go rename to gdbi/tpath/namepath.go index 9bce43c2..bb84c834 100644 --- a/travelerpath/jsonpath.go +++ b/gdbi/tpath/namepath.go @@ -1,13 +1,11 @@ -package travelerpath +package tpath import ( "strings" - - "github.com/bmeg/grip/gripql" ) // Current represents the 'current' traveler namespace -var Current = "__current__" +const CURRENT = "_current" // GetNamespace returns the namespace of the provided path // @@ -20,37 +18,36 @@ func GetNamespace(path string) string { namespace = strings.TrimPrefix(parts[0], "$") } if namespace == "" { - namespace = Current + namespace = CURRENT } return namespace } -// GetJSONPath strips the namespace from the path and returns the valid -// Json path within the document referenced by the namespace +// NormalizePath // // Example: -// GetJSONPath("gene.symbol.ensembl") returns "$.data.symbol.ensembl" -func GetJSONPath(path string) string { +// NormalizePath("gene.symbol.ensembl") returns "$_current.symbol.ensembl" + +func NormalizePath(path string) string { + namespace := CURRENT parts := strings.Split(path, ".") + if strings.HasPrefix(parts[0], "$") { - parts = parts[1:] - } - if len(parts) == 0 { - return "" - } - found := false - for _, v := range gripql.ReservedFields { - if parts[0] == v { - found = true - parts[0] = strings.TrimPrefix(parts[0], "_") + if len(parts[0]) > 1 { + namespace = parts[0][1:] } + parts = parts[1:] } - if !found { - parts = append([]string{"data"}, parts...) - } + parts = append([]string{"$" + namespace}, parts...) + return strings.Join(parts, ".") +} - parts = append([]string{"$"}, parts...) +func ToLocalPath(path string) string { + parts := strings.Split(path, ".") + if strings.HasPrefix(parts[0], "$") { + parts[0] = "$" + } return strings.Join(parts, ".") } diff --git a/gdbi/tpath/namepath_test.go b/gdbi/tpath/namepath_test.go new file mode 100644 index 00000000..26cd7bd6 --- /dev/null +++ b/gdbi/tpath/namepath_test.go @@ -0,0 +1,22 @@ +package tpath + +import "testing" + +func TestPathNormalize(t *testing.T) { + + pairs := [][]string{ + {"_label", "$_current._label"}, + {"name", "$_current.name"}, + {"$.name", "$_current.name"}, + {"$name", "$name"}, + {"$a.name", "$a.name"}, + } + + for _, p := range pairs { + o := NormalizePath(p[0]) + if o != p[1] { + t.Errorf("Normalize %s error: %s != %s", p[0], o, p[1]) + } + } + +} diff --git a/gdbi/tpath/render.go b/gdbi/tpath/render.go new file mode 100644 index 00000000..a7d468e2 --- /dev/null +++ b/gdbi/tpath/render.go @@ -0,0 +1,37 @@ +package tpath + +import ( + "github.com/bmeg/jsonpath" +) + +func Render(template any, data map[string]any) (any, error) { + switch elem := template.(type) { + case string: + path := NormalizePath(elem) + return jsonpath.JsonPathLookup(data, path) + case map[string]interface{}: + o := make(map[string]interface{}) + for k, v := range elem { + val, err := Render(v, data) + if err == nil { + o[k] = val + } else { + o[k] = v + } + } + return o, nil + case []any: + o := make([]any, len(elem)) + for i := range elem { + val, err := Render(elem[i], data) + if err == nil { + o[i] = val + } else { + o[i] = elem[i] + } + } + return o, nil + default: + return template, nil + } +} diff --git a/gdbi/traveler.go b/gdbi/traveler.go index 7d923734..af7dfc8c 100644 --- a/gdbi/traveler.go +++ b/gdbi/traveler.go @@ -218,19 +218,21 @@ func (elem *DataElement) ToDict() map[string]interface{} { if elem == nil { return out } + for k, v := range elem.Data { + out[k] = v + } if elem.ID != "" { - out["gid"] = elem.ID + out["_gid"] = elem.ID } if elem.Label != "" { - out["label"] = elem.Label + out["_label"] = elem.Label } if elem.To != "" { - out["to"] = elem.To + out["_to"] = elem.To } if elem.From != "" { - out["from"] = elem.From + out["_from"] = elem.From } - out["data"] = elem.Data return out } diff --git a/gdbi/jsonpath.go b/gdbi/traveler_doc.go similarity index 69% rename from gdbi/jsonpath.go rename to gdbi/traveler_doc.go index 01f9a2fb..f59c7f5a 100644 --- a/gdbi/jsonpath.go +++ b/gdbi/traveler_doc.go @@ -1,66 +1,47 @@ package gdbi import ( - // "fmt" - "strings" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/log" - "github.com/bmeg/grip/travelerpath" "github.com/bmeg/jsonpath" ) -// GetDoc returns the document referenced by the provided namespace. -// -// Example for a traveler containing: -// -// { -// "current": {...}, -// "marks": { -// "gene": { -// "gid": 1, -// "label": "gene", -// "data": { -// "symbol": { -// "ensembl": "ENSG00000012048", -// "hgnc": 1100, -// "entrez": 672 -// } -// } -// } -// } -// } -// } -// -// GetDoc(traveler, "gene") returns: -// -// { -// "gid": 1, -// "label": "gene", -// "data": { -// "symbol": { -// "ensembl": "ENSG00000012048", -// "hgnc": 1100, -// "entrez": 672 -// } -// } -// } -func GetDoc(traveler Traveler, namespace string) map[string]interface{} { - var tmap map[string]interface{} - if namespace == travelerpath.Current { - dr := traveler.GetCurrent() - if dr == nil { - return nil +// GetDoc returns the document representing the traveler data +func TravelerGetDoc(traveler Traveler, ns ...string) map[string]any { + if len(ns) == 0 { + out := map[string]any{} + out[tpath.CURRENT] = traveler.GetCurrent().Get().ToDict() + for _, k := range traveler.ListMarks() { + out[k] = traveler.GetMark(k).Get().ToDict() } - tmap = dr.Get().ToDict() - } else { - dr := traveler.GetMark(namespace) - if dr == nil { - return nil + return out + } + out := map[string]any{} + for _, n := range ns { + if n == tpath.CURRENT { + out[n] = traveler.GetCurrent().Get().ToDict() + } else { + m := traveler.GetMark(n) + if m != nil { + out[n] = m.Get().ToDict() + } } - tmap = dr.Get().ToDict() } - return tmap + return out +} + +// TravelerGetMarkDoc returns the document representing the traveler data +func TravelerGetMarkDoc(traveler Traveler, ns string) map[string]any { + if ns == tpath.CURRENT { + return traveler.GetCurrent().Get().ToDict() + } + m := traveler.GetMark(ns) + if m != nil { + return m.Get().ToDict() + } + return nil } // TravelerPathLookup gets the value of a field in the given Traveler @@ -68,7 +49,7 @@ func GetDoc(traveler Traveler, namespace string) map[string]interface{} { // Example for a traveler containing: // // { -// "current": {...}, +// "_current": {...}, // "marks": { // "gene": { // "gid": 1, @@ -87,14 +68,19 @@ func GetDoc(traveler Traveler, namespace string) map[string]interface{} { // // TravelerPathLookup(travler, "$gene.symbol.ensembl") returns "ENSG00000012048" func TravelerPathLookup(traveler Traveler, path string) interface{} { - namespace := travelerpath.GetNamespace(path) - field := travelerpath.GetJSONPath(path) - doc := GetDoc(traveler, namespace) + field := tpath.NormalizePath(path) + jpath := tpath.ToLocalPath(field) + namespace := tpath.GetNamespace(field) + var doc map[string]any + if namespace == tpath.CURRENT { + doc = traveler.GetCurrent().Get().ToDict() + } else { + doc = traveler.GetMark(namespace).Get().ToDict() + } if field == "" { - //fmt.Printf("Null field, return %#v\n", doc) return doc } - res, err := jsonpath.JsonPathLookup(doc, field) + res, err := jsonpath.JsonPathLookup(doc, jpath) if err != nil { return nil } @@ -103,49 +89,32 @@ func TravelerPathLookup(traveler Traveler, path string) interface{} { // TravelerSetValue(travler, "$gene.symbol.ensembl", "hi") inserts the value in the location" func TravelerSetValue(traveler Traveler, path string, val interface{}) error { - namespace := travelerpath.GetNamespace(path) - field := travelerpath.GetJSONPath(path) + field := tpath.NormalizePath(path) + namespace := tpath.GetNamespace(field) if field == "" { return nil } - doc := GetDoc(traveler, namespace) + doc := TravelerGetDoc(traveler, namespace) return jsonpath.JsonPathSet(doc, field, val) } // TravelerPathExists returns true if the field exists in the given Traveler func TravelerPathExists(traveler Traveler, path string) bool { - namespace := travelerpath.GetNamespace(path) - field := travelerpath.GetJSONPath(path) + field := tpath.NormalizePath(path) + namespace := tpath.GetNamespace(field) if field == "" { return false } - doc := GetDoc(traveler, namespace) + doc := TravelerGetDoc(traveler, namespace) _, err := jsonpath.JsonPathLookup(doc, field) return err == nil } // RenderTraveler takes a template and fills in the values using the data structure func RenderTraveler(traveler Traveler, template interface{}) interface{} { - switch elem := template.(type) { - case string: - return TravelerPathLookup(traveler, elem) - case map[string]interface{}: - o := make(map[string]interface{}) - for k, v := range elem { - val := RenderTraveler(traveler, v) - o[k] = val - } - return o - case []interface{}: - o := make([]interface{}, len(elem)) - for i := range elem { - val := RenderTraveler(traveler, elem[i]) - o[i] = val - } - return o - default: - return nil - } + doc := TravelerGetDoc(traveler) + out, _ := tpath.Render(template, doc) + return out } // SelectTravelerFields returns a new copy of the traveler with only the selected fields @@ -159,16 +128,16 @@ KeyLoop: exclude = true key = strings.TrimPrefix(key, "-") } - namespace := travelerpath.GetNamespace(key) + namespace := tpath.GetNamespace(key) switch namespace { - case travelerpath.Current: + case tpath.CURRENT: // noop default: log.Errorf("SelectTravelerFields: only can select field from current traveler") continue KeyLoop } - path := travelerpath.GetJSONPath(key) - path = strings.TrimPrefix(path, "$.") + path := tpath.NormalizePath(key) + path = strings.TrimPrefix(path, "$.") //FIXME if exclude { excludePaths = append(excludePaths, path) @@ -215,7 +184,7 @@ func includeFields(new, old *DataElement, paths []string) *DataElement { Include: for _, path := range paths { switch path { - case "gid", "label", "from", "to": + case "_gid", "_label", "_from", "_to": // noop case "data": for k, v := range old.Data { @@ -271,13 +240,13 @@ func excludeFields(elem *DataElement, paths []string) *DataElement { Exclude: for _, path := range paths { switch path { - case "gid": + case "_gid": result.ID = "" - case "label": + case "_label": result.Label = "" - case "from": + case "_from": result.From = "" - case "to": + case "_to": result.To = "" case "data": result.Data = map[string]interface{}{} diff --git a/gdbi/jsonpath_test.go b/gdbi/traveler_doc_test.go similarity index 85% rename from gdbi/jsonpath_test.go rename to gdbi/traveler_doc_test.go index a3e05d9a..6673f6a5 100644 --- a/gdbi/jsonpath_test.go +++ b/gdbi/traveler_doc_test.go @@ -4,7 +4,8 @@ import ( "os" "testing" - "github.com/bmeg/grip/travelerpath" + "github.com/bmeg/grip/gdbi/tpath" + "github.com/stretchr/testify/assert" ) @@ -44,68 +45,68 @@ func TestMain(m *testing.M) { func TestGetNamespace(t *testing.T) { expected := "foo" - result := travelerpath.GetNamespace("$foo.bar[1:3].baz") + result := tpath.GetNamespace("$foo.bar[1:3].baz") assert.Equal(t, expected, result) - result = travelerpath.GetNamespace("foo.bar[1:3].baz") + result = tpath.GetNamespace("foo.bar[1:3].baz") assert.NotEqual(t, expected, result) } func TestGetJSONPath(t *testing.T) { - expected := "$.data.a" - result := travelerpath.GetJSONPath("a") + expected := "$_current.a" + result := tpath.NormalizePath("a") assert.Equal(t, expected, result) - expected = "$.data.a" - result = travelerpath.GetJSONPath("_data.a") + expected = "$_current.a" + result = tpath.NormalizePath("$.a") assert.Equal(t, expected, result) - expected = "$.data.e[1].nested" - result = travelerpath.GetJSONPath("e[1].nested") + expected = "$_current.e[1].nested" + result = tpath.NormalizePath("e[1].nested") assert.Equal(t, expected, result) - expected = "$.data.a" - result = travelerpath.GetJSONPath("$testMark.a") + expected = "$testMark.a" + result = tpath.NormalizePath("$testMark.a") assert.Equal(t, expected, result) - expected = "$.data.a" - result = travelerpath.GetJSONPath("testMark.a") - assert.NotEqual(t, expected, result) + expected = "$_current.testMark.a" + result = tpath.NormalizePath("testMark.a") + assert.Equal(t, expected, result) } -func TestGetDoc(t *testing.T) { +func TestGetMarkDoc(t *testing.T) { expected := traveler.GetMark("testMark").Get().ToDict() - result := GetDoc(traveler, "testMark") + result := TravelerGetMarkDoc(traveler, "testMark") assert.Equal(t, expected, result) expected = traveler.GetMark("i-dont-exist").Get().ToDict() - result = GetDoc(traveler, "i-dont-exist") + result = TravelerGetMarkDoc(traveler, "i-dont-exist") assert.Equal(t, expected, result) expected = traveler.GetCurrent().Get().ToDict() - result = GetDoc(traveler, travelerpath.Current) + result = TravelerGetMarkDoc(traveler, tpath.CURRENT) assert.Equal(t, expected, result) } func TestTravelerPathExists(t *testing.T) { assert.True(t, TravelerPathExists(traveler, "_gid")) + assert.True(t, TravelerPathExists(traveler, "$_gid")) assert.True(t, TravelerPathExists(traveler, "_label")) assert.True(t, TravelerPathExists(traveler, "a")) - assert.True(t, TravelerPathExists(traveler, "_data.a")) + assert.True(t, TravelerPathExists(traveler, "$a")) + assert.True(t, TravelerPathExists(traveler, "$_current.a")) assert.False(t, TravelerPathExists(traveler, "non-existent")) - assert.False(t, TravelerPathExists(traveler, "_data.non-existent")) + assert.False(t, TravelerPathExists(traveler, "$_current.non-existent")) assert.True(t, TravelerPathExists(traveler, "$testMark._gid")) assert.True(t, TravelerPathExists(traveler, "$testMark._label")) assert.True(t, TravelerPathExists(traveler, "$testMark.a")) - assert.True(t, TravelerPathExists(traveler, "$testMark._data.a")) assert.False(t, TravelerPathExists(traveler, "$testMark.non-existent")) - assert.False(t, TravelerPathExists(traveler, "$testMark._data.non-existent")) } func TestRender(t *testing.T) { expected := traveler.GetCurrent().Get().Data["a"] - result := RenderTraveler(traveler, "a") + result := RenderTraveler(traveler, "$.a") assert.Equal(t, expected, result) expected = []interface{}{ @@ -141,15 +142,15 @@ func TestRender(t *testing.T) { "current.a": "a", "current.b": "b", "current.c": "c", - "current.d": "_data.d", + "current.d": "d", "mark.gid": "$testMark._gid", "mark.label": "$testMark._label", "mark.a": "$testMark.a", "mark.b": "$testMark.b", - "mark.c": "$testMark._data.c", + "mark.c": "$testMark.c", "mark.d": "$testMark.d", "mark.d[0]": "$testMark.d[0]", - "current.e[0].nested": "_data.e[0].nested", + "current.e[0].nested": "e[0].nested", "current.e.nested": "e.nested", "current.f": "f", }) diff --git a/gripql/inspect/inspect.go b/gripql/inspect/inspect.go index 64ebda44..a2212a30 100644 --- a/gripql/inspect/inspect.go +++ b/gripql/inspect/inspect.go @@ -3,9 +3,10 @@ package inspect import ( "fmt" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" "github.com/bmeg/grip/log" - "github.com/bmeg/grip/travelerpath" + "github.com/bmeg/grip/util/protoutil" ) @@ -99,8 +100,11 @@ func PipelineStepOutputs(stmts []*gripql.GraphStatement) map[string][]string { case *gripql.GraphStatement_Render: val := gs.GetRender().AsInterface() - names := travelerpath.GetAllNamespaces(val) + names := tpath.GetAllNamespaces(val) for _, n := range names { + if n == tpath.CURRENT { + out[steps[i]] = []string{"*"} + } if a, ok := asMap[n]; ok { out[a] = []string{"*"} } @@ -111,8 +115,8 @@ func PipelineStepOutputs(stmts []*gripql.GraphStatement) map[string][]string { //if there is a distinct step, we need to load data, but only for requested fields fields := protoutil.AsStringList(gs.GetDistinct()) for _, f := range fields { - n := travelerpath.GetNamespace(f) - if n == travelerpath.Current { + n := tpath.GetNamespace(f) + if n == tpath.CURRENT { out[steps[i]] = []string{"*"} } if a, ok := asMap[n]; ok { diff --git a/gripql/javascript/gripql.js b/gripql/javascript/gripql.js index 1a4279c5..0fe9789b 100644 --- a/gripql/javascript/gripql.js +++ b/gripql/javascript/gripql.js @@ -263,6 +263,23 @@ function count(name) { } } +function field(name, field){ + return { + "name": name, + "field": { + "field": field + } + } +} + +gripql = { + "lt" : lt, + "gt" : gt, + "lte" : lte, + "gte" : gte, + "eq" : eq, +} + function V(id) { return query().V(id) } diff --git a/kvgraph/index.go b/kvgraph/index.go index 5023da89..6707ffca 100644 --- a/kvgraph/index.go +++ b/kvgraph/index.go @@ -7,7 +7,6 @@ import ( "github.com/bmeg/grip/gripql" "github.com/bmeg/grip/log" - "github.com/bmeg/grip/travelerpath" ) func (kgraph *KVGraph) setupGraphIndex(graph string) error { @@ -33,7 +32,7 @@ func (kgraph *KVGraph) deleteGraphIndex(graph string) { } func normalizePath(path string) string { - path = travelerpath.GetJSONPath(path) + //path = travelerpath.GetJSONPath(path) path = strings.TrimPrefix(path, "$.") path = strings.TrimPrefix(path, "data.") return path diff --git a/mongo/compile.go b/mongo/compile.go index 770770e5..2fe95585 100644 --- a/mongo/compile.go +++ b/mongo/compile.go @@ -6,9 +6,9 @@ import ( "github.com/bmeg/grip/engine/core" "github.com/bmeg/grip/gdbi" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" "github.com/bmeg/grip/log" - "github.com/bmeg/grip/travelerpath" "github.com/bmeg/grip/util/protoutil" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" @@ -538,7 +538,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile hasKeys := bson.M{} keys := protoutil.AsStringList(stmt.HasKey) for _, key := range keys { - key = travelerpath.GetJSONPath(key) + key = tpath.NormalizePath(key) key = strings.TrimPrefix(key, "$.") hasKeys[key] = bson.M{"$exists": true} } @@ -576,13 +576,13 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile keys := bson.M{} match := bson.M{} for _, f := range fields { - namespace := travelerpath.GetNamespace(f) - f = travelerpath.GetJSONPath(f) - f = strings.TrimPrefix(f, "$.") + namespace := tpath.GetNamespace(f) + f = tpath.NormalizePath(f) + f = strings.TrimPrefix(f, "$.") //FIXME if f == "gid" { f = "_id" } - if namespace != travelerpath.Current { + if namespace != tpath.CURRENT { f = fmt.Sprintf("marks.%s.%s", namespace, f) } match[f] = bson.M{"$exists": true} @@ -630,8 +630,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile if err := gripql.ValidateFieldName(stmt.As); err != nil { return &Pipeline{}, fmt.Errorf(`"as" statement invalid; %v`, err) } - if stmt.As == travelerpath.Current { - return &Pipeline{}, fmt.Errorf(`"as" statement invalid; uses reserved name %s`, travelerpath.Current) + if stmt.As == tpath.CURRENT { + return &Pipeline{}, fmt.Errorf(`"as" statement invalid; uses reserved name %s`, tpath.CURRENT) } markTypes[stmt.As] = lastType query = append(query, bson.D{primitive.E{Key: "$addFields", Value: bson.M{"marks": bson.M{stmt.As: "$$ROOT"}}}}) @@ -698,13 +698,13 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile exclude = true f = strings.TrimPrefix(f, "-") } - namespace := travelerpath.GetNamespace(f) - if namespace != travelerpath.Current { + namespace := tpath.GetNamespace(f) + if namespace != tpath.CURRENT { log.Errorf("FieldsProcessor: only can select field from current traveler") continue SelectLoop } - f = travelerpath.GetJSONPath(f) - f = strings.TrimPrefix(f, "$.") + f = tpath.NormalizePath(f) + f = strings.TrimPrefix(f, "$.") //FIXME if exclude { excludeFields = append(excludeFields, f) } else { @@ -753,8 +753,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile switch a.Aggregation.(type) { case *gripql.Aggregate_Term: agg := a.GetTerm() - field := travelerpath.GetJSONPath(agg.Field) - field = strings.TrimPrefix(field, "$.") + field := tpath.NormalizePath(agg.Field) + field = strings.TrimPrefix(field, "$.") //FIXME if field == "gid" { field = "_id" } @@ -776,8 +776,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile case *gripql.Aggregate_Histogram: agg := a.GetHistogram() - field := travelerpath.GetJSONPath(agg.Field) - field = strings.TrimPrefix(field, "$.") + field := tpath.NormalizePath(agg.Field) + field = strings.TrimPrefix(field, "$.") //FIXME stmt := []bson.M{ { "$match": bson.M{ @@ -801,7 +801,7 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile case *gripql.Aggregate_Percentile: agg := a.GetPercentile() - field := travelerpath.GetJSONPath(agg.Field) + field := tpath.NormalizePath(agg.Field) field = strings.TrimPrefix(field, "$.") stmt := []bson.M{ { @@ -835,8 +835,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile case *gripql.Aggregate_Type: agg := a.GetType() - field := travelerpath.GetJSONPath(agg.Field) - field = strings.TrimPrefix(field, "$.") + field := tpath.NormalizePath(agg.Field) + field = strings.TrimPrefix(field, "$.") //FIXME stmt := []bson.M{ { "$match": bson.M{ @@ -861,8 +861,8 @@ func (comp *Compiler) Compile(stmts []*gripql.GraphStatement, opts *gdbi.Compile case *gripql.Aggregate_Field: agg := a.GetField() - field := travelerpath.GetJSONPath(agg.Field) - field = strings.TrimPrefix(field, "$.") + field := tpath.NormalizePath(agg.Field) + field = strings.TrimPrefix(field, "$.") //FIXME stmt := []bson.M{ { "$match": bson.M{ diff --git a/mongo/compile_test.go b/mongo/compile_test.go index b3bf2c6f..486fab93 100644 --- a/mongo/compile_test.go +++ b/mongo/compile_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" - "github.com/bmeg/grip/travelerpath" "github.com/bmeg/grip/util" "go.mongodb.org/mongo-driver/bson" ) @@ -37,14 +37,14 @@ func TestDistinctPathing(t *testing.T) { keys := bson.M{} for _, f := range fields { - namespace := travelerpath.GetNamespace(f) + namespace := tpath.GetNamespace(f) fmt.Printf("Namespace: %s\n", namespace) - f = travelerpath.GetJSONPath(f) + f = tpath.NormalizePath(f) f = strings.TrimPrefix(f, "$.") if f == "gid" { f = FIELD_ID } - if namespace != travelerpath.Current { + if namespace != tpath.CURRENT { f = fmt.Sprintf("marks.%s.%s", namespace, f) } match[f] = bson.M{"$exists": true} diff --git a/mongo/has_evaluator.go b/mongo/has_evaluator.go index 5e6dbc22..0e804eff 100644 --- a/mongo/has_evaluator.go +++ b/mongo/has_evaluator.go @@ -1,11 +1,12 @@ package mongo import ( + "fmt" "strings" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" "github.com/bmeg/grip/log" - "github.com/bmeg/grip/travelerpath" "go.mongodb.org/mongo-driver/bson" ) @@ -80,11 +81,12 @@ func convertHasExpression(stmt *gripql.HasExpression, not bool) bson.M { } func convertPath(key string) string { - key = travelerpath.GetJSONPath(key) + key = tpath.NormalizePath(key) key = strings.TrimPrefix(key, "$.") if key == "gid" { key = "_id" } + fmt.Printf("Key: %s\n", key) return key } diff --git a/mongo/index.go b/mongo/index.go index 55e7a9ff..91eddd61 100644 --- a/mongo/index.go +++ b/mongo/index.go @@ -5,9 +5,9 @@ import ( "fmt" "strings" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" "github.com/bmeg/grip/log" - "github.com/bmeg/grip/travelerpath" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" @@ -16,8 +16,8 @@ import ( // AddVertexIndex add index to vertices func (mg *Graph) AddVertexIndex(label string, field string) error { log.WithFields(log.Fields{"label": label, "field": field}).Info("Adding vertex index") - field = travelerpath.GetJSONPath(field) - field = strings.TrimPrefix(field, "$.") + field = tpath.NormalizePath(field) + field = strings.TrimPrefix(field, "$.") //FIXME idx := mg.ar.VertexCollection(mg.graph).Indexes() @@ -36,8 +36,8 @@ func (mg *Graph) AddVertexIndex(label string, field string) error { // DeleteVertexIndex delete index from vertices func (mg *Graph) DeleteVertexIndex(label string, field string) error { log.WithFields(log.Fields{"label": label, "field": field}).Info("Deleting vertex index") - field = travelerpath.GetJSONPath(field) - field = strings.TrimPrefix(field, "$.") + field = tpath.NormalizePath(field) + field = strings.TrimPrefix(field, "$.") //FIXME idx := mg.ar.VertexCollection(mg.graph).Indexes() cursor, err := idx.List(context.TODO()) From 610803b7d868eab4384d4affdc5c2cfd46f7749f Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Thu, 15 Feb 2024 23:05:03 -0800 Subject: [PATCH 7/9] Fixing more conformance testing issues --- conformance/tests/ot_fields.py | 2 +- gdbi/traveler_doc.go | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/conformance/tests/ot_fields.py b/conformance/tests/ot_fields.py index 8ad06aa0..134e5ebd 100644 --- a/conformance/tests/ot_fields.py +++ b/conformance/tests/ot_fields.py @@ -11,7 +11,7 @@ def test_fields(man): } resp = G.query().V("Character:1").fields(["name"]).execute() if resp[0] != expected: - errors.append("vertex contains incorrect fields: \nexpected:%s\nresponse:%s" % (expected, resp)) + errors.append("""Query 'V("Character:1").fields(["name"])' vertex contains incorrect fields: \nexpected:%s\nresponse:%s""" % (expected, resp)) expected = { u"gid": u"Character:1", diff --git a/gdbi/traveler_doc.go b/gdbi/traveler_doc.go index f59c7f5a..2a6c5f3c 100644 --- a/gdbi/traveler_doc.go +++ b/gdbi/traveler_doc.go @@ -137,12 +137,12 @@ KeyLoop: continue KeyLoop } path := tpath.NormalizePath(key) - path = strings.TrimPrefix(path, "$.") //FIXME - + jpath := tpath.ToLocalPath(path) + spath := strings.TrimPrefix(jpath, "$.") if exclude { - excludePaths = append(excludePaths, path) + excludePaths = append(excludePaths, spath) } else { - includePaths = append(includePaths, path) + includePaths = append(includePaths, spath) } } @@ -186,10 +186,6 @@ Include: switch path { case "_gid", "_label", "_from", "_to": // noop - case "data": - for k, v := range old.Data { - newData[k] = v - } default: parts := strings.Split(path, ".") var data map[string]interface{} From eaef88e412262c2f7dafa0f41191aa890eb8f0f6 Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Fri, 16 Feb 2024 20:52:35 -0800 Subject: [PATCH 8/9] Fixing various issues to get the key/value graph store to work --- engine/core/processors.go | 5 +- gdbi/data_element.go | 144 ++++++++++++++++++++++++++++++++++++++ gdbi/interface.go | 3 + gdbi/tpath/fields.go | 5 ++ gdbi/traveler.go | 120 +++---------------------------- gdbi/traveler_doc.go | 36 ++++++++-- 6 files changed, 196 insertions(+), 117 deletions(-) create mode 100644 gdbi/data_element.go create mode 100644 gdbi/tpath/fields.go diff --git a/engine/core/processors.go b/engine/core/processors.go index 8aead1f2..c9c851c8 100644 --- a/engine/core/processors.go +++ b/engine/core/processors.go @@ -10,6 +10,7 @@ import ( "github.com/bmeg/grip/engine/logic" "github.com/bmeg/grip/gdbi" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/gripql" "github.com/bmeg/grip/log" "github.com/bmeg/grip/util/copy" @@ -1106,7 +1107,9 @@ func (agg *aggregate) Process(ctx context.Context, man gdbi.Manager, in gdbi.InP val := gdbi.TravelerPathLookup(t, fa.Field) if m, ok := val.(map[string]interface{}); ok { for k := range m { - fieldCounts[k]++ + if !tpath.IsGraphField(k) { + fieldCounts[k]++ + } } } } diff --git a/gdbi/data_element.go b/gdbi/data_element.go new file mode 100644 index 00000000..b3fca12f --- /dev/null +++ b/gdbi/data_element.go @@ -0,0 +1,144 @@ +package gdbi + +import ( + "errors" + "fmt" + + "github.com/bmeg/grip/gripql" + "google.golang.org/protobuf/types/known/structpb" +) + +// ToVertex converts data element to vertex +func (elem *DataElement) ToVertex() *gripql.Vertex { + sValue, err := structpb.NewStruct(elem.Data) + if err != nil { + fmt.Printf("Error: %s %#v\n", err, elem.Data) + } + return &gripql.Vertex{ + Gid: elem.ID, + Label: elem.Label, + Data: sValue, + } +} + +// ToEdge converts data element to edge +func (elem *DataElement) ToEdge() *gripql.Edge { + sValue, _ := structpb.NewStruct(elem.Data) + return &gripql.Edge{ + Gid: elem.ID, + From: elem.From, + To: elem.To, + Label: elem.Label, + Data: sValue, + } +} + +// ToDict converts data element to generic map +func (elem *DataElement) ToDict() map[string]interface{} { + /* + out := map[string]interface{}{ + "gid": "", + "label": "", + "to": "", + "from": "", + "data": map[string]interface{}{}, + } + */ + out := map[string]interface{}{} + if elem == nil { + return out + } + for k, v := range elem.Data { + out[k] = v + } + if elem.ID != "" { + out["_gid"] = elem.ID + } + if elem.Label != "" { + out["_label"] = elem.Label + } + if elem.To != "" { + out["_to"] = elem.To + } + if elem.From != "" { + out["_from"] = elem.From + } + return out +} + +func (elem *DataElement) FromDict(d map[string]any) { + if elem.Data == nil { + elem.Data = map[string]any{} + } + for k, v := range d { + switch k { + case "_to": + if vStr, ok := v.(string); ok { + elem.To = vStr + } + case "_from": + if vStr, ok := v.(string); ok { + elem.From = vStr + } + case "_gid": + if vStr, ok := v.(string); ok { + elem.ID = vStr + } + case "_label": + if vStr, ok := v.(string); ok { + elem.Label = vStr + } + default: + elem.Data[k] = v + } + } + elem.Loaded = true +} + +// Validate returns an error if the vertex is invalid +func (vertex *Vertex) Validate() error { + if vertex.ID == "" { + return errors.New("'gid' cannot be blank") + } + if vertex.Label == "" { + return errors.New("'label' cannot be blank") + } + for k := range vertex.Data { + err := gripql.ValidateFieldName(k) + if err != nil { + return err + } + } + return nil +} + +func NewGraphElement(g *gripql.GraphElement) *GraphElement { + o := GraphElement{Graph: g.Graph} + if g.Vertex != nil { + o.Vertex = NewElementFromVertex(g.Vertex) + } + if g.Edge != nil { + o.Edge = NewElementFromEdge(g.Edge) + } + return &o +} + +func NewElementFromVertex(v *gripql.Vertex) *Vertex { + return &Vertex{ + ID: v.Gid, + Label: v.Label, + Data: v.Data.AsMap(), + Loaded: true, + } +} + +func NewElementFromEdge(e *gripql.Edge) *Edge { + return &Edge{ + ID: e.Gid, + Label: e.Label, + To: e.To, + From: e.From, + Data: e.Data.AsMap(), + Loaded: true, + } +} diff --git a/gdbi/interface.go b/gdbi/interface.go index 331392d5..08384fbc 100644 --- a/gdbi/interface.go +++ b/gdbi/interface.go @@ -98,7 +98,10 @@ type Traveler interface { Copy() Traveler HasMark(label string) bool GetMark(label string) DataRef + // AddMark adds a new mark to the data and return a duplicated Traveler AddMark(label string, r DataRef) Traveler + // UpdateMark changes the data of a mark in the original traveler (vs AddMark which changes a copy of the traveler) + UpdateMark(label string, r DataRef) ListMarks() []string GetSelections() map[string]DataRef GetRender() interface{} diff --git a/gdbi/tpath/fields.go b/gdbi/tpath/fields.go new file mode 100644 index 00000000..f6cd6c4e --- /dev/null +++ b/gdbi/tpath/fields.go @@ -0,0 +1,5 @@ +package tpath + +func IsGraphField(f string) bool { + return f == "_gid" || f == "_label" || f == "_to" || f == "_from" +} diff --git a/gdbi/traveler.go b/gdbi/traveler.go index af7dfc8c..b5162e13 100644 --- a/gdbi/traveler.go +++ b/gdbi/traveler.go @@ -1,12 +1,8 @@ package gdbi import ( - "errors" - "fmt" - - "github.com/bmeg/grip/gripql" + "github.com/bmeg/grip/gdbi/tpath" "github.com/bmeg/grip/util/copy" - "google.golang.org/protobuf/types/known/structpb" ) // These consts mark the type of a Pipeline traveler chan @@ -120,6 +116,14 @@ func (t *BaseTraveler) AddMark(label string, r DataRef) Traveler { return &o } +func (t *BaseTraveler) UpdateMark(label string, r DataRef) { + if label == tpath.CURRENT { + t.Current = r.Get() + return + } + t.Marks[label] = r.Get() +} + // GetMark gets stored result in travels state using its label func (t *BaseTraveler) GetMark(label string) DataRef { return t.Marks[label] @@ -157,109 +161,3 @@ func (t *BaseTraveler) GetPath() []DataElementID { func (t BaseTraveler) GetAggregation() *Aggregate { return t.Aggregation } - -func NewElementFromVertex(v *gripql.Vertex) *Vertex { - return &Vertex{ - ID: v.Gid, - Label: v.Label, - Data: v.Data.AsMap(), - Loaded: true, - } -} - -func NewElementFromEdge(e *gripql.Edge) *Edge { - return &Edge{ - ID: e.Gid, - Label: e.Label, - To: e.To, - From: e.From, - Data: e.Data.AsMap(), - Loaded: true, - } -} - -// ToVertex converts data element to vertex -func (elem *DataElement) ToVertex() *gripql.Vertex { - sValue, err := structpb.NewStruct(elem.Data) - if err != nil { - fmt.Printf("Error: %s %#v\n", err, elem.Data) - } - return &gripql.Vertex{ - Gid: elem.ID, - Label: elem.Label, - Data: sValue, - } -} - -// ToEdge converts data element to edge -func (elem *DataElement) ToEdge() *gripql.Edge { - sValue, _ := structpb.NewStruct(elem.Data) - return &gripql.Edge{ - Gid: elem.ID, - From: elem.From, - To: elem.To, - Label: elem.Label, - Data: sValue, - } -} - -// ToDict converts data element to generic map -func (elem *DataElement) ToDict() map[string]interface{} { - /* - out := map[string]interface{}{ - "gid": "", - "label": "", - "to": "", - "from": "", - "data": map[string]interface{}{}, - } - */ - out := map[string]interface{}{} - if elem == nil { - return out - } - for k, v := range elem.Data { - out[k] = v - } - if elem.ID != "" { - out["_gid"] = elem.ID - } - if elem.Label != "" { - out["_label"] = elem.Label - } - if elem.To != "" { - out["_to"] = elem.To - } - if elem.From != "" { - out["_from"] = elem.From - } - return out -} - -// Validate returns an error if the vertex is invalid -func (vertex *Vertex) Validate() error { - if vertex.ID == "" { - return errors.New("'gid' cannot be blank") - } - if vertex.Label == "" { - return errors.New("'label' cannot be blank") - } - for k := range vertex.Data { - err := gripql.ValidateFieldName(k) - if err != nil { - return err - } - } - return nil -} - -func NewGraphElement(g *gripql.GraphElement) *GraphElement { - o := GraphElement{Graph: g.Graph} - if g.Vertex != nil { - o.Vertex = NewElementFromVertex(g.Vertex) - } - if g.Edge != nil { - o.Edge = NewElementFromEdge(g.Edge) - } - return &o -} diff --git a/gdbi/traveler_doc.go b/gdbi/traveler_doc.go index 2a6c5f3c..9e414934 100644 --- a/gdbi/traveler_doc.go +++ b/gdbi/traveler_doc.go @@ -91,22 +91,48 @@ func TravelerPathLookup(traveler Traveler, path string) interface{} { func TravelerSetValue(traveler Traveler, path string, val interface{}) error { field := tpath.NormalizePath(path) namespace := tpath.GetNamespace(field) + jpath := tpath.ToLocalPath(field) if field == "" { return nil } - doc := TravelerGetDoc(traveler, namespace) - return jsonpath.JsonPathSet(doc, field, val) + doc := TravelerGetMarkDoc(traveler, namespace) + err := jsonpath.JsonPathSet(doc, jpath, val) + if err != nil { + return err + } + r := DataElement{} + r.FromDict(doc) + traveler.UpdateMark(namespace, &r) + return nil } +/* +func TravelerSetMarkDoc(traveler Traveler, ns string, doc map[string]any ) error { + + d = DataElement{} + + + if ns == tpath.CURRENT { + return traveler.GetCurrent().Get().ToDict() + } + m := traveler.GetMark(ns) + if m != nil { + return m.Get().ToDict() + } + return nil +} +*/ + // TravelerPathExists returns true if the field exists in the given Traveler func TravelerPathExists(traveler Traveler, path string) bool { field := tpath.NormalizePath(path) + jpath := tpath.ToLocalPath(field) namespace := tpath.GetNamespace(field) - if field == "" { + if jpath == "" { return false } - doc := TravelerGetDoc(traveler, namespace) - _, err := jsonpath.JsonPathLookup(doc, field) + doc := TravelerGetMarkDoc(traveler, namespace) + _, err := jsonpath.JsonPathLookup(doc, jpath) return err == nil } From f52dce8f86194bdb91c716dc4f16a2db2fcecafe Mon Sep 17 00:00:00 2001 From: Kyle Ellrott Date: Fri, 16 Feb 2024 21:25:53 -0800 Subject: [PATCH 9/9] Fixing small issues around gripper driver testing --- conformance/tests/ot_aggregations.py | 3 ++- gripper/test-graph/test_gripper.py | 2 +- server/server.go | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/conformance/tests/ot_aggregations.py b/conformance/tests/ot_aggregations.py index e5809f53..2b9b42c5 100644 --- a/conformance/tests/ot_aggregations.py +++ b/conformance/tests/ot_aggregations.py @@ -190,7 +190,8 @@ def test_traversal_gid_aggregation(man): def test_field_aggregation(man): errors = [] - fields = [ "_gid", "_label", 'orbital_period', 'gravity', 'terrain', 'name','climate', 'system', 'diameter', 'rotation_period', 'url', 'population', 'surface_water'] + # TODO: find way to get gripper driver to drop id field + fields = [ "id", "_gid", "_label", 'orbital_period', 'gravity', 'terrain', 'name','climate', 'system', 'diameter', 'rotation_period', 'url', 'population', 'surface_water'] G = man.setGraph("swapi") count = 0 diff --git a/gripper/test-graph/test_gripper.py b/gripper/test-graph/test_gripper.py index 6ee4e464..c6c29b7c 100644 --- a/gripper/test-graph/test_gripper.py +++ b/gripper/test-graph/test_gripper.py @@ -39,7 +39,7 @@ def test_plugin_start(self): self.assertTrue(found) with open(os.path.join(BASE, "test-graph/swapi.yaml")) as handle: - mappingGraph = yaml.load(handle.read()) + mappingGraph = yaml.load(handle.read(), Loader=yaml.BaseLoader) conn = gripql.Connection(SERVER) graphName = "posted_tabledata_%s" % (''.join(random.choices(string.ascii_uppercase + string.digits, k=4))) conn.postMapping(graphName, mappingGraph['vertices'], mappingGraph['edges']) diff --git a/server/server.go b/server/server.go index a693efa7..dede2cd3 100644 --- a/server/server.go +++ b/server/server.go @@ -86,6 +86,8 @@ func NewGripServer(conf *config.Config, baseDir string, drivers map[string]gdbi. g, err := StartDriver(dConfig, sources) if err == nil { gdbs[name] = g + } else { + log.Errorf("Driver start error: %s", err) } } }