Multiple-get in one go - Redis - lua

How can we use lua scripting to implement multi-get.
Let's say if I've set name_last to Beckham and name_first to David. What should the lua script be in order to get both name_last and name_first in one go?
I can implement something like:
eval "return redis.call('get',KEYS[1])" 1 foo
to get the value of single key. Just wondering on how to enhance that scripting part to get values related to all keys (or multiple keys) by just making one call to redis server.

First, you want to send the fields you want to return to EVAL (the 0 indicates that there are no KEYS so these arguments will be accessible from ARGV):
eval "..." 0 name_last name_first
Second, you can query the values for the individual fields using MGET:
local values = redis.call('MGET', unpack(ARGV))
Third, you can maps the values back to the field names (the index of each value corresponds to the same field):
local results = {}
for i, key in ipairs(ARGV) do
results[key] = values[i]
end
return results
The command you'll end up executing would be:
eval "local values = redis.call('MGET', unpack(ARGV)); local results = {}; for i, key in ipairs(ARGV) do results[key] = values[i] end; return results" 0 name_last name_first

Do a loop over the KEYS table and for each store its GET response in a take that you return. Untested code:
local t = {}
for _, k in pairs(KEYS) do
t[#t+1] = redis.call('GET', k)
end
return t
P.S. You can also use MGET instead btw :)

Related

why is table.remove and in pairs function both not working in Roblox Studio?

