Поиск общей суммы путем агрегирования 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

Создать индекс

Хотя бы одно поле должно быть проиндексировано. Вот несколько возможных вариантов:

  1. Индексирование:

            FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $.visit_count AS visit_count SORTABLE 
    
  2. Индексирование

            FT.CREATE idx ON JSON PREFIX 1 doc: SCHEMA $.page_name AS page_name TAG 
    
  3. Индексирование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"

РЕДАКТИРОВАТЬ

Судя по всему, ты можешьGROUPBY0 поле, поэтому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 для сканирования всех ключей и суммирования этого значения. Сканирование всего пространства ключей может быть медленным, если у вас много ключей, но оно не потребует дополнительных затрат памяти и времени на индексацию при изменении или добавлении новых ключей.

Другие вопросы по тегам