How to obtain result as array of Hashes in Ruby (mysql2 gem) - mysql

I'm using Ruby's mysql2 gem found here:
https://github.com/brianmario/mysql2
I have the following code:
client = Mysql2::Client.new(
:host => dbhost,
:port => dbport, :database => dbname,
:username => dbuser,
:password => dbpass)
sql = "SELECT column1, column2, column3 FROM table WHERE id=#{id}"
res = client.query(sql, :as => :array)
p res # prints #<Mysql2::Result:0x007fa8e514b7d0>
Is it possible the above .query call to return array of hashes, each hesh in the res array to be in the format column => value. I can do this manually but from the docs I was left with the impression that I can get the results directly loaded in memory in the mentioned format. I need this, because after that I have to encode the result in json anyway, so there is no advantage for me to fetch the rows one by one. Also the amount of data is always very small.

Change
res = client.query(sql, :as => :array)
to:
res = client.query(sql, :as => :hash)
As #Tadman says, :as => :hash is the default, so actually you don't have to specify anything.

You can always fetch the results as JSON directly:
res = client.query(sql, :as => :json)
The default format, as far as I know, is an array of hashes. If you want symbol keys you need to ask for those. A lot of this is documented in the gem itself.
You should also be extremely cautious about inserting things into your query with string substitution. Whenever possible, use placeholders. These aren't supported by the mysql2 driver directly, so you should use an adapter layer like ActiveRecord or Sequel.

The source code for mysql2 implemented MySql2::Result to simply include Enumerable, so the obvious way to access the data is by using any method implemented in Enumerabledoc here.
For example, #each, #each_with_index, #collect and #to_a are all useful ways to access the Result's elements.
puts res.collect{ |row| "Then the next result was #{row}" }.join("\t")

Related

Number Stored as Text when Writing Query to a Worksheet

I am creating a report using the following gems:
require "mysql2"
require "watir"
require "io/console"
require "writeexcel"
After I query a database with mysql2 and convert the query into a multidimensional array like so:
Mysql2::Client.default_query_options.merge!(:as => :array)
mysql = Mysql2::Client.new(:host => "01.02.03.405", :username => "user", :password => "pass123", :database => "db")
report = mysql.query("SELECT ... ASC;")
arr = []
report.each {|row| arr << row}
and then finally write the data to an Excel spreadsheet like so:
workbook = WriteExcel.new("File.xls")
worksheet = workbook.add_worksheet(sheetname = "Report")
header = ["Column A Title", ... , "Column N Title"]
worksheet.write_row("A1", header)
worksheet.write_col("A2", arr)
workbook.close
when I open the file in the latest edition of Excel for OSX (Office 365) I get the following error for every cell containing mostly numerals:
This report has a target audience that may become distracted with such an error.
I have attempted all the .set_num_format enumerable methods found in the documentation for writeexcel here.
How can I create a report with columns that contain special characters and numerals, such as currency, with write excel?
Should I look into utilizing another gem entirely?
Define the format after you create the worksheet.
format01 = workbook.add_format
format01.set_num_format('#,##0.00')
then write the column with the format.
worksheet.write_col("A2", arr, format01)
Since I'm not a Ruby user, this is just a S.W.A.G.

NameError - undefined local variable - Parsing array from ruby file to haml file

