Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Repesquisa agregada de retorno top 5 de cada grupo


Primeiro:
  • Certifique-se de desativar os recursos que você não usará (NOOFFSETS , NOHL ,NOFREQS , STOPWORDS 0 )
  • Usar SORTABLE para seu NUMERIC score .

Aqui está o esquema que usei para testar:
FT.CREATE product_tags NOOFFSETS NOHL NOFREQS STOPWORDS 0
    SCHEMA product_name TEXT tags TAG score NUMERIC SORTABLE

Você quer pensar em FT.AGGREGATE como um pipeline.

O primeiro passo será classificar os produtos por @score, para que mais tarde, no pipeline, quando REDUCE TOLIST 1 @product_name , a lista sai ordenada:
SORTBY 2 @score DESC

Acho que você já está fazendo LOAD /APPLY para lidar com as tags, como TAG de outra forma, os campos seriam agrupados pela lista completa de tags de strings separadas por vírgulas, por produto. Consulte Permitir GROUPBY no problema de campos de tag. Então, nosso próximo passo está no pipeline é:
LOAD 1 @tags 
APPLY split(@tags) as TAG 

Em seguida, agrupamos por @TAG e aplicamos as duas reduções. Nossa lista de produtos sairá ordenada.
GROUPBY 1 @TAG
    REDUCE SUM 1 @score AS total_score
    REDUCE TOLIST 1 @product_name AS products

Por fim, classificamos por @total_score :
SORTBY 2 @total_score DESC

Aqui uma visão final do comando:
FT.AGGREGATE product_tags *
    SORTBY 2 @score DESC 
    LOAD 1 @tags 
    APPLY split(@tags) as TAG
    GROUPBY 1 @TAG
        REDUCE SUM 1 @score AS total_score 
        REDUCE TOLIST 1 @product_name AS products
    SORTBY 2 @total_score DESC

Aqui uma lista completa de comandos para ilustrar o resultado. Eu usei productXX com pontuação XX para verificar facilmente visualmente a classificação dos produtos.
> FT.CREATE product_tags NOOFFSETS NOHL NOFREQS STOPWORDS 0 SCHEMA product_name TEXT tags TAG score NUMERIC SORTABLE
OK
> FT.ADD product_tags pt:product10 1 FIELDS product_name product10 tags tag2,tag3,tag4 score 10
OK
> FT.ADD product_tags pt:product1 1 FIELDS product_name product1  tags tag1,tag2,tag3 score 1
OK
> FT.ADD product_tags pt:product100 1 FIELDS product_name product100 tags tag2,tag3 score 100
OK
> FT.ADD product_tags pt:product5 1 FIELDS product_name product5 tags tag1,tag4 score 5
OK
> FT.SEARCH product_tags *
1) (integer) 4
2) "pt:product5"
3) 1) "product_name"
   2) "product5"
   3) "tags"
   4) "tag1,tag4"
   5) "score"
   6) "5"
4) "pt:product100"
5) 1) "product_name"
   2) "product100"
   3) "tags"
   4) "tag2,tag3"
   5) "score"
   6) "100"
6) "pt:product1"
7) 1) "product_name"
   2) "product1"
   3) "tags"
   4) "tag1,tag2,tag3"
   5) "score"
   6) "1"
8) "pt:product10"
9) 1) "product_name"
   2) "product10"
   3) "tags"
   4) "tag2,tag3,tag4"
   5) "score"
   6) "10"
> FT.AGGREGATE product_tags * SORTBY 2 @score DESC LOAD 1 @tags APPLY split(@tags) as TAG GROUPBY 1 @TAG REDUCE SUM 1 @score AS total_score REDUCE TOLIST 1 @product_name AS products SORTBY 2 @total_score DESC
1) (integer) 4
2) 1) "TAG"
   2) "tag2"
   3) "total_score"
   4) "111"
   5) "products"
   6) 1) "product100"
      2) "product10"
      3) "product1"
3) 1) "TAG"
   2) "tag3"
   3) "total_score"
   4) "111"
   5) "products"
   6) 1) "product100"
      2) "product10"
      3) "product1"
4) 1) "TAG"
   2) "tag4"
   3) "total_score"
   4) "15"
   5) "products"
   6) 1) "product10"
      2) "product5"
5) 1) "TAG"
   2) "tag1"
   3) "total_score"
   4) "6"
   5) "products"
   6) 1) "product5"
      2) "product1"

Você está obtendo a lista completa de produtos classificados, não apenas os 5 principais. Em termos de complexidade, não faz diferença, nós pagamos o preço. O impacto está no buffer, na carga útil da rede e no seu cliente.

Você pode limitar ao top 5 usando um script Lua:
eval "local arr = redis.call('FT.AGGREGATE', KEYS[1], '*', 'SORTBY', '2', '@score', 'DESC', 'LOAD', '1', '@tags', 'APPLY', 'split(@tags)', 'as', 'TAG', 'GROUPBY', '1', '@TAG', 'REDUCE', 'SUM', '1', '@score', 'AS', 'total_score', 'REDUCE', 'TOLIST', '1', '@product_name', 'AS', 'products', 'SORTBY', '2', '@total_score', 'DESC') \n for i=2,(arr[1]+1) do \n arr[i][6] = {unpack(arr[i][6], 1, ARGV[1])} \n end \n return arr" 1 product_tags 5

Aqui uma visão amigável do script Lua acima:
local arr = redis.call('FT.AGGREGATE', KEYS[1], ..., 'DESC')
for i=2,(arr[1]+1) do 
    arr[i][6] = {unpack(arr[i][6], 1, ARGV[1])}
end
return arr

Estamos passando uma chave (o índice) e um argumento (o limite para os principais produtos, 5 no seu caso):1 product_tags 3 .

Com isso, limitamos o impacto apenas ao armazenamento em buffer, à carga útil da rede salva e à carga em seu cliente.