mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-14 17:08:31 +08:00
172 lines
4.7 KiB
Go
172 lines
4.7 KiB
Go
package channels
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"ragflow/internal/harness/graph/types"
|
|
)
|
|
|
|
// ReducerChannel wraps a channel with a reducer function.
|
|
type ReducerChannel struct {
|
|
Channel
|
|
reducer types.ReducerFunc
|
|
}
|
|
|
|
// NewReducerChannel creates a new ReducerChannel.
|
|
func NewReducerChannel(channel Channel, reducer types.ReducerFunc) *ReducerChannel {
|
|
return &ReducerChannel{
|
|
Channel: channel,
|
|
reducer: reducer,
|
|
}
|
|
}
|
|
|
|
// Update applies the reducer to combine new values with the current value.
|
|
func (rc *ReducerChannel) Update(values []interface{}) (bool, error) {
|
|
if len(values) == 0 {
|
|
return false, nil
|
|
}
|
|
|
|
// Read current value from the wrapped channel.
|
|
current, err := rc.Channel.Get()
|
|
|
|
// Combine all values with the current value using the reducer.
|
|
// If the channel is empty, start from values[0] and combine the rest.
|
|
combined := values[0]
|
|
if err == nil {
|
|
combined = rc.reducer(current, combined)
|
|
}
|
|
for i := 1; i < len(values); i++ {
|
|
combined = rc.reducer(combined, values[i])
|
|
}
|
|
|
|
return rc.Channel.Update([]interface{}{combined})
|
|
}
|
|
|
|
// Copy returns a copy of the ReducerChannel.
|
|
func (rc *ReducerChannel) Copy() Channel {
|
|
return &ReducerChannel{
|
|
Channel: rc.Channel.Copy(),
|
|
reducer: rc.reducer,
|
|
}
|
|
}
|
|
|
|
// Checkpoint returns the checkpoint of the wrapped channel.
|
|
func (rc *ReducerChannel) Checkpoint() interface{} {
|
|
return rc.Channel.Checkpoint()
|
|
}
|
|
|
|
// FromCheckpoint restores the wrapped channel from a checkpoint.
|
|
func (rc *ReducerChannel) FromCheckpoint(checkpoint interface{}) Channel {
|
|
return &ReducerChannel{
|
|
Channel: rc.Channel.FromCheckpoint(checkpoint),
|
|
reducer: rc.reducer,
|
|
}
|
|
}
|
|
|
|
// CreateReducerChannel creates a reducer channel based on type hints.
|
|
// It inspects the field type and annotation to determine appropriate channel.
|
|
func CreateReducerChannel(fieldName string, fieldType reflect.Type, reducer types.ReducerFunc) (Channel, error) {
|
|
// Determine default channel based on type
|
|
var channel Channel
|
|
|
|
switch fieldType.Kind() {
|
|
case reflect.Slice, reflect.Array:
|
|
// For slices, use BinaryOperatorAggregate with append operator
|
|
channel = NewBinaryOperatorAggregate(fieldType, ListAppend)
|
|
case reflect.Map:
|
|
// For maps, use BinaryOperatorAggregate with merge operator
|
|
channel = NewBinaryOperatorAggregate(fieldType, func(a, b interface{}) interface{} {
|
|
if aMap, ok := a.(map[string]interface{}); ok {
|
|
if bMap, ok := b.(map[string]interface{}); ok {
|
|
result := make(map[string]interface{}, len(aMap)+len(bMap))
|
|
for k, v := range aMap {
|
|
result[k] = v
|
|
}
|
|
for k, v := range bMap {
|
|
result[k] = v
|
|
}
|
|
return result
|
|
}
|
|
}
|
|
return b
|
|
})
|
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
|
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
|
reflect.Float32, reflect.Float64:
|
|
// For numeric types, use BinaryOperatorAggregate with add operator.
|
|
// Pass reflect.Zero(fieldType).Interface() instead of fieldType itself
|
|
// so createZeroValue uses the correct underlying numeric type for its
|
|
// reflect.Zero call, not the reflect.Type descriptor.
|
|
channel = NewBinaryOperatorAggregate(reflect.Zero(fieldType).Interface(), IntAdd)
|
|
default:
|
|
// Default to LastValue channel
|
|
channel = NewLastValue(fieldType)
|
|
}
|
|
|
|
// Set the channel key
|
|
channel.SetKey(fieldName)
|
|
|
|
// If a reducer is provided, wrap the channel with it
|
|
if reducer != nil {
|
|
return NewReducerChannel(channel, reducer), nil
|
|
}
|
|
|
|
return channel, nil
|
|
}
|
|
|
|
// Built-in reducer functions for common types.
|
|
var (
|
|
// AddReducer adds numeric values.
|
|
AddReducer = func(current, update interface{}) interface{} {
|
|
if current == nil {
|
|
return update
|
|
}
|
|
// Try int addition
|
|
if ci, ok := current.(int); ok {
|
|
if ui, ok := update.(int); ok {
|
|
return ci + ui
|
|
}
|
|
}
|
|
// Try float64 addition
|
|
if cf, ok := current.(float64); ok {
|
|
if uf, ok := update.(float64); ok {
|
|
return cf + uf
|
|
}
|
|
}
|
|
// Fallback: return update
|
|
return update
|
|
}
|
|
|
|
// AppendReducer appends to slices.
|
|
AppendReducer = func(current, update interface{}) interface{} {
|
|
if current == nil {
|
|
return []interface{}{update}
|
|
}
|
|
if slice, ok := current.([]interface{}); ok {
|
|
return append(slice, update)
|
|
}
|
|
// Convert to slice
|
|
return []interface{}{current, update}
|
|
}
|
|
|
|
// MergeReducer merges maps.
|
|
MergeReducer = func(current, update interface{}) interface{} {
|
|
if current == nil {
|
|
return update
|
|
}
|
|
if currentMap, ok := current.(map[string]interface{}); ok {
|
|
if updateMap, ok := update.(map[string]interface{}); ok {
|
|
result := make(map[string]interface{}, len(currentMap)+len(updateMap))
|
|
for k, v := range currentMap {
|
|
result[k] = v
|
|
}
|
|
for k, v := range updateMap {
|
|
result[k] = v
|
|
}
|
|
return result
|
|
}
|
|
}
|
|
return update
|
|
}
|
|
)
|