Lua 如何使用Redis将搜索文本与其他条件相结合?

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成功地编写了文本搜索和其他条件的交集。为了实现这一点,我使用了Lua脚本。问题是,我不仅在阅读,而且在编写脚本中的值。在Redis3.2中,可以通过调用
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上运行

一些考虑:

  • 我知道我可以在Redis之外进行部分处理(比如迭代光标或相交匹配),但这会影响应用程序的整体性能
  • 我不想自己部署Redis实例来使用3.2版
  • 上面的标准(价格范围和名称)只是一个简单的例子。我有其他字段和匹配类型,不仅仅是这些
  • 我不确定我存储数据的方式是否是最好的方式。我愿意听听关于这件事的建议

  • 我在这里发现的唯一问题是将值存储在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
    。这是最安全的选择,也是最慢的选择
  • 如果希望在单个原子Lua命令中执行尽可能多的处理,请执行以下操作:

  • 使用Redis 3.2和脚本效果复制
  • 在脚本中执行扫描,但将值返回给客户端并从那里开始写入。(也就是说,Karthikeyan Gopall的回答。)
  • 代替
    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中进行过滤。我已经用我能看到的选项更新了答案。