Lua 如何使用Redis将搜索文本与其他条件相结合?
我使用Redis成功地编写了文本搜索和其他条件的交集。为了实现这一点,我使用了Lua脚本。问题是,我不仅在阅读,而且在编写脚本中的值。在Redis3.2中,可以通过调用Lua 如何使用Redis将搜索文本与其他条件相结合?,lua,redis,Lua,Redis,我使用Redis成功地编写了文本搜索和其他条件的交集。为了实现这一点,我使用了Lua脚本。问题是,我不仅在阅读,而且在编写脚本中的值。在Redis3.2中,可以通过调用Redis.replicate_commands(),但不能在3.2之前调用 下面是我如何存储这些值的 名称 > HSET product:name 'Cool product' 1 > HSET product:name 'Nice product' 2 价格 > ZADD product:price 49.
Redis.replicate_commands()
,但不能在3.2之前调用
下面是我如何存储这些值的
名称
> HSET product:name 'Cool product' 1
> HSET product:name 'Nice product' 2
价格
> ZADD product:price 49.90 1
> ZADD product:price 54.90 2
然后,要获取所有与'ice'
匹配的产品,例如,我调用:
> HSCAN product:name 0 MATCH *ice*
但是,由于HSCAN
使用游标,我必须多次调用它以获取所有结果。这就是我使用Lua脚本的地方:
local cursor = 0
local fields = {}
local ids = {}
local key = 'product:name'
local value = '*' .. ARGV[1] .. '*'
repeat
local result = redis.call('HSCAN', key, cursor, 'MATCH', value)
cursor = tonumber(result[1])
fields = result[2]
for i, id in ipairs(fields) do
if i % 2 == 0 then
ids[#ids + 1] = id
end
end
until cursor == 0
return ids
由于无法将脚本的结果用于另一个调用,例如SADD key EVAL(SHA)…
。而且,在脚本中不可能使用全局变量。我已更改字段循环内的部分,以访问脚本外的ID列表:
if i % 2 == 0 then
ids[#ids + 1] = id
redis.call('SADD', KEYS[1], id)
end
我不得不在第一行添加redis.replicate_commands()
。通过此更改,我可以从调用脚本时传递的密钥中获取所有ID(请参见KEYS[1]
)
最后,为了获得100个定价在40到50之间且名称包含“ice”的产品ID列表,我执行以下操作:
> ZUNIONSTORE tmp:price 1 product:price WEIGHTS 1
> ZREMRANGEBYSCORE tmp:price 0 40
> ZREMRANGEBYSCORE tmp:price 50 +INF
> EVALSHA b81c2b... 1 tmp:name ice
> ZINTERSTORE tmp:result tmp:price tmp:name
> ZCOUNT tmp:result -INF +INF
> ZRANGE tmp:result 0 100
我使用ZCOUNT
调用,通过执行count/100
预先知道我将拥有多少结果页
正如我之前所说的,这在Redis 3.2中运行良好。但是当我试图在AWS上运行代码时,它只支持高达2.8的Redis,我无法再让它工作了。我不知道如何在不使用脚本或不从脚本写入的情况下使用HSCAN
游标进行迭代。有没有办法让它在Redis2.8上运行
一些考虑:
我在这里发现的唯一问题是将值存储在lua scirpt中。因此,与其将它们存储在lua中,不如将该值存储在lua之外(返回字符串[]的值)。使用sadd(key,members[])将它们存储在不同调用的集合中。然后进行交叉并返回结果
> ZUNIONSTORE tmp:price 1 product:price WEIGHTS 1
> ZREVRANGEBYSCORE tmp:price 0 40
> ZREVRANGEBYSCORE tmp:price 50 +INF
> nameSet[] = EVALSHA b81c2b... 1 ice
> SADD tmp:name nameSet
> ZINTERSTORE tmp:result tmp:price tmp:name
> ZCOUNT tmp:result -INF +INF
> ZRANGE tmp:result 0 100
在我看来,你的设计是最理想的。一个建议是尽可能使用管道,因为它可以一次性处理所有事情
希望这有帮助
更新
lua中没有类似于array([])的东西,您必须使用lua表来实现它。在您的脚本中,您返回的ID是正确的,它本身就是一个数组,您可以将其用作单独的调用来实现sadd
String [] nameSet = (String[]) evalsha b81c2b... 1 ice -> This is in java
SADD tmp:name nameSet
相应的lua脚本与第一个脚本相同
local cursor = 0
local fields = {}
local ids = {}
local key = 'product:name'
local value = '*' .. ARGV[1] .. '*'
repeat
local result = redis.call('HSCAN', key, cursor, 'MATCH', value)
cursor = tonumber(result[1])
fields = result[2]
for i, id in ipairs(fields) do
if i % 2 == 0 then
ids[#ids + 1] = id
end
end
until cursor == 0
return ids
问题不在于您正在向数据库写入数据,而在于您在
HSCAN
之后执行写入操作,这是一个不确定的命令
在我看来,在Lua脚本中使用SCAN
命令很少有好的理由。该命令的主要目的是允许您以小批量进行操作,这样您就不会在处理巨大的密钥空间(或散列密钥空间)时锁定服务器。但是,由于脚本是原子的,使用HSCAN
无法帮助您锁定服务器,直到整个事情完成
以下是我可以看到的选项:
如果您不能冒险用冗长的命令锁定服务器:
HSCAN
。这是最安全的选择,也是最慢的选择HSCAN
,在脚本中执行HKEYS
,并使用Lua的模式匹配过滤结果。由于HKEYS
是确定性的,因此后续写入不会有问题。当然,缺点是你必须先读入所有的键,不管它们是否符合你的模式。(尽管HSCAN
的散列大小也是O(N)我不知道可以将脚本结果存储在变量中。令人惊叹的!今天晚些时候我会尝试一下,让你知道它是否有效。是的,我可能已经尝试了一个与你的场景类似的示例,效果很好。我用绝地图书馆在JavaHey中实现了这一点!尝试将脚本结果分配给该变量时,我遇到以下错误:
ERR unknown命令'nameSet[]”
Hi更新了答案以供参考@古斯塔沃斯特劳贝明白了。然而,这样我将不得不在客户端执行部分流程—存储集合并将其转发到下一步—我知道这是可能的。我想实现的是在Redis/Lua上实现所有功能。@KevinChristopherHenry以下调用需要复制:Redis.call('SADD',KEYS[1],id)
。我不确定如何使用HKEYS
执行匹配
,就像我使用HSCAN
@GustavoStraube一样:你必须在Lua中进行过滤。我已经用我能看到的选项更新了答案。