Get more than one value from memcache with Ruby - ruby

I installed *memcache_client* GEM Ruby from http://seattlerb.rubyforge.org/memcache-client/
It's easy to get a single value:
cache.get('foo', 'bar')
How to get all values, starting with 'foo', for example foo_1, foo_2, foo_3, foo_* ?
Something like "SELECT * FROM foo", but for Memcached.
There will be about 10 000 "foo_n" entries.

Not a perfect solution, but look at the get_multi function:
keys = (1..10_000).map{ |n| "foo_#{n}" }
data = cache.get_multi(*keys)
Unfortunately memcached doesn't support regex key lookups, or even let you get a list of all the keys to process on your own. One alternative would be to use Redis which can get a list of keys using a glob style pattern.

Might want to look at Redis as an alternative to memcache. It supports lists, sets, sorted sets and hashes. http://code.google.com/p/redis/

Related

Redis pipeline as atomic

Is there a way to have the following in a 1-request 1-response way?
given a redis set with key name and for each key has a redis hash with fields
How can I fetch the hashes of those keys in one go?
something like this, which is the best I can do (not sure this will work, but you get the idea)
hashes = []
keys = redis.smembers("myset")
redis.multi do
keys.map do |k|
hashes << redis.hgetall(k)
end
end
hashes = hash.map(&:value) # to resolve future values
but this does at least two requests (which isn't the best but ok), not sure how Redis::Future resolves to value (if it sends another request or not)
No, Redis don't have a command which would return hashes for multiple keys* at once. You're stuck with n + 1 requests - one to get the keys, and one for each key.
You could try to write a Lua script, which to do all that in Redis and return the combined results. See the documentation of the EVAL command for an introduction to scripting in Redis. Note that if you do this, you cannot use a cluster, as keys must be explicitly provided to the script for it to be safe in a cluster.
*MGET does return values for multiple keys, but only if they're strings.

How to create unique ID in format xx-123 on rails

is it possible to create some unique ID for articles on rails?
For example, first article will get ID - aa-001,
second - aa-002
...
article #999 - aa-999,
article #1000 - ab-001 and so on?
Thanks in advance for your help!
The following method gives the next id in the sequence, given the one before:
def next_id(id, limit = 3, seperator = '-')
if id[/[0-9]+\z/] == ?9 * limit
"#{id[/\A[a-z]+/i].next}#{seperator}#{?0 * (limit - 1)}1"
else
id.next
end
end
> next_id("aa-009")
=> "aa-010"
> next_id("aa-999")
=> "ab-001"
The limit parameter specifies the number of digits. You can use as many prefix characters as you want.
Which means you could use it like this in your application:
> Post.last.special_id
=> "bc-999"
next_id(Post.last.special_id)
=> "bd-001"
However, I'm not sure I'd advice you to do it like this. Databases have smart methods to avoid race conditions for creating ids when entries are created concurrently. In Postgres, for example, it doesn't guarantee gapless ids.
This approach has no such mechanism, which could potentially lead to race conditions. However, if this is extremely unlikely to happen such in a case where you are the only one writing articles, you could do it anyway. I'm not exactly sure what you want to use this for, but you might want to look into to_param.
You may want to look into the FriendlyId gem. There’s also a Railscast on this topic which covers a manual approach as well as the usage of FriendlyId.

How do I find and remove duplicate mongo documents with ruby

I have a collection in Mongo with duplicates on a specific key that I need to remove all but one of. The Map Reduce solutions don't seem to make it clear how to remove all but one of the duplicates. I am using Ruby, how can I do this in a somewhat efficient way? My current solution is unbelievably slow!
I currently just iterate over an array of the duplicate keys and delete the first document that is returned but this only works if there are at most 1 duplicate document for each key and it is really slow.
dupes.each do |key|
$mongodb.collection("some_collection").remove($mongodb.collection("some_collection").find({key: key}).first)
end
I think you should use the MongoDB ensureIndex() to remove the duplicates. For instance, in your case, you want to drop the duplicate documents give the key duplicate_key, you can do
db.duplicate_collection.ensureIndex({'duplicate_key' : 1},{unique: true, dropDups: true})
where duplicate_collection is the collection where your duplicate documents are. This operation will only preserve single document if there are duplicate documents give a particular key.
After the operation, if you think you want to remove the index, just do the dropIndex operation. For details, you can search the mongodb documentation.
A lot of solutions suggest Map Reduce (which is fast and fine) but I implemented a solution in Ruby that seems pretty fast as well and makes it easy to leave the one document from each duplicate set.
Basically you find all your duplicate keys by adding them to a hash and any time you find a duplicate key in the collection you add the id of that document to an array which you will use in a bulk removal at the end.
all_keys = {}
dupes = []
dupe_key = "some_key"
$mongodb.collection("some_collection").find.each do |doc|
all_keys[doc[dupe_key]].present? ? dupes << doc["_id"] : asins[doc[dupe_key]] = 1
end
$mongodb.collection("some_collection").remove({_id: {"$in" => dupes } })
The only issue with this method is that it potentially won't work if the total list of keys/dupe ids can't be stored in memory. The map reduce solution would probably be best at that point.

How would I convert this Ruby hash to an array?

I have this data in a hash:
[{"total_time"=>"00:04:48.563044"}, {"total_time"=>"00:05:29.835918"}, {"total_time"=>"00:09:38.622569"}]
But I want this:
["00:04:48.563044", "00:05:29.835918", "00:09:38.622569"]
Needs to work with Ruby 1.8.7.
You might manage with this:
list.collect(&:values).flatten
There's a ton of ways to accomplish this. Let's break it down into the basic steps you need to accomplish:
Iterate over each item in the array of hashes
For each item, grab the time value
Reassemble those into a list
Since you want to grab the result for each item, not just look at it, you'll want to use map (or collect, they're actuality the same method). That will take care of steps 1 and 3. And step 2, by itself, is pretty easy. You just need to get the value for a key with item['total_time']. Put it all together, and you've got this:
times.map{ |time| time['total_time'] }
Speaking about a ton of ways to accomplish this:
a = [{"total_time"=>"00:04:48.563044"}, {"total_time"=>"00:05:29.835918"}, {"total_time"=>"00:09:38.622569"}]
p a.map(&:flatten).map(&:last)

