service/rank/algo_data.go (291 lines of code) (raw):
package rank
import (
"reflect"
"sync"
"github.com/alibaba/pairec/v2/algorithm/eas"
"github.com/alibaba/pairec/v2/algorithm/eas/easyrec"
"github.com/alibaba/pairec/v2/algorithm/response"
"github.com/alibaba/pairec/v2/module"
"github.com/alibaba/pairec/v2/utils"
)
type IAlgoData interface {
GetFeatures() interface{}
SetError(err error)
Error() error
GetItems() []*module.Item
GetAlgoResult() map[string][]response.AlgoResponse
SetAlgoResult(algoName string, results []response.AlgoResponse)
}
type IAlgoDataGenerator interface {
AddFeatures(item *module.Item, itemFeatures map[string]interface{}, userFeatures map[string]interface{})
GeneratorAlgoData() IAlgoData
// GeneratorAlgoDataDebug generator algo data with debug mode
GeneratorAlgoDataDebug() IAlgoData
GeneratorAlgoDataDebugWithLevel(level int) IAlgoData
HasFeatures() bool
SetItemFeatures([]string)
}
func CreateAlgoDataGenerator(processor string, contextFeatures []string) IAlgoDataGenerator {
if processor == eas.Eas_Processor_EASYREC {
return NewEasyrecAlgoDataGenerator(contextFeatures)
} else {
return NewAlgoDataGenerator()
}
}
type AlgoDataBase struct {
Items []*module.Item
// RequestData []map[string]interface{}
AlgoResult map[string][]response.AlgoResponse
Err error
Mutex sync.Mutex
}
func (a *AlgoDataBase) SetError(err error) {
a.Err = err
}
func (a *AlgoDataBase) Error() error {
return a.Err
}
func (a *AlgoDataBase) SetAlgoResult(algoName string, results []response.AlgoResponse) {
a.Mutex.Lock()
defer a.Mutex.Unlock()
a.AlgoResult[algoName] = results
}
func (a *AlgoDataBase) GetAlgoResult() map[string][]response.AlgoResponse {
return a.AlgoResult
}
func (a *AlgoDataBase) GetItems() []*module.Item {
return a.Items
}
type AlgoData struct {
*AlgoDataBase
RequestData []map[string]interface{}
}
func (d *AlgoData) GetFeatures() interface{} {
return d.RequestData
}
type EasyrecAlgoData struct {
*AlgoDataBase
easyrecRequest *easyrec.PBRequest
}
func (d *EasyrecAlgoData) GetFeatures() interface{} {
return d.easyrecRequest
}
type AlgoDataGenerator struct {
requestData []map[string]interface{}
requestItem []*module.Item
}
// SetItemFeatures implements IAlgoDataGenerator.
func (g *AlgoDataGenerator) SetItemFeatures([]string) {
}
func NewAlgoDataGenerator() *AlgoDataGenerator {
return &AlgoDataGenerator{
requestData: make([]map[string]interface{}, 0, 100),
requestItem: make([]*module.Item, 0, 100),
}
}
func (g *AlgoDataGenerator) AddFeatures(item *module.Item, itemFeatures map[string]interface{}, userFeatures map[string]interface{}) {
if item != nil {
g.requestItem = append(g.requestItem, item)
}
features := make(map[string]interface{}, len(itemFeatures)+len(userFeatures))
for k, v := range userFeatures {
features[k] = v
}
for k, v := range itemFeatures {
features[k] = v
}
g.requestData = append(g.requestData, features)
}
func (g *AlgoDataGenerator) GeneratorAlgoData() IAlgoData {
copydata := make([]map[string]interface{}, len(g.requestData))
copyItems := make([]*module.Item, len(g.requestItem))
copy(copyItems, g.requestItem)
copy(copydata, g.requestData)
algoData := &AlgoData{
AlgoDataBase: &AlgoDataBase{
Items: copyItems,
AlgoResult: make(map[string][]response.AlgoResponse),
},
RequestData: copydata,
}
g.requestData = g.requestData[:0]
g.requestItem = g.requestItem[:0]
return algoData
}
func (g *AlgoDataGenerator) GeneratorAlgoDataDebug() IAlgoData {
return g.GeneratorAlgoData()
}
func (g *AlgoDataGenerator) GeneratorAlgoDataDebugWithLevel(level int) IAlgoData {
return g.GeneratorAlgoData()
}
func (g *AlgoDataGenerator) HasFeatures() bool {
return len(g.requestData) > 0
}
type feature struct {
name string
valueType reflect.Type
}
func (f *feature) defaultValue() interface{} {
switch f.valueType.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return int(0)
case reflect.Float32, reflect.Float64:
return float64(0)
case reflect.String:
return ""
default:
return ""
}
}
type EasyrecAlgoDataGenerator struct {
requestItem []*module.Item
contextFeatures map[string][]interface{}
parseFeature bool
itemFeatures []*feature
userFeatures map[string]interface{}
inputItemFeatures []*feature
inputItemFeatureMap map[string][]interface{}
parseInputItemFeature bool
}
func NewEasyrecAlgoDataGenerator(contextFeatures []string) *EasyrecAlgoDataGenerator {
generator := &EasyrecAlgoDataGenerator{
requestItem: make([]*module.Item, 0, 100),
contextFeatures: make(map[string][]interface{}, 8),
parseFeature: true,
}
if len(contextFeatures) > 0 {
for _, featureName := range contextFeatures {
feature := &feature{
name: featureName,
valueType: reflect.TypeOf(""),
}
generator.itemFeatures = append(generator.itemFeatures, feature)
}
}
return generator
}
// SetItemFeatures implements IAlgoDataGenerator.
func (generator *EasyrecAlgoDataGenerator) SetItemFeatures(inputItemFeatures []string) {
if len(inputItemFeatures) > 0 {
generator.inputItemFeatureMap = make(map[string][]interface{}, 8)
if inputItemFeatures[0] != "*" {
generator.parseInputItemFeature = true
for _, featureName := range inputItemFeatures {
feature := &feature{
name: featureName,
valueType: reflect.TypeOf(""),
}
generator.inputItemFeatures = append(generator.inputItemFeatures, feature)
}
}
} else {
generator.parseInputItemFeature = true
}
}
func (g *EasyrecAlgoDataGenerator) AddFeatures(item *module.Item, itemFeatures map[string]interface{}, userFeatures map[string]interface{}) {
if item != nil {
g.requestItem = append(g.requestItem, item)
}
if !g.parseFeature {
for k, v := range itemFeatures {
feature := &feature{
name: k,
valueType: reflect.TypeOf(v),
}
g.itemFeatures = append(g.itemFeatures, feature)
}
g.userFeatures = userFeatures
g.parseFeature = true
}
if !g.parseInputItemFeature {
var contextItemFeatureNames []string
for _, v := range g.itemFeatures {
contextItemFeatureNames = append(contextItemFeatureNames, v.name)
}
for k, v := range itemFeatures {
if utils.IndexOfArray(contextItemFeatureNames, k) == -1 {
feature := &feature{
name: k,
valueType: reflect.TypeOf(v),
}
g.inputItemFeatures = append(g.inputItemFeatures, feature)
}
}
g.parseInputItemFeature = true
}
if len(g.userFeatures) == 0 {
g.userFeatures = userFeatures
}
for _, f := range g.itemFeatures {
if v, ok := itemFeatures[f.name]; ok {
g.contextFeatures[f.name] = append(g.contextFeatures[f.name], v)
} else {
g.contextFeatures[f.name] = append(g.contextFeatures[f.name], f.defaultValue())
}
}
for _, f := range g.inputItemFeatures {
if v, ok := itemFeatures[f.name]; ok {
g.inputItemFeatureMap[f.name] = append(g.inputItemFeatureMap[f.name], v)
} else {
g.inputItemFeatureMap[f.name] = append(g.inputItemFeatureMap[f.name], f.defaultValue())
}
}
}
func (g *EasyrecAlgoDataGenerator) GeneratorAlgoData() IAlgoData {
copyItems := make([]*module.Item, len(g.requestItem))
copy(copyItems, g.requestItem)
builder := easyrec.NewEasyrecRequestBuilder()
for k, v := range g.userFeatures {
builder.AddUserFeature(k, v)
}
for _, item := range g.requestItem {
builder.AddItemId(string(item.Id))
}
for k, v := range g.contextFeatures {
builder.AddContextFeature(k, v)
g.contextFeatures[k] = g.contextFeatures[k][:0]
}
for k, v := range g.inputItemFeatureMap {
builder.AddItemFeature(k, v)
g.inputItemFeatureMap[k] = g.inputItemFeatureMap[k][:0]
}
algoData := &EasyrecAlgoData{
AlgoDataBase: &AlgoDataBase{
Items: copyItems,
AlgoResult: make(map[string][]response.AlgoResponse),
},
easyrecRequest: builder.EasyrecRequest(),
}
g.requestItem = g.requestItem[:0]
return algoData
}
func (g *EasyrecAlgoDataGenerator) GeneratorAlgoDataDebug() IAlgoData {
return g.GeneratorAlgoDataDebugWithLevel(1)
}
func (g *EasyrecAlgoDataGenerator) GeneratorAlgoDataDebugWithLevel(level int) IAlgoData {
copyItems := make([]*module.Item, len(g.requestItem))
copy(copyItems, g.requestItem)
builder := easyrec.NewEasyrecRequestBuilderDebugWithLevel(level)
for k, v := range g.userFeatures {
builder.AddUserFeature(k, v)
}
for _, item := range g.requestItem {
builder.AddItemId(string(item.Id))
}
for k, v := range g.contextFeatures {
builder.AddContextFeature(k, v)
g.contextFeatures[k] = g.contextFeatures[k][:0]
}
for k, v := range g.inputItemFeatureMap {
builder.AddItemFeature(k, v)
g.inputItemFeatureMap[k] = g.inputItemFeatureMap[k][:0]
}
algoData := &EasyrecAlgoData{
AlgoDataBase: &AlgoDataBase{
Items: copyItems,
AlgoResult: make(map[string][]response.AlgoResponse),
},
easyrecRequest: builder.EasyrecRequest(),
}
g.requestItem = g.requestItem[:0]
return algoData
}
func (g *EasyrecAlgoDataGenerator) HasFeatures() bool {
return len(g.requestItem) > 0
}