Redis
 sql >> Base de données >  >> NoSQL >> Redis

L'agrégat Redisearch renvoie le top 5 de chaque groupe

Premièrement :

  • Assurez-vous de désactiver les fonctionnalités que vous n'utiliserez pas (NOOFFSETS , NOHL ,NOFREQS , STOPWORDS 0 )
  • Utilisez SORTABLE pour votre NUMERIC score .

Voici le schéma que j'ai utilisé pour tester :

FT.CREATE product_tags NOOFFSETS NOHL NOFREQS STOPWORDS 0
    SCHEMA product_name TEXT tags TAG score NUMERIC SORTABLE

Vous voulez penser à FT.AGGREGATE comme pipeline.

La première étape consistera à trier les produits par @score, de sorte que plus tard, dans le pipeline, lorsque nous REDUCE TOLIST 1 @product_name , la liste sort triée :

SORTBY 2 @score DESC

Je pense que vous faites déjà LOAD /APPLY pour traiter les balises, comme TAG les champs seraient autrement regroupés par la liste complète des balises de chaîne séparées par des virgules, par produit. Voir Autoriser GROUPBY sur le problème des champs de balise. Notre prochaine étape est donc en préparation :

LOAD 1 @tags 
APPLY split(@tags) as TAG 

Nous regroupons ensuite par @TAG, et appliquons les deux réductions. Notre liste de produits sortira triée.

GROUPBY 1 @TAG
    REDUCE SUM 1 @score AS total_score
    REDUCE TOLIST 1 @product_name AS products

Enfin, nous trions par @total_score :

SORTBY 2 @total_score DESC

Voici une vue finale de la commande :

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

Voici une liste complète des commandes pour illustrer le résultat. J'ai utilisé productXX avec la note XX pour vérifier facilement visuellement le tri des produits.

> 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"

Vous obtenez la liste complète des produits triés, pas seulement les 5 meilleurs. En termes de complexité, cela ne fait aucune différence, nous en avons payé le prix. L'impact est dans la mise en mémoire tampon, la charge utile du réseau et votre client.

Vous pouvez limiter au top 5 en utilisant un 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

Voici une vue conviviale du script Lua ci-dessus :

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

Nous transmettons une clé (l'index) et un argument (la limite pour les meilleurs produits, 5 dans votre cas) :1 product_tags 3 .

Avec cela, nous avons limité l'impact à la mise en mémoire tampon uniquement, économisé la charge utile du réseau et la charge sur votre client.