我想列出redis
中现有产品的清单,但首先我想检查产品名称是否已经存在(重复检查)。
我的清单目前可以接受重复项,所以:有人可以帮我展示如何在清单中添加唯一值吗?
不要使用列表,使用集合。集合是存储唯一对象的容器。每个对象只能在集合中出现一次。在这里查看与集合相关的命令:http://redis.io/commands/#set。
以下是使用 redis-cli 的示例(我们尝试两次添加“产品一”,但它只在产品列表中出现一次):
$ redis-cli
127.0.0.1:6379> sadd products "Product One"
(integer) 1
127.0.0.1:6379> sadd products "Product Two"
(integer) 1
127.0.0.1:6379> sadd products "Product Three"
(integer) 1
127.0.0.1:6379> sadd products "Product One"
(integer) 0
127.0.0.1:6379> smembers products
1) "Product Three"
2) "Product One"
3) "Product Two"
127.0.0.1:6379>
为什么不直接在调用Redis.lrem
方法之前先判断一下是否存在该项呢?如果存在则删除,否则不做任何操作。可以像这样:
def push_item_to_the_list(LIST_KEY, item)
Redis.lrem(LIST_KEY, 0, item)
Redis.lpush(LIST_KEY, item)
end
这是我(鲁莽的)解决Redis List唯一性的方案。(Ruby实现)
def push_item_to_the_list(LIST_KEY, item)
insert_status = Redis.linsert(LIST_KEY, 'before', item, item)
if insert_status == -1
Redis.lpush(LIST_KEY, item)
else
Redis.lrem(LIST_KEY, 1, item)
end
end
LINSERT
命令是否能够将此项放在相同的项目后面(这是我知道的唯一检查项是否已在Redis列表中的方法)。LINSERT
返回状态-1,则表示它无法在列表中找到该项 - 一切正常(现在您可以将其推送或插入)。LINSERT
返回其他值(在另一种情况下为列表的大小),则表示它已经能够找到该项并且已经能够插入另一个项目,紧随前一个项目。这意味着您有(至少一个)重复项。您现在可以删除其中之一。def items_to_queue
["1", "2", "3", "4", "5"]
end
redis = Redis.new
# build the queue
redis.rpush(:my_queue, items_to_queue)
# verify the values are as expected
redis.lrange(:my_queue, 0, -1) # => ["1", "2", "3", "4", "5"]
# remove some values
redis.lpop(:my_queue) # => "1"
redis.lpop(:my_queue) # => "2"
# While uniqueness is not a built in function of lists,
# we can use their values to filter the incoming values
queued_values = redis.lrange(:my_queue, 0, -1) # => ["3", "4", "5"]
unqueued_values = items_to_queue - queued_values # => ["1", "2"]
# add the unqueued items to the queue
redis.rpush(:my_queue, unqueued_values)
# verify the values are as expected
redis.lrange(:my_queue, 0, -1) # => ["3", "4", "5", "1", "2"]
明确表达:
如果items_to_queue
被定义为返回整数而不是字符串[1, 2, 3, 4, 5]
,上述代码将无法工作。由于Redis返回的值是字符串,过滤将无法起作用。在这种情况下,该代码的最终输出将是:["3", "4", "5", "1", "2", "3", "4", "5"]
。如果你处理类型差异,请相应地处理。
对象示例(我的实现):
def items_to_queue
["1", "2", "3", "4", "5"]
end
class RedisInstance
cattr_accessor(*%i[redis])
# to solve for a production error explained here: https://ogirginc.github.io/en/heroku-redis-ssl-error
self.redis = Redis.new(ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
end
class RedisList < RedisInstance
attr_accessor(*%i[key])
def initialize(key)
self.key = key
end
def add_values(values, distinct: false)
values -= self.values if distinct
rpush(key, values) if values.present? # prevent Redis::CommandError if values ends up empty / nil
end
def first_value
lpop(key)
end
alias next_value first_value
# not used in this example but here for consistency
def last_value
rpop(key)
end
def values(min: 0, max: -1)
lrange(key, min, max)
end
private
delegate(*Redis::Commands::Lists.instance_methods, to: :redis)
end
list = RedisList.new(:my_queue) => #<Redisable::List @key=:my_queue>
list.add_values(items_to_queue, distinct: true)
list.values # => ["1", "2", "3", "4", "5"]
list.next_value # => "1"
list.next_value # => "2"
list.add_values(items_to_queue, distinct: true)
list.values # => ["3", "4", "5", "1", "2"]
public function addJobs(array $jobs): int
{
foreach ($jobs as $key => $job) {
$hash = hash('md4', $job);
if (0 === $this->client->hsetnx('QUEUE-HASHES', $hash, 1)) {
unset($jobs[$key]);
}
}
return $this->client->rpush('QUEUE', $jobs);
}
public function popJobs(int $count): array
{
if ($count < 1) {
throw new \InvalidArgumentException('Jobs count must be greater than zero');
}
$index = $count - 1;
$jobs = $this->client->lrange('QUEUE', 0, $index);
if (\count($jobs)) {
$this->client->ltrim('QUEUE', $count, -1);
$hashes = [];
foreach ($jobs as $job) {
$hashes[] = hash('md4', $job);
}
$this->client->hdel('QUEUE-HASHES', $hashes);
}
return $jobs;
}
如果您需要维护顺序和唯一性,可以使用排序集合
127.0.0.1:6379> zadd products 1 "Product One"
(integer) 1
127.0.0.1:6379> zadd products 2 "Product Two"
(integer) 1
127.0.0.1:6379> zadd products 3 "Product Tree"
(integer) 1
127.0.0.1:6379> zadd products 4 "Product Four"
(integer) 1
127.0.0.1:6379> zrange products 0 -1
1) "Product One"
2) "Product Two"
3) "Product Tree"
4) "Product Four"