From 18c4c555b5b56fa58114386308b299ce440d2f71 Mon Sep 17 00:00:00 2001 From: Barak Michener Date: Sun, 29 Jun 2014 21:20:54 -0400 Subject: [PATCH] Fix #21 and change the MQL semantics slightly to match the original MQL --- graph/all_iterator.go | 2 +- graph/and_iterator.go | 3 +++ query/mql/build_iterator.go | 42 +++++++++++++++++++++++++----------------- query/mql/fill.go | 8 +++++++- query/mql/query.go | 6 ++++-- query/mql/session.go | 4 ++-- 6 files changed, 42 insertions(+), 23 deletions(-) diff --git a/graph/all_iterator.go b/graph/all_iterator.go index 4be790e..989e1b5 100644 --- a/graph/all_iterator.go +++ b/graph/all_iterator.go @@ -59,7 +59,7 @@ func (it *Int64AllIterator) Clone() Iterator { // Prints the All iterator as just an "all". func (it *Int64AllIterator) DebugString(indent int) string { - return fmt.Sprintf("%s(%s)", strings.Repeat(" ", indent), it.Type()) + return fmt.Sprintf("%s(%s tags: %v)", strings.Repeat(" ", indent), it.Type(), it.Tags()) } // Next() on an Int64 all iterator is a simple incrementing counter. diff --git a/graph/and_iterator.go b/graph/and_iterator.go index f4a17b7..0100708 100644 --- a/graph/and_iterator.go +++ b/graph/and_iterator.go @@ -173,6 +173,9 @@ func (it *AndIterator) checkCheckList(val TSVal) bool { break } } + if ok { + it.Last = val + } return CheckLogOut(it, val, ok) } diff --git a/query/mql/build_iterator.go b/query/mql/build_iterator.go index 77569ad..5594552 100644 --- a/query/mql/build_iterator.go +++ b/query/mql/build_iterator.go @@ -33,7 +33,7 @@ func (q *Query) buildFixed(s string) graph.Iterator { func (q *Query) buildResultIterator(path Path) graph.Iterator { all := q.ses.ts.GetNodesAllIterator() all.AddTag(string(path)) - return graph.NewOptionalIterator(all) + return all } func (q *Query) BuildIteratorTree(query interface{}) { @@ -42,16 +42,16 @@ func (q *Query) BuildIteratorTree(query interface{}) { q.queryResult = make(map[ResultPath]map[string]interface{}) q.queryResult[""] = make(map[string]interface{}) - q.it, q.err = q.buildIteratorTreeInternal(query, NewPath()) - if q.err != nil { - q.isError = true + var isOptional bool + q.it, isOptional, q.err = q.buildIteratorTreeInternal(query, NewPath()) + if isOptional { + q.err = errors.New("Optional iterator at the top level?") } } -func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (graph.Iterator, error) { - var it graph.Iterator - var err error +func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (it graph.Iterator, optional bool, err error) { err = nil + optional = false switch t := query.(type) { case bool: // for JSON booleans @@ -78,8 +78,9 @@ func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (graph.I q.isRepeated[path] = true if len(t) == 0 { it = q.buildResultIterator(path) + optional = true } else if len(t) == 1 { - it, err = q.buildIteratorTreeInternal(t[0], path) + it, optional, err = q.buildIteratorTreeInternal(t[0], path) } else { err = errors.New(fmt.Sprintf("Multiple fields at location root%s", path.DisplayString())) } @@ -88,14 +89,15 @@ func (q *Query) buildIteratorTreeInternal(query interface{}, path Path) (graph.I it, err = q.buildIteratorTreeMapInternal(t, path) case nil: it = q.buildResultIterator(path) + optional = true default: log.Fatal("Unknown JSON type?", query) } if err != nil { - return nil, err + return nil, false, err } it.AddTag(string(path)) - return it, nil + return it, optional, nil } func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path Path) (graph.Iterator, error) { @@ -105,6 +107,7 @@ func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path err = nil outputStructure := make(map[string]interface{}) for key, subquery := range query { + optional := false outputStructure[key] = nil reverse := false pred := key @@ -122,13 +125,13 @@ func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path // Other special constructs here var subit graph.Iterator if key == "id" { - subit, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) + subit, optional, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) if err != nil { return nil, err } - it.AddSubIterator(subit) } else { - subit, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) + var builtIt graph.Iterator + builtIt, optional, err = q.buildIteratorTreeInternal(subquery, path.Follow(key)) if err != nil { return nil, err } @@ -137,17 +140,22 @@ func (q *Query) buildIteratorTreeMapInternal(query map[string]interface{}, path predFixed.AddValue(q.ses.ts.GetIdFor(pred)) subAnd.AddSubIterator(graph.NewLinksToIterator(q.ses.ts, predFixed, "p")) if reverse { - lto := graph.NewLinksToIterator(q.ses.ts, subit, "s") + lto := graph.NewLinksToIterator(q.ses.ts, builtIt, "s") subAnd.AddSubIterator(lto) hasa := graph.NewHasaIterator(q.ses.ts, subAnd, "o") - it.AddSubIterator(hasa) + subit = hasa } else { - lto := graph.NewLinksToIterator(q.ses.ts, subit, "o") + lto := graph.NewLinksToIterator(q.ses.ts, builtIt, "o") subAnd.AddSubIterator(lto) hasa := graph.NewHasaIterator(q.ses.ts, subAnd, "s") - it.AddSubIterator(hasa) + subit = hasa } } + if optional { + it.AddSubIterator(graph.NewOptionalIterator(subit)) + } else { + it.AddSubIterator(subit) + } } if err != nil { return nil, err diff --git a/query/mql/fill.go b/query/mql/fill.go index 5ecc2b3..dabae91 100644 --- a/query/mql/fill.go +++ b/query/mql/fill.go @@ -24,6 +24,9 @@ func (q *Query) treeifyResult(tags map[string]graph.TSVal) map[ResultPath]string // Transform the map into something a little more interesting. results := make(map[Path]string) for k, v := range tags { + if v == nil { + continue + } results[Path(k)] = q.ses.ts.GetNameFor(v) } resultPaths := make(map[ResultPath]string) @@ -78,7 +81,10 @@ func (q *Query) treeifyResult(tags map[string]graph.TSVal) map[ResultPath]string // Fill values for _, path := range paths { currentPath := path.getPath() - value := resultPaths[path] + value, ok := resultPaths[path] + if !ok { + continue + } namePath := path.AppendValue(value) if _, ok := q.queryStructure[currentPath]; ok { // We're dealing with ids. diff --git a/query/mql/query.go b/query/mql/query.go index 7460e48..73675ff 100644 --- a/query/mql/query.go +++ b/query/mql/query.go @@ -32,10 +32,13 @@ type Query struct { queryResult map[ResultPath]map[string]interface{} results []interface{} resultOrder []string - isError bool err error } +func (q *Query) isError() bool { + return q.err != nil +} + func (q *Query) copyPathStructure(path Path) map[string]interface{} { output := make(map[string]interface{}) for k, v := range q.queryStructure[path] { @@ -106,6 +109,5 @@ func NewQuery(ses *Session) *Query { q.results = make([]interface{}, 0) q.resultOrder = make([]string, 0) q.err = nil - q.isError = false return &q } diff --git a/query/mql/session.go b/query/mql/session.go index b249dd7..f148afb 100644 --- a/query/mql/session.go +++ b/query/mql/session.go @@ -79,7 +79,7 @@ func (s *Session) ExecInput(input string, c chan interface{}, limit int) { } s.currentQuery = NewQuery(s) s.currentQuery.BuildIteratorTree(mqlQuery) - if s.currentQuery.isError { + if s.currentQuery.isError() { return } it, _ := s.currentQuery.it.Optimize() @@ -131,7 +131,7 @@ func (s *Session) BuildJson(result interface{}) { func (s *Session) GetJson() (interface{}, error) { s.currentQuery.buildResults() - if s.currentQuery.isError { + if s.currentQuery.isError() { return nil, s.currentQuery.err } else { return s.currentQuery.results, nil