We are using a Mysql database and make use of haml files.
The issue we have now is when we dynamically create a table with data from our mysql database it gives us this error:
NameError - undefined local variable or method `allsites' for #<Sinatra::Application:0x00
00000128c980>:
/home/usr/testsinatra/views/sites.haml:16:in `block in singleton class'
/home/usr/testsinatra/views/sites.haml:-8:in `instance_eval'
/home/usr/testsinatra/views/sites.haml:-8:in `singleton class'
/home/usr/testsinatra/views/sites.haml:-10:in `__tilt_12132720'
The weird thing is that the table is actually created !
This is the part of the ruby file where we fill our array with mysql data.
get '/getsites' do
allsites = con2.query("SELECT * FROM tblSites", :as => :array)
haml :sitesOverzicht, :locals => {:allsites => allsites}
end
This is the part where we make our table dynamically:
%table{:border => "1px"}
%tbody
%h1 All Sites
-allsites.each do |id,name|
%tr
%td
= id
%td
= name
The other strange part is when the page loads, we don't get any error and the table is created when the page loads.
But when we create a new tablerow in another table from another array and store this data with Ajax, we get this error for this variable even if we don't do anything.
I've managed to find a fix for my own problem !
To make use of our local variable we had to use the "#" sign like this for our Query:
#allSites = con2.query("SELECT * FROM tblSites", :as => :array)
Afterwards we changed it also in our link to send it to the haml file:
haml :sitesOverzicht,:locals => {:allSites => #allSites}
Afterwards in our haml file we got the NoMethodException but we found out that we had to make use of the to_a.each function to make use of the "each" function we allready had like this:
-#allSites.to_a.each do |id,name|
%tr
%td
= id
%td
= name
With the to_a.each function you say that you will make use of an array as parameter. So the each function will loop over an array. If you use the each function without saying you are using an array, it will give a nill exception.
Your Answer maybe work but it's not really nice.
Because with # you define a Instance variable probably not what you want. But of course you can do that but then you can remove :locals => {:allSites => #allSites}
But in most cases it's better to use locals:
app.rb
get "/" do
allsites = {"1" => "name", "2" => "name 2", "3" => "name3"}
p allsites
haml :index, :locals => {:allsites => allsites}
end
views/index.haml
-allsites.each do |id,name|
%tr
%td
= id
%td
= name
works fine for me so this indicate that what ever your con2.query("SELECT * FROM tblSites", :as => :array) returns it's not exactly what you expected.

Is it possible to implement SUBSTRING_INDEX logic using Ruby Sequel to create a column alias?

I have a client who has a database of images/media that uses a file naming convention that contains a page number for each image in the filename itself.
The images are scans of books and page 1 is often simply the cover image and the actual “page 1” of the book is scanned on something like scan number 3. With that in mind the filenames would look like this in the database field filename:
great_book_001.jpg
great_book_002.jpg
great_book_003_0001.jpg
great_book_004_0002.jpg
great_book_005_0003.jpg
With that in mind, I would like to extract that page number from the filename using MySQL’s SUBSTRING_INDEX. And using pure MySQL it took me about 5 minutes to come up with this raw query which works great:
SELECT `id`, `filename`, SUBSTRING_INDEX(SUBSTRING_INDEX(`filename`, '.', 1), '_', -1) as `page`
FROM `media_files`
WHERE CHAR_LENGTH(SUBSTRING_INDEX(SUBSTRING_INDEX(`filename`, '.', 1), '_', -1)) = 4
ORDER BY `page` ASC
;
The issue is I am trying to understand if it’s possible to implement column aliasing using SUBSTRING_INDEX while using the Sequel Gem for Ruby?
So far I don’t seem to be able to do this with the initial creation of a dataset like this:
# Fetch a dataset of media files.
one_to_many :media_files, :class => MediaFiles,
:key => :id, :order => :rank
Since the returned dataset is an array, I am doing is using the Ruby map method to roll through the fetched dataset & then doing some string processing before inserting a page into the dataset using the Ruby merge:
# Roll through the dataset & set a page value for files that match the page pattern.
def media_files_final
media_files.map{ |m|
split_value = m[:filename].split(/_/, -1).last.split(/ *\. */, 2).first
if split_value != nil && split_value.length == 4
m.values.merge({ :page => split_value })
else
m.values.merge({ :page => nil })
end
}
end
That works fine. But this seems clumsy to me when compared to a simple MySQL query which can do it all in one fell swoop. So the question is, is there any way I can achieve the same results using the Sequel Gem for Ruby?
I gather that perhaps SUBSTRING_INDEX is not easily supported within the Sequel framework. But if not, is there any chance I can insert raw MySQL instead of using Sequel methods to achieve this goal?
If you want your association to use that additional selected column and that filter, just use the :select and :conditions options:
substring_index = Sequel.expr{SUBSTRING_INDEX(SUBSTRING_INDEX(:filename, '.', 1), '_', -1)}
one_to_many :media_files, :class => MediaFiles,
:key => :id, :order => :page,
:select=>[:id, :filename, substring_index.as(:page)],
:conditions => {Sequel.function(:CHAR_LENGTH, substring_index) => 4}

Neo4j::Rails::Model to_json - node id is missing

I have a Jruby on Rails application with Neo4j.rb and a model, let's say Auth, defined like this:
class Auth < Neo4j::Rails::Model
property :uid, :type => String, :index => :exact
property :provider, :type => String, :index => :exact
property :email, :type => String, :index => :exact
end
And this code:
a = Auth.find :uid => 324, :provider => 'twitter'
# a now represents a node
a.to_json
# outputs: {"auth":{"uid": "324", "provider": "twitter", "email": "email#example.com"}}
Notice that the ID of the node is missing from the JSON representation. I have a RESTful API within my application and I need the id to perform DELETE and UPDATE actions.
I tried this to see if it works:
a.to_json :only => [:id]
But it returns an empty JSON {}.
Is there any way I can get the ID of the node in the JSON representation without rewriting the whole to_json method?
Update The same problems applies also to the to_xml method.
Thank you!
I am answering my own question. I still think that there is a better way to do this, but, for now, I am using the following hack:
In /config/initializers/neo4j_json_hack.rb I put the following code:
class Neo4j::Rails::Model
def as_json(options={})
repr = super options
repr.merge! '_nodeId' => self.id if self.persisted?
end
end
And now every JSON representations of my persisted Neo4j::Rails::Model objects have a _nodeId parameter.
The ID is typically not included because it shouldn't be exposed outside the Neo4j database. Neo4j doesn't guarantee that the ID will be identical from instance to instance, and it wouldn't surprise me if the ID changed in a distributed, enterprise installation of Neo4j.
You should create your own ID (GUID?), save it as a property on the node, index it, and use that to reference your nodes. Don't expose the Neo4j ID to your users or application, and definitely don't rely on it beyond a single request (e.g. don't save a reference to it in another database or use it to test for equality).

Extract data from array in DB (rails)

I am trying to extract some data from an array with the following syntax:
#entries_from_db = XrEntry.find(:all, :conditions => [:FeedURI => uri ], :select => 'json')
The :FeedURI is the record that contains an array with uri's ["123456", "23345", "4453"]
The uri is the variable wich contains the current uri.
The statement I'm trying to make is 'select JSON from XrEntry where FeedURI contains uri'
Im stuck on the part to access the array and always get several error msg's when I'm trying different code.
Does anyone has an idea?
Thanks!
I solved it with this syntax
#entries_from_db = XrEntry.find(:all, :conditions => ["FeedURI like ?", "%#{uri}%"] , :select => 'json')
the "%#{your_rails_variable}%" is needed to read in an array
You seem to have switched the condition syntax. you chould start with the db attribute and then the variable.
#entries_from_db = XrEntry.find(:all,
:conditions => { :uri => FeedURI },
:select => 'json')
That will return an array of XrEntry objects with only the json attribute present. To get an array of only the json data you could map it like this:
#json_array = #entries_from_db.map(&:json)