@@ -46,13 +46,77 @@ func stripSpaces(str string) string {
4646 }, str )
4747}
4848
49- func parseFacetsJSON (m map [string ]interface {}, prefix string ) ([]* api.Facet , error ) {
49+ // handleBasicFacetsType parses a facetVal to string/float64/bool/datetime type.
50+ func handleBasicFacetsType (key string , facetVal interface {}) (* api.Facet , error ) {
51+ var jsonValue interface {}
52+ var valueType api.Facet_ValType
53+ switch v := facetVal .(type ) {
54+ case string :
55+ if t , err := types .ParseTime (v ); err == nil {
56+ valueType = api .Facet_DATETIME
57+ jsonValue = t
58+ } else {
59+ facet , err := facets .FacetFor (key , strconv .Quote (v ))
60+ if err != nil {
61+ return nil , err
62+ }
63+
64+ // FacetFor function already converts the value to binary so there is no need
65+ // for the conversion again after the switch block.
66+ return facet , nil
67+ }
68+ case json.Number :
69+ number := facetVal .(json.Number )
70+ if strings .Contains (number .String (), "." ) {
71+ jsonFloat , err := number .Float64 ()
72+ if err != nil {
73+ return nil , err
74+ }
75+ jsonValue = jsonFloat
76+ valueType = api .Facet_FLOAT
77+ } else {
78+ jsonInt , err := number .Int64 ()
79+ if err != nil {
80+ return nil , err
81+ }
82+ jsonValue = jsonInt
83+ valueType = api .Facet_INT
84+ }
85+ case bool :
86+ jsonValue = v
87+ valueType = api .Facet_BOOL
88+ default :
89+ return nil , errors .Errorf ("facet value can only be string/number/bool." )
90+ }
91+
92+ // Convert facet val interface{} to binary.
93+ binaryValueFacet , err := facets .ToBinary (key , jsonValue , valueType )
94+ if err != nil {
95+ return nil , err
96+ }
97+
98+ return binaryValueFacet , nil
99+ }
100+
101+ // parseMapFacets parses facets which are of map type. Facets for scalar list predicates are
102+ // specified in map format. For example below predicate nickname and kind facet associated with it.
103+ // Here nickname "bob" doesn't have any facet associated with it.
104+ // {
105+ // "nickname": ["alice", "bob", "josh"],
106+ // "nickname|kind": {
107+ // "0": "friends",
108+ // "2": "official"
109+ // }
110+ // }
111+ // Parsed response would a slice of maps[int]*api.Facet, one map for each facet.
112+ // Map key would be the index of scalar value for respective facets.
113+ func parseMapFacets (m map [string ]interface {}, prefix string ) ([]map [int ]* api.Facet , error ) {
50114 // This happens at root.
51115 if prefix == "" {
52116 return nil , nil
53117 }
54118
55- var facetsForPred [ ]* api.Facet
119+ var mapSlice [] map [ int ]* api.Facet
56120 for fname , facetVal := range m {
57121 if facetVal == nil {
58122 continue
@@ -61,56 +125,54 @@ func parseFacetsJSON(m map[string]interface{}, prefix string) ([]*api.Facet, err
61125 continue
62126 }
63127
64- key := fname [len (prefix ):]
65- var jsonValue interface {}
66- var valueType api.Facet_ValType
67- switch v := facetVal .(type ) {
68- case string :
69- if t , err := types .ParseTime (v ); err == nil {
70- valueType = api .Facet_DATETIME
71- jsonValue = t
72- } else {
73- facet , err := facets .FacetFor (key , strconv .Quote (v ))
74- if err != nil {
75- return nil , err
76- }
128+ fm , ok := facetVal .(map [string ]interface {})
129+ if ! ok {
130+ return nil , errors .Errorf ("facets format should be of type map for " +
131+ "scalarlist predicates, found: %v for facet: %v" , facetVal , fname )
132+ }
77133
78- // the FacetFor function already converts the value to binary
79- // so there is no need for the conversion again after the switch block
80- facetsForPred = append (facetsForPred , facet )
81- continue
134+ idxMap := make (map [int ]* api.Facet , len (fm ))
135+ for sidx , val := range fm {
136+ key := fname [len (prefix ):]
137+ facet , err := handleBasicFacetsType (key , val )
138+ if err != nil {
139+ return nil , errors .Wrapf (err , "facet: %s, index: %s" , fname , sidx )
82140 }
83- case json.Number :
84- number := facetVal .(json.Number )
85- if strings .Contains (number .String (), "." ) {
86- jsonFloat , err := number .Float64 ()
87- if err != nil {
88- return nil , err
89- }
90- jsonValue = jsonFloat
91- valueType = api .Facet_FLOAT
92- } else {
93- jsonInt , err := number .Int64 ()
94- if err != nil {
95- return nil , err
96- }
97- jsonValue = jsonInt
98- valueType = api .Facet_INT
141+ idx , err := strconv .Atoi (sidx )
142+ if err != nil {
143+ return nil , errors .Wrapf (err , "facet: %s, index: %s" , fname , sidx )
99144 }
100- case bool :
101- jsonValue = v
102- valueType = api .Facet_BOOL
103- default :
104- return nil , errors .Errorf ("Facet value for key: %s can only be string/float64/bool." ,
105- fname )
145+ idxMap [idx ] = facet
106146 }
147+ mapSlice = append (mapSlice , idxMap )
148+ }
107149
108- // convert facet val interface{} to binary
109- binaryValueFacet , err := facets .ToBinary (key , jsonValue , valueType )
150+ return mapSlice , nil
151+ }
152+
153+ // parseScalarFacets parses facets which should be of type string/json.Number/bool.
154+ // It returns []*api.Facet, one *api.Facet for each facet.
155+ func parseScalarFacets (m map [string ]interface {}, prefix string ) ([]* api.Facet , error ) {
156+ // This happens at root.
157+ if prefix == "" {
158+ return nil , nil
159+ }
160+
161+ var facetsForPred []* api.Facet
162+ for fname , facetVal := range m {
163+ if facetVal == nil {
164+ continue
165+ }
166+ if ! strings .HasPrefix (fname , prefix ) {
167+ continue
168+ }
169+
170+ key := fname [len (prefix ):]
171+ facet , err := handleBasicFacetsType (key , facetVal )
110172 if err != nil {
111- return nil , err
173+ return nil , errors . Wrapf ( err , "facet: %s" , fname )
112174 }
113- facetsForPred = append (facetsForPred , binaryValueFacet )
175+ facetsForPred = append (facetsForPred , facet )
114176 }
115177
116178 return facetsForPred , nil
@@ -383,18 +445,21 @@ func (buf *NQuadBuffer) mapToNquads(m map[string]interface{}, op int, parentPred
383445 continue
384446 }
385447
386- prefix := pred + x .FacetDelimeter
387- // TODO - Maybe do an initial pass and build facets for all predicates. Then we don't have
388- // to call parseFacets everytime.
389- fts , err := parseFacetsJSON (m , prefix )
390- if err != nil {
391- return mr , err
392- }
393-
394448 nq := api.NQuad {
395449 Subject : mr .uid ,
396450 Predicate : pred ,
397- Facets : fts ,
451+ }
452+
453+ prefix := pred + x .FacetDelimeter
454+ // TODO - Maybe do an initial pass and build facets for all predicates. Then we don't have
455+ // to call parseFacets everytime.
456+ // Only call parseBasicFacets when value type for the predicate is not list.
457+ if _ , ok := v .([]interface {}); ! ok {
458+ fts , err := parseScalarFacets (m , prefix )
459+ if err != nil {
460+ return mr , err
461+ }
462+ nq .Facets = fts
398463 }
399464
400465 // Here we split predicate and lang directive (ex: "name@en"), if needed. With JSON
@@ -435,7 +500,14 @@ func (buf *NQuadBuffer) mapToNquads(m map[string]interface{}, op int, parentPred
435500 buf .PushPredHint (pred , pb .Metadata_SINGLE )
436501 case []interface {}:
437502 buf .PushPredHint (pred , pb .Metadata_LIST )
438- for _ , item := range v {
503+ // TODO(Ashish): We need to call this only in case of scalarlist, for other lists
504+ // this can be avoided.
505+ facetsMapSlice , err := parseMapFacets (m , prefix )
506+ if err != nil {
507+ return mr , err
508+ }
509+
510+ for idx , item := range v {
439511 nq := api.NQuad {
440512 Subject : mr .uid ,
441513 Predicate : pred ,
@@ -446,6 +518,34 @@ func (buf *NQuadBuffer) mapToNquads(m map[string]interface{}, op int, parentPred
446518 if err := handleBasicType (pred , iv , op , & nq ); err != nil {
447519 return mr , err
448520 }
521+ // Here populate facets from facetsMapSlice. Each map has mapping for single
522+ // facet from item(one of predicate value) idx to *api.Facet.
523+ // {
524+ // "friend": ["Joshua", "David", "Josh"],
525+ // "friend|from": {
526+ // "0": "school"
527+ // },
528+ // "friend|age": {
529+ // "1": 20
530+ // }
531+ // }
532+ // facetMapSlice looks like below. First map is for friend|from facet and second
533+ // map is for friend|age facet.
534+ // [
535+ // map[int]*api.Facet{
536+ // 0: *api.Facet
537+ // },
538+ // map[int]*api.Facet{
539+ // 1: *api.Facet
540+ // }
541+ // ]
542+ var fts []* api.Facet
543+ for _ , fm := range facetsMapSlice {
544+ if ft , ok := fm [idx ]; ok {
545+ fts = append (fts , ft )
546+ }
547+ }
548+ nq .Facets = fts
449549 buf .Push (& nq )
450550 case map [string ]interface {}:
451551 // map[string]interface{} can mean geojson or a connecting entity.
@@ -475,7 +575,7 @@ func (buf *NQuadBuffer) mapToNquads(m map[string]interface{}, op int, parentPred
475575 }
476576 }
477577
478- fts , err := parseFacetsJSON (m , parentPred + x .FacetDelimeter )
578+ fts , err := parseScalarFacets (m , parentPred + x .FacetDelimeter )
479579 mr .fcts = fts
480580 return mr , err
481581}
0 commit comments