How to parse a SQL result and convert it to Ruby hash - sql

I want to convert a SQL query result into a Ruby hash, where only two rows are shown and the first act as key and second as value. For example, if my query got this result:
+----+--------+
| id | name |
+--- +--------+
| 1 | a |
| 2 | b |
| 3 | c |
| 4 | d |
| 5 | e |
+----+--------+
I want to manipulate this data to get a Ruby hash like this:
h = { '1' => 'a',
'2' => 'b'.
'3' => 'c',
'4' => 'd',
'5' => 'e' }
How can I get this done?

I use ruby Sequel in most of my non-rails projects. It's an excellent ORM for SQL databases.
Here is a code sample using SQLite (in memory):
#!/usr/bin/env ruby
require 'sequel'
# create db in memory
DB = Sequel.sqlite
# create table
DB.create_table :items do
primary_key :id
String :name
end
# Create a dataset
items = DB[:items]
# Populate the table
items.insert(:name => 'john')
items.insert(:name => 'mike')
items.insert(:name => 'nick')
puts "#{items.all}"
# => [{:id=>1, :name=>"john"}, {:id=>2, :name=>"mike"}, {:id=>3, :name=>"nick"}]
# initialize hash object
h = {}
# Create the hash in the form you want
items.all.each do |entry|
h[entry[:id]]= entry[:name]
end
# p the hash
p h # => {1=>"john", 2=>"mike", 3=>"nick"}
NOTE: Sequel is extremely powerful. There might be a method to do what you what you want directly, without passing the data through the loop. However, you'll have to read the documentation to find out if you need to clean your code.
Hope this helps!
UPDATE: So here is the updated code after Jeremy Evan's (author of Sequel):
#!/usr/bin/env ruby
require 'sequel'
# create db in memory
DB = Sequel.sqlite
# create table
DB.create_table :items do
primary_key :id
String :name
end
# Create a dataset
items = DB[:items]
# Populate the table
items.insert(:name => 'john')
items.insert(:name => 'mike')
items.insert(:name => 'nick')
# Return hash of items
p items.select_hash(:id, :name) # => {1=>"john", 2=>"mike", 3=>"nick"}

Related

Get distinct results from database that has a "dictionary"

I have a database with a "dictionary" like this:
Id (INT) | Key (VARCHAR) | Value (VALUE) | ForeignKey (INT?)
------------------------------------------------------------
1 | foo | bar | 23
2 | bar | foo | NULL
3 | foobar | value | NULL
4 | foobar | othervalue | 47
Now I want to get all keys and values for a specific foreign key, but also all foreign keys that are NULL, so that the result looks like this:
ForeignKey = 23: ForeignKey = 47:
foo | bar bar | foo
bar | foo foobar | othervalue
foobar | value
Originally I tried this approach:
dbContext.Table
.Where(t => t.ForeignKey == 47 || t.ForeignKey == null)
but that gives me foobar twice.
I then though about multiple requests and unionizing the results:
var t1 = _dbContext.Table
.Where(t => t.ForeignKey == 47);
var t2 = _dbContext.Table
.Where(t => t.ForeignKey == null && !t1.Any(tt => tt.Key == t.Key));
var final = t1.Union(t2);
This seemingly works, but it creates this SQL (roughly) which seems excessive with three SELECT, three WHERE and a UNION
SELECT [t1].[Id], [t1].[Key], [t1].[Value], [t1].ForeignKey
FROM [Table] AS [t1]
WHERE [t1].[ForeignKey] = 47
UNION
SELECT [t2].[Id], [t2].[Key], [t2].[Value], [t2].ForeignKey
FROM [Table] AS [t2]
WHERE [t2].[ForeignKey] IS NULL AND NOT (EXISTS (
SELECT 1
FROM [Table] AS [t3]
WHERE ([t3].[ForeignKey] = 47) AND ([t3].[Key] = [t2].[Key])))
I just have a gut feeling that "there's got to be a better way"... so, is there? How can I get keys and values for a specific foreign key, but also keys not already fetched that are NULL?
The SQL you're seeing doesn't seem excessive to me. Unless you've tested this and found it to have unacceptably bad performance, I wouldn't necessarily spend too much time trying to optimize it.
Here's an alternative approach, though, which may be preferable. You'd have to do some testing to see if it yields better SQL, execution plan, and performance. Mostly I'd go with whichever approach will be easier for other developers to understand.
var final = dbContext.Table.Where(t => t.ForeignKey == 47 || t.ForeignKey == null)
.GroupBy(t => t.Key)
.Select(g => g.OrderBy(t => t.ForeignKey == null ? 1 : 0).FirstOrDefault());

