Lua 脚本代码 ———— 实现经典“四则运算”算法优化 Redis 集合运算
-- #!/usr/local/bin/lua
--[[
栈定义
格式示例:{ stack_table={"A","B","C"} }
函数:
新建并初始化栈:new(o) -- o为基础栈,可为 nil
入栈:push(element)
出栈:pop()
获取栈顶元素:top()
判断是否空栈:isEmpty()
栈大小:size()
清空栈:clear()
打印栈元素:printElement()
--]]
local Stack = {}
-- 新建并初始化栈
function Stack:new(o)
o = o or {}
setmetatable(o, self)
self.__index = self
if o.stack_table == nil or #o.stack_table == 0 then
self.stack_table = {}
else
self.stack_table = o.stack_table
end
return o;
end
-- 入栈
function Stack:push(element)
local size = self:size()
self.stack_table[size + 1] = element
end
-- 出栈
function Stack:pop()
local size = self:size()
if self:isEmpty() then
print("Stack is empty!")
return
end
return table.remove(self.stack_table,size)
end
-- 获取栈顶元素
function Stack:top()
local size = self:size()
if self:isEmpty() then
print("Stack is empty!")
return
end
return self.stack_table[size]
end
-- 是否为空栈
function Stack:isEmpty()
local size = self:size()
if size == 0 then
return true
end
return false
end
-- 栈大小
function Stack:size()
return #self.stack_table or 0
end
-- 清空栈
function Stack:clear()
self.stack_table = nil
self.stack_table = {}
end
-- 打印栈元素
function Stack:printElement()
local size = self:size()
if self:isEmpty() then
print("Stack is empty!")
return
end
local str = "{"..self.stack_table[size]
size = size - 1
while size > 0 do
str = str..", "..self.stack_table[size]
size = size - 1
end
str = str.."}"
print(str)
end
--[[
队列定义
格式示例:{ queue_table={"A","B","C"}, capacity = 100, size_ = 3, head = 0, rear = 0}
函数:
新建并初始化队列:new(o) -- o为基础队列,可为 nil
入队列:enQueue(element)
出队列:deQueue()
判断是否空队列:isEmpty()
队列大小:size()
清空队列:clear()
打印队列元素:printElement()
--]]
local Queue = {}
-- 默认队列容量
local default_capacity = 10000;
-- 新建并初始化队列
function Queue:new(o)
o = o or {queue_table = {}}
setmetatable(o, self)
self.__index = self
if o.queue_table == nil or #o.queue_table == 0 then
self.capacity = default_capacity
self.queue_table = {}
self.size_ = 0
self.rear = 0
self.head = 0
else
self.capacity = default_capacity
self.queue_table = o.queue_table
self.size_ = #o.queue_table
self.rear = #o.queue_table
self.head = o.head or 0
end
return o;
end
-- 入队列
function Queue:enQueue(element)
if self.size_ == 0 then
self.head = 0
self.rear = 1
self.size_ = 1
self.queue_table[self.rear] = element
else
local temp = (self.rear + 1) % self.capacity
if temp == self.head then
print("Queue is full!")
return
else
self.rear = temp
end
self.queue_table[self.rear] = element
self.size_ = self.size_ + 1
end
end
-- 出队列
function Queue:deQueue()
if self:isEmpty() then
print("Queue is empty!")
return
end
self.size_ = self.size_ - 1
self.head = (self.head + 1) % self.capacity
local value = self.queue_table[self.head]
-- self.queue_table[self.head] = nil -- 对于table,如果元素中含有nil,则nil之后的所有元素都不存在,比如 {‘a’,nil,'b'} 其实为 {‘a’},所以这里元素不能设置为nil
return value
end
-- 是否空队列
function Queue:isEmpty()
return self:size() == 0
end
-- 队列大小
function Queue:size()
return self.size_
end
-- 清空队列
function Queue:clear()
self.queue_table = nil
self.queue_table = {}
self.size_ = 0
self.head = 0
self.rear = 0
end
-- 打印队列元素
function Queue:printElement()
local h = self.head
local r = self.rear
if h == r then
print("Queue is empty!")
return
end
local str = nil
local first_flag = true
while h ~= r do
if first_flag == true then
str = "{"..self.queue_table[h + 1]
h = (h + 1) % self.capacity
first_flag = false
else
str = str..","..self.queue_table[h+1]
h = (h+1) % self.capacity
end
end
str = str.."}"
print(str)
end
--[[
逻辑表达式计算器定义 (利用闭包的方式模拟面向对象编程的类)
私有成员属性:
logic_expr:逻辑表达式
key_final_set:最终结果集的key
is_sorted_set_calc:是否是有序集合的运算
私有成员函数:
判断是否为操作符:isOperator(key)
判断表达式是否有保留字'#':hasNoSharpInExpr()
校验表达式括号是否成对出现:isBracketsMatch()
str 表达式转 queue_table:strToQueueTable(str_expr)
中缀表达式转后缀:infix2Suffix(expr_queue)
从单词中获取key:getKeyFromWord(word)
判断单词是否为数字:is_operand_a_num(operand)
计算后缀逻辑表达式:calcInRedis()
公有成员函数:
计算逻辑表达式主流程:calc()
--]]
local function LogicExprCalculator(key_final_set, logic_expr, is_sorted_set_calc_argv)
local is_sorted_set_calc = false
if is_sorted_set_calc_argv ~= nil then
is_sorted_set_calc = is_sorted_set_calc_argv
end
-- 私有属性
local self = {logic_expr = logic_expr, key_final_set = key_final_set, is_sorted_set_calc = is_sorted_set_calc}
-- 操作符定义:
-- '&&'' -> 交集,'||'' -> 并集,'^'' -> 差集(适用于 set 和 sorted set)
-- '>','<','>=','<=','=','!=' (适用于 sorted set)
-- 定义操作符优先级,key 有操作符,value 为优先级,数字越大,优先级越高(即 4 > 3 > 2 > 1 > 0 > -1)
-- 栈外优先级
local opr_priority_out_table = { ["("] = 4, [">"] = 3, ["<"] = 3, [">="] = 3, ["<="] = 3, ["="] = 3, ["!="] = 3, ["||"] = 2, ["&&"] = 2, ["^"] = 2, [")"] = 1, ["#"] = -1 }
-- 栈内优先级
local opr_priority_in_table = { ["("] = 0, [">"] = 3, ["<"] = 3, [">="] = 3, ["<="] = 3, ["="] = 3, ["!="] = 3, ["||"] = 2, ["&&"] = 2, ["^"] = 2, [")"] = 1, ["#"] = -1 }
-- 是否为操作符
local isOperator = function (key)
return opr_priority_out_table[key] ~= nil
end
-- 校验原始表达式是否含有保留字'#'
local hasNoSharpInExpr = function ()
if string.find(self.logic_expr, "#") then
return false
end
return true
end
-- 校验表达式括号是否成对出现
local isBracketsMatch = function (table_expr)
local count_left_bracket = 0
local count_right_bracket = 0
for k, v in pairs(table_expr)
do
if v == "(" then
count_left_bracket = count_left_bracket + 1
elseif v == ")" then
count_right_bracket = count_right_bracket + 1
end
end
-- 判断左括号和右括号出现次数是否一样多
-- 实际上还要判断是否都是成对出现,也就是说,每一个右括号,都有对应的左括号,而不会出现类似 ”key1||key2)(“ 这样的表达式,这种情况当真正读取字符队列时再判断
if count_left_bracket == count_right_bracket then
return true
end
return false
end
-- str 表达式转 queue_table
local strToQueueTable = function ()
assert(self.logic_expr ~= nil, "logic_expr is nil.")
assert(hasNoSharpInExpr(), "logic_expr has keyword '#'.")
-- 为逻辑表达式添加末尾标记 #
local str_expr = self.logic_expr.."#"
-- 替换表达式中的空格,并分割逻辑表达式每个字符,并存入 table
local table_expr = {}
str_expr = str_expr:gsub(" ", "") -- 替换空格
str_expr:gsub(".", function(c) table.insert(table_expr, c) end) -- 分割表达式存入 table
-- 校验表达式括号是否匹配
local status, result = pcall(isBracketsMatch, table_expr)
assert(result, "logic_expr has wrong num of brackets.")
local queue_table = {}
local word = "" -- 暂存的单词,比如表达式 key1&&key2||key3,当前读到"2"时,word原先暂存 "key",处理完"2",则word暂存”key2“
local opr = "" -- 暂存的操作符,比如表达式 key1&&key2||key3,当前读到第二个&时,opr原先暂存 "&"
local is_pre_word_not_opr = true -- 前一个单词是否为操作符, true 不是操作符,false 是操作符
local preGreaterOrLess = ""
for k, v in pairs(table_expr) do
-- 如果遇到 "(" 或者 ")" ,并且之前扫描的是单词字符( word 有值),则 word 都存入 table 中,清空 word
-- 继续,"(" 或者 ")" 存入table 中
if v == "(" or v == ")" then
if v == ")" then
assert(is_pre_word_not_opr, "logic_expr has wrong operator around ["..k.."]:'"..v.."'.")
end
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
table.insert(queue_table, v)
is_pre_word_not_opr = true
-- 如果遇到 "^",并且之前扫描的是单词字符( word 有值),则 word 都存入 table 中,清空 word
-- 继续,"^" 存入table 中
elseif v == "^" then
assert(is_pre_word_not_opr, "logic_expr has wrong operator around ["..k.."]:'"..v.."'.")
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
table.insert(queue_table, v)
is_pre_word_not_opr = false
-- 如果遇到 "=",则需要判断前一个字符是否为操作符
elseif v == "=" then
-- 如果前一个字符是操作符,则必须为 ">","<" 或者 "!",以组成 ">=","<=" 或者 "!="
-- 并且之前扫描的是单词字符( word 有值),则 word 都存入 table 中,清空 word
-- 继续,"=" 附加到 opr 后,opr 此时为 ">=","<=" 或者 "!=",存入 table 中,清空 opr
-- 并且扫描前一个字符为 ">" 或者 "<" 的 preGreaterOrLess 设置为 ""
if not is_pre_word_not_opr then
assert(opr == "!" or opr == ">" or opr == "<", "logic_expr has wrong operator around ["..k.."]:'"..v.."'.")
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
opr = opr..v
table.insert(queue_table, opr)
opr = ""
preGreaterOrLess = ""
-- 如果前一个字符是单词,则 word 都存入 table 中,清空 word
-- 继续,"=" 存入table 中
else
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
table.insert(queue_table, v)
end
is_pre_word_not_opr = false
-- 如果遇到 "!",并且之前扫描的是单词字符( word 有值),则 word 都存入 table 中,清空 word
-- 继续,"!" 附加到 opr 后,opr 此时为 "!",存入 table 中
elseif v =="!" then
assert(is_pre_word_not_opr, "logic_expr has wrong operator around ["..k.."]:'"..v.."'.")
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
opr = opr..v
is_pre_word_not_opr = false
-- 如果遇到 ">" 或者 "<",则前一个字符必须为单词字符( word 有值),则 word 都存入 table 中,清空 word
-- 继续,">" 或者 "<",附加到 opr 后,opr 此时为 ">" 或者 "<",存入 table 中
-- 继续,">" 或者 "<" 暂时存入 preGreaterOrLess
elseif v == "<" or v == ">" then
assert(is_pre_word_not_opr, "logic_expr has wrong operator around ["..k.."]:'"..v.."'.")
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
opr = opr..v
preGreaterOrLess = v
is_pre_word_not_opr = false
-- 如果遇到 "&" 或者 "|" ,并且之前扫描的是单词字符(word有值),则 word 都存入 table 中,清空 word
-- 继续,"&" 或者 "|" 附加到 opr 后,如果 opr 此时为 "&&" 或者 "||",则 opr 存入 table 中,清空 opr,否则继续循环读取
elseif v == "&" or v == "|" then
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
opr = opr..v
if opr == "&&" or opr == "||" then
table.insert(queue_table, opr)
opr = ""
end
is_pre_word_not_opr = false
-- 如果遇到 “#”(到达逻辑表达式末尾),则word 存入 table
elseif v == "#" then
if word ~= "" then
table.insert(queue_table, word)
word = ""
end
is_pre_word_not_opr = true
-- 否则为单词字符,此时如果暂存大小于符号的 preGreaterOrLess 不为空,则表明前一个字符为 ">" 或者 ”<“,而当前字符为单词字符
-- 继续,把前一个字符 ”>“ 或者 "<" 存入 table 中,并设置 opr 和 preGreaterOrLess 为”“
-- 继续,单词字符附加到 word 后,等待组成完整的 word 存入 table 中
else
if preGreaterOrLess ~= "" then
table.insert(queue_table, preGreaterOrLess)
opr=""
preGreaterOrLess = ""
end
assert(opr=="", "logic_expr has wrong operator around ["..k.."]:'"..v.."'.")
word = word..v
is_pre_word_not_opr = true
end
end
table.insert(queue_table, "#") -- 逻辑表达式添加末尾标记 #
return queue_table;
end
-- 中缀表达式转后缀
local infix2Suffix = function (expr_queue)
assert(expr_queue ~= nil, "expr_queue is nil.")
assert(expr_queue.queue_table[#expr_queue.queue_table]=="#", "expr_queue does not end with '#'.")
-- 定义局部操作符栈,并初始化
local origin_stack = {stack_table={"#"}}
local operator_stack = Stack:new(origin_stack)
local cur_word = expr_queue:deQueue()
-- 循环读取表达式队列的单词
while (cur_word ~= "#")
do
-- 如果是操作数(不是操作符),则直接入队列
if not isOperator(cur_word) then
expr_queue:enQueue(cur_word)
-- 如果是操作符
else
local pop_word = nil
-- 如果是 ")" 操作符,则栈顶元素出栈,入队列,直到遇到第一个 “(”
if cur_word == ")" then
pop_word = operator_stack:pop()
while (pop_word ~= "(")
do
-- 如果还没读取到 ”(“,就已经读到了 "#",说明表达式左括号和右括号不匹配
if pop_word == "#" then
error("logic_expr's brackets do not match.")
end
expr_queue:enQueue(pop_word)
pop_word = operator_stack:pop()
end
-- 否则,如果是 “(” 操作符,则直接入栈
elseif cur_word == "(" then
operator_stack:push(cur_word)
-- 否则,比较当前单词,与栈顶单词的优先级,如果当前单词 <= 栈顶单词,则栈顶元素出栈,入队列,直到遇到优先级 > 当前单词的栈顶单词,或者遇到“#”,当前单词入栈
elseif opr_priority_out_table[cur_word] <= opr_priority_in_table[operator_stack:top()] then
expr_queue:enQueue(operator_stack:pop())
while (opr_priority_out_table[cur_word] <= opr_priority_in_table[operator_stack:top()] and (operator_stack:top() ~= "#"))
do
expr_queue:enQueue(operator_stack:pop())
end
operator_stack:push(cur_word)
-- 否则,(当前单词优先级 > 栈顶单词)当前单词入栈
else
operator_stack:push(cur_word)
end
end
-- 读取队列单词,继续循环
cur_word = expr_queue:deQueue()
end
-- 当队列读取到末尾,栈中所有元素依次出栈,并入队列(#也入队列)
local pop_word = operator_stack:pop()
while (pop_word ~= nil)
do
expr_queue:enQueue(pop_word)
pop_word = operator_stack:pop()
end
expr_queue:printElement()
return expr_queue
end
-- 从单词中获取key,比如从 tag1:score 获取到 tag1
local getKeyFromWord = function (word)
return string.gmatch(word, "[%w|.]+")()
end
-- 判断单词是否为数字
local is_operand_a_num = function(operand)
return string.gmatch(operand, ":score")() == nil
end
-- 计算后缀逻辑表达式
local calcInRedis = function (suffix_expr_queue)
local origin_stack = {stack_table={"#"}}
local operand_stack = Stack:new(origin_stack)
local cur_word = suffix_expr_queue:deQueue()
local count = 0
local operand_count = 0
local pre_word = nil
local sorted_set_first_in = true
-- 循环读取表达式队列的单词
while (cur_word ~= "#")
do
pre_word = cur_word
-- 如果是操作数(不是操作符),则直接入栈
if not isOperator(cur_word) then
operand_stack:push(cur_word)
operand_count = operand_count + 1
-- 否则是操作符,栈顶弹出两个操作符,做逻辑运算
else
local operand_rear = operand_stack:pop();
local operand_font = operand_stack:pop();
assert(operand_rear and operand_font and operand_rear ~= "#" and operand_font ~= "#", "logic_expr has wrong num of operators.")
count = count + 1
-- 如果不是 sorted set,则做 sinterstore 或者 sunionstore 运算
if self.is_sorted_set_calc == false or self.is_sorted_set_calc == "false" then
-- 做集合运算,交集 && 或者并集 ||,临时结果存入临时 key (key_final_set.."underline"..count) 中
-- 比如如果 key_final_set 为 ”key1“,则临时 key 类似 key1underline1,key1underline2...
if cur_word == "&&" then
redis.pcall('sinterstore', self.key_final_set.."underline"..count, operand_font, operand_rear)
elseif cur_word == "||" then
redis.pcall('sunionstore', self.key_final_set.."underline"..count, operand_font, operand_rear)
elseif cur_word == "^" then
redis.pcall('sdiffstore', self.key_final_set.."underline"..count, operand_font, operand_rear)
else
return {-1, "logic_expr has wrong operators."}
end
-- 如果是 sorted set,则做 >、<、zintersore 或者 zunionstore 运算
elseif self.is_sorted_set_calc == true or self.is_sorted_set_calc == "true" then
if cur_word == ">" or cur_word == "<" or cur_word == ">="
or cur_word == "<=" or cur_word == "=" or cur_word == "!=" then
if sorted_set_first_in == true then
assert((string.find(operand_font, ":") ~= nil and string.find(operand_rear, ":") == nil)
or (string.find(operand_rear, ":") ~= nil and string.find(operand_font, ":") == nil)
, "logic_expr has wrong num of operands around '>' or '<'.")
end
sorted_set_first_in = false
-- 判断两个操作数,哪个是数字,哪个是key
local is_operand_rear_a_num = is_operand_a_num(operand_rear)
local is_operand_front_a_num = is_operand_a_num(operand_font)
-- 如果操作数格式如 tagId1:score,则取 tagId1
operand_rear = getKeyFromWord(operand_rear)
operand_font = getKeyFromWord(operand_font)
if cur_word == ">" then
if is_operand_front_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_rear)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, operand_font, '+inf')
elseif is_operand_rear_a_num then
-- 如果操作数格式如 tagId1:score,则取 tagId1
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_font)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '-inf', operand_rear)
else
error("logic_expr has wrong operands arounds '>'.")
end
elseif cur_word == "<" then
if is_operand_front_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_rear)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '-inf', operand_font)
elseif is_operand_rear_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_font)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, operand_rear, '+inf')
else
error("logic_expr has wrong operands arounds '<'.")
end
elseif cur_word == ">=" then
if is_operand_front_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_rear)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '('..operand_font, '+inf')
elseif is_operand_rear_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_font)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '-inf', '('..operand_rear)
else
error("logic_expr has wrong operands arounds '>='.")
end
elseif cur_word == "<=" then
if is_operand_front_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_rear)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '-inf', '('..operand_font)
elseif is_operand_rear_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_font)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '('..operand_rear, '+inf')
else
error("logic_expr has wrong operands arounds '<='.")
end
elseif cur_word == "=" then
if is_operand_front_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_rear)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '-inf', '('..operand_font)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '('..operand_font, '+inf')
elseif is_operand_rear_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_font)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '-inf', '('..operand_rear)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '('..operand_rear, '+inf')
else
error("logic_expr has wrong operands arounds '<='.")
end
elseif cur_word == "!=" then
if is_operand_front_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_rear)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '('..operand_font-1, "("..operand_font+1)
elseif is_operand_rear_a_num then
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 1, operand_font)
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '('..operand_rear-1, "("..operand_rear+1)
else
error("logic_expr has wrong operands arounds '!='.")
end
end
elseif cur_word == "&&" then
operand_rear = getKeyFromWord(operand_rear)
operand_font = getKeyFromWord(operand_font)
redis.pcall('zinterstore', self.key_final_set.."underline"..count, 2, operand_font, operand_rear)
elseif cur_word == "||" then
operand_rear = getKeyFromWord(operand_rear)
operand_font = getKeyFromWord(operand_font)
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 2, operand_font, operand_rear)
elseif cur_word == "^" then
operand_rear = getKeyFromWord(operand_rear)
operand_font = getKeyFromWord(operand_font)
redis.pcall('zunionstore', self.key_final_set.."underline"..count, 2, operand_font, operand_rear, "weights", 1, 0, "aggregate", "min")
redis.pcall('zremrangebyscore', self.key_final_set.."underline"..count, '-inf', 0)
else
error("logic_expr has other operands except '>', '<', '&&' and '||'.")
end
else
error("error input argv 'is_sorted_set_calc' : "..is_sorted_set_calc..".")
end
-- 临时结果的 key 入栈,继续参与表达式运算
if cur_word == ">" or cur_word == "<" or cur_word == ">="
or cur_word == "<=" or cur_word == "=" or cur_word == "!=" then
operand_stack:push(self.key_final_set.."underline"..count..":score")
else
operand_stack:push(self.key_final_set.."underline"..count)
end
end
cur_word = suffix_expr_queue:deQueue()
end
local size_final_set = -1 -- 运算结果集的元素个数
-- 如果 count 为 0
if count == 0 then
-- 如果只有一个操作数
if operand_count == 1 then
if self.is_sorted_set_calc == false or self.is_sorted_set_calc == "false" then
redis.pcall('sinterstore', self.key_final_set, pre_word, pre_word)
size_final_set = redis.pcall('scard', self.key_final_set)
elseif self.is_sorted_set_calc == true or self.is_sorted_set_calc == "true" then
redis.pcall('zunionstore', self.key_final_set, 1, pre_word)
size_final_set = redis.pcall('zcard', self.key_final_set)
else
error("error input argv 'is_sorted_set_calc' : "..is_sorted_set_calc..".")
end
-- 否则,多余一个操作数,而无操作符,表达式有误,返回 -1
else
error("logic_expr has no operators with more than one operand.")
end
-- 否则,将临时结果转为最终结果,存入 key_final_set 中,并取最终结果集的元素个数
else
if self.is_sorted_set_calc == false or self.is_sorted_set_calc == "false" then
redis.pcall('sinterstore', self.key_final_set, self.key_final_set.."underline"..count, self.key_final_set.."underline"..count)
size_final_set = redis.pcall('scard', self.key_final_set)
elseif self.is_sorted_set_calc == true or self.is_sorted_set_calc == "true" then
redis.pcall('zunionstore', self.key_final_set, 1, self.key_final_set.."underline"..count)
size_final_set = redis.pcall('zcard', self.key_final_set)
else
error("error input argv 'is_sorted_set_calc' : "..is_sorted_set_calc..".")
end
-- 同时,把之前的临时结果集在 redis 中删除
if count >= 1 then
for i = 1, count
do
redis.pcall('del', self.key_final_set.."underline"..i)
end
end
end
return {size_final_set, "success"};
end
-- 计算逻辑表达式主流程
local calc = function ()
-- str 表达式转 queue_table
local status, queue_table_ = pcall(strToQueueTable)
if not status then
return {-1, queue_table_}
end
-- 初始化表达式队列
local origin_queue = {queue_table = queue_table_}
local expr_queue = Queue:new(origin_queue)
-- 中缀表达式转后缀
local status, suffix_queue = pcall(infix2Suffix, expr_queue)
if not status then
return {-1, suffix_queue}
end
--计算后缀表达式,得到结果集的元素个数
local status, num_final_set = pcall(calcInRedis, suffix_queue)
if not status then
return {-1, num_final_set}
end
return num_final_set
-- return {1, "success"}
end
-- 发布公有成员方法
return {calc = calc}
end
--[[
脚本执行入口
KEYS[1] 最终结果集的key
ARGV[1] 逻辑表达式
calc_result 最终结果
--]]
-- local key_final_set = "tagz12"
-- local logic_expr = "10=tagz1:score"
-- local is_sorted_set_calc = true
local key_final_set = KEYS[1]
local logic_expr = ARGV[1]
local is_sorted_set_calc = ARGV[2]
local calc_result = {-1, "logic_expr or key_final_set is nil."}
if key_final_set ~= nil and logic_expr ~= nil then
local logicExprCalculator = LogicExprCalculator(key_final_set, logic_expr, is_sorted_set_calc)
calc_result = logicExprCalculator.calc()
end
-- print(calc_result[2])
return calc_result