Ruby SQLite adapter fails when using escape characters - ruby-on-rails

I am using the sqlite3 adapter for Ruby and getting some strange behavior. I am using ? to escape input into my queries. The following works:
db.execute 'SELECT * FROM table_name WHERE id = ?', 1
=> [{ "id"=> 1, "name" => "Hannah" }]
db.execute 'SELECT * FROM table_name WHERE name = ?, 'Hannah'
=> [{ "id"=> 1, "name" => "Hannah" }]
db.execute 'SELECT * FROM table_name WHERE id = 1 AND name = ?', 'Hannah'
=> [{ "id"=> 1, "name" => "Hannah" }]
The following, however, does not work:
db.execute 'SELECT * FROM table_name WHERE id = ? AND name = ?', 1, 'Hannah'
=> []
Why is this?

Related

Ruby how to format nested hash

I have two queries I am running and iterating over both and my final hash is seen below. But, I want to have format on how the data is being stored in the hash that I'm creating or format it after I'm done creating it. But I am not sure how to achieve the desired format where the names fall under the same id as show below
desired format of example data:
[
{
id: 1,
accepted: false,
trans: 10234
names: [
{ name: "Joe", amount: "$1,698.00" },
{ name: "Smith", amount: "$674.24" },
]
},
{
id: 2,
accepted: true,
trans: 10234,
names: [
{ name: "Joe", amount: "$1,698.00" },
{ name: "Smith", amount: "$674.24" },
]
}
]
current format I have
[
{
:id => 1,
:accepted => false,
:trans => 8,
:name => "Smith",
:amount => 36.0
},
{
:id => 1,
:amount => false,
:trans => 8,
:name => "Joe",
:amount => 6.0
},
{
:id => 3,
:accepted => false,
:trans => 8,
:name => "Tom",
:amount => 34.0
},
{
:id => 3,
:accepted => false,
:trans=> 8,
:name => "Martha",
:amount => 4.0
}
],
[
{
:id => 2,
:accepted => true,
:trans => 7,
:name => "Bob",
:amount => 35.0
},
{
:id => 2,
:accepted => true,
:trans => 7,
:name => "John",
:amount => 5.0
}
]
logic for creating hash
imports = ListImports.limit(20).order(created_at: :DESC)
groups = imports.map{|import| ListImportGroup.where(list_import_id: import.id)}
pub_hash_true = []
pub_hash_false = []
hash = []
imports.map do |import|
hash << {
id: import.id,
trans: import.trans,
accepted: import.amount
}
end
hash.each do |import|
groups.flatten.each do |group|
accepted = import[:accepted]
num_transactions = import[:trans]
if accepted == false
pub_hash_false << {id: import[:id], accepted: accepted, trans: num_transactions, name: group.name, amount: group.amount}
else
pub_hash_true << {id: import[:id], accepted: accepted, trans: num_transactions, name: group.name, amount: group.amount}
end
end
end
# Note: You didn't specify what is the association between `ListImport` and `ListImportGroup`.
# However, I'm fairly sure you could be fetching this data via a JOIN query like below,
# rather than making up to 20 additional database calls to fetch the associated records.
imports = ListImports.limit(20).order(created_at: :DESC).includes(:list_import_group)
result = imports.map do |import|
{
id: import.id,
trans: import.trans,
accepted: import.amount,
names: import.list_import_groups.pluck(:name, :amount)
}
end
And if you do actually need to filter for imports where accepted is true or false, you could do something like this instead of building separate arrays manually:
accepted_imports = result.select { |import| import[:accepted] }
# and
rejected_imports = result.reject { |import| import[:accepted] }
# or even:
accepted_imports, rejected_imports = result.partition { |import| import[:accepted] }
You didn't specify the exact correspondence between the desired and current formats.
But I assume
For the entries with the same id, the values of accepted and trans are identical.
the desired amount for Joe in the current format is identical in the corresponding amount in the desired amount. (In your example, the former is 6.0 whereas the latter is "$1,698.00", which does not make sense.)
Then, the following would do the conversion. The array ahout is in the desired format.
# Let us assume "a1" is the original array in the "current format"
hout = {}
a1.flatten.map{|h|
h.slice(*(%i(id trans name amount accepted))).values
}.each{ |a|
hout[a[0]] = {id: a[0], accepted: a[4], trans: a[1], names: []} if !hout.key? a[0]
hout[a[0]][:names].push(
{name: a[2], amount: "$"+helper.number_with_precision(a[3], precision: 2, delimiter: ',')}
)
}
ahout = hout.values
You may want to sort ahout, if you like.
Note that I am assuming you are using Rails 5+. Otherwise, the method helper may not work. In that case, you can use sprintf or whatever formatting method.

