// CreatePrimaryIndex implements datastore.Indexer{} interface. Create or // return a primary index on this keyspace func (gsi *gsiKeyspace) CreatePrimaryIndex( requestId, name string, with value.Value) (datastore.PrimaryIndex, errors.Error) { var withJSON []byte var err error if with != nil { if withJSON, err = with.MarshalJSON(); err != nil { return nil, errors.NewError(err, "GSI error marshalling WITH clause") } } defnID, err := gsi.gsiClient.CreateIndex( name, gsi.keyspace, /*bucket-name*/ string(c.ForestDB), /*using, by default always forestdb*/ "N1QL", /*exprType*/ "", /*partnStr*/ "", /*whereStr*/ nil, /*secStrs*/ true, /*isPrimary*/ withJSON) if err != nil { return nil, errors.NewError(err, "GSI CreatePrimaryIndex()") } // refresh to get back the newly created index. if err := gsi.Refresh(); err != nil { return nil, err } index, errr := gsi.IndexById(defnID2String(defnID)) if errr != nil { return nil, errr } return index.(datastore.PrimaryIndex), nil }
/* This method removes all the occurences of the second value from the first array value. */ func (this *ArrayRemove) Apply(context Context, first, second value.Value) (value.Value, error) { if first.Type() == value.MISSING { return first, nil } if first.Type() != value.ARRAY { return value.NULL_VALUE, nil } if second.Type() <= value.NULL { return first, nil } fa := first.Actual().([]interface{}) if len(fa) == 0 { return first, nil } ra := make([]interface{}, 0, len(fa)) for _, f := range fa { if !second.Equals(value.NewValue(f)).Truth() { ra = append(ra, f) } } return value.NewValue(ra), nil }
/* Perform either case-sensitive or case-insensitive field lookup. */ func (this *Field) Apply(context Context, first, second value.Value) (value.Value, error) { switch second.Type() { case value.STRING: s := second.Actual().(string) v, ok := first.Field(s) if !ok && this.caseInsensitive { s = strings.ToLower(s) fields := first.Fields() for f, val := range fields { if s == strings.ToLower(f) { return value.NewValue(val), nil } } } return v, nil case value.MISSING: return value.MISSING_VALUE, nil default: if first.Type() == value.MISSING { return value.MISSING_VALUE, nil } else { return value.NULL_VALUE, nil } } }
/* This method returns an object value. The input of types missing, null and object return themselves. For all other values, return an _EMPTY_OBJECT value. */ func (this *ToObject) Apply(context Context, arg value.Value) (value.Value, error) { switch arg.Type() { case value.MISSING, value.NULL, value.OBJECT: return arg, nil } return _EMPTY_OBJECT, nil }
/* Evaluates the Is Missing comparison operation for expressions. Return true if the input argument value is a missing value, else return false. */ func (this *IsMissing) Apply(context Context, arg value.Value) (value.Value, error) { switch arg.Type() { case value.MISSING: return value.TRUE_VALUE, nil default: return value.FALSE_VALUE, nil } }
func (this *LE) Apply(context Context, first, second value.Value) (value.Value, error) { cmp := first.Compare(second) switch actual := cmp.Actual().(type) { case float64: return value.NewValue(actual <= 0), nil } return cmp, nil }
func (this *Base64) Apply(context Context, operand value.Value) (value.Value, error) { if operand.Type() == value.MISSING { return operand, nil } bytes, _ := operand.MarshalJSON() // Ignore errors from BINARY values str := base64.StdEncoding.EncodeToString(bytes) return value.NewValue(str), nil }
func testObjectRemove(e1, e2 Expression, er value.Value, t *testing.T) { eop := NewObjectRemove(e1, e2) rv, err := eop.Evaluate(nil, nil) if err != nil { t.Errorf("received error %v", err) } if er.Collate(rv) != 0 { t.Errorf("mismatch received %v expected %v", rv.Actual(), er.Actual()) } }
func testArrayInsert_eval(e1, e2, e3 Expression, er value.Value, t *testing.T) { eai := NewArrayInsert(e1, e2, e3) rv, err := eai.Evaluate(nil, nil) if err != nil { t.Errorf("received error %v", err) } if er.Collate(rv) != 0 { t.Errorf("mismatch received %v expected %v", rv.Actual(), er.Actual()) } }
/* Aggregate input partial values into cumulative result value. If partial result is null return the current cumulative value, and if the cumulative result is null, return the partial value. For non null partial and cumulative values, call Collate and return the smaller value depending on the N1QL collation order. */ func (this *Min) cumulatePart(part, cumulative value.Value, context Context) (value.Value, error) { if part == value.NULL_VALUE { return cumulative, nil } else if cumulative == value.NULL_VALUE { return part, nil } else if part.Collate(cumulative) < 0 { return part, nil } else { return cumulative, nil } }
func (this *ParentScan) RunOnce(context *Context, parent value.Value) { this.once.Do(func() { defer context.Recover() // Recover from any panic defer close(this.itemChannel) // Broadcast that I have stopped defer this.notify() // Notify that I have stopped // Shallow copy of the parent includes // correlated and annotated aspects this.sendItem(parent.Copy().(value.AnnotatedValue)) }) }
func (this *Not) Apply(context Context, arg value.Value) (value.Value, error) { switch arg.Type() { case value.MISSING, value.NULL: return arg, nil default: if arg.Truth() { return value.FALSE_VALUE, nil } else { return value.TRUE_VALUE, nil } } }
/* Aggregates input data by evaluating operands. For missing item values, return the input value itself. Call setAdd to compute the intermediate aggregate value and return it. */ func (this *ArrayAggDistinct) CumulateInitial(item, cumulative value.Value, context Context) (value.Value, error) { item, e := this.Operand().Evaluate(item, context) if e != nil { return nil, e } if item.Type() <= value.MISSING || item.Type() == value.BINARY { return cumulative, nil } return setAdd(item, cumulative) }
/* Aggregates input data by evaluating operands. For missing item values, return the input value itself. Call cumulatePart to compute the intermediate aggregate value and return it. */ func (this *ArrayAgg) CumulateInitial(item, cumulative value.Value, context Context) (value.Value, error) { item, e := this.Operand().Evaluate(item, context) if e != nil { return nil, e } if item.Type() <= value.MISSING || item.Type() == value.BINARY { return cumulative, nil } return this.cumulatePart(value.NewValue([]interface{}{item}), cumulative, context) }
/* Aggregates input data by evaluating operands.For all values other than Number, return the input value itself. Call setAdd to compute the intermediate aggregate value and return it. */ func (this *AvgDistinct) CumulateInitial(item, cumulative value.Value, context Context) (value.Value, error) { item, e := this.Operand().Evaluate(item, context) if e != nil { return nil, e } if item.Type() != value.NUMBER { return cumulative, nil } return setAdd(item, cumulative) }
func (this *Sum) CumulateInitial(item, cumulative value.Value, context Context) (value.Value, error) { item, e := this.Operand().Evaluate(item, context) if e != nil { return nil, e } if item.Type() != value.NUMBER { return cumulative, nil } return this.cumulatePart(item, cumulative, context) }
func (this *Order) Less(i, j int) bool { v1 := this.values[i] v2 := this.values[j] var ev1, ev2 value.Value var c int var e error for i, term := range this.plan.Terms() { s := this.terms[i] sv1 := v1.GetAttachment(s) switch sv1 := sv1.(type) { case value.Value: ev1 = sv1 default: ev1, e = term.Expression().Evaluate(v1, this.context) if e != nil { this.context.Error(errors.NewEvaluationError(e, "ORDER BY")) return false } v1.SetAttachment(s, ev1) } sv2 := v2.GetAttachment(s) switch sv2 := sv2.(type) { case value.Value: ev2 = sv2 default: ev2, e = term.Expression().Evaluate(v2, this.context) if e != nil { this.context.Error(errors.NewEvaluationError(e, "ORDER BY")) return false } v2.SetAttachment(s, ev2) } c = ev1.Collate(ev2) if c == 0 { continue } else if term.Descending() { return c > 0 } else { return c < 0 } } return false }
/* Aggregates input data by evaluating operands. For all values other than Number, return the input value itself. Call cumulatePart to compute the intermediate aggregate value and return it. */ func (this *Avg) CumulateInitial(item, cumulative value.Value, context Context) (value.Value, error) { item, e := this.Operand().Evaluate(item, context) if e != nil { return nil, e } if item.Type() != value.NUMBER { return cumulative, nil } part := value.NewValue(map[string]interface{}{"sum": item.Actual(), "count": 1}) return this.cumulatePart(part, cumulative, context) }
/* This method checks to see if the values of the two input expressions are equal, and if true then returns a positive infinity using the math package method Inf(1). If not it returns the first input value. Use the Equals method for the two values to determine equality. */ func (this *PosInfIf) Apply(context Context, first, second value.Value) (value.Value, error) { eq := first.Equals(second) switch eq.Type() { case value.MISSING, value.NULL: return eq, nil default: if eq.Truth() { return _POS_INF_VALUE, nil } else { return first, nil } } }
func (this *Cover) Evaluate(item value.Value, context Context) (value.Value, error) { var rv value.Value switch item := item.(type) { case value.AnnotatedValue: rv = item.GetCover(this.text) } if rv == nil { return value.MISSING_VALUE, errors.NewEvaluationError(nil, "cover("+this.text+")") } return rv, nil }
/* Retrieve the set for annotated values. If the attachment type is not a set, then throw an invalid distinct set error and return. */ func getSet(item value.Value) (*value.Set, error) { switch item := item.(type) { case value.AnnotatedValue: ps := item.GetAttachment("set") switch ps := ps.(type) { case *value.Set: return ps, nil default: return nil, fmt.Errorf("Invalid DISTINCT set %v of type %T.", ps, ps) } default: return nil, fmt.Errorf("Invalid DISTINCT %v of type %T.", item, item) } }
/* It returns a string based on the input expr value. Values missing, null and strings return themselves. False, true (boolean) and numbers return their string representation. This is done using the Sprint method defined in fmt for Go. All other values map to null. */ func (this *ToString) Apply(context Context, arg value.Value) (value.Value, error) { switch arg.Type() { case value.MISSING, value.NULL, value.STRING: return arg, nil case value.BOOLEAN: return value.NewValue(fmt.Sprint(arg.Actual())), nil case value.NUMBER: f := arg.Actual().(float64) if f == -0 { f = 0 } s := strconv.FormatFloat(f, 'f', -1, 64) return value.NewValue(s), nil case value.BINARY: raw, ok := arg.Actual().([]byte) if !ok { return value.NULL_VALUE, nil } s := string(raw) return value.NewValue(s), nil default: return value.NULL_VALUE, nil } }
func testObjectAdd(e1, e2, e3 Expression, er value.Value, fail bool, t *testing.T) { eop := NewObjectAdd(e1, e2, e3) rv, err := eop.Evaluate(nil, nil) if err != nil { if fail && rv.Actual() == nil { return } t.Errorf("received error %v", err) } else if fail { t.Errorf("error expected, received success") } if er.Collate(rv) != 0 { t.Errorf("mismatch received %v expected %v", rv.Actual(), er.Actual()) } }
/* Aggregates input data by evaluating operands. For missing and null values return the input value itself. Call cumulatePart to compute the intermediate aggregate value and return it. */ func (this *Count) CumulateInitial(item, cumulative value.Value, context Context) (value.Value, error) { if this.Operand() != nil { item, e := this.Operand().Evaluate(item, context) if e != nil { return nil, e } if item.Type() <= value.NULL { return cumulative, nil } } return this.cumulatePart(value.ONE_VALUE, cumulative, context) }
/* Aggregate input partial values into cumulative result number value. If the partial and current cumulative result are both float64 numbers, add them and return. */ func (this *Count) cumulatePart(part, cumulative value.Value, context Context) (value.Value, error) { actual := part.Actual() switch actual := actual.(type) { case float64: count := cumulative.Actual() switch count := count.(type) { case float64: return value.NewValue(count + actual), nil default: return nil, fmt.Errorf("Invalid COUNT %v of type %T.", count, count) } default: return nil, fmt.Errorf("Invalid partial COUNT %v of type %T.", actual, actual) } }
/* This method evaluates the input value and returns the length based on its type. If the input argument is a missing then return a missing value. Convert it to a valid Go type. If it is a string slice of interfaces or object then return its length cast as a number float64. By default return a null value. */ func (this *PolyLength) Apply(context Context, arg value.Value) (value.Value, error) { if arg.Type() == value.MISSING { return value.MISSING_VALUE, nil } switch oa := arg.Actual().(type) { case string: return value.NewValue(float64(len(oa))), nil case []interface{}: return value.NewValue(float64(len(oa))), nil case map[string]interface{}: return value.NewValue(float64(len(oa))), nil default: return value.NULL_VALUE, nil } }
/* IN evaluates to TRUE if the right-hand-side first value is an array and directly contains the left-hand-side second value. If either of the input operands are missing, return missing value, and if the second is not an array return null. Range over the elements of the array and check if any element is equal to the first value, return true. For all other cases, return false. */ func (this *In) Apply(context Context, first, second value.Value) (value.Value, error) { if first.Type() == value.MISSING || second.Type() == value.MISSING { return value.MISSING_VALUE, nil } else if second.Type() != value.ARRAY { return value.NULL_VALUE, nil } sa := second.Actual().([]interface{}) for _, s := range sa { if first.Equals(value.NewValue(s)).Truth() { return value.TRUE_VALUE, nil } } return value.FALSE_VALUE, nil }
/* This method ranges through the array and returns the position of the second value in the array (first value). If the input values are of type missing return a missing value, and for all non array values return null. If not found then return -1. */ func (this *ArrayPosition) Apply(context Context, first, second value.Value) (value.Value, error) { if first.Type() == value.MISSING || second.Type() == value.MISSING { return value.MISSING_VALUE, nil } else if first.Type() != value.ARRAY { return value.NULL_VALUE, nil } fa := first.Actual().([]interface{}) for i, f := range fa { if second.Equals(value.NewValue(f)).Truth() { return value.NewValue(float64(i)), nil } } return value.NewValue(float64(-1)), nil }
/* Directly call the evaluate method for aggregate functions and passe in the receiver, current item and current context, for count with an input expression operand. For a count with no operands (count (*)), get the count from the attachment and then evaluate. */ func (this *Count) Evaluate(item value.Value, context expression.Context) (result value.Value, e error) { if this.Operand() != nil { return this.evaluate(this, item, context) } // Full keyspace count is short-circuited switch item := item.(type) { case value.AnnotatedValue: count := item.GetAttachment("count") if count != nil { return value.NewValue(count), nil } } return this.evaluate(this, item, context) }
// CreateIndex implements datastore.Indexer{} interface. Create a secondary // index on this keyspace func (gsi *gsiKeyspace) CreateIndex( requestId, name string, seekKey, rangeKey expression.Expressions, where expression.Expression, with value.Value) ( datastore.Index, errors.Error) { var partnStr string if seekKey != nil && len(seekKey) > 0 { partnStr = expression.NewStringer().Visit(seekKey[0]) } var whereStr string if where != nil { whereStr = expression.NewStringer().Visit(where) } secStrs := make([]string, len(rangeKey)) for i, key := range rangeKey { s := expression.NewStringer().Visit(key) secStrs[i] = s } var withJSON []byte var err error if with != nil { if withJSON, err = with.MarshalJSON(); err != nil { return nil, errors.NewError(err, "GSI error marshalling WITH clause") } } defnID, err := gsi.gsiClient.CreateIndex( name, gsi.keyspace, /*bucket-name*/ string(c.ForestDB), /*using, by default always forestdb*/ "N1QL", /*exprType*/ partnStr, whereStr, secStrs, false, /*isPrimary*/ withJSON) if err != nil { return nil, errors.NewError(err, "GSI CreateIndex()") } // refresh to get back the newly created index. if err := gsi.Refresh(); err != nil { return nil, err } return gsi.IndexById(defnID2String(defnID)) }