假设我有这种格式的文档:

product_name TEXT tags TAG score NUMERIC

[product1, [tag1, tag2, tag3], 10]
[product2, [tag2, tag3, tag4], 100]
....

我希望查询以产品得分最高总和以及每个标签的前5个产品的顺序返回标签:
[tag3, 110, [product2, product 1]]
[tag2, 110, [product2, product 1]]
[tag4, 100, [product2]]
[tag1, 10, [product 1]]

到目前为止,我的工作是分别存储每个产品/标签密钥(每个标签重复),因此对于每个产品,我们为每个标签都有一个单独的文档,并且id是产品名称和标签的组合:product_name TEXT tag TAG score NUMERIC
现在,我可以运行一个聚合查询来获取顶级标签的列表:
FT.AGGREGATE product_tags *
   GROUP BY 1 @TAG
     REDUCE SUM 1 @score as total_score
   SORT BY 2 @total_score DESC

这将按顺序排列顶部标签,但如果我想获得每个标签的前5个产品,我发现只有REDUCE TOLIST 1 @product_name会返回所有未排序的产品,而REDUCE FIRST_VALUE 4 @product_name BY @score DESC只会返回第一个顶级产品。

有什么方法可以让我们在一个查询中为每个标签指定5个顶级产品。如果不是,是否可以通过某种方式更改文档存储格式(或添加其他格式),以使这种查询成为可能,或者尽可能少地进行查询?

没关系,但我正在使用python Redisearch客户端。

最佳答案

第一:

  • 确保禁用不使用的功能(NOOFFSETSNOHLNOFREQSSTOPWORDS 0)
  • SORTABLE用作NUMERIC score

  • 这是我用来测试的架构:
    FT.CREATE product_tags NOOFFSETS NOHL NOFREQS STOPWORDS 0
        SCHEMA product_name TEXT tags TAG score NUMERIC SORTABLE
    

    您想将FT.AGGREGATE视为管道。

    第一步将通过@score对产品进行排序,以便稍后在管道中,当我们REDUCE TOLIST 1 @product_name时,列表按照排序:
    SORTBY 2 @score DESC
    

    我认为您已经在使用LOAD / APPLY来处理标签,因为TAG字段将按每个产品的完整逗号分隔字符串标签列表进行分组。参见Allow GROUPBY on tag fields issue。因此,我们下一步的工作是:
    LOAD 1 @tags
    APPLY split(@tags) as TAG
    

    然后,我们按@TAG分组,并应用这两个减少项。我们的产品 list 将排序。
    GROUPBY 1 @TAG
        REDUCE SUM 1 @score AS total_score
        REDUCE TOLIST 1 @product_name AS products
    

    最后,我们按@total_score排序:
    SORTBY 2 @total_score DESC
    

    这是命令的最终 View :
    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
    

    这里是说明结果的完整命令列表。我使用productXXXX分数来轻松直观地验证产品的分类。
    > 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"
    

    您将获得已排序产品的完整列表,而不仅仅是排名前5位的产品。复杂度没有任何区别,我们为此付出了代价。影响在于缓冲,网络有效负载和客户端。

    您可以使用Lua脚本限制到前5名:
    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
    

    这里是上面的Lua脚本的友好 View :
    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
    

    我们传递了一个键(索引)和一个参数(顶级产品的限制,在您的情况下为5)1 product_tags 3

    这样,我们将影响限制为仅缓冲,节省的网络有效负载和客户端上的负载。

    关于python - Redisearch总返回率各组前5名,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59530799/

    10-10 20:04