How to query over range of range key of dynamodb?

I'm using 'aws-sdk', '~> 2.6.44'. I've an activities table where I store all the activities performed by a user.
params = {
table_name: 'activities', # required
key_schema: [ # required
{
attribute_name: 'actor', # required User.1
key_type: 'HASH', # required, accepts HASH, RANGE
},
{
attribute_name: 'created_at', # timestamp
key_type: 'RANGE'
}
],....
I want to query this table with all the activities performed by user in past 1 day. Looks like the AWS documentation site has the documentation for SDK version 3.
tableName = 'activities'
params = {
table_name: tableName,
key_condition_expression: "#user = :actor and #time between :start_time and :end_time",
expression_attribute_names: {
"#user" => 'actor',
"#time" => "created_at"
},
expression_attribute_values: {
actor: 'User.1',
":start_time" => (Time.now - 1.days).to_i,
":end_time" => (Time.now + 1.days).to_i
}
}
DynamodbClient.client.get_item(params)
# Throws: ArgumentError: no such member :key_condition_expression
I tried with filter expression:
tableName = 'activities'
params = {
table_name: tableName,
key: {
actor: 'User.1'
},
filter_expression: "created_at BETWEEN (:id1, :id2)",
expression_attribute_values: { ":id1" => (Time.now - 1.days).to_i,":id2" => (Time.now + 1.days).to_i},
projection_expression: "actor"
}
DynamodbClient.client.get_item(params)
# Throws ArgumentError: no such member :filter_expression
What should be right way to query DynamoDB table with a ranged option for range key?
Looks like I should use query if I'm not trying to retrieve a specific record.
Following query worked:
tableName = 'activities'
params = {
table_name: tableName,
key_condition_expression: "#user = :actor and #time between :start_time and :end_time",
expression_attribute_names: {
"#user" => 'actor',
"#time" => "created_at"
},
expression_attribute_values: {
":actor" => 'User.1',
":start_time" => (Time.now - 1.days).to_i,
":end_time" => (Time.now + 1.days).to_i
}
}
DynamodbClient.client.query(params)
=> #<struct Aws::DynamoDB::Types::QueryOutput items=[{"actor"=>"User.1", "action"=>"Like", "created_at"=>#<BigDecimal:7fa6418b86e0,'0.150683976E10',18(27)>, "source"=>"FeedSource.661", "body"=>{"id"=>#<BigDecimal:7fa6418b82f8,'0.72E2',9(18)>}, "target"=>"FeedEntry.8419"}], count=1, scanned_count=1, last_evaluated_key=nil, consumed_capacity=nil>
:)

Rails array group_by

