Rails postgres hstore - how to ommit a nil input value from hash - ruby-on-rails-3

I have a form that displays inputs based on user preferences. I am storing the values as an hstore hash since I dont know ahead of time exactly what the form input for each user will be. The problem I am running in to is that even though a user has an input preferenced doesnt mean they have to enter a value for it each time. Which, can result in :foo => "".
All the doc examples show you how to find records you know the key name of. In my case, I dont know the key name...I need to find all the keys in a hash whose value => "".
Then, I should be able to do something like the docs shows...for each empty value
person.destroy_key(:data, :foo).destroy_key(:data, :bar).save
avals(hstore) is likely what I need to user... How do you use avals with rails?

Since hstore is just a hash in rails...you just need to evaluate the hash before saving it.
...in model
before_save :remove_blanks
private
def remove_blanks
self.hstore = self.hstore.reject{ |k,v| v.blank? }
end
replace 'hstore' with your hstore column name

Related

How can I get Rails 5 to play nicely with a primary key that has a period in it?

I'm working with a database I have no control over, and cannot make alterations to. This database has a table called warehouse_items. Each warehouse item is uniquely identified by a primary key indicating the item id.
Unfortunately, that primary key attribute is named WAREHOUSE_ITEM.ID
(Note the obnoxious period between "item" and "id")
When I try to run a basic query, such as:
WarehouseItem.find('wh3453')
I get an Undefined Table error.
Fortunately, when looking at what Rails is attempting to do, the problem becomes obvious:
: SELECT "warehouse_items".* FROM "warehouse_items" WHERE "WAREHOUSE_ITEM"."ID" = $1 LIMIT $2
Because of the period in the attribute name, Rails is treating "WAREHOUSE_ITEM.ID" as a table/attribute combination, rather than an attribute name with a period in it.
When I run the following PSQL query by hand, I get exactly what I need:
SELECT "warehouse_items".* FROM "warehouse_items" WHERE "warehouse_items"."WAREHOUSE_ITEM.ID" = 'wh3453'
Why is Rails screwing this up, and how can I fix it?
EDIT:
Also worth noting: I've tried using self.primary_key to override the primary key to no avail.
I've tried both a string and a symbol, as in:
self.primary_key="WAREHOUSE_ITEM.ID"
and
self.primary_key=:"WAREHOUSE_ITEM.ID"
Neither one has worked...
Thanks for all the help, everyone!
A suggestion in the comments to use find_by_sql does work! However, I stumbled onto a different solution that works even better.
First, I aliased the annoying attribute name to something simple: id
alias_attribute :id, :"WAREHOUSE_ITEM.ID"
Notice that it's still a symbol, which is important for the next step.
I then overwrite the primary_key method with a custom function:
def self.primary_key
return "id"
end
Now, when I do WarehouseItem.find('wh3453'), Rails defaults to checking id, which is aliased to the correct symbol and it works as intended!!!

Add field/string length to logstash event

I'm trying to add a string length field to an index. Ideally, I'd like to use the kibana script feature as I can 'add' this field later but I keep getting a null_pointer_exception with the following code... I'm trying to sort in a visualization based on the fields length.
doc['field'].value ? doc['field'].length() : 0
Is this correct?
I thought it was because my field isn't always set (sparse data), but I added the ?:0 to combat that (which didn't work)
Any ideas?
You can define an scripted field in Kibana, of type int, language painless, and try this:
return (doc['field'].value != null? doc['field'].value.length(): 0);

"putting" a certain object to the screen from inside an array

I have an array, "templates".
puts templates
gives me the following output:
{"id"=>4, "subject"=>"invoice", "body"=>"dear sirs", "description"=>"banking", "groups"=>"123", 0=>4, 1=>"invoice", 2=>"dear sirs", 3=>"banking", 4=>"123"}
I would like to "put" a certain element e.g. "dear sirs". I have tried:
puts templates[2]
but this just returns nil. What is the correct way to do this?
You access "dear sirs" using the key that's associated with it, "body":
puts templates["body"]
Suppose if you have hash like this
#a = {"id"=>4, "subject"=>"invoice", "body"=>"dear sirs", "description"=>"banking", "groups"=>"123", 0=>4, 1=>"invoice", 2=>"dear sirs", 3=>"banking", 4=>"123"}
And if you want to get value of key name 'body', then you can get output like this,
puts #a['body'] //Output = dear sirs
puts #a['subject'] //Output = invoice
For more information for ruby hash Ruby Hash
If you want to get a hash value by numeric index then you can do templates.values[index]
e.g
templates.values[0] => 4
templates.values[1] => "invoice"
templates.values[2] => "dear sirs"
Note: My answer is based on strong assumptions that may not be true. I have provided steps to validate that.
In-case you are on older ruby version you need to do puts templates.inspect in-order to print a Hash. Therefore suggesting your variable templates is a String. Best way to verify:
templates.class
#=> returns Hash or String accordingly.
If it return String, you can proceed as follows:
Convert the String into Hash
hash = eval(templates)
#=> {"subject"=>"invoice", 0=>4, "description"=>"banking", 1=>"invoice", 2=>"dear sirs", "id"=>4, 3=>"banking", "body"=>"dear sirs", 4=>"123", "groups"=>"123"}
Now that its a Hash you can access any value using its key like:
hash[key]
#=> val
Example for your case:
hash[2]
#=> "dear sirs"

