I would like to access value with the given pattern from hash in redis with out providing key.
Example
HSET myKey va11 "Hello" val2 "Hi" Val3 "GooMorning" val4 "Good Evening"
HSET myKey2 va11 "one val2 "two" Val3 "three" val4 "four"
I have set of keys with their values as above. Is there any way to retrieve values without providing keys.
i just want to check is there any value with Good* something like that without providing key.
I saw that you're using the "lua" tag - if LUA is not a a must, please consider the following example using HVALS. I'l provide some redis-py code to go with:
import redis
# connect to local redis-server:6379
r = redis.Redis()
# initialize sample "myKey" hash
r.hmset("myKey", {'val1': "Hello", 'val2': "Hi", 'val3': "GoodMorning", 'val4': "Good Evening"})
# provide "starts with" pattern (this could obviously be a regex as well)
pattern = "Good"
# find all values starting with the pattern and return them
values_matching = filter(lambda x: x.startswith(pattern), r.hvals("myKey"))
# print values: ["GoodMorning", "Good Evening"]
print values_matching
You don't say that you need the keys for the matching values. If you do, then you should look into the HGETALL command.
EDIT after reading your comments: yes, you will need to loop over all key/values in the hash using HGETALL. An amended example following:
import redis
r = redis.Redis()
r.hmset("myKey", {'val1': "Hello", 'val2': "Hi", 'val3': "GoodMorning", 'val4': "Good Evening"})
pattern = "Good"
# use hgetall() to iterate over all key-value pairs and form values_matching by looking only at pairs where the value starts with "Good"
values_matching = dict([(k, v) for k, v in r.hgetall("myKey").iteritems() if v.startswith(pattern)])
# print values: {'val3': 'GoodMorning', 'val4': 'Good Evening'}
print values_matching
Redis doesn't provide this functionality out of the box. However, if I understand your question correctly, you can easily add it.
Keep a sorted set, for example call it allvals, with all the values from your hashes (i.e. do a ZADD to it on every hash update). Use ZRANGEBYLEX on it for suffix searching as means for existence checking:
ZRANGEBYLEX allvals "[Good" "[Good\xff"
EDIT: Given your clarification, this approach is not what you're looking for. Here's my take on the revised challenge.
So. What you're looking for is a way to fetch hash key names if they contain a specific value in one of their fields. One way would be to scan all you hashes and look for that value (as #tobiash suggested) but, depending on the size of your dataset, this may be quite expensive. Another way to go about this is to use sets as means for indexing your hashes. The basic premise is to take every value in the hash(es) and create sets for it. Each such set's members would be the actual key names of the hashes that contain that value.
Let's see how your example's myKey should be indexed (yes, we're building an index here). To begin with, you'll create the following sets out of it:
SADD idx:Hello myKey
SADD idx:Hi myKey
SADD idx:GooMorning myKey
SADD "idx:Good Evening" myKey
Now, by doing SMEMBERS on idx:Hello, you'll get all the key names that have Hello as values. However, since what you're looking for is doing suffix searching, for each value you'll actually need to maintain multiple sets, where each set indexes a substring of the value. For example, for the string Hello you'll actually need:
SADD idx:H myKey
SADD idx:He myKey
SADD idx:Hel myKey
SADD idx:Hell myKey
SADD idx:Hello myKey
Lua can help here if you'll create a script that you'll call to update your hashes. The script should be given the hash key name and the field names + values. Once called, the script will need not only to create/update the actual hash, but also to iterate over all field values and construct the index for them (and their respective substrings).
Let us know if you need help with writing that script ;)
Related
Working an learning some Redis and I am running into an issue that I can't figure out why it is not working.
It is my understanding from some posts and documentation I read that I can store data with similar keys like this
redis.set("fruit:1", "apple")
# OK
redis.set("fruit:2", "banana")
# OK
and it is my understanding I should be able to get all fruit like this
redis.get("fruit:*")
But I am missing something because this keep returning null and I cannot figure out what I need to do to return something like
=> apple, banana
I was able to figure out, using scan (suggested in answer below as well) how to return all matching fruit keys, but what I need is to search for all matching fruit keys, and then return the values for them.
#redis.scan_each(match: "fruit:*") do |fruit|
Rails.logger.debug('even run?')
fruits << fruit
end
=> fruit:1, fruit:2 # What I need though is apple, banana
What solved it (at least so far) is using mGet
#redis.scan_each(match: "fruit:*") do |fruit|
Rails.logger.debug('even run?')
fruits << #redis.mGet(fruit)
end
As far as I know, you can not do that (it would be cool to be possible).
Instead you have to run a scan SCAN 0 MATCH "fruit:*" COUNT 500 where 500 is number of keys to parse in the first iteration. So you have to run multiple iterations... until you reach the end of your key count.
If you want to get all keys with a single iteration, first you need to know how many keys there are in your DB, so you can run a command to find the total amount of keys:
dbsize which you can use instead of the 500 value in my example.
redis-cli dbsize
or
redis-cli INFO Keyspace | grep ^db0 | cut -f 1 -d "," | cut -f 2 -d"="
To return all values from all keys you can use MGET
https://redis.io/commands/mget
Maybe you can also take a deeper look at data structures
Maybe the solution above is not the correct one, in your case you can also use a set just to add your fruits in there.
https://redis.io/commands/sadd
SCAN can be a very expensive operation as it must traverse all the keys, even if you use a pattern. Because Redis is single-threaded, while that SCAN is happening, nothing else can happen and other requests are blocked. If you only have a few keys, this is fine. But if you have a lot of keys, this can cause serious performance issues.
What you need is an index that contains the keys of all of your fruits. You could use a Set to do this and whenever you add a fruit, you add the key to the Set:
> SET fruit:1 apple
> SADD fruit:index 1
Then, when you need all the fruits, you just get the members of the Set:
> SMEMBERS fruit:index
1) "1"
2) "2"
Then get the values of the fruits:
> GET fruit:1
> GET fruit:2
I have a hash in which id is the key and name is the value. Both id and value are unique.
Something like this:
h[1] = "ABC"
h[3] = "DEF"
So, if I am given the key of 1, I can easily return a value "ABC".
I need to do a reverse lookup as well, which means if I am given a value of "DEF", I should return 3.
Also, instead of a single value or single key to do the lookup,
I may be provided with an array of values or array of keys instead.
Should I implement two hashes, one for each, or is there any other way in ruby or rails to achieve that?
Edit: This question is not related to finding a key by its value in a hash. It is related to doing a two way lookup not in O(n) time with a better method other than creating two separate hashes.
You can use Hash#invert as below,
reversed_h = h.invert
reversed_h['DEF']
# => 3
You can get your key this way:
hash.key(value) => key
Hash#key
h = { 1 => 'ABC', 3 => 'DEF' }
puts h.key('DEF')
#=> 3
I am trying to write a Lua script that will return multiple keys back from a Redis DB. The script I am trying to run is:
script load "local values = redis.call('MGET', unpack(ARGV)); local results = {}; for i, key in ipairs(ARGV) do results[key] = values[i] end; return results"
I would then try and run it using evalsha (whatever the sha number is that it returns) 0 dog cat (where dog and cat are two keys I have saved in my DB).
Ideally, it would return "woof" "meow" as those are the values stored in the keys dog and cat. For some reason, this script will always return an empty list or set, and I do not understand why. Any help figuring it out would be appreciated!
results should be an array, i.e. indexed with number, NOT string. See conversion between Lua and Redis data types for details.
In order to make it work, change results[key] = values[i] into results[i] = values[i].
Also, in fact, you can just return values, there's no need to convert values to results.
My understanding is that pairs(t) simply returns next, t, nil.
If I change that to next, t, someKey (where someKey is a valid key in my table) will next start at/after that key?
I tried this on the Lua Demo page:
t = { foo = "foo", bar = "bar", goo = "goo" }
for k,v in next, t, t.bar do
print(k);
end
And got varying results each time I ran the code. So specifying a starting key has an effect, unfortunately the effect seems somewhat random. Any suggestions?
Every time you run a program that traverses a Lua table the order will be different because Lua internally uses a random salt in hash tables.
This was introduced in Lua 5.2. See luai_makeseed.
From the lua documentation:
The order in which the indices are enumerated is not specified, even
for numeric indices. (To traverse a table in numeric order, use a
numerical for.)
My goal is to determine whether there is a blank in a hash like this:
{"departure_time_slots"=>{"date"=>[""], "time"=>{"start"=>[""], "end"=>[""]}}}
The strings are nested arbitrary times. I do not want to check each of them manually. It would be nice if I can extract all the strings regardless of their depth. Is there a simple way to do that?
Here's an example of how you can extract the values. However you will still have the problem of matching the values to the keys. You can't really have both at the same time.
# Extend the Hash class
class Hash
def recursive_values
self.values.collect { |v|
v.is_a?(Hash) ? v.recursive_values : v
}.flatten
end
end
Usage:
h = {"departure_time_slots"=>{"date"=>[""], "time"=>{"start"=>[""], "end"=>[""]}}}
h.recursive_values
=> ["", "", ""]
It will be better if you will use sth like that:
departure_time_slots = {:date => Time.new, :time_start => nil, :time_end => nil}
And when you use keys in Hash it is good practise to using Symbols for keys. (http://www.ruby-doc.org/core-1.9.3/Symbol.html)
No not possible. Because they are totally present in different scope with respect to each other.
For e.g.. keys start and end is totally unknown and masked from the departure_time_slots object in the example above.
One round abut way could be, getting all the values of the hashmap which are of type hashmap again and obtaining their keys recusively.
Fetch keys of departure_time_slots and then from the value list of that map, find all the keys from every value, if that were to be a hashmap. Other than that, I don't think there is another way.
P.S. On side note, see if u can modify your structure to an array where elements can also be arrays, and try and use flatten concept of arrays. :P