I have the following code which Roblox Developer and the Lua.org manual both say should work to remove an instance from the table so I can store as a local, but the local is only holding a nil value.
The table is there. It shows up on the print function. It just will not store to be useful in the app.
I have tried multiple versions of this code including going with just the pairs function, just the table.remove function, and going with and without the position for the table remove, and it all generates nil variable.
response = HttpService:GetAsync(mining)
data = HttpService:JSONDecode(response, Enum.HttpContentType.ApplicationJson)
local function tprint(t)
for k,v in pairs(t) do print(k,v) end
end
tprint(data)
local a = table.remove(data, 4)
local b = table.remove(data, 3)
local c = table.remove(data, 2)
local d = table.remove(data, 1)
The solution ended up being so simple, and yet so profound. I can now use this to link crypto, bank accounts, credit cards, and anything else I want directly into Roblox or any other lua based program.
a = (data["result"]["amount"])
Before I dig into the error you're seeing, first some background information.
Lua tables have two methods of indexing values: numerically, and by keys. Often times you will see these two different methods be used to describe the kind of data structure that uses it.
Arrays and lists are tables that use numeric keys to index information.
local arr = {}
arr[1] = "abc"
arr[2] = 123
arr[3] = true
-- print the length of the array
print(#arr) -- 3
-- print the contents of the array
for i, v in ipairs(arr) do
print(i, v)
-- 1 abc
-- 2 123
-- 3 true
end
On the other side of things, dictionaries and hash maps and associative arrays use keys to store information :
local dict = {}
dict["foo"] = "abc"
dict["bar"] = 123
dict["blah"] = true
dict["katz"] = { 1, 2, 3 }
-- print the number of numerical keys in the dictionary
print(#dict) -- 0
-- print the contents of the dictionary
for k, v in pairs(dict) do
print(k, v)
-- foo abc
-- bar 123
-- blah true
-- katz table
end
While lua allows a table to use both of these indexing methods simultaneously, it's important never to mix the two, as behaviors can get real funky when you do. When a table has keys, treat it like a dictionary. When a table has numerical indices, treat it like an array.
When you use HttpService to decode a JSON string into a table, it generates a dictionary that reflects the heirarchical structure of the original data.
The table library, which you call with table.insert() and table.remove() expects that the table you're working with is an array.
When your data is arranged like this :
local data = {}
data["Success"] = true
data["StatusCode"] = 200
data["StatusMessage"] = "Success"
data["Headers"] = {} -- a dictionary of headers
data["Body"] = {
result = {
amount = 1,
depositAddress = "blah",
},
} -- after HttpService:JSONDecode() is called...
And you tell it to remove a numbered index with table.remove(data, 4), it won't work because there's no data stored at index number 4. data is a dictionary, not an array.
Often, it is annoying to try to print out the contents of a table with multiple layers of data, especially JSON tables, as the pairs function will only index one level at a time. Thankfully, Roblox's print function and Output widget are smart enough to do this for you. You can simply print(data) and it will show you the full table in the output and allow you to inspect each level.
Then once you know how your data is structured you can step through it value by value.
local amount = data["Body"]["result"]["amount"]
-- or
local amount = data.Body.result.amount

Table sorting index names LUA [duplicate]

I have gone through many questions and Google results but couldn't find the solution.
I am trying to sort a table using table.sort function in Lua but I can't figure out how to use it.
I have a table that has keys as random numeric values. I want to sort them in ascending order. I have gone through the Lua wiki page also but table.sort only works with the table values.
t = { [223]="asd", [23]="fgh", [543]="hjk", [7]="qwe" }
I want it like:
t = { [7]="qwe", [23]="fgh", [223]="asd", [543]="hjk" }
You cannot set the order in which the elements are retrieved from the hash (which is what your table is) using pairs. You need to get the keys from that table, sort the keys as its own table, and then use those sorted keys to retrieve the values from your original table:
local t = { [223]="asd", [23]="fgh", [543]="hjk", [7]="qwe" }
local tkeys = {}
-- populate the table that holds the keys
for k in pairs(t) do table.insert(tkeys, k) end
-- sort the keys
table.sort(tkeys)
-- use the keys to retrieve the values in the sorted order
for _, k in ipairs(tkeys) do print(k, t[k]) end
This will print
7 qwe
23 fgh
223 asd
543 hjk
Another option would be to provide your own iterator instead of pairs to iterate the table in the order you need, but the sorting of the keys may be simple enough for your needs.
What was said by #lhf is true, your lua table holds its contents in whatever order the implementation finds feasible. However, if you want to print (or iterate over it) in a sorted manner, it is possible (so you can compare it element by element). To achieve this, you can do it in the following way
for key, value in orderedPairs(mytable) do
print(string.format("%s:%s", key, value))
end
Unfortunately, orderedPairs is not provided as a part of lua, you can copy the implementation from here though.
The Lua sort docs provide a good solution
local function pairsByKeys (t, f)
local a = {}
for n in pairs(t) do table.insert(a, n) end
table.sort(a, f)
local i = 0 -- iterator variable
local iter = function () -- iterator function
i = i + 1
if a[i] == nil then return nil
else return a[i], t[a[i]]
end
end
return iter
end
Then you traverse the sorted structure
local t = { b=1, a=2, z=55, c=0, qa=53, x=8, d=7 }
for key,value in pairsByKeys(t) do
print(" " .. tostring(key) .. "=" .. tostring(value))
end
There is no notion of order in Lua tables: they are just sets of key-value pairs.
The two tables below have exactly the same contents because they contain exactly the same pairs:
t = { [223] = "asd" ,[23] = "fgh",[543]="hjk",[7]="qwe"}
t = {[7]="qwe",[23] = "fgh",[223] = "asd" ,[543]="hjk"}

[lua]: Key with value 1 doesnt do the same as regular 1

Ive been coding for a mod Im making for a game but I ran into an issue with tables not returning values when the key is entered:
for k, v in pairs(self.math) do
print(self.exce[1])
print(self.exce[k])
print(k)
if self.exce[k] ~= nil then
self.math[k] = nil
end
end
This is the specific part of the script that is breaking. When I run these in the game it returns:
[lua]: true
[lua]: nil
[lua]: 1
Which means is basically saying that 1 is not equal to 1.
The function I used to store my data is
function filterExceptions.server_onException( self, id )
if self.exce[id] == nil then
self.exce[id] = true
self.network:sendToClients( "client_onList", id )
else
self.exce[id] = true
self.network:sendToClients( "client_offList", id )
end
end
In this code the self is a table made by the game you can acces and get game data from or store it in and the id comes from a function I made to get the players id. This id in this case is a 1 (I printed it multiple times).I know that every part of this code is working except for the code in the first block, and escpecialy the part where it tries to do self.exce[k]. Ive tried a lot like going trough every variable in self.exce to see if it was in there and then do stuff, but it still wouldn't work. Its very annoying how lua thinks that k ~= 1 while it definitely is, ive even used similar code in a part that is working.
So what is wrong about this code that its not printing the self.exce[k] while self.exce[1] does work? Dont worry about the creation of the table and stuff, cuz that is already happening whenever it is needed, else it would have given errors about that too.
Putting together a couple different comments and your code here, it looks like the index value of the array in some particular iteration of the "for in pairs" loop (or perhaps all of them, but I'll touch on that in a minute) is a string instead of an integer.
To summarize if you don't want to read the entire thing, "for k, v in pairs" loops will iterate through an entire array, setting k to the index of the value v. It appears your "for in pairs" loop is attempting to iterate through a value of nil where k is a string instead of an integer. You may also want to look into using ipairs instead of pairs in your for loop.
The value of someArray[1]is different than the value of someArray["1"].
The index [1] is a completely different index than the index ["1"] for any given array.
A simple fix would be to use
ind = tonumber(k)
print(self.exce[ind])
This converts the string k to a number type. Be aware this may throw an error if k is a non-numerical string. If the array has any values where the index is a non-numerical string, you may get an error. As the other answer suggests, converting the index k to a string instead of an integer would work as well, and would not throw errors if you used a non-numerical value for your indices.
My guess as to why this is happening would be that the function that you're using to store your data to an array, filterExceptions.server_onException( self, id ), is being passed a string instead of an integer, which would result in the k value being set to a string in that particular iteration of the "for in pairs" loop.
To help better understand this, here's a bit of example code:
a = {true, false, false}
a[1] = true
a["1"] = true
print("Raw for in pairs loop")
for k, v in pairs (a) do
print(type(k)..k)
end
print("For in pairs converting k to a number")
for k, v in pairs (a) do
ind = tonumber(k)
print(type(ind)..ind)
end
print("For in ipairs")
--which I'm not sure I completely understand but
--it seems to skip over any iteration where k is not a number
for k, v in ipairs(a) do
print(type(k)..k)
end
This code produces the following output:
Raw for in pairs loop
number1
number2
number3
string1
For in pairs converting k to a number
number1
number2
number3
number1
For in ipairs
number1
number2
number3
EDIT: Not sure what's going on in the self.math table so I can't comment on that.
EDIT2: I'd also refer you to the following link: lua: iterate through all pairs in table
The top answer there should help understand the difference between pairs and ipairs, if you don't already. You may want to use ipairs to prevent values of k where v == nil from being iterated through with pairs. pairs will iterate through every key/value pair, whereas ipairs will iterate through integer keys starting at 1 and going until it hits a nil value.
EDIT3: I'm sorry this is such a long answer...I just wanted to be thorough.
It apears converting the id to a string fixes this, tough im still confused as to why this same code worked on another block and not this one.
function filterExceptions.server_onException( self, id )
local id2 = tostring(id)
if self.exce[id2] == nil then
self.exce[id2] = true
self.network:sendToClients( "client_onList", id )
else
self.exce[id2] = true
self.network:sendToClients( "client_offList", id )
end
end

lua - how to initialize a table using a string

I have the following string:
mystring = "a=test;b=12345"
I would like to know how to initialize a table in one shot, assign it the value of the string. The string originates from another external application, and if possible I want to avoid having to split it up. Something like this:
mytable = {mystring:gsub(";",",")}
Is it possible to do something like this? I know how to do it in multiple steps... but just wondering if it's possible to do it all at once.
Here's what I've tried and the respective output:
> mystring = "a=123;b=2345"
> myarray = {mystring:gsub(";",",")}
> for key,value in pairs(myarray) do print(key,value) end
1 a=123,b=2345
2 1
>
whereas I was hoping to end up with an array / table where like this:
key value
a 123
b 2345
-- Lua 5.2+ required
function string_to_table (str)
local result = {}
load(str, '', 't', setmetatable({}, {
__index = function(t,k) return k end,
__newindex = result
}))()
return result
end
mytable = string_to_table("a=test;b=12345;c=a") -- {a="test", b=12345, c="a"}
Try this, which lets Lua do the hard work:
function parse(s)
local t={}
load(s,"",nil,t)()
return t
end
mytable=parse("a=123;b=2345")
for k,v in pairs(mytable) do print(k,v) end
Note that this executes the code in the given string, which may be dangerous if it comes from an untrusted source. On the other hand, the damage is limited because the code is executed in an empty environment and so cannot affect existing variables. Malicious code may contain infinite loops or consume all memory, though.
mytable = {}
for key, value in string.gmatch("a=123;b=456", "(%w+)=(%w+)") do
mytable[key] = value
end
print(mytable.a, mytable.b)
Returns:
123
456
as expected. This only works, of course, with alphanumeric and no punctuation.

Lua script to return efficient dictionary from Redis HGETALL call

I need to use Redis HMGET from a Lua script and extract specific values in following code.
But redis.call('HMGET', table_key, hkey1, hkey2, ...) return a flat array of {hkey1, val1, hkey2, val2, ...}
To extract values by key I wrote:
local function flat_map_get(flat_map, hash_key)
local i = 1
while flat_map[i] do
if flat_map[i] == hash_key then
return flat_map[i+1]
end
i = i+2
end
end
Of course, as usage grow, multiple calls to this function presented major performance drop.
What is an efficient way to read values from the flat array returned by HMGET?
Or otherwise, to convert the returned value into a proper key-value table?
After some profiling and tests, we found the following function to have good performance and use it to get a proper table.
This save the need to call a getter function for each hash key retrieval.
local function hgetall(hash_key)
local flat_map = redis.call('HGETALL', hash_key)
local result = {}
for i = 1, #flat_map, 2 do
result[flat_map[i]] = flat_map[i + 1]
end
return result
end

Resources