veyron/services/store/memstore/query: Handle QueryResult.Fields when present.

If a query uses selection, subsequent pipelines must refer to the
fields produced by that selection.

Added more todos for things that I need to implement and test.
They are dependent on sorting for deterministic tests, so I
can't do them now.

Change-Id: I0f30a1a5edc6bbe342c1fb0d1c41a511b1f3b000
diff --git a/services/store/memstore/query/eval.go b/services/store/memstore/query/eval.go
index 7061a67..bfee9ee 100644
--- a/services/store/memstore/query/eval.go
+++ b/services/store/memstore/query/eval.go
@@ -6,6 +6,8 @@
 	"path"
 	"reflect"
 	"runtime"
+	"sort"
+	"strings"
 	"sync"
 
 	"veyron/services/store/memstore/state"
@@ -448,6 +450,7 @@
 		pos:          p.Pos,
 	}
 	for i, a := range p.SubPipelines {
+		// TODO(kash): Protect against aliases that have slashes in them?
 		e.subpipelines[i] = alias{convertPipeline(a.Pipeline), a.Alias, a.Hidden}
 	}
 	return e
@@ -811,12 +814,31 @@
 
 // value implements the expr method.
 func (e *exprName) value(c *context, result *store.QueryResult) interface{} {
-	// TODO(kash): The name might be in result.Fields.  Check there first.
+	if result.Fields != nil {
+		// TODO(kash): Handle multipart names.  This currently only works if
+		// e.name has no slashes.
+		val, ok := result.Fields[e.name]
+		if !ok {
+			sendError(c.errc, fmt.Errorf("name '%s' was not selected from '%s', found: [%s]",
+				e.name, result.Name, mapKeys(result.Fields)))
+			return nil
+		}
+		return val
+	}
 	fullpath := path.Join(result.Name, e.name)
 	entry, err := c.sn.Get(c.clientID, storage.ParsePath(fullpath))
 	if err != nil {
-		c.errc <- fmt.Errorf("could not look up name '%s' relative to '%s': %v", e.name, result.Name, err)
+		sendError(c.errc, fmt.Errorf("could not look up name '%s' relative to '%s': %v", e.name, result.Name, err))
 		return nil
 	}
 	return entry.Value
 }
+
+func mapKeys(m map[string]idl.AnyData) string {
+	s := make([]string, 0, len(m))
+	for key, _ := range m {
+		s = append(s, key)
+	}
+	sort.Strings(s)
+	return strings.Join(s, ", ")
+}
diff --git a/services/store/memstore/query/eval_test.go b/services/store/memstore/query/eval_test.go
index 9a9d08c..4f8202f 100644
--- a/services/store/memstore/query/eval_test.go
+++ b/services/store/memstore/query/eval_test.go
@@ -175,6 +175,19 @@
 					nil},
 			},
 		},
+		{
+			"'teams/cardinals' | {Name as myname, Location as myloc} | ? myname == 'cardinals'",
+			[]*store.QueryResult{
+				&store.QueryResult{
+					0,
+					"teams/cardinals",
+					map[string]idl.AnyData{
+						"myname": "cardinals",
+						"myloc":  "CA",
+					},
+					nil},
+			},
+		},
 	}
 	for _, test := range tests {
 		it := Eval(st.Snapshot(), rootPublicID, query.Query{test.query})
@@ -215,6 +228,8 @@
 		// TODO(kash): We probably want an error message that says that you must
 		// use a type filter.
 		{"teams/* | ?Name > 'foo'", "could not look up name 'Name' relative to 'teams': not found"},
+		{"'teams/cardinals' | {Name as myname, Location as myloc} | ? Name == 'foo'", "name 'Name' was not selected from 'teams/cardinals', found: [myloc, myname]"},
+
 		// TODO(kash): Selection with conflicting names.
 	}
 	for _, test := range tests {