Prefix or append a string to meta_title, meta_key, meta_description in category page

Category page meta_title, meta_key and meta_description tags come from the table ps_category_lang.
mysql> select * from ps_category_lang limit 1;
+-------------+---------+---------+-------+-------------+--------------+------------+---------------+------------------+
| id_category | id_shop | id_lang | name | description | link_rewrite | meta_title | meta_keywords | meta_description |
+-------------+---------+---------+-------+-------------+--------------+------------+---------------+------------------+
| 1 | 1 | 1 | Raíz | | raiz | | | |
+-------------+---------+---------+-------+-------------+--------------+------------+---------------+------------------+
1 row in set (0.00 sec)
Is it possible to add a prefix (or suffix) to those three values, so it uses the information from the database but it appends or prefixs a certain value?
If so, what shall be needed to be done? I already have a custom module overriding the category page with extended template and controller.
Prestashop 1.7.1
The best way is by overriding the /classes/controller/FrontController.php, specifically the method getTemplateVarPage() in the code:
$page = array(
'title' => '',
'canonical' => $this->getCanonicalURL(),
'meta' => array(
'title' => $meta_tags['meta_title'],
'description' => $meta_tags['meta_description'],
'keywords' => $meta_tags['meta_keywords'],
'robots' => 'index',
),
'page_name' => $page_name,
'body_classes' => $body_classes,
'admin_notifications' => array(),
);
Here you could validate the current page and alterate it as your needs.
for each standard controller in PrestaShop, you have a dedicated function in the Meta class, in your case, the getCategoryMetas() function that you can override and adapt to fit your needs.
You also can use the previous answer to rewrite the metas firstly computed in the Meta::getCategoryMetas() in CategoryController::getTemplateVarPage() function.
Good luck

Lithium Model find query with <> operator

