Redis, отсортированный по строковым значениям

Предположим, у вас есть хэш «пользователи», записи которого сопоставляют числовые идентификаторы с массивами в кодировке JSON, поэтому, например, целое число 1 сопоставляется со строкой {имя: 'Джон', фамилия: 'Доу', род занятий: 'сантехник'} .

Числовые идентификаторы элементов в хэше хранятся в различных списках. Таким образом, если 'foobar' является одним из этих списков, для извлечения из него фактических данных я бы запустил простой скрипт Lua (реализующий операцию соединения на стороне сервера). Или, как я только что узнал, я мог бы использовать что-то вроде

SORT foobar BY inexistent_key GET user:*

но это подразумевает хранение данных каждого пользователя в отдельном ключе, что кажется дорогостоящим (в моем случае у меня много небольших коллекций, поэтому я хочу воспользоваться сжатием хэшей Redis).

Наконец, возникает вопрос: мне нужно упорядочить эти списки в алфавитном порядке, скажем, по фамилии каждого пользователя, а затем по имени. Как лучше всего добиться этого, не меняя слишком сильно базовую модель данных (если возможно)?

Пока что лучшее, что я мог придумать, - это использовать команду SORT вместе с предложениями BY и STORE, то есть запускать

SORT foobar BY surname:* STORE foobar

всякий раз, когда список изменяется, но для этого мне понадобится много ключей. Мне кажется, если бы я мог использовать хеш в предложении BY, это было бы идеальным решением.

Если бы поля, которые я хочу отсортировать, были как-то ограничены (например, всего пара сотен фамилий), я мог бы подумать о сопоставлении строк с целыми числами и использовать отсортированный набор Redis, но, похоже, это не так.


person idrarig    schedule 14.07.2012    source источник


Ответы (2)


Вы можете сортировать по хэш-ключам без сложности сценариев Lua, но вам придется дублировать ключи в вашей структуре Json на хэш-ключи Redis.

Приведенный ниже пример имеет следующую структуру:

Пользователи - это набор с идентификатором пользователя в запросе.

user: X - это хэш Redis, который содержит повторяющиеся данные name / фамилия.

userdata: X - хэш Json исходного пользователя.


redis> hmset user:1 name First surname User
OK
redis> set userdata:1 "{occupation: 'Tester'}"
OK
redis> hmset user:2 name Last surname Violet
OK
redis> set userdata:2 "{occupation: 'Bookseller'}"
OK
redis> hmset user:3 name Middle surname Veredict
OK
redis> set userdata:3 "{occupation: 'Judge'}"
OK
redis> hmset user:4 name Ultimate surname Veredict
OK
redis> set userdata:4 "{occupation: 'Ultimate Judge'}"
OK
redis> sadd users 1
(integer) 1
redis> sadd users 2
(integer) 1
redis> sadd users 3
(integer) 1
redis> sadd users 4
(integer) 1
redis> sort users by user:*->surname get user:*->name get user:*->surname get userdata:* alpha
1) "First"
2) "User"
3) "{occupation: 'Tester'}"
4) "Middle"
5) "Veredict"
6) "{occupation: 'Judge'}"
7) "Ultimate"
8) "Veredict"
9) "{occupation: 'Ultimate Judge'}"
10) "Last"
11) "Violet"
12) "{occupation: 'Bookseller'}"

Редактировать

Я упустил из виду, что несколько By учитывают только последнее предложение. Таким образом, вы не можете сортировать по более чем одной клавише в одной команде.

Также для команды SORT, используемой для лексикографического упорядочения, теперь требуется модификатор alpha.

person Niloct    schedule 15.07.2012
comment
Спасибо за ответ, Нилокт. Если бы я принял ваше решение, мне пришлось бы добавить по одному хешу для каждого пользователя, user: *, чего я пытаюсь избежать. Если бы я сделал это, я бы также объединил хэш пользовательских данных с хешами user: *. Я проведу несколько тестов, чтобы увидеть разницу в использовании памяти между использованием большого количества небольших хешей данных в кодировке JSON (группы пользователей в примере) и использованием одного хеша для каждого элемента (по одному на пользователя в примере). - person idrarig; 16.07.2012
comment
Что ж, разница в использовании памяти в моем случае огромна, как и ожидалось. Тем временем я понял, что мне бы очень хотелось СОРТИРОВАТЬ foobar ПО весу - ›* GET table -› * но, увы, это кажется невозможным ... - person idrarig; 16.07.2012
comment
Вы заметили, что разрешено несколько BY и GET? Вам нужно будет ввести по одному на каждое поле, как в примере. - person Niloct; 16.07.2012
comment
Извините, я считаю, что мой комментарий был недостаточно ясным. Когда я говорю, что хотел бы иметь возможность SORT foobar BY весов - ›* я имею в виду, что вес элемента X в foobar - это то, что вы найдете, выполнив HGET веса X. Таким образом, у вас будет всего три ключа: пользователи (хэш, который связывает данные JSON с идентификаторами), веса (хеш, который связывает веса с идентификаторами) и foobar (набор или список, содержащий некоторые из идентификаторов). Вместо этого, если я буду следовать вашей схеме, мне понадобится хотя бы один ключ для каждого элемента в хэше «пользователи». Занимает в десять раз больше памяти с моими данными (поэтому небольшие хэши автоматически архивируются). - person idrarig; 17.07.2012
comment
Понял. Оператор "звезда" в настоящее время не выдает ошибку при использовании после разделителя ->, но результаты несовместимы. Похоже, вам придется освободить место для отдельных ключей или пойти другим путем. - person Niloct; 17.07.2012
comment
Да! Я заметил, что при использовании * после - ›ошибка не возникает, поэтому сначала я надеялся, но на самом деле это не работает так, как мне хотелось бы. Сегодня я придумал небольшой хак, который, я не уверен, оптимален, но экономит мне много памяти, я опубликую его ниже! - person idrarig; 17.07.2012
comment
Если у кого-то возникнут проблемы с этим, этот пример не работает в версии 2.6. Для вашей сортировки необходимо добавить модификатор ALPHA, например: сортировать пользователей по пользователю: * - ›фамилия по пользователю: * -› имя получить пользователя: * - ›имя получить пользователя: * -› фамилия получить данные пользователя: * ALPHA - person Dashron; 29.08.2013

В конце концов, мне кажется, что способ решить мою проблему - использовать функцию table.sort (), доступную в Lua. В дополнение к небольшому хешу 'users' и небольшому списку идентификаторов 'foobar' я ввел еще один небольшой хеш, скажем, 'users: sort-strings', где я храню строки, по которым я хотел бы отсортировать идентификаторы в ' foobar '(в вымышленном примере сочетание фамилии и имени). Чтобы отсортировать «foobar», я бы запустил следующий фрагмент Lua в Redis, передав в качестве ключей «foobar», «users: sort-strings» и «foobar: tmp» (временный ключ).

local lst = redis.call('LRANGE', KEYS[1], 0, -1)
local sort_function = function (id0, id1)
   local s0 = redis.call('HGET', KEYS[2], id0)
   local s1 = redis.call('HGET', KEYS[2], id1)
   return (s0 < s1)
end
table.sort(lst, sort_function)
for key, value in ipairs(lst) do
   redis.call('RPUSH', KEYS[3], value)
end
redis.call('DEL', KEYS[1])
redis.call('RENAME', KEYS[3], KEYS[1])
person idrarig    schedule 17.07.2012
comment
Интересный! Как это соотносится с потреблением ресурсов? Это быстро? - person Niloct; 17.07.2012