I have a dynamic array of object result from a database query.
USERNAME choice_indx legend
USER1 3 4
USER2 0 4
USER3 0 4
USER1 9 2
USER2 9 2
USER3 8 2
USER1 3 1
USER2 9 1
USER3 8 1
Query:
SELECT survey_answers.anonymous_user_id, survey_answers.choice_index, survey_questions.legend
FROM `survey_answers`
LEFT JOIN surveys ON surveys.id = survey_answers.survey_id
LEFT JOIN survey_questions ON survey_questions.id = survey_answers.survey_question_id
WHERE (survey_questions.legend IN (1,2,4)) AND (survey_answers.track_id =2) AND (survey_answers.survey_id =2) AND (surveys.survey_type =2)
How can I group this by user and result it like this:
final_array = {
"USER1" => [[3,4],[9,2],[3,1]],
"USER2" => [[0,4],[9,2],[9,1]],
"USER3" => [[0,4],[8,2],[8,1]]
}
I've tried using group_by in rails, but the result was not the same from what I want. Can anyone lend me a hand? Thanks.
Assuming objects is an Enumerable of ActiveModel objects with anonymous_user_id, choice_index, and legend attributes, this will do what you want:
objects.map {|obj| obj.values_at(:anonymous_user_id, :choice_index, :legend) }
.group_by(&:shift)
You can skip the map if instead you use pluck in your ActiveRecord query, e.g.:
MyModel.where(...)
.pluck(:anonymous_user_id, :choice_index, :legend)
.group_by(&:shift)
Edit
In reply to your comment, yes it is, although it's not quite as clean:
MyModel.where(...)
.pluck(:anonymous_user_id, :choice_index, :legend)
.map {|vals| Hash[ %w[ __key__ c_idx legend ].zip(vals) ] }
.group_by {|hsh| hsh.delete("__key__") }
Or:
MyModel.where(...)
.pluck(:anonymous_user_id, :choice_index, :legend)
.each_with_object(Hash.new {|h,k| h[k] = [] }) do |(key, c_idx, legend), hsh|
hsh[key] << { "c_idx" => c_idx, "legend" => legend }
end
I don't believe there is a rails way to do this, but with ruby you could do
# create hash with a empty array for each username
results = Hash[#query.pluck(:username).map { |username| [username, []] }]
#query.each { |data| results[data.username] << [data.choice_idx,
data.legend] }
Assuming the result from the query is data_hash (array of hash), then the following will give you the desired result:
data_hash = [{ 'USERNAME' => 'USER1', 'choice_indx' => 3, 'legend' => 4 },
{ 'USERNAME' => 'USER2', 'choice_indx' => 0, 'legend' => 4 },
{ 'USERNAME' => 'USER3', 'choice_indx' => 0, 'legend' => 4 },
{ 'USERNAME' => 'USER1', 'choice_indx' => 9, 'legend' => 2 },
{ 'USERNAME' => 'USER2', 'choice_indx' => 9, 'legend' => 2 },
{ 'USERNAME' => 'USER3', 'choice_indx' => 8, 'legend' => 2 },
{ 'USERNAME' => 'USER1', 'choice_indx' => 3, 'legend' => 1 },
{ 'USERNAME' => 'USER2', 'choice_indx' => 9, 'legend' => 1 },
{ 'USERNAME' => 'USER3', 'choice_indx' => 8, 'legend' => 1 }]
final_array = Hash.new{|h,k|h[k]=[]}
data_hash.each do |data|
final_array[data['USERNAME']] << [data['choice_indx'], data['legend']]
end
p final_array
# => {"USER1"=>[[3, 4], [9, 2], [3, 1]], "USER2"=>[[0, 4], [9, 2], [9, 1]], "USER3"=>[[0, 4], [8, 2], [8, 1]]}

How would I save multiple records at once in Rails?

