diff --git a/query/pb/graphresponse.pb.go b/query/pb/graphresponse.pb.go index 75212c7fb9da34453e5d1ca815a2ae6c4339479e..2fd972a71b0eac8ffc8b8f4081bc82d4f4722c92 100644 --- a/query/pb/graphresponse.pb.go +++ b/query/pb/graphresponse.pb.go @@ -9,8 +9,6 @@ It is generated from these files: graphresponse.proto It has these top-level messages: - UidList - Result GraphRequest GraphResponse */ @@ -34,32 +32,6 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. const _ = proto.ProtoPackageIsVersion1 -type UidList struct { - Uids []uint64 `protobuf:"varint,1,rep,name=uids" json:"uids,omitempty"` -} - -func (m *UidList) Reset() { *m = UidList{} } -func (m *UidList) String() string { return proto.CompactTextString(m) } -func (*UidList) ProtoMessage() {} -func (*UidList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } - -type Result struct { - Values [][]byte `protobuf:"bytes,1,rep,name=values,proto3" json:"values,omitempty"` - Uidmatrix []*UidList `protobuf:"bytes,2,rep,name=uidmatrix" json:"uidmatrix,omitempty"` -} - -func (m *Result) Reset() { *m = Result{} } -func (m *Result) String() string { return proto.CompactTextString(m) } -func (*Result) ProtoMessage() {} -func (*Result) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } - -func (m *Result) GetUidmatrix() []*UidList { - if m != nil { - return m.Uidmatrix - } - return nil -} - type GraphRequest struct { Query string `protobuf:"bytes,1,opt,name=query" json:"query,omitempty"` } @@ -67,30 +39,22 @@ type GraphRequest struct { func (m *GraphRequest) Reset() { *m = GraphRequest{} } func (m *GraphRequest) String() string { return proto.CompactTextString(m) } func (*GraphRequest) ProtoMessage() {} -func (*GraphRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (*GraphRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } type GraphResponse struct { - Attribute string `protobuf:"bytes,1,opt,name=attribute" json:"attribute,omitempty"` - Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"` - Query *UidList `protobuf:"bytes,3,opt,name=query" json:"query,omitempty"` - Children []*GraphResponse `protobuf:"bytes,4,rep,name=children" json:"children,omitempty"` + Attribute string `protobuf:"bytes,1,opt,name=attribute" json:"attribute,omitempty"` + Values map[string][]byte `protobuf:"bytes,2,rep,name=values" json:"values,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value,proto3"` + Children []*GraphResponse `protobuf:"bytes,3,rep,name=children" json:"children,omitempty"` } func (m *GraphResponse) Reset() { *m = GraphResponse{} } func (m *GraphResponse) String() string { return proto.CompactTextString(m) } func (*GraphResponse) ProtoMessage() {} -func (*GraphResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } - -func (m *GraphResponse) GetResult() *Result { - if m != nil { - return m.Result - } - return nil -} +func (*GraphResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } -func (m *GraphResponse) GetQuery() *UidList { +func (m *GraphResponse) GetValues() map[string][]byte { if m != nil { - return m.Query + return m.Values } return nil } @@ -103,8 +67,6 @@ func (m *GraphResponse) GetChildren() []*GraphResponse { } func init() { - proto.RegisterType((*UidList)(nil), "pb.UidList") - proto.RegisterType((*Result)(nil), "pb.Result") proto.RegisterType((*GraphRequest)(nil), "pb.GraphRequest") proto.RegisterType((*GraphResponse)(nil), "pb.GraphResponse") } @@ -181,22 +143,20 @@ var _DGraph_serviceDesc = grpc.ServiceDesc{ } var fileDescriptor0 = []byte{ - // 265 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x64, 0x90, 0x4d, 0x4e, 0xc3, 0x30, - 0x10, 0x85, 0x49, 0x9b, 0x1a, 0x32, 0x2d, 0x12, 0x0c, 0x08, 0x45, 0x08, 0x24, 0xb0, 0x58, 0x94, - 0x05, 0x59, 0x94, 0x0d, 0x07, 0x40, 0x62, 0x01, 0x1b, 0x2c, 0x71, 0x80, 0x84, 0x58, 0xd4, 0x52, - 0x68, 0x8c, 0x7f, 0x10, 0x9c, 0x87, 0x8b, 0xe2, 0x8c, 0x0d, 0xa5, 0x62, 0x37, 0x93, 0xf7, 0xe5, - 0xbd, 0x37, 0x86, 0x83, 0x17, 0x53, 0xeb, 0xa5, 0x91, 0x56, 0xf7, 0x2b, 0x2b, 0x2b, 0x6d, 0x7a, - 0xd7, 0xe3, 0x48, 0x37, 0xfc, 0x14, 0xb6, 0x9f, 0x54, 0xfb, 0xa0, 0xac, 0x43, 0x84, 0xdc, 0xab, - 0xd6, 0x96, 0xd9, 0xd9, 0x78, 0x9e, 0x0b, 0x9a, 0xf9, 0x3d, 0x30, 0x21, 0xad, 0xef, 0x1c, 0x1e, - 0x01, 0x7b, 0xaf, 0x3b, 0x2f, 0xa3, 0x3e, 0x13, 0x69, 0xc3, 0x4b, 0x28, 0x02, 0xf9, 0x5a, 0x3b, - 0xa3, 0x3e, 0xca, 0x51, 0x90, 0xa6, 0x8b, 0x69, 0xa5, 0x9b, 0x2a, 0xb9, 0x8a, 0xb5, 0xca, 0x2f, - 0x60, 0x76, 0x37, 0xd4, 0x10, 0xf2, 0x2d, 0xfc, 0xe9, 0xf0, 0x10, 0x26, 0x61, 0x30, 0x9f, 0xc1, - 0x31, 0x9b, 0x17, 0x22, 0x2e, 0xfc, 0x2b, 0x83, 0xdd, 0x84, 0xc5, 0xb6, 0x78, 0x02, 0x45, 0xed, - 0x82, 0x43, 0xe3, 0x9d, 0x4c, 0xec, 0xfa, 0x03, 0x72, 0x60, 0x86, 0x2a, 0x86, 0xf4, 0x2c, 0xa4, - 0xc3, 0x90, 0x1e, 0x4b, 0x8b, 0xa4, 0xe0, 0xf9, 0x4f, 0xd2, 0x98, 0x90, 0x8d, 0x82, 0x51, 0xc1, - 0x2b, 0xd8, 0x79, 0x5e, 0xaa, 0xae, 0x35, 0x72, 0x55, 0xe6, 0x74, 0xc6, 0xfe, 0x40, 0x6d, 0x34, - 0x11, 0xbf, 0xc8, 0xe2, 0x06, 0xd8, 0x2d, 0x69, 0x58, 0xc1, 0xe4, 0x91, 0x1c, 0xf6, 0xfe, 0xf0, - 0x74, 0xe0, 0xf1, 0x7f, 0x07, 0xbe, 0xd5, 0x30, 0x7a, 0xfc, 0xeb, 0xef, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x18, 0x87, 0x57, 0xd8, 0x93, 0x01, 0x00, 0x00, + // 227 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0x2f, 0x4a, 0x2c, + 0xc8, 0x28, 0x4a, 0x2d, 0x2e, 0xc8, 0xcf, 0x2b, 0x4e, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, + 0x62, 0x2a, 0x48, 0x52, 0x52, 0xe1, 0xe2, 0x71, 0x07, 0x49, 0x05, 0xa5, 0x16, 0x96, 0xa6, 0x16, + 0x97, 0x08, 0x89, 0x70, 0xb1, 0x02, 0x19, 0x45, 0x95, 0x12, 0x8c, 0x0a, 0x8c, 0x1a, 0x9c, 0x41, + 0x10, 0x8e, 0xd2, 0x39, 0x46, 0x2e, 0x5e, 0xa8, 0x32, 0x88, 0x09, 0x42, 0x32, 0x5c, 0x9c, 0x89, + 0x25, 0x25, 0x45, 0x99, 0x49, 0xa5, 0x25, 0xa9, 0x50, 0xb5, 0x08, 0x01, 0x21, 0x53, 0x2e, 0xb6, + 0xb2, 0xc4, 0x1c, 0xa0, 0x81, 0x12, 0x4c, 0x0a, 0xcc, 0x1a, 0xdc, 0x46, 0xb2, 0x7a, 0x05, 0x49, + 0x7a, 0x28, 0x06, 0xe8, 0x85, 0x81, 0xe5, 0x5d, 0xf3, 0x4a, 0x8a, 0x2a, 0x83, 0xa0, 0x8a, 0x85, + 0x74, 0xb9, 0x38, 0x92, 0x33, 0x32, 0x73, 0x52, 0x8a, 0x52, 0xf3, 0x24, 0x98, 0xc1, 0x1a, 0x05, + 0x31, 0x34, 0x06, 0xc1, 0x95, 0x48, 0x59, 0x72, 0x71, 0x23, 0x99, 0x22, 0x24, 0xc0, 0xc5, 0x9c, + 0x9d, 0x0a, 0x73, 0x38, 0x88, 0x09, 0xf2, 0x0c, 0xd8, 0x64, 0xa0, 0x2b, 0x18, 0x35, 0x78, 0x82, + 0x20, 0x1c, 0x2b, 0x26, 0x0b, 0x46, 0x23, 0x0b, 0x2e, 0x36, 0x17, 0xb0, 0xb1, 0x42, 0x7a, 0x5c, + 0xac, 0x81, 0x20, 0x3f, 0x0a, 0x09, 0x20, 0x59, 0x05, 0x0e, 0x0b, 0x29, 0x4c, 0xcb, 0x95, 0x18, + 0x92, 0xd8, 0xc0, 0x61, 0x67, 0x0c, 0x08, 0x00, 0x00, 0xff, 0xff, 0x30, 0x75, 0x05, 0xc3, 0x52, + 0x01, 0x00, 0x00, } diff --git a/query/pb/graphresponse.proto b/query/pb/graphresponse.proto index c87873acd8da87e319ea4771079b0a82df90a8bf..532e60c2d3fa9e6c590574c9b8a70cf4615e652d 100644 --- a/query/pb/graphresponse.proto +++ b/query/pb/graphresponse.proto @@ -8,22 +8,12 @@ service DGraph { rpc Query (GraphRequest) returns (GraphResponse) {} } -message UidList { - repeated uint64 uids = 1; -} - -message Result { - repeated bytes values = 1; - repeated UidList uidmatrix = 2; -} - message GraphRequest { string query = 1; } message GraphResponse { string attribute = 1; - Result result = 2; - UidList query = 3; - repeated GraphResponse children = 4; // Each node can have multiple children + map<string, bytes> values = 2; + repeated GraphResponse children = 3; // Each node can have multiple children } diff --git a/query/query.go b/query/query.go index 70771b4bb334ced23ecf04c2c5e48b38735b05c3..ba2713437505944e2ae72bc5e99d1cfd5c8a2aa4 100644 --- a/query/query.go +++ b/query/query.go @@ -252,59 +252,72 @@ func (g *SubGraph) ToJson(l *Latency) (js []byte, rerr error) { return json.Marshal(r) } +type result struct { + values [][]byte + uidmatrix [][]uint64 +} + // This method take in a flatbuffer result, extracts values and uids from it // and converts it to a protocol buffer result -func extract(q *task.Query, r *task.Result) (*pb.UidList, *pb.Result, error) { - result := &pb.Result{} - query := &pb.UidList{} +func extract(q *task.Query, r *task.Result) ([]uint64, *result, error) { + re := new(result) + var qu []uint64 var ul task.UidList for i := 0; i < q.UidsLength(); i++ { uid := q.Uids(i) - query.Uids = append(query.Uids, uid) + qu = append(qu, uid) } for i := 0; i < r.UidmatrixLength(); i++ { if ok := r.Uidmatrix(&ul, i); !ok { - return query, result, fmt.Errorf("While parsing UidList") + return qu, re, fmt.Errorf("While parsing UidList") } - uidList := &pb.UidList{} + var uidList []uint64 for j := 0; j < ul.UidsLength(); j++ { uid := ul.Uids(j) - uidList.Uids = append(uidList.Uids, uid) + uidList = append(uidList, uid) } - result.Uidmatrix = append(result.Uidmatrix, uidList) + re.uidmatrix = append(re.uidmatrix, uidList) } var tv task.Value for i := 0; i < r.ValuesLength(); i++ { if ok := r.Values(&tv, i); !ok { - return query, result, fmt.Errorf("While parsing value") + return qu, re, fmt.Errorf("While parsing value") } var ival interface{} if err := posting.ParseValue(&ival, tv.ValBytes()); err != nil { - return query, result, err + return qu, re, err } if ival == nil { ival = "" } - result.Values = append(result.Values, []byte(ival.(string))) + re.values = append(re.values, []byte(ival.(string))) } - return query, result, nil + return qu, re, nil +} + +// Struct to store reference to the subgraph associated with a protocol buffer +// response +type sgreference struct { + uid uint64 + sg *SubGraph } -// This method performs a pre traversal on a subgraph and converts it to a -// protocol buffer Graph Response. -func (g *SubGraph) PreTraverse() (gr *pb.GraphResponse, rerr error) { +// This method converts a subgraph to a protocol buffer response. It transforms +// the predicate based subgraph to an entity based protocol buffer subgraph. +func (g *SubGraph) ToProtocolBuffer() (gr *pb.GraphResponse, rerr error) { gr = &pb.GraphResponse{} + gr.Attribute = g.Attr + if len(g.query) == 0 { return gr, nil } - gr.Attribute = g.Attr ro := flatbuffers.GetUOffsetT(g.result) r := new(task.Result) r.Init(g.result, ro) @@ -313,24 +326,89 @@ func (g *SubGraph) PreTraverse() (gr *pb.GraphResponse, rerr error) { q := new(task.Query) q.Init(g.query, uo) - query, result, err := extract(q, r) + _, result, err := extract(q, r) if err != nil { return gr, err } - gr.Query = query - gr.Result = result + re := &sgreference{} + re.sg = g + // Stores the uid for the root node in the reference struct. + re.uid = result.uidmatrix[0][0] - for _, child := range g.Children { - childPb, err := child.PreTraverse() + gr.Values, gr.Children, rerr = re.pretraverse() + if rerr != nil { + x.Err(glog, rerr).Error("Error while traversal") + return gr, rerr + } + return gr, nil +} + +// This function performs a binary search on the uids slice and returns the +// index at which it finds the uid, else returns -1 +func indexOf(uid uint64, uids []uint64) int { + low, mid, high := 0, 0, len(uids)-1 + for low <= high { + mid = (low + high) / 2 + if uids[mid] == uid { + return mid + } else if uids[mid] > uid { + high = mid - 1 + } else { + low = mid + 1 + } + } + return -1 +} + +// This method gets the values and children for a GraphResponse. +func (re *sgreference) pretraverse() (map[string][]byte, + []*pb.GraphResponse, error) { + vals := make(map[string][]byte) + var children []*pb.GraphResponse + + for _, child := range re.sg.Children { + ro := flatbuffers.GetUOffsetT(child.result) + r := new(task.Result) + r.Init(child.result, ro) + + uo := flatbuffers.GetUOffsetT(child.query) + q := new(task.Query) + q.Init(child.query, uo) + + query, result, err := extract(q, r) if err != nil { - x.Err(glog, err).Error("Error while traversal") - return gr, err + x.Err(glog, err).Error("Error while extracting query, result") + return vals, children, fmt.Errorf("While extracting query, result") } - gr.Children = append(gr.Children, childPb) + idx := indexOf(re.uid, query) + + if len(child.Children) == 0 { + vals[child.Attr] = result.values[idx] + } else { + uids := result.uidmatrix[idx] + // We create as many predicate children as the number of uids. + for _, uid := range uids { + predChild := new(pb.GraphResponse) + predChild.Attribute = child.Attr + + ref := new(sgreference) + ref.sg = child + ref.uid = uid + + vals, ch, rerr := ref.pretraverse() + if rerr != nil { + x.Err(glog, rerr).Error("Error while traversal") + return vals, children, rerr + } + + predChild.Values, predChild.Children = vals, ch + children = append(children, predChild) + } + } } - return gr, nil + return vals, children, nil } func treeCopy(gq *gql.GraphQuery, sg *SubGraph) { diff --git a/query/query_test.go b/query/query_test.go index 792741f14ebcd68ad4e355ffc5de8fdde18a7fdb..b25b63f5af5b2e3c3f360036b88b297fc1ca826f 100644 --- a/query/query_test.go +++ b/query/query_test.go @@ -320,7 +320,7 @@ func TestToJson(t *testing.T) { fmt.Printf(string(js)) } -func TestPreTraverse(t *testing.T) { +func TestToProtocolBuffer(t *testing.T) { dir, _ := populateGraph(t) defer os.RemoveAll(dir) @@ -353,44 +353,39 @@ func TestPreTraverse(t *testing.T) { t.Error(err) } - ugr, err := sg.PreTraverse() + gr, err := sg.ToProtocolBuffer() if err != nil { t.Error(err) } - if len(ugr.Children) != 4 { - t.Errorf("Expected len 4. Got: %v", ugr.Children) + if gr.Attribute != "_root_" { + t.Errorf("Expected attribute _root_, Got: %v", gr.Attribute) } - child := ugr.Children[0] - if child.Attribute != "name" { - t.Errorf("Expected attr name. Got: %v", child.Attribute) + if len(gr.Values) != 3 { + t.Errorf("Expected values map to contain 3 properties, Got: %v", + len(gr.Values)) } - if string(child.Result.Values[0]) != "Michonne" { - t.Errorf("Expected value Michonne. Got %v", - string(child.Result.Values[0])) + if string(gr.Values["name"]) != "Michonne" { + t.Errorf("Expected property name to have value Michonne, Got: %v", + string(gr.Values["name"])) } - child = ugr.Children[3] + if len(gr.Children) != 5 { + t.Errorf("Expected 5 children, Got: %v", len(gr.Children)) + } + + child := gr.Children[0] if child.Attribute != "friend" { - t.Errorf("Expected attr friend. Got: %v", child.Attribute) + t.Errorf("Expected attribute friend, Got: %v", child.Attribute) } - uids := child.Result.Uidmatrix[0].Uids - if uids[0] != 23 || uids[1] != 24 || uids[2] != 25 || uids[3] != 31 || - uids[4] != 101 { - t.Errorf("Friend ids don't match") + if len(child.Values) != 1 { + t.Errorf("Expected values map to contain 1 property, Got: %v", + len(child.Values)) } - // To check for name of friends - child = child.Children[0] - if child.Attribute != "name" { - t.Errorf("Expected attr friend. Got: %v", child.Attribute) + if string(child.Values["name"]) != "Rick Grimes" { + t.Errorf("Expected property name to have value Rick Grimes, Got: %v", + string(child.Values["name"])) } - if len(child.Query.Uids) != 5 { - t.Errorf("Expected 5 uids in query. Got: %v", len(child.Query.Uids)) - } - - names := child.Result.Values - - if string(names[0]) != "Rick Grimes" || string(names[1]) != "Glenn Rhee" || - string(names[2]) != "Daryl Dixon" || string(names[3]) != "Andrea" { - t.Errorf("Names don't match") + if len(child.Children) != 0 { + t.Errorf("Expected 0 children, Got: %v", len(child.Children)) } } diff --git a/server/main.go b/server/main.go index a5e53c188368d976dc2b2a1c4b56caffebdfacd3..332d41e14d5a0dfa5faeb788e7853becc8ed8673 100644 --- a/server/main.go +++ b/server/main.go @@ -241,7 +241,7 @@ func (s *server) Query(ctx context.Context, } glog.WithField("q", req.Query).Debug("Graph processed.") - resp, err = sg.PreTraverse() + resp, err = sg.ToProtocolBuffer() if err != nil { x.Err(glog, err).Error("While converting to protocol buffer.") return resp, err