通过聚合Redis JSON来计算总和

wd2eg0qa  于 2023-10-15  发布在  Redis
关注(0)|答案(1)|浏览(143)

我想通过总结单个页面的访问量来找到总的网站访问量。
在我的Redis中,我有JSON:

{'page_name': 'home', 'visit_count' : 10}
{'page_name': 'add_to_cart', 'visit_count' : 7}
{'page_name': 'checkout', 'visit_count' : 5}

我想使用FT.AGGREATE运行一个聚合逻辑,这样就可以得到,total_visits = 10 + 7 + 5 = 22
我可以添加必要的索引,如果需要的话。

prdp8dxp

prdp8dxp1#

你可以使用FT.AGGREGATE来实现。您必须为此至少索引一个字段。
下面是一个使用您的数据的示例。

新增数据

127.0.0.1:6379> JSON.SET doc:1 $ '{"page_name": "home", "visit_count" : 10}' 
OK
127.0.0.1:6379> JSON.SET doc:2 $ '{"page_name": "add_to_cart", "visit_count" : 7}' 
OK
127.0.0.1:6379> JSON.SET doc:3 $ '{"page_name": "checkout", "visit_count" : 5}' 
OK

创建索引

至少需要对一个字段进行索引。以下是几个可能的选项:
1.索引visit_count

FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $.visit_count AS visit_count SORTABLE

1.索引page_name

FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $.page_name AS page_name TAG

1.索引page_name并引用visit_count(多行以提高可读性):

FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA 
    $.visit_count AS visit_count SORTABLE NOINDEX 
    $.page_name AS page_name TAG

请注意,我提到visit_countNOINDEX是为了快速访问,但没有索引(因此它不能在查询部分引用,但可以作为聚合的一部分引用)。

查询索引

诀窍是使用一些在所有文档中都相等的虚拟字段。如果你有一个现有的,你可以使用它,但是如果没有,你可以使用APPLY创建一个(文档在这里):

APPLY 1 AS __dummy

将向所有文档添加一个名为__dummy的字段,值为1。这仅适用于查询上下文,不会添加到JSON对象本身。当然,任何名称和值都是可能的。
对于情况1和3:

FT.AGGREGATE idx * APPLY 1 AS __dummy GROUPBY 1 @__dummy REDUCE SUM 1 @visit_count AS count

对于情况2:

FT.AGGREGATE idx * APPLY 1 AS __dummy LOAD 3 $.visit_count AS visit_count GROUPBY 1 @__dummy REDUCE SUM 1 @visit_count AS count

输出(适用于所有情况):

1) (integer) 1
2) 1) "__dummy"
   2) "1"
   3) "count"
   4) "22"

编辑

显然,你可以GROUPBY 0字段,所以APPLY技巧是多余的。
使用

FT.AGGREGATE idx * GROUPBY 0 REDUCE SUM 1 @visit_count AS count

对于情况1和3,或者,对于情况2:

FT.AGGREGATE idx * LOAD 3 $.visit_count AS visit_count GROUPBY 0 REDUCE SUM 1 @visit_count AS count

获取输出:

1) (integer) 1
2) 1) "count"
   2) "22"

使用redis-py:

import redis
import redis.commands.search
import redis.commands.search.reducers as reducers
from redis.commands.search.aggregation import AggregateRequest
from redis.commands.json.path import Path
from redis.commands.search.field import (
    NumericField,
    TagField,
)
from redis.commands.search.indexDefinition import IndexDefinition, IndexType

conn = redis.Redis(host="localhost", port=6379, db=0)

definition = IndexDefinition(
    prefix=["doc:"],
    index_type=IndexType.JSON,
)

conn.ft().create_index((
        TagField("$.page_name", as_name="page_name"),
        NumericField("$.visit_count", as_name="visit_count", sortable=True, no_index=True),
    ),
    definition=definition,
)

conn.json().set("doc:1", Path.root_path(), {"page_name": "home", "visit_count": 10})
conn.json().set("doc:2", Path.root_path(), {"page_name": "add_to_cart", "visit_count": 7})
conn.json().set("doc:3", Path.root_path(), {"page_name": "checkout", "visit_count": 5})

total_sum = conn.ft().aggregate(AggregateRequest().group_by([], reducers.sum("visit_count"))).rows[0][1]
print(int(total_sum)) # 22

备选办法

如果你不需要索引文档的其他原因,你可以使用一个LUA script来扫描所有的键,并总结这个值。如果有很多键,扫描整个键空间可能会很慢,但在修改或添加新键时,它不需要额外的内存开销和索引时间

相关问题