Generate dynamic hstore key calls in Prawn

I have an hstore column that I'm using to build a table in Prawn (pdf builder). The data will consist of records for a given month. Since it is hstore, the keys used will likely change from day to day so this needs to be dynamic.
I need to determine:
1 What unique keys are used that month
I created a helper to find the unique keys that were used in the month. These will be used as column headers.
keys(#users_logs)
# this returns an array like - ["XC", "PIC", "Mountain"]
The table will display a users dutylog data for the month. For testing...If I explicitly call known hstore keys...the data displays correctly. But, since its hstore...I wont know what the table column will be in production.
For testing, I call known hstore keys...this creates the prawn table row data per duty log.
#users_logs.map do |dutylog|
[ dutylog.properties["XC"],
dutylog.properties["PIC"],
dutylog.properties["Mountain"]
]
end
But, since this is hstore...I wont know what keys to call in production. So, I need to make the above iteration dynamic.
I tried, without success, to iterate over each dutylog entry, then iterate over each unique key and output one "dutylog.properties[x]" call for each key value...but, this just outputs the array of key values. I tried using send() in the block, but that didnt help.
#users_logs.map do |dutylog|
[ keys(#users_logs).each { |k| dutylog.properties[k] }.join(",") ]
end
Any ideas on how I could make the "dutylog.properties[k]" dynamic?
Took some head scratching...but turning out to be quit easy
This will build the rows for the Prawn table
def hstore_duty_log_rows
[keys(#users_logs)] +
#users_logs.map do |dutylog|
keys(#users_logs).map { |key| dutylog.properties.keys.include?(key) ? "#{dutylog.properties[key]}" : "0" }
end
end

Rails: Efficiently searching by both firstname and surname

I'm trying to create a 'search box' that matches users by name.
The difficulty is that a user has both a firstname and a surname. Each of those can have spaces in them (eg "Jon / Bon Jovi", or "Neil Patrick / Harris"), and I'm wondering about the most efficient way to ensure the search is carried out on a concatenation of both the firstname and surname fields.
The list of users is quite large, so performance is a concern. I could just throw a "fullname" def in the user model, but I suspect this isn't the wisest move performance wise. My knowledge of multi-column rails indexes is weak, but I suspect there's a way of doing it via an index with a " " in it?
Just to clarify, I don't need fuzzy matching - exact match only is fine...I just need it to be run on a concatenation of two fields.
Cheers...
You could create a new field in your database called full_name with a regular index, then use a callback to populate this whenever the record is saved/updated:
before_save :populate_full_name
protected
def populate_full_name
self.full_name = "#{first_name} #{last_name}"
end
If you can modify the database, you can and should use the solution provided by gjb.
Here is the solution that does not require you to alter the database. Simply gather all the possible first-name/last-name pairs you can get from the search box. Some code:
# this method returns an array of first/last name pairs given the string
# it returns nil when the string does not look like a proper name
# (i.e. "Foo Bar" or "Foo Bar Baz", but not "Foo" or "Foo "
def name_pairs(string)
return nil unless string =~ /^\w+(\s+\w+)+$/
words = string.split(/\s+/) # split on spaces
result = []
# in the line below: note that there is ... and .. in the ranges
1.upto(words.size-1) {|n| result << [words[0...n], words[n..-1]]}
result.collect {|f| f.collect {|nm| nm.join(" ")}}
end
This method gives you an array of two-element arrays, which you can use to create an or query. Here is how the method looks:
#> name_pairs("Jon Bon Jovi")
=> [["Jon", "Bon Jovi"], ["Jon", "Bon Jovi"]]
#> name_pairs("John Bongiovi")
=> [["John", "Bongiovi"]]
#> name_pairs("jonbonjovi")
=> nil
Of course, this method is not perfect (it does not capitalise the names, but you can do it after splitting) and is probably not optimal in terms of speed, but it works. You can also reopen String and add the method there, so you can go with "Jon Bon Jovi".name_pairs.