Skip to content

Commit

Permalink
Support alias for facets.
Browse files Browse the repository at this point in the history
  • Loading branch information
Pawan Rawal committed Nov 24, 2017
1 parent 3dae05b commit 7d3ceea
Show file tree
Hide file tree
Showing 13 changed files with 704 additions and 421 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ and this project will adhere to [Semantic Versioning](http://semver.org/spec/v2.

## [Unreleased]

### Added

* Support for alias while asking for facets.

### Fixed

* `expand(_all_)` now correctly gives all language variants of a string.
Expand Down
2 changes: 1 addition & 1 deletion dgraph/cmd/zero/zero.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func (s *Server) Connect(ctx context.Context,
if ctx.Err() != nil {
return &emptyConnectionState, ctx.Err()
}
if m.CluterInfoOnly {
if m.ClusterInfoOnly {
// This request only wants to access the membership state, and nothing else. Most likely
// from our clients.
ms, err := s.latestMembershipState(ctx)
Expand Down
76 changes: 39 additions & 37 deletions gql/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ type GraphQuery struct {
RecurseArgs RecurseArgs
Cascade bool
IgnoreReflex bool
Facets *Facets
Facets *protos.FacetParams
FacetsFilter *FilterTree
GroupbyAttrs []GroupByAttr
FacetVar map[string]string
Expand Down Expand Up @@ -157,12 +157,6 @@ type Function struct {
IsValueVar bool // eq(val(s), 5)
}

// Facet holds the information about gql Facets (edge key-value pairs).
type Facets struct {
AllKeys bool
Keys []string // should be in sorted order.
}

// filterOpPrecedence is a map from filterOp (a string) to its precedence.
var filterOpPrecedence map[string]int
var mathOpPrecedence map[string]int
Expand Down Expand Up @@ -1614,7 +1608,7 @@ L:
}

type facetRes struct {
f *Facets
f *protos.FacetParams
ft *FilterTree
vmap map[string]string
facetOrder string
Expand All @@ -1633,7 +1627,8 @@ func parseFacets(it *lex.ItemIterator) (res facetRes, err error) {
}

type facetItem struct {
facetName string
name string
alias string
varName string
ordered bool
orderdesc bool
Expand All @@ -1644,7 +1639,7 @@ type facetItem struct {
// different function.
func tryParseFacetItem(it *lex.ItemIterator) (res facetItem, parseOk bool, err error) {
// We parse this:
// [{orderdesc|orderasc}:] [varname as] facetName
// [{orderdesc|orderasc|alias}:] [varname as] name

savePos := it.Save()
defer func() {
Expand All @@ -1657,35 +1652,39 @@ func tryParseFacetItem(it *lex.ItemIterator) (res facetItem, parseOk bool, err e
if !ok {
return res, false, nil
}

isOrderasc := item.Val == "orderasc"
if isOrderasc || item.Val == "orderdesc" {
if _, ok := tryParseItemType(it, itemColon); ok {
if _, ok := tryParseItemType(it, itemColon); ok {
if isOrderasc || item.Val == "orderdesc" {
res.ordered = true
res.orderdesc = !isOrderasc
// Step past colon.
item, ok = tryParseItemType(it, itemName)
if !ok {
return res, false, x.Errorf("Expected name after colon")
}
} else {
res.alias = item.Val
}

// Step past colon.
item, ok = tryParseItemType(it, itemName)
if !ok {
return res, false, x.Errorf("Expected name after colon")
}
}

// We've possibly set ordered, orderdesc, and now we have consumed another item, which is a
// name.
// We've possibly set ordered, orderdesc, alias and now we have consumed another item,
// which is a name.
name1 := item.Val

// Now try to consume "as".
if !trySkipItemVal(it, "as") {
name1 = collectName(it, name1)
res.facetName = name1
res.name = name1
return res, true, nil
}
item, ok = tryParseItemType(it, itemName)
if !ok {
return res, false, x.Errorf("Expected name in facet list")
}

res.facetName = collectName(it, item.Val)
res.name = collectName(it, item.Val)
res.varName = name1
return res, true, nil
}
Expand All @@ -1704,15 +1703,15 @@ func tryParseFacetList(it *lex.ItemIterator) (res facetRes, parseOk bool, err er
// Skip past '('
if _, ok := tryParseItemType(it, itemLeftRound); !ok {
it.Restore(savePos)
var facets Facets
var facets protos.FacetParams
facets.AllKeys = true
res.f = &facets
res.vmap = make(map[string]string)
return res, true, nil
}

facetVar := make(map[string]string)
var facets Facets
var facets protos.FacetParams
var orderdesc bool
var orderkey string

Expand All @@ -1734,43 +1733,46 @@ func tryParseFacetList(it *lex.ItemIterator) (res facetRes, parseOk bool, err er
// Combine the facetitem with our result.
{
if facetItem.varName != "" {
if _, has := facetVar[facetItem.facetName]; has {
if _, has := facetVar[facetItem.name]; has {
return res, false, x.Errorf("Duplicate variable mappings for facet %v",
facetItem.facetName)
facetItem.name)
}
facetVar[facetItem.facetName] = facetItem.varName
facetVar[facetItem.name] = facetItem.varName
}
facets.Keys = append(facets.Keys, facetItem.facetName)
facets.Param = append(facets.Param, &protos.FacetParam{
Key: facetItem.name,
Alias: facetItem.alias,
})
if facetItem.ordered {
if orderkey != "" {
return res, false, x.Errorf("Invalid use of orderasc/orderdesc in facets")
}
orderdesc = facetItem.orderdesc
orderkey = facetItem.facetName
orderkey = facetItem.name
}
}

// Now what? Either close-paren or a comma.
if _, ok := tryParseItemType(it, itemRightRound); ok {
sort.Slice(facets.Keys, func(i, j int) bool {
return facets.Keys[i] < facets.Keys[j]
sort.Slice(facets.Param, func(i, j int) bool {
return facets.Param[i].Key < facets.Param[j].Key
})
// deduplicate facets
out := facets.Keys[:0]
flen := len(facets.Keys)
out := facets.Param[:0]
flen := len(facets.Param)
for i := 1; i < flen; i++ {
if facets.Keys[i-1] == facets.Keys[i] {
if facets.Param[i-1].Key == facets.Param[i].Key {
continue
}
out = append(out, facets.Keys[i-1])
out = append(out, facets.Param[i-1])
}
out = append(out, facets.Keys[flen-1])
facets.Keys = out
out = append(out, facets.Param[flen-1])
facets.Param = out
res.f, res.vmap, res.facetOrder, res.orderdesc = &facets, facetVar, orderkey, orderdesc
return res, true, nil
}
if item, ok := tryParseItemType(it, itemComma); !ok {
if len(facets.Keys) < 2 {
if len(facets.Param) < 2 {
// We have only consumed ``'@facets' '(' <facetItem>`, which means parseFilter might
// succeed. Return no-parse, no-error.
return res, false, nil
Expand Down
62 changes: 48 additions & 14 deletions gql/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3093,19 +3093,25 @@ func TestParseFacetsOrderError2(t *testing.T) {
require.Contains(t, err.Error(), "Expected ( after func name [a]")
}

func TestParseFacetsOrderError3(t *testing.T) {
func TestParseFacetsOrderWithAlias(t *testing.T) {
query := `
query {
me(func: uid(0x1)) {
friends @facets(orderdesc: closeness, order: abc) {
name
friends @facets(orderdesc: closeness, b as some, order: abc, key, key1: val, abcd) {
val(b)
}
}
}
`
_, err := Parse(Request{Str: query, Http: true})
require.Error(t, err)
require.Contains(t, err.Error(), "Expected ',' or ')'")
res, err := Parse(Request{Str: query, Http: true})
require.NoError(t, err)
node := res.Query[0].Children[0].Facets
require.Equal(t, 6, len(node.Param))
require.Equal(t, "order", node.Param[0].Alias)
require.Equal(t, "abc", node.Param[0].Key)
require.Equal(t, "abcd", node.Param[1].Key)
require.Equal(t, "val", node.Param[5].Key)
require.Equal(t, "key1", node.Param[5].Alias)
}

func TestParseFacetsDuplicateVarError(t *testing.T) {
Expand Down Expand Up @@ -3197,7 +3203,7 @@ func TestParseOrderbyFacet(t *testing.T) {
require.Equal(t, []string{"name"}, childAttrs(res.Query[0].Children[0]))
require.NotNil(t, res.Query[0].Children[0].Children[0].Facets)
require.Equal(t, false, res.Query[0].Children[0].Children[0].Facets.AllKeys)
require.Equal(t, "facet1", res.Query[0].Children[0].Children[0].Facets.Keys[0])
require.Equal(t, "facet1", res.Query[0].Children[0].Children[0].Facets.Param[0].Key)
}

func TestParseFacetsMultiple(t *testing.T) {
Expand All @@ -3221,7 +3227,35 @@ func TestParseFacetsMultiple(t *testing.T) {
require.Equal(t, []string{"name"}, childAttrs(res.Query[0].Children[0]))
require.NotNil(t, res.Query[0].Children[0].Children[0].Facets)
require.Equal(t, false, res.Query[0].Children[0].Children[0].Facets.AllKeys)
require.Equal(t, 3, len(res.Query[0].Children[0].Children[0].Facets.Keys))
require.Equal(t, 3, len(res.Query[0].Children[0].Children[0].Facets.Param))
}

func TestParseFacetsAlias(t *testing.T) {
query := `
query {
me(func: uid(0x1)) {
friends @facets {
name @facets(a1: key1, a2: key2, a3: key3)
}
}
}
`
res, err := Parse(Request{Str: query, Http: true})
require.NoError(t, err)
require.NotNil(t, res.Query[0])

require.NotNil(t, res.Query[0].Children[0].Facets)
require.Equal(t, true, res.Query[0].Children[0].Facets.AllKeys)
require.Equal(t, []string{"name"}, childAttrs(res.Query[0].Children[0]))
require.NotNil(t, res.Query[0].Children[0].Children[0].Facets)

node := res.Query[0].Children[0].Children[0].Facets
require.Equal(t, false, node.AllKeys)
require.Equal(t, 3, len(node.Param))
require.Equal(t, "a1", node.Param[0].Alias)
require.Equal(t, "key1", node.Param[0].Key)
require.Equal(t, "a3", node.Param[2].Alias)
require.Equal(t, "key3", node.Param[2].Key)
}

func TestParseFacetsMultipleVar(t *testing.T) {
Expand All @@ -3248,7 +3282,7 @@ func TestParseFacetsMultipleVar(t *testing.T) {
require.Equal(t, []string{"name"}, childAttrs(res.Query[0].Children[0]))
require.NotNil(t, res.Query[0].Children[0].Children[0].Facets)
require.Equal(t, false, res.Query[0].Children[0].Children[0].Facets.AllKeys)
require.Equal(t, 3, len(res.Query[0].Children[0].Children[0].Facets.Keys))
require.Equal(t, 3, len(res.Query[0].Children[0].Children[0].Facets.Param))
require.Equal(t, "a", res.Query[0].Children[0].Children[0].FacetVar["key1"])
require.Equal(t, "", res.Query[0].Children[0].Children[0].FacetVar["key2"])
require.Equal(t, "b", res.Query[0].Children[0].Children[0].FacetVar["key3"])
Expand All @@ -3275,7 +3309,7 @@ func TestParseFacetsMultipleRepeat(t *testing.T) {
require.Equal(t, []string{"name"}, childAttrs(res.Query[0].Children[0]))
require.NotNil(t, res.Query[0].Children[0].Children[0].Facets)
require.Equal(t, false, res.Query[0].Children[0].Children[0].Facets.AllKeys)
require.Equal(t, 3, len(res.Query[0].Children[0].Children[0].Facets.Keys))
require.Equal(t, 3, len(res.Query[0].Children[0].Children[0].Facets.Param))
}

func TestParseFacetsEmpty(t *testing.T) {
Expand All @@ -3295,7 +3329,7 @@ func TestParseFacetsEmpty(t *testing.T) {
require.Equal(t, []string{"friends", "hometown", "age"}, childAttrs(res.Query[0]))
require.NotNil(t, res.Query[0].Children[0].Facets)
require.Equal(t, false, res.Query[0].Children[0].Facets.AllKeys)
require.Equal(t, 0, len(res.Query[0].Children[0].Facets.Keys))
require.Equal(t, 0, len(res.Query[0].Children[0].Facets.Param))
}

func TestParseFacetsFail1(t *testing.T) {
Expand Down Expand Up @@ -3387,9 +3421,9 @@ func TestFacetsFilterAll(t *testing.T) {
require.NotNil(t, res.Query[0])
require.Equal(t, []string{"name", "friend"}, childAttrs(res.Query[0]))
require.NotNil(t, res.Query[0].Children[1].Facets)
require.Equal(t, "close", res.Query[0].Children[1].Facets.Keys[0])
require.Equal(t, "family", res.Query[0].Children[1].Facets.Keys[1])
require.Equal(t, "since", res.Query[0].Children[1].Facets.Keys[2])
require.Equal(t, "close", res.Query[0].Children[1].Facets.Param[0].Key)
require.Equal(t, "family", res.Query[0].Children[1].Facets.Param[1].Key)
require.Equal(t, "since", res.Query[0].Children[1].Facets.Param[2].Key)
require.NotNil(t, res.Query[0].Children[1].FacetsFilter)
require.Equal(t, `(OR (eq close "true") (eq family "true"))`,
res.Query[0].Children[1].FacetsFilter.debugString())
Expand Down
2 changes: 1 addition & 1 deletion posting/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -1045,7 +1045,7 @@ func (l *List) findPosting(readTs uint64, uid uint64) (found bool, pos *protos.P
}

// Facets gives facets for the posting representing value.
func (l *List) Facets(readTs uint64, param *protos.Param, langs []string) (fs []*protos.Facet,
func (l *List) Facets(readTs uint64, param *protos.FacetParams, langs []string) (fs []*protos.Facet,
ferr error) {
l.RLock()
defer l.RUnlock()
Expand Down
Loading

0 comments on commit 7d3ceea

Please sign in to comment.