mirror of
https://github.com/MontFerret/ferret.git
synced 2025-08-15 20:02:56 +02:00
Refactor loop projection logic: remove redundant loop
parameter from projection functions, optimize group variable handling, and enhance integration tests for COLLECT
queries.
This commit is contained in:
@@ -40,10 +40,8 @@ func (c *LoopCollectCompiler) compileCollect(ctx fql.ICollectClauseContext, aggr
|
|||||||
return core.NewKV(vm.NoopOperand, vm.NoopOperand), nil
|
return core.NewKV(vm.NoopOperand, vm.NoopOperand), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
loop := c.ctx.Loops.Current()
|
|
||||||
|
|
||||||
kv, groupSelectors := c.initializeCollector(grouping)
|
kv, groupSelectors := c.initializeCollector(grouping)
|
||||||
projectionVarName, collectorType := c.initializeProjection(ctx, loop, kv, counter, grouping != nil)
|
projectionVarName, collectorType := c.initializeProjection(ctx, kv, counter, grouping != nil)
|
||||||
|
|
||||||
// If we use aggregators, we need to collect group items by key
|
// If we use aggregators, we need to collect group items by key
|
||||||
if aggregation && collectorType != core.CollectorTypeKeyGroup {
|
if aggregation && collectorType != core.CollectorTypeKeyGroup {
|
||||||
@@ -51,6 +49,7 @@ func (c *LoopCollectCompiler) compileCollect(ctx fql.ICollectClauseContext, aggr
|
|||||||
collectorType = core.CollectorTypeKeyGroup
|
collectorType = core.CollectorTypeKeyGroup
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loop := c.ctx.Loops.Current()
|
||||||
c.finalizeCollector(loop, collectorType, kv)
|
c.finalizeCollector(loop, collectorType, kv)
|
||||||
|
|
||||||
// If we are using a projection, we need to ensure the loop is set to ForInLoop
|
// If we are using a projection, we need to ensure the loop is set to ForInLoop
|
||||||
@@ -115,13 +114,13 @@ func (c *LoopCollectCompiler) finalizeCollector(loop *core.Loop, collectorType c
|
|||||||
|
|
||||||
// initializeProjection handles the projection setup for group variables and counters.
|
// initializeProjection handles the projection setup for group variables and counters.
|
||||||
// Returns the projection variable name and the appropriate collector type.
|
// Returns the projection variable name and the appropriate collector type.
|
||||||
func (c *LoopCollectCompiler) initializeProjection(ctx fql.ICollectClauseContext, loop *core.Loop, kv *core.KV, counter fql.ICollectCounterContext, hasGrouping bool) (string, core.CollectorType) {
|
func (c *LoopCollectCompiler) initializeProjection(ctx fql.ICollectClauseContext, kv *core.KV, counter fql.ICollectCounterContext, hasGrouping bool) (string, core.CollectorType) {
|
||||||
projectionVariableName := ""
|
projectionVariableName := ""
|
||||||
collectorType := core.CollectorTypeKey
|
collectorType := core.CollectorTypeKey
|
||||||
|
|
||||||
// Handle group variable projection
|
// Handle group variable projection
|
||||||
if groupVar := ctx.CollectGroupVariable(); groupVar != nil {
|
if groupVar := ctx.CollectGroupVariable(); groupVar != nil {
|
||||||
projectionVariableName = c.compileGroupVariableProjection(loop, kv, groupVar)
|
projectionVariableName = c.compileGroupVariableProjection(kv, groupVar)
|
||||||
collectorType = core.CollectorTypeKeyGroup
|
collectorType = core.CollectorTypeKeyGroup
|
||||||
return projectionVariableName, collectorType
|
return projectionVariableName, collectorType
|
||||||
}
|
}
|
||||||
@@ -176,15 +175,15 @@ func (c *LoopCollectCompiler) compileGrouping(ctx fql.ICollectGroupingContext) (
|
|||||||
}
|
}
|
||||||
|
|
||||||
// compileGroupVariableProjection processes group variable projections (both default and custom).
|
// compileGroupVariableProjection processes group variable projections (both default and custom).
|
||||||
func (c *LoopCollectCompiler) compileGroupVariableProjection(loop *core.Loop, kv *core.KV, groupVar fql.ICollectGroupVariableContext) string {
|
func (c *LoopCollectCompiler) compileGroupVariableProjection(kv *core.KV, groupVar fql.ICollectGroupVariableContext) string {
|
||||||
// Handle default projection (identifier)
|
// Handle default projection (identifier)
|
||||||
if identifier := groupVar.Identifier(); identifier != nil {
|
if identifier := groupVar.Identifier(); identifier != nil {
|
||||||
return c.compileDefaultGroupProjection(loop, kv, identifier, groupVar.CollectGroupVariableKeeper())
|
return c.compileDefaultGroupProjection(kv, identifier, groupVar.CollectGroupVariableKeeper())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle custom projection (selector expression)
|
// Handle custom projection (selector expression)
|
||||||
if selector := groupVar.CollectSelector(); selector != nil {
|
if selector := groupVar.CollectSelector(); selector != nil {
|
||||||
return c.compileCustomGroupProjection(loop, kv, selector)
|
return c.compileCustomGroupProjection(kv, selector)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
@@ -226,7 +225,7 @@ func (c *LoopCollectCompiler) compileGroupSelectorVariables(selectors []fql.ICol
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *LoopCollectCompiler) compileDefaultGroupProjection(loop *core.Loop, kv *core.KV, identifier antlr.TerminalNode, keeper fql.ICollectGroupVariableKeeperContext) string {
|
func (c *LoopCollectCompiler) compileDefaultGroupProjection(kv *core.KV, identifier antlr.TerminalNode, keeper fql.ICollectGroupVariableKeeperContext) string {
|
||||||
if keeper == nil {
|
if keeper == nil {
|
||||||
variables := c.ctx.Symbols.LocalVariables()
|
variables := c.ctx.Symbols.LocalVariables()
|
||||||
scope := core.NewScopeProjection(c.ctx.Registers, c.ctx.Emitter, c.ctx.Symbols, variables)
|
scope := core.NewScopeProjection(c.ctx.Registers, c.ctx.Emitter, c.ctx.Symbols, variables)
|
||||||
@@ -255,7 +254,7 @@ func (c *LoopCollectCompiler) compileDefaultGroupProjection(loop *core.Loop, kv
|
|||||||
return identifier.GetText()
|
return identifier.GetText()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *LoopCollectCompiler) compileCustomGroupProjection(_ *core.Loop, kv *core.KV, selector fql.ICollectSelectorContext) string {
|
func (c *LoopCollectCompiler) compileCustomGroupProjection(kv *core.KV, selector fql.ICollectSelectorContext) string {
|
||||||
selectorReg := c.ctx.ExprCompiler.Compile(selector.Expression())
|
selectorReg := c.ctx.ExprCompiler.Compile(selector.Expression())
|
||||||
c.ctx.Emitter.EmitMove(kv.Value, selectorReg)
|
c.ctx.Emitter.EmitMove(kv.Value, selectorReg)
|
||||||
c.ctx.Registers.Free(selectorReg)
|
c.ctx.Registers.Free(selectorReg)
|
||||||
|
@@ -513,5 +513,60 @@ FOR u IN users
|
|||||||
"uniqueSkillCount": 4,
|
"uniqueSkillCount": 4,
|
||||||
},
|
},
|
||||||
}, "Should aggregate with array operations"),
|
}, "Should aggregate with array operations"),
|
||||||
|
CaseArray(`
|
||||||
|
LET users = [
|
||||||
|
{
|
||||||
|
active: true,
|
||||||
|
married: true,
|
||||||
|
age: 31,
|
||||||
|
gender: "m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
active: true,
|
||||||
|
married: false,
|
||||||
|
age: 25,
|
||||||
|
gender: "f"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
active: true,
|
||||||
|
married: false,
|
||||||
|
age: 36,
|
||||||
|
gender: "m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
active: false,
|
||||||
|
married: true,
|
||||||
|
age: 69,
|
||||||
|
gender: "m"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
active: true,
|
||||||
|
married: true,
|
||||||
|
age: 45,
|
||||||
|
gender: "f"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
FOR i IN 0..4
|
||||||
|
LET u = users[i]
|
||||||
|
COLLECT gender = u.gender
|
||||||
|
AGGREGATE minAge = MIN(u.age), maxAge = MAX(u.age)
|
||||||
|
|
||||||
|
RETURN {
|
||||||
|
gender: gender,
|
||||||
|
minAge,
|
||||||
|
maxAge
|
||||||
|
}
|
||||||
|
`, []any{
|
||||||
|
map[string]any{
|
||||||
|
"gender": "f",
|
||||||
|
"maxAge": 45,
|
||||||
|
"minAge": 25,
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"gender": "m",
|
||||||
|
"maxAge": 69,
|
||||||
|
"minAge": 31,
|
||||||
|
},
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@@ -610,7 +610,7 @@ func TestForWhileNested(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
SkipCaseArray(`
|
CaseArray(`
|
||||||
LET users = [
|
LET users = [
|
||||||
{
|
{
|
||||||
active: true,
|
active: true,
|
||||||
@@ -652,40 +652,45 @@ func TestForWhileNested(t *testing.T) {
|
|||||||
gender: CONCAT(gender, n),
|
gender: CONCAT(gender, n),
|
||||||
values: genders
|
values: genders
|
||||||
}
|
}
|
||||||
`, []any{map[string]any{
|
`, []any{
|
||||||
"gender": "f0",
|
|
||||||
"values": []map[string]any{
|
|
||||||
{
|
|
||||||
"i": map[string]any{
|
|
||||||
"active": true,
|
|
||||||
"age": 25,
|
|
||||||
"gender": "f",
|
|
||||||
"married": false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"i": map[string]any{
|
|
||||||
"active": true,
|
|
||||||
"age": 45,
|
|
||||||
"gender": "f",
|
|
||||||
"married": true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"gender": "f1",
|
"gender": "f0",
|
||||||
"values": []map[string]any{
|
"values": []any{
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 1,
|
||||||
|
"u": map[string]any{
|
||||||
"active": true,
|
"active": true,
|
||||||
"age": 25,
|
"age": 25,
|
||||||
"gender": "f",
|
"gender": "f",
|
||||||
"married": false,
|
"married": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 4,
|
||||||
|
"u": map[string]any{
|
||||||
|
"active": true,
|
||||||
|
"age": 45,
|
||||||
|
"gender": "f",
|
||||||
|
"married": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"gender": "f1",
|
||||||
|
"values": []any{
|
||||||
|
map[string]any{
|
||||||
|
"i": 1,
|
||||||
|
"u": map[string]any{
|
||||||
|
"active": true,
|
||||||
|
"age": 25,
|
||||||
|
"gender": "f",
|
||||||
|
"married": false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"i": 4,
|
||||||
|
"u": map[string]any{
|
||||||
"active": true,
|
"active": true,
|
||||||
"age": 45,
|
"age": 45,
|
||||||
"gender": "f",
|
"gender": "f",
|
||||||
@@ -696,25 +701,28 @@ func TestForWhileNested(t *testing.T) {
|
|||||||
},
|
},
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"gender": "m0",
|
"gender": "m0",
|
||||||
"values": []map[string]any{
|
"values": []any{
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 0,
|
||||||
|
"u": map[string]any{
|
||||||
"active": true,
|
"active": true,
|
||||||
"age": 31,
|
"age": 31,
|
||||||
"gender": "m",
|
"gender": "m",
|
||||||
"married": true,
|
"married": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 2,
|
||||||
|
"u": map[string]any{
|
||||||
"active": true,
|
"active": true,
|
||||||
"age": 36,
|
"age": 36,
|
||||||
"gender": "m",
|
"gender": "m",
|
||||||
"married": false,
|
"married": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 3,
|
||||||
|
"u": map[string]any{
|
||||||
"active": false,
|
"active": false,
|
||||||
"age": 69,
|
"age": 69,
|
||||||
"gender": "m",
|
"gender": "m",
|
||||||
@@ -725,25 +733,28 @@ func TestForWhileNested(t *testing.T) {
|
|||||||
},
|
},
|
||||||
map[string]any{
|
map[string]any{
|
||||||
"gender": "m1",
|
"gender": "m1",
|
||||||
"values": []map[string]any{
|
"values": []any{
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 0,
|
||||||
|
"u": map[string]any{
|
||||||
"active": true,
|
"active": true,
|
||||||
"age": 31,
|
"age": 31,
|
||||||
"gender": "m",
|
"gender": "m",
|
||||||
"married": true,
|
"married": true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 2,
|
||||||
|
"u": map[string]any{
|
||||||
"active": true,
|
"active": true,
|
||||||
"age": 36,
|
"age": 36,
|
||||||
"gender": "m",
|
"gender": "m",
|
||||||
"married": false,
|
"married": false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
map[string]any{
|
||||||
"i": map[string]any{
|
"i": 3,
|
||||||
|
"u": map[string]any{
|
||||||
"active": false,
|
"active": false,
|
||||||
"age": 69,
|
"age": 69,
|
||||||
"gender": "m",
|
"gender": "m",
|
||||||
@@ -753,7 +764,7 @@ func TestForWhileNested(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
SkipCaseArray(`
|
CaseArray(`
|
||||||
LET users = [
|
LET users = [
|
||||||
{
|
{
|
||||||
active: true,
|
active: true,
|
||||||
|
Reference in New Issue
Block a user