I have one model call DataIndicator, it contains daily data,
And It has the following column.
:id => :integer,
:date => :datetime,
:dau => :integer,
:login_count => :integer
It had many data, but now I need to change some of it.
How do I massive update its value by date?
EX:
The original
{ "id" => 1, "date" => 2017-01-01 00:00:00 UTC, "dau" => 5 , "login_count" => 150 },
{ "id" => 2, "date" => 2017-01-02 00:00:00 UTC, "dau" => 5 , "login_count" => 140 },
{ "id" => 3, "date" => 2017-01-03 00:00:00 UTC, "dau" => 5 , "login_count" => 300 }
Now I have a hash value, which would be referred to modify the original data.
Like this
update_date = {
"2017-01-01" => {
"dau" => 5,
"login_count" => 5,
},
"2017-01-02" => {},
"2017-01-03" => {
"dau" => 5,
},
...
}
As you can see, the update_date will not contain all attributes, it may only have one or even zero new data.
What is the best way to update this value?
I can only think about the bad one.
Like this
update_date.each do |k, v|
data_by_date = DataIndicator.where(date: DateTime.parse(k)).first
next if data_by_date.nil?
data_by_date.update(v)
end
I think I misread your question.
Wouldn't it be easier if you just picked all DataIndicator records and then checked if the hash contained data for it?
DataIndicator.all.each do |di|
date = di.date.strftime("%Y-%m-%d")
to_update = update_date[date]
next if to_update.blank?
to_update.each do |field,value|
di.send("#{field}=".to_sym, value)
end
di.save!
end
This works for me.
For Rails 4+
DataIndicator.where(["date(date) = ?"], "2017-01-01").update_all(dau: 5, login_count: 5)
Related
The docs say this:
{ :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
=> {"name": "Konata Izumi", "1": 2, "age": 16}
But when I actually try it in the rails console it looks like this:
{ :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
=> "{\"name\":\"Konata Izumi\",\"age\":16,\"1\":2}"
How do I get it to return an object/hash instead of a string? I don't want the outer quotes around the curly braces. I want {...} instead of "{...}".
irb(main):012:0> x = { :name => "Konata Izumi", 'age' => 16, 1 => 2 }.to_json
irb(main):013:0> puts x
{"name":"Konata Izumi","age":16,"1":2}
=> nil
If you capture the results in a variable and then print it, you'll see that it is working as expected. The quotes are just the console's way of indicating that the stuff after => is a string.
JSON is a format for serializing data to text. It simply is a String, it cannot be anything else, that would be non-sensical.
If you want to keep a Ruby object instead of serializing to a string, simply don't serialize to a string.
I have integrated elasticsearch in a ROR application.
I am using elastic search to find products named 'xyz' and filtered on basis of star count = 1
This is my mapping:
{
:product => {
:dynamic => false,
:properties => {
:name => {
:analyzer => "english",
:type => "text"
},
:description => {
:analyzer => "english",
:type => "text"
},
:available => {
:type => :boolean
},
:last_updated => {
:type => :date
},
:categories_names => {
:type => "text"
},
:stars => {
:type => :integer
}
}
}
}
I am using the following query:
{
"query": {
"bool": {
"must": [
{
"multi_match": {
"query": "xyz",
"fields": ["name", "description"],
"type": "most_fields"
}
},
{
"match": {
"available": true
}
}
],
"filter": {
"term": {
"stars": 1
}
}
}
}
}
This gives me no match but I have product records with stars having value 1.
Also, range query is not giving any results with field stars.
This is the record that should match the above query:
#<Product> {
"id" => 90,
"name" => "xyz",
"last_updated" => Tue, 22 May 2018 15:04:43 UTC +00:00,
"stars" => 1,
"description" => "training",
"available" => true,
"created_at" => Thu, 21 Jun 2018 11:56:15 UTC +00:00,
"updated_at" => Fri, 29 Jun 2018 10:30:06 UTC +00:00,
}
try adding doc_value: true while indexing the field
indexes :stars, type: 'integer', doc_values: true
Most fields are indexed by default, which makes them searchable. The inverted index allows queries to look up the search term in unique sorted list of terms, and from that immediately have access to the list of documents that contain the term.
Sorting, aggregations, and access to field values in scripts require a different data access pattern. Instead of looking up the term and finding documents, we need to be able to look up the document and find the terms that it has in a field.
Doc values are the on-disk data structure, built at document index time, which makes this data access pattern possible. They store the same values as the _source but in a column-oriented fashion that is way more efficient for sorting and aggregations. Doc values are supported on almost all field types, with the notable exception of analyzed string fields.
Though default doc_values is true but explicitly adding it solved my purpose
Mapping and query looks good, maybe the issue in within the data (or maybe the mapping in your code is not the same in your cluster?).
Also may I suggest you move the "available: true" match as a "term" filter in the filter part? Filter is an array too, like must.
I have a huge array full of a bunch of hashes. What I need to do is single out one index hash from the array that meets a specific criteria. (doing this due to an rspec test, but having trouble singling out one of them)
My array is like this
[
{
"name" => "jon doe",
"team" => "team2",
"price" => 2000,
"eligibility_settings" => {}
},
{
"name" => "jonny doe",
"team" => "team1",
"value" => 2000,
"eligibility_settings" => {
"player_gender" => male,
"player_max_age" => 26,
"player_min_age" => 23,
"established_union_only" => true
}
},
{
"name" => "jonni doe",
"team" => "team3",
"price" => 2000,
"eligibility_settings" => {}
},
]
I need to single out the second one, based on its eligibility settings. I just took three of them from my array, have lots more, so simple active record methods like (hash.second) won't work in this instance.
I've tried things like
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
However when I try this, I get a nil response. (which is odd)
I've also looked into using the ruby detect method, which hasn't gotten me anywhere either
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Would anybody have any idea what to do with this one?
Notes
players.team.map(&:hash).find{ |x| x[ 'eligibility_settings?' ] == true}
Players.team.map(&:hash).['hash.seligibiltiy_settings'].detect { true }
Is is players or Players ?
Why is it plural?
If you can call map on team, it probably should be plural
Why do you convert to a hash?
eligibility_settings? isn't a key in your hash. eligibility_settings is
eligibility_settings can be a hash, but it cannot be true
If you want to check if it isn't empty, use !h['eligibility_settings'].empty?
Possible solution
You could use :
data = [
{
'name' => 'jon doe',
'team' => 'team2',
'price' => 2000,
'eligibility_settings' => {}
},
{
'name' => 'jonny doe',
'team' => 'team1',
'value' => 2000,
'eligibility_settings' => {
'player_gender' => 'male',
'player_max_age' => 26,
'player_min_age' => 23,
'established_union_only' => true
}
},
{
'name' => 'jonni doe',
'team' => 'team3',
'price' => 2000,
'eligibility_settings' => {}
}
]
p data.find { |h| !h['eligibility_settings'].empty? }
# {"name"=>"jonny doe", "team"=>"team1", "value"=>2000, "eligibility_settings"=>{"player_gender"=>"male", "player_max_age"=>26, "player_min_age"=>23, "established_union_only"=>true}}
If h['eligibility_settings'] can be nil, you can use :
data.find { |h| !h['eligibility_settings'].blank? }
or
data.find { |h| h['eligibility_settings'].present? }
Doing a query with aggregate functions directly on ActiveRecord with Postgres seems to be working ok.
ActiveRecord::Base.connection.execute("
SELECT created_at::date as date,
sum(item1_count) as sum_item1,
sum(item2_count) as sum_item2,
sum(item3) as sum_item3 from items
GROUP by
created_at::date ORDER BY date desc").to_a
And returns something like this which is ok.
[
{
"date" => "2014-01-23",
"sum_item1" => "3239",
"sum_item2" => "90",
"sum_item3" => "0.00000"
},
{
"date" => "2014-01-22",
"sum_item1" => "1981",
"sum_item2" => "19",
"sum_item3" => "0.00000"
}
]
The problem is when trying to do the same using scopes, for instance.
class Item < ActiveRecord::Base
scope :myscope, -> {
select("created_at::date as date, sum(item1_count) as sum_item1,
sum(item2_count) as sum_item2,
sum(item3) as sum_item3")
.group("created_at::date")
.order("date desc") }
end
The result here is different. When running user.items.myscope.to_a I get the following result missing the aggregate values and adding an id field that should not be there.
[
#<Item:0x00000103cc3d38> {
:id => nil,
:date => Thu, 23 Jan 2014
},
#<Item:0x00000103cc39a0> {
:id => nil,
:date => Wed, 22 Jan 2014
}
]
How it would be possible to pass the aggregate functions to the scope?
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