func (this *KeyJoin) joinItems(item *dparval.Value, keyItem *dparval.Value) bool { if keyItem == nil { if this.Type == "LEFT" { return this.Base.SendItem(item) } return true } newItem := item.Duplicate() /* join the item and ship it */ if this.Projection != nil { keyProj, Error := this.Base.Evaluate(this.Projection, keyItem) if Error != nil { switch err := Error.(type) { case *dparval.Undefined: return true default: return this.Base.SendError(query.NewError(err, "Internal error in KeyJoin")) } } newItem.SetPath(this.As, keyProj) } else { newItem.SetPath(this.As, keyItem) } this.rowsFetched += 1 this.Base.SendItem(newItem) return true }
func (this *CollectionFirstOperator) Evaluate(item *dparval.Value) (*dparval.Value, error) { // first evaluate the over ov, err := this.Over.Evaluate(item) if err != nil { return nil, err } // see if we're dealing with an array if ov.Type() == dparval.ARRAY { // by accessing the array contents this way // we avoid parsing it ok := true index := 0 for ok { inner, err := ov.Index(index) index = index + 1 if err != nil { switch err := err.(type) { case *dparval.Undefined: ok = false default: return nil, err } } else { // duplicate the existing context innerContext := item.Duplicate() // add this object named as the alias innerContext.SetPath(this.As, inner) if this.Condition != nil { // now evaluate the condition in this new context innerResult, err := this.Condition.Evaluate(innerContext) if err != nil { switch err := err.(type) { case *dparval.Undefined: // this is not true, keep trying continue default: // any other error should be returned to caller return nil, err } } innerResultVal := innerResult.Value() // check to see if this value is true innerBoolResult := ValueInBooleanContext(innerResultVal) if innerBoolResult == true { // now we have to evaluate the output expression outputResult, err := this.Output.Evaluate(innerContext) return outputResult, err } } else { // now we have to evaluate the output expression outputResult, err := this.Output.Evaluate(innerContext) return outputResult, err } } } return nil, &dparval.Undefined{} } return nil, &dparval.Undefined{} }
func (this *KeyJoin) flushBatch(baseItem *dparval.Value, ids []string) bool { bulkResponse, err := this.bucket.BulkFetch(ids) if err != nil { return this.Base.SendError(query.NewError(err, "error getting bulk response")) } // now we need to emit the bulk fetched items in the correct order (from the id list) for _, v := range ids { item, ok := bulkResponse[v] newItem := baseItem.Duplicate() if item == nil { if this.Type == "LEFT" { this.Base.SendItem(newItem) } continue } if ok { if this.Projection != nil { projectedVal, err := this.Base.projectedValueOfResultExpression(item, ast.NewResultExpression(this.Projection)) if err != nil { switch err := err.(type) { case *dparval.Undefined: // undefined contributes nothing to the result map continue default: return this.Base.SendError(query.NewError(err, "unexpected error projecting fetch expression")) } } else { newItem.SetPath(this.As, projectedVal) } } else { newItem.SetPath(this.As, item) } this.Base.SendItem(newItem) this.rowsFetched += 1 } } return true }
func (this *Unnest) processItem(item *dparval.Value) bool { val, err := this.Base.Evaluate(this.Over, item) if err != nil { switch err := err.(type) { case *dparval.Undefined: if val == nil && this.Type == "LEFT" { return this.Base.SendItem(item) } return true default: return this.Base.SendError(query.NewError(err, "Internal Error")) } } if val.Type() == dparval.ARRAY { ok := true index := 0 for ok { inner, err := val.Index(index) index = index + 1 if err != nil { switch err := err.(type) { case *dparval.Undefined: ok = false default: this.Base.SendError(query.NewError(err, "Internal Error")) return false } } else { newItem := item.Duplicate() newItem.SetPath(this.As, inner) this.Base.SendItem(newItem) } } } else if this.Type == "LEFT" { // send back the item since this is a left join this.Base.SendItem(item) } return true }
func (this *KeyNest) processItem(item *dparval.Value) bool { if item == nil { return true } this.Right = make([]interface{}, 0) newItem := item.Duplicate() val, err := this.Base.Evaluate(this.Keys.Expr, item) if err != nil { switch err := err.(type) { case *dparval.Undefined: if this.Type == "LEFT" { this.Base.SendItem(newItem) return true } return true default: return this.Base.SendError(query.NewError(err, "Internal error in KeyNest")) } } if val.Type() == dparval.STRING { if this.Keys.Type == "KEYS" { this.Base.SendError(query.NewError(fmt.Errorf("KEYS expression should evaluate to an array"), "")) return false } if val.Value() == nil { if this.Type == "LEFT" { return this.Base.SendItem(item) } return true } fetch_id := val.Value().(string) keyitem, err := this.bucket.Fetch(fetch_id) if err != nil { this.Base.SendError(query.NewError(err, "Unable to fetch key")) return false } if keyitem != nil { if this.Projection != nil { keyproj, Error := this.Base.Evaluate(this.Projection, keyitem) if Error != nil { switch err := Error.(type) { case *dparval.Undefined: return true default: return this.Base.SendError(query.NewError(err, "Internal error in KeyNest")) } } this.Right = append(this.Right, keyproj) } else { this.Right = append(this.Right, keyitem) } newItem.SetPath(this.As, this.Right) this.Base.SendItem(newItem) } else if this.Type == "LEFT" { newItem.SetPath(this.As, this.Right) this.Base.SendItem(newItem) } this.rowsFetched += 1 } else if val.Type() == dparval.ARRAY { ok := true index := 0 ids := make([]string, 0, FETCH_BATCH_SIZE) if this.Keys.Type == "KEY" { this.Base.SendError(query.NewError(fmt.Errorf("KEY used with an array argument"), "")) return false } for ok { id, err := val.Index(index) index = index + 1 array_len := len(val.Value().([]interface{})) if array_len == 0 { if this.Type == "LEFT" { item.SetPath(this.As, this.Right) this.Base.SendItem(item) } return true } if err != nil { if len(ids) != 0 { return this.flushBatch(item, ids) } return true } fetch_id := id.Value() switch fetch_id.(type) { case string: default: item.SetPath(this.As, this.Right) this.Base.SendItem(item) return false } if fetch_id != nil { ids = append(ids, fetch_id.(string)) } else if this.Type == "LEFT" { this.Base.SendItem(item) continue } if this.rowsFetched != 0 && index%FETCH_BATCH_SIZE == 0 { // do a bulk fetch err := this.flushBatch(newItem, ids) if err != true { return false } ids = make([]string, 0, FETCH_BATCH_SIZE) } } newItem.SetPath(this.As, this.Right) this.Base.SendItem(newItem) } else if this.Type == "LEFT" { this.Base.SendItem(newItem) } return true }