How would I save this array in one call with Rails?
tax_rates = [{
:income_from => 0
:income_to => 18200
:start => "01-07-2013"
:finish => "30-06-2014"
:rate => nil
:premium => nil
},{
:income_from => 18201
:income_to => 37000
:start => "01-07-2013"
:finish => "30-06-2014"
:rate => 0.19
:premium => nil
},{
:income_from => 18201
:income_to => 37000
:start => "01-07-2013"
:finish => "30-06-2014"
:rate => 0.19
:premium => nil
}]
Can I just call Rails.create(tax_rates)?
Also, is there a way to remove duplicate symbols so they look neater?
Your example is almost correct.
Use ActiveRecord::Persistence#create, which can accept an array of hashes as a parameter.
tax_rates = [
{
income_from: 0,
income_to: 18200,
start: "01-07-2013",
finish: "30-06-2014",
rate: nil,
premium: nil,
},
{
income_from: 18201,
income_to: 37000,
start: "01-07-2013",
finish: "30-06-2014",
rate: 0.19,
premium: nil,
},
# ...
]
TaxRate.create(tax_rates) # Or `create!` to raise if validations fail
A nice solution is to use the active record import gem. I recommend it over now built-in Rails bulk insert because it's more flexible in the options in case of constraint violation.
TaxRate.import(
[:income_from, :income_to, :start, :finish, :rate, :premium],
tax_rates
)
Its definitely better than my old answer which would trigger a db commit per entry in the array :)
Old answer:
tax_rates.map {|tax_rate| TaxRate.new(tax_rate).save }
This way you'll retrieve an Array with true or false to know which did succeed and which didn't.
If you want all of them to be saved .or, non of them to be saved even if one fails, you can use 'ActiveRecord::Base.transaction'
e.g.
ActiveRecord::Base.transaction do
tax_rate.each do |tax_rt|
TaxRate.new(tax_rt).save
end
end
I am not sure about rails < 4.2 but I have tried it in rails 4.2 you can simply do this
TaxRate.create(tax_rt)
Here is an example like yours:
a = []
a << B.new(:name => "c")
a << B.new(:name => "s")
a << B.new(:name => "e")
a << B.new(:name => "t")
The array is saved all at once with:
a.each(&:save)
This will call B#save on each item in the array.
use a gem 'fast_inserter': https://github.com/joinhandshake/fast_inserter
it generates a single sql query of thousand records.
movie_data = [1, 'Climates (Iklimler)', 'Clay Pauwel', 'Drama'],
[2, 'Tinpis Run', 'Andros Glazer', 'Comedy'],
[3, 'Naked City, The', 'Bethena Chatband', 'Mystery'],
[4, 'Small Time Crooks', 'Naomi Plom', 'Crime'],
[5, 'Shadowboxer', 'Georgeanne Widdicombe', 'Thriller']
params = {
table: 'movies',
static_columns: {
created_at: '0000-00-00 00:00:00',
updated_at: '0000-00-00 00:00:00',
},
options: {
timestamps: false,
unique: true,
check_for_existing: true
},
group_size: 100,
variable_columns: %w(id title director description),
values: movie_data
}
inserter = FastInserter::Base.new(params)
inserter.fast_insert

Ruby: group an array of ActiveRecord objects in a hash

I'd like to group an array of ActiveRecord objects into a hash with an interface that's simple to query after an SQL statement that looks like the following:
SELECT name,value from foo where name IN ('bar', 'other_bar') LIMIT 2;
After that query, I want a hash where I can go:
foo[:bar] # output: value1
foo[:other_bar] # output: value2
What's the best way to collect the objects with ActiveRecord and group them so I can use the interface above?
In Rails 2
foos = Foo.all :select => "name, value",
:conditions => ["name in (?)", %w(bar other_bar)],
:limit => 2
In Rails 3
foos = Foo.where("name in (?)", %w(bar other_bar)).select("name, value").limit(2)
Then
foo = Hash[foos.map { |f| [f.name.to_sym, f.value] }]
or
foo = foos.inject({}) { |h, f| h[f.name.to_sym] = f.value; h }
or in Ruby 1.9
foo = foos.each_with_object({}) { |f, hash| hash[f.name.to_sym] = f.value }
If I understood you correctly:
foo = Hash[Foo.find(:all, :limit => 2, :select => "name, value", :conditions => ["name in ('bar', 'other_bar')"]).map { |s| [s.name.intern, s.value] }]
Hash[result.map { |r| [r[:name].to_sym, r[:value]] } ]
models.inject({}) {|h,m| h[ m.name.to_sym ] = m.value; h }

Resources