I'm trying to fetch rows excluding the one which already are in an other table :
| Content | ContentSelected
| –––––––– | ––––––––––––––––––––––––-
| id | content_id | operation_id
| 1 | 1 | 9999
| 2 | 3 | 1000
| 3
=> 2,3
Here how I tried to run this query :
Content::find('all', array(
'joins' => array(new Query(array(
'source' => 'ContentSelected',
'constraints' => array('ContentSelected.operation_id' => 9999),
'conditions' => array(
'Content.id' => array('<>' => 'ContentSelected.content_id')
));
Here the SQL query run by the adapter :
SELECT * FROM "Content" AS "Content"
JOIN "ContentSelected" ON "ContentSelected"."operation_id" = 1
WHERE ("Content"."id" <> 0);
Is there another way to performed a query excluding results, or to force the adapter to write ContentSelected.content_id instead of 0 in the where clause ?
Thanks
You can use Model::connection()->read() to run a native sql in Li3. Check this out http://www.jblotus.com/2011/10/25/executing-raw-mysql-queries-in-lithium/
Content::connection()->read($your_sql);
But before using such a solution. Try to implement your model properly using Model Relationships (http://li3.me/docs/manual/models/relationships.md) so that you don't need to use a native query and let the model does the work for you.

Redis find key based on set value

Im using redis to store tags for certain entities. Examples of the data:
+-------------+--------------------------------+
| key | value |
+-------------+--------------------------------+
| book:1:tags | [python, ruby, rails] |
+-------------+--------------------------------+
| book:2:tags | [fiction, fantasy] |
+-------------+--------------------------------+
| book:3:tags | [fiction, adventure] |
+-------------+--------------------------------+
How do I find all books with a particular tag, ie all books tagged with fiction?
You have to maintain reverse indexes yourself. Along with those keys that you posted, you should create reverse references.
tag:python => [1]
tag:ruby => [1]
tag:rails => [1]
tag:fiction => [2, 3]
tag:fantasy => [2]
tag:adventure => [3]
Then it's trivial to do what you want. But maybe you should consider using another tool for the job. For example, MongoDB can efficiently index and query arrays.

SQL Populate table with random data

I have a table with two fields:
id(UUID) that is primary Key and
description (var255)
I want to insert random data with SQL sentence.
I would like that description would be something random.
PS: I am using PostgreSQL.
I dont know exactly if this fits the requirement for a "random description", and it's not clear if you want to generate the full data: but, for example, this generates 10 records with consecutive ids and random texts:
test=# SELECT generate_series(1,10) AS id, md5(random()::text) AS descr;
id | descr
----+----------------------------------
1 | 65c141ee1fdeb269d2e393cb1d3e1c09
2 | 269638b9061149e9228d1b2718cb035e
3 | 020bce01ba6a6623702c4da1bc6d556e
4 | 18fad4813efe3dcdb388d7d8c4b6d3b4
5 | a7859b3bcf7ff11f921ceef58dc1e5b5
6 | 63691d4a20f7f23843503349c32aa08c
7 | ca317278d40f2f3ac81224f6996d1c57
8 | bb4a284e1c53775a02ebd6ec91bbb847
9 | b444b5ea7966cd76174a618ec0bb9901
10 | 800495c53976f60641fb4d486be61dc6
(10 rows)
The following worked for me:
create table t_random as select s, md5(random()::text) from generate_Series(1,5) s;
Here it is a more elegant way using the latest features. I will use the Unix dictionary (/usr/share/dict/words) and copy it into my PostgreSQL data:
cp /usr/share/dict/words data/pg95/words.list
Then, you can easily create a ton of no sense description BUT searchable using dictionary words with the following steps:
1) Create table and function. getNArrayS gets all the elements in an array and teh number of times it needs to concatenate.
CREATE TABLE randomTable(id serial PRIMARY KEY, description text);
CREATE OR REPLACE FUNCTION getNArrayS(el text[], count int) RETURNS text AS $$
SELECT string_agg(el[random()*(array_length(el,1)-1)+1], ' ') FROM generate_series(1,count) g(i)
$$
VOLATILE
LANGUAGE SQL;
Once you have all in place, run the insert using CTE:
WITH t(ray) AS(
SELECT (string_to_array(pg_read_file('words.list')::text,E'\n'))
)
INSERT INTO randomTable(description)
SELECT getNArrayS(T.ray, 3) FROM T, generate_series(1,10000);
And now, select as usual:
postgres=# select * from randomtable limit 3;
id | description
----+---------------------------------------------
1 | ultracentenarian splenodiagnosis manurially
2 | insequent monopolarity funipendulous
3 | ruminate geodic unconcludable
(3 rows)
I assume sentance == statement? You could use perl or plperl as perl has some good random data generators. Check out perl CPAN module Data::Random to start.
Here's a sample of a perl script to generate some different random stuff taken from CPAN.
use Data::Random qw(:all);
my #random_words = rand_words( size => 10 );
my #random_chars = rand_chars( set => 'all', min => 5, max => 8 );
my #random_set = rand_set( set => \#set, size => 5 );
my $random_enum = rand_enum( set => \#set );
my $random_date = rand_date();
my $random_time = rand_time();
my $random_datetime = rand_datetime();
open(FILE, ">rand_image.png") or die $!;
binmode(FILE);
print FILE rand_image( bgcolor => [0, 0, 0] );
close(FILE);