Is there a way to alias/anchor an array in YAML?

I'm using Jammit to package assets up for a Rails application and I have a few asset files that I'd like to be included in each of a few groups. For example, I'd like Sammy and its plugins to be in both my mobile and screen JS packages.
I've tried this:
sammy: &SAMMY
- public/javascripts/vendor/sammy.js
- public/javascripts/vendor/sammy*.js
mobile:
<<: *SAMMY
- public/javascripts/something_else.js
and this:
mobile:
- *SAMMY
but both put the Sammy JS files in a nested Array, which Jammit can't understand. Is there a syntax for including the elements of an Array directly in another Array?
NB: I realize that in this case there are only two elements in the SAMMY Array, so it wouldn't be too bad to give each an alias and reference both in each package. That's fine for this case, but quickly gets unmaintainable when there are five or ten elements that have a specific load order.
Closest solution I know of is this one:
sammy:
- &SAMMY1
public/javascripts/vendor/sammy.js
- &SAMMY2
public/javascripts/vendor/sammy*.js
mobile:
- *SAMMY1
- *SAMMY2
- public/javascripts/something_else.js
Alternatively, as already suggested, flatten the nested lists in a code snippet.
Note: according to yaml-online-parser, your first suggestion is not a valid use of << (used to merge keys from two dictionaries. The anchor then has to point to another dictionary I believe.
If you want mobile to be equal to sammy, you can just do:
mobile: *SAMMY
However if you want mobile to contain other elements in addition to those in sammy, there's no way to do that in YAML to the best of my knowledge.
Your example is valid YAML (a convenient place to check is YPaste), but it's not defined what the merge does. Per the spec, a merge key can have a value:
A mapping, in which case it's merged into the parent mapping.
A sequence of mappings, in which case each is merged, one-by-one, into the parent mapping.
There's no way of merging sequences on YAML level.
You can, however, do this in code. Using the YAML from your second idea:
mobile:
- *SAMMY
you'll get nested sequences - so flatten them! Assuming you have a mapping of such nested sequences:
data = YAML::load(File.open('test.yaml'))
data.each_pair { |key, value| value.flatten! }
(Of course, if you have a more complicated YAML file, and you don't want every sequence flattened (or they're not all sequences), you'll have to do some filtering.)
This solution is for Symfony/PHP only (considerations for other languages, see below)
Note about array keys from the PHP array manual page:
Strings containing valid decimal ints, unless the number is preceded by a + sign, will be cast to the int type. E.g. the key "8" will actually be stored under 8. [...]
This means that if you actually index your anchor array with integer keys, you can simply add new keys by continuing the initial list. So your solution would look like this:
sammy: &SAMMY
1: public/javascripts/vendor/sammy.js
2: public/javascripts/vendor/sammy*.js
mobile:
<<: *SAMMY
3: public/javascripts/something_else.js
You can even overwrite keys and still add new ones:
laptop:
<<: *SAMMY
1: public/javascripts/sammy_laptop.js
3: public/javascripts/something_else.js
In both cases the end result is a perfectly valid indexed array, just like before.
Other programming languages
Depending on your YAML implementation and how you iterate over your array, this could conceivably also be used in other programming languages. Though with a caveat.
For instance, in JS you can access numerical string keys by their integer value as well:
const sammy = {"1": "public/javascripts/vendor/sammy.js"}
sammy["1"]; // "public/javascripts/vendor/sammy.js"
sammy[1]; // "public/javascripts/vendor/sammy.js"
But you'd need to keep in mind, that your initial array is now an object, and that you would need to iterate over it accordingly, e.g.:
Object.keys(sammy).forEach(key => console.log(sammy[key]))
As it has been suggested, when you need to flatten a list, at least in ruby, it is trivial to add a "!flatten" type specifier to mobile and implement a class that extends Array, adds the yaml_tag and flattens the coder seq on init_with.

Resources