2018-09-18 22:42:38 +02:00
|
|
|
package expressions
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/collections"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/core"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/expressions/clauses"
|
|
|
|
"github.com/MontFerret/ferret/pkg/runtime/values"
|
|
|
|
)
|
|
|
|
|
|
|
|
type ForExpression struct {
|
|
|
|
src core.SourceMap
|
2018-10-25 03:30:05 +02:00
|
|
|
dataSource collections.Iterable
|
2018-09-18 22:42:38 +02:00
|
|
|
predicate core.Expression
|
2018-10-25 03:30:05 +02:00
|
|
|
distinct bool
|
2018-09-18 22:42:38 +02:00
|
|
|
spread bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewForExpression(
|
|
|
|
src core.SourceMap,
|
2018-10-25 03:30:05 +02:00
|
|
|
dataSource collections.Iterable,
|
2018-09-18 22:42:38 +02:00
|
|
|
predicate core.Expression,
|
2018-10-25 03:30:05 +02:00
|
|
|
distinct,
|
2018-09-18 22:42:38 +02:00
|
|
|
spread bool,
|
|
|
|
) (*ForExpression, error) {
|
2018-10-28 07:45:26 +02:00
|
|
|
if dataSource == nil {
|
|
|
|
return nil, core.Error(core.ErrMissedArgument, "missed source expression")
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
if predicate == nil {
|
|
|
|
return nil, core.Error(core.ErrMissedArgument, "missed return expression")
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return &ForExpression{
|
|
|
|
src,
|
|
|
|
dataSource,
|
|
|
|
predicate,
|
2018-10-25 03:30:05 +02:00
|
|
|
distinct,
|
2018-09-18 22:42:38 +02:00
|
|
|
spread,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
func (e *ForExpression) AddLimit(src core.SourceMap, size, count int) error {
|
|
|
|
limit, err := clauses.NewLimitClause(src, e.dataSource, size, count)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
e.dataSource = limit
|
|
|
|
|
|
|
|
return nil
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
func (e *ForExpression) AddFilter(src core.SourceMap, exp core.Expression) error {
|
|
|
|
filter, err := clauses.NewFilterClause(src, e.dataSource, exp)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
e.dataSource = filter
|
|
|
|
|
|
|
|
return nil
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
func (e *ForExpression) AddSort(src core.SourceMap, sorters ...*clauses.SorterExpression) error {
|
|
|
|
sort, err := clauses.NewSortClause(src, e.dataSource, sorters...)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
e.dataSource = sort
|
|
|
|
|
|
|
|
return nil
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
func (e *ForExpression) AddCollect(src core.SourceMap, params *clauses.Collect) error {
|
|
|
|
collect, err := clauses.NewCollectClause(src, e.dataSource, params)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
e.dataSource = collect
|
|
|
|
|
|
|
|
return nil
|
2018-10-07 07:23:35 +02:00
|
|
|
}
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
func (e *ForExpression) AddStatement(stmt core.Expression) error {
|
|
|
|
tap, ok := e.dataSource.(*BlockExpression)
|
|
|
|
|
|
|
|
if !ok {
|
|
|
|
t, err := NewBlockExpression(e.dataSource)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
tap = t
|
|
|
|
e.dataSource = tap
|
|
|
|
}
|
|
|
|
|
|
|
|
tap.Add(stmt)
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-09-18 22:42:38 +02:00
|
|
|
func (e *ForExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
|
|
|
|
iterator, err := e.dataSource.Iterate(ctx, scope)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return values.None, err
|
|
|
|
}
|
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
// Hash map for a check for uniqueness
|
|
|
|
var hashTable map[uint64]bool
|
|
|
|
|
|
|
|
if e.distinct {
|
|
|
|
hashTable = make(map[uint64]bool)
|
|
|
|
}
|
|
|
|
|
2018-09-18 22:42:38 +02:00
|
|
|
res := values.NewArray(10)
|
2018-10-28 07:45:26 +02:00
|
|
|
for {
|
|
|
|
nextScope, err := iterator.Next(ctx, scope)
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return values.None, core.SourceError(e.src, err)
|
|
|
|
}
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
// no data anymore
|
|
|
|
if nextScope == nil {
|
|
|
|
break
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
|
2018-10-28 07:45:26 +02:00
|
|
|
out, err := e.predicate.Exec(ctx, nextScope)
|
2018-09-18 22:42:38 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return values.None, err
|
|
|
|
}
|
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
var add bool
|
|
|
|
|
|
|
|
// The result shouldn't be distinct
|
|
|
|
// Just add the output
|
|
|
|
if !e.distinct {
|
|
|
|
add = true
|
2018-09-18 22:42:38 +02:00
|
|
|
} else {
|
2018-10-25 03:30:05 +02:00
|
|
|
// We need to check whether the value already exists in the result set
|
|
|
|
hash := out.Hash()
|
|
|
|
_, exists := hashTable[hash]
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
if !exists {
|
|
|
|
hashTable[hash] = true
|
|
|
|
add = true
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
2018-10-25 03:30:05 +02:00
|
|
|
}
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
if add {
|
|
|
|
if !e.spread {
|
|
|
|
res.Push(out)
|
|
|
|
} else {
|
|
|
|
elements, ok := out.(*values.Array)
|
2018-09-18 22:42:38 +02:00
|
|
|
|
2018-10-25 03:30:05 +02:00
|
|
|
if !ok {
|
|
|
|
return values.None, core.Error(core.ErrInvalidOperation, "spread of non-array value")
|
|
|
|
}
|
|
|
|
|
|
|
|
elements.ForEach(func(i core.Value, _ int) bool {
|
|
|
|
res.Push(i)
|
|
|
|
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
}
|
2018-09-18 22:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return res, nil
|
|
|
|
}
|