Поиск общей суммы путем агрегирования Redis JSON
Я хочу найти общее количество посещений веб-сайта, суммируя посещения отдельных страниц.
В моем 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.
Если требуется, могу добавить необходимую индексацию.
1 ответ
Вы можете использовать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
Создать индекс
Хотя бы одно поле должно быть проиндексировано. Вот несколько возможных вариантов:
Индексирование:
FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $.visit_count AS visit_count SORTABLE
Индексирование
FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $.page_name AS page_name TAG
Индексирование
page_name
и ссылка на (несколько строк для удобства чтения):FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $.visit_count AS visit_count SORTABLE NOINDEX $.page_name AS page_name TAG
Обратите внимание, что я упоминаю
visit_count
сNOINDEX
для быстрого доступа, но без индексации (поэтому на него нельзя ссылаться в части запроса, но на него можно ссылаться как на часть агрегатов).
Запрос индекса
Хитрость заключается в том, чтобы использовать какое-то фиктивное поле, одинаковое во всех документах. Вы можете использовать существующий, если он у вас есть, но если нет, вы можете создать его, используя ( документация здесь ):
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 для сканирования всех ключей и суммирования этого значения. Сканирование всего пространства ключей может быть медленным, если у вас много ключей, но оно не потребует дополнительных затрат памяти и времени на индексацию при изменении или добавлении новых ключей.