Finding the content in the bracket rails - ruby-on-rails

So I have a variable called #gases that holds gas element such as Carbon Monoxide [CO], Carbon Dioxide [CO2]. Right now if someone searches CO in the search function Carbon Dioxide [CO2] shows up first. So I wanted to know if I could do something like this
where("(lower(gas_analytes.gas)\[.*?\]) LIKE lower(?)", "#{gas_analyte}")
so the code above, if someone only searches for the element and not the whole content, I used \[.*?\], so if the search input equals the element in the bracket, but this does not work.
Is there anything similar I can do?

I would probably just add the square brackets to the search pattern like this:
where("lower(gas_analytes.gas) LIKE ?", "%[#{gas_analyte.downcase}]%")
Note that if it is possible that gas_analyze might include % characters then you should sanitize that string before using it:
where("lower(gas_analytes.gas) LIKE ?", "%[#{sanitize_sql_like(gas_analyte.downcase)}]%")
Because like is very slow you might consider splitting the string and extracting the abbreviation into a database column on it own. That would improve the query time a lot.

This code worked for my (I'm using SQLite database):
# seeds.rb
# Type `rake db:seed` in terminal to run this file
# GasAnalyte.destroy_all
10.times do |t|
GasAnalyte.create!(
gas: "#{SecureRandom.uuid} [CO2]"
)
end
p ['All', GasAnalyte.all].inspect
p ['Searching for "CO"', GasAnalyte.search_gas("CO")].inspect
# gas_analyte.rb
class GasAnalyte < ApplicationRecord
validates :gas, presence: true
scope :search_gas,
->(gas_analyte) { where("lower(gas_analytes.gas) LIKE lower(?)", "%[#{gas_analyte}%]") }
end
Here you can read about LIKE operator: https://www.w3schools.com/sql/sql_like.asp

Related

Rails/Postgres query Hstore for presence

I have some HStore columns in my Rails app. Looking around the Posgresql documentation and some blog posts, I've discovered that, given an HStore value that is a hash like this:
{ some_key: 'value' }
I can query the columns like so:
Model.where("the_hstore_column -> 'some_key' = 'value'")
There are a bunch of issues with this as a sufficient querying tool, however:
It really only makes sense for super simple values. If the value is
itself a hash or something, I have no idea how to search for it
effectively, if I don't know all its contents. (Even if I did, I'd
have to turn them all to stringified JSON or something).
It isn't helpful (at least, I can't make it helpful) for doing
queries for the presence or non-presence of the column's content (or
the content of any key under the column).
In other words (in pseudo-sql), I'd love to be able to do this:
Model.where("the_hstore_column = {}")
Model.where.not("the_hstore_column = {}")
Or:
Model.where("the_hstore_column -> 'some_key' = NULL")
Model.where.not("the_hstore_column -> 'some_key' = NULL")
Or best yet, given an HStore of value { some_key: { sub_key: 'value' } }:
Model.where("the_hstore_column -> 'some_key' INCLUDES (?)", 'sub_key')
Model.where.not("the_hstore_column -> 'some_key' INCLUDES (?)", 'sub_key')
These appear not to be working, but for the life of me, I can't find great information on how to conduct these queries. Does anyone know how to write them, or where I could look for better information?
UPDATE After more looking around, I found this post, which looked promising, but I can't get the code to actually work. For example Model.where("the_hstore_column ? :key", key: 'some_key') is returning an empty relation, even if there are many Model objects with some_key in the_hstore_column.
As requested by Andrius Buivydas, I'm pasting the relevant portion of my model below. It's relatively brief, because I decided to abstract out the Hstore-accessing into a module I wrote, essentially turning it into a hash store (which I though, apparently incorrectly, was its whole purpose). I tried using the built-in store_accessor, but it didn't work to my liking, (didn't seem to help parse saved hashes, for now obvious-seeming reasons) thus the ActiveRecord::MetaExt::HstoreAccessor module below. (ParsedHstore just takes an HStore string and parses it into a hash).
class Place.rb
include ActiveRecord::MetaExt::HstoreAccessor
hstore_accessor :hours, :extra
end
(In a separate file):
module ActiveRecord
module MetaExt
module HstoreAccessor
module ClassMethods
def hstore_accessor(*symbols)
symbols.each do |field|
class_eval do
define_method(field) do
ParsedHstore.new(self[field.to_sym]).hash_value
end
define_method("add_to_#{field}!") do |arg|
self[field.to_sym] = self[field.to_sym].merge(arg)
send("#{field}_will_change!")
save
arg
end
define_method("add_to_#{field}") do |arg|
self[field.to_sym] = self[field.to_sym].merge(arg)
send("#{field}_will_change!")
arg
end
define_method("remove_from_#{field}") do |arg|
other = self[field].dup
other.delete(arg.to_s); other.delete(arg.to_sym)
self[field.to_sym] = other
send("#{field}_will_change!")
self[field.to_sym]
end
define_method("remove_from_#{field}!") do |arg|
other = self[field].dup
other.delete(arg.to_s); other.delete(arg.to_sym)
self[field.to_sym] = other
send("#{field}_will_change!")
save
self[field.to_sym]
end
define_method("set_#{field}") do |arg|
self[field.to_sym] = arg
send("#{field}_will_change!")
self[field.to_sym]
end
define_method("set_#{field}!") do |arg|
self[field.to_sym] = arg
send("#{field}_will_change!")
self[field.to_sym]
end
end
end
end
end
def self.included(base)
base.extend ClassMethods
end
end
end
end
The idea is that this lets me easily add/remove values to an HStore field, without having to think about the merging and _will_change! logic every time. So I could do this, for example: Place.first.add_to_extra({ key: 'value'}).
Should I have made these fields json or jsonb? I feel like I'm reinventing the wheel here, or, more aptly, trying to turn a horse into a wheel or something.
Also, I may be misunderstanding the query example. I literally tried this query in my database (which has many places with a non-empty ranking key under the extra field), and turned up with an empty relation:
Place.where("extra ? :key", key: 'ranking')
I wouldn't be surprised if I messed this syntax up, as it seems really strange. Wouldn't that replace the ? with 'ranking', turning the query into this?: Place.where("extra ranking :key")? Seems weird and emphatically different from any other SQL I've run. Or is it turning to Place.where("extra ? ranking")? But ? is usually for safe injection of variables, no?
Please let me know if something else in my model or elsewhere would be more relevant.
It really only makes sense for super simple values. If the value is itself a hash or something, I have no idea how to search for it effectively, if I don't know all its contents.
Postgresql HStore stores key, values pairs that are both strings, only. So you can't store a hash, a nil or something else like an object - they will be converted to the strings.
Model.where("the_hstore_column ? :key", key: 'some_key')
That should work if everything is defined correctly. Could you paste an extract of the model file with the definition of the hstore column values?
Another way to find empty hstore columns is
Model.where(hstore_column: ['',nil])
OR
Model.where("hstore_column='' OR hstore_column IS NULL")

how to delete specific characters in ruby?

There is already created record, like
Company "Life"
How to make this record to the species
сompany-life
I used parameterize, but it turns:
company-quot-life-quot
As I understand, .gsub(""", "") is not suitable for implementation, since to create too large list of exceptions
Is there may be a way to make record in raw format? (to parameterize later)
thanks in advance!
Here is a non-Rails approach:
require 'cgi'
str = 'Company "Life"'
puts CGI.unescape_html(str).gsub(/"/, '').gsub(/\s+/, '-').downcase
# => company-life
And a pure regex solution:
puts str.gsub(/&\w+;/, '').gsub(/\s+/, '-').downcase
# => company-life
And if you are inside Rails(thanks to #nzifnab):
str.gsub(/&\w+;/, '').parameterize
As #meager said, you shouldn't be storing the html-encoded entities in the database to begin with, how did it get in there with "? Theoretically this would work:
class Page < ActiveRecord::Base
before_validation :unescape_entities
private
def unescape_entities
self.name = CGI.unescape_html(name)
end
end
But I'm still curious how name would be getting there in the first place with html entities in it. What's your action/form look like?
"Company "Life"".html_safe.parameterize
"Company "Life"".gsub(/&[^;]+;/, "-").parameterize.downcase
# => "company-life"
Firstly, gsub gets rid of html entities, then parameterize gets rid from all but Ascii alphanumeric (and replaces them with dash), then downcase. Note that "_" will be preserved too, if you don't like them, another gsub('_', '-') is needed.

How to show excerpts from pg-search multisearch results

I've set up pg_search in my Rails app on Heroku:
#query = 'fast'
PgSearch.multisearch(#query) #=>
[#<PgSearch::Document searchable: ferrari, :content: 'this car is really fast'>,
#<PgSearch::Document searchable: viper, :content: 'a very fast car'>]
I'd like to display these results with excerpts from content to show where the match occurs. I can call excerpt(content, #query) to get exactly what I want when #query is only one word, but excerpt() only handles exact matches, so if:
#query = 'car fast'
PgSearch.multisearch(#query) #=>
[#<PgSearch::Document searchable: ferrari, :content: 'this car is really fast'>,
#<PgSearch::Document searchable: viper, :content: 'a very fast car'>]
then excerpt(content, #query) is nil because nowhere in content does the exact phrase 'car fast' appear.
I considered excerpt(content, #query.split(' ').first) to at least show something for multi-word queries, but there are still cases such as this:
#query = 'car?'
#results = PgSearch.multisearch(#query) #=>
[#<PgSearch::Document searchable: ferrari, :content: 'this car is really fast'>,
#<PgSearch::Document searchable: viper, :content: 'a very fast car'>]
excerpt(#results.first.content, #query) #=> nil
So, how do folks show excerpts from search results when using pg_search?
I'm the author and maintainer of pg_search.
Right now there isn't a built-in way to get excerpts along-side your results in pg_search, but there easily could be if I or someone else has the time to build it in.
PostgreSQL has a function ts_headline that you can call which returns a string excerpt as a column.
It might be possible to call something like this (I haven't tested it yet):
PgSearch.multisearch(#query).select(["ts_headline(pg_search_documents.content, plainto_tsquery(?)) AS excerpt", #query])
Then each of your results should have an excerpt method that returns something like what you want.
By the way, this is something that I eventually want to make automatic in pg_search. I just haven't had the time to delve too deeply into it yet.
FWIW— Following nertzy's example above, I was able to get this to work with the following:
PgSearch.multisearch(#query).select("ts_headline(pg_search_documents.content, plainto_tsquery('english', ''' ' || unaccent('#{#query}') || ' ''' || ':*')) AS excerpt")
I was having trouble getting plainto_tsquery(?) to work, as it was throwing a syntax error. My solution above was simply the result of doing
PgSearch.multisearch(#query).select(["ts_headline(pg_search_documents.content, plainto_tsquery(?)) AS excerpt", #query]).to_sql
and then plugging in the to_tsquery arguments for the new plainto_tsquery call—something I'm sure is not entirely sound, but seems to work.
If you interpolate the string, you will be subject to sql injection attacks.
Since the .select won't accept a parameterized statement like the .where does (Users.where("id = ?", params[:id])), you will need to sanitize explicitly.
sanitized = ActionController::Base.helpers.sanitize(params[:q])
#results = PgSearch.multisearch(params[:q])
.select(["ts_headline(pg_search_documents.content, plainto_tsquery('english', ''' ' || '#{sanitized}' || ' ''' || ':*')) AS excerpt"])
There's a much easier way, if you don't feel like digging through SQL - you can make use of built in pg_search gem functionality to display excerpts in a really simple and straightforward way:
In your controller:
#articles = Article.search(params[:search]).with_pg_search_highlight
In your view:
= raw(article.pg_search_highlight)
That should do it.

How to convert a string "560,000" into an integer 560000 when importing from csv into database in Rails

just started learning Rails and have managed to import a csv file into a database, but the price field in the csv has quotes and a comma like this: "560,000"
But if I make the price field as t.integer in the migration file, then add the data, the price gets imported as 560. So, how do I remove the quotes and the comma before importing it? thanks, Adam
edit: here's the rake file:
require 'csv'
task :csv_to_properties => [:environment] do
CSV.foreach("lib/assets/cbmb_sale.csv", :headers => true) do |row|
Property.create!(row.to_hash)
end
end
Try something like:
csvvalue = csvvalue.gsub!(/,/,'').to_i
Cheers!
Thanks for posting your code. I don't do a ton with converting csv's to hashes but something like this will probably work:
Property.create!(row.to_hash.each_pair{|k,v| row.store(k,v.gsub(/,/,'').to_i)})
Pretty ugly but probably pretty close to what you want.
In your code example, assuming the price field is in row element 4:
CSV.foreach("lib/assets/cbmb_sale.csv", :headers => true) do |row|
row[price=4].gsub!(/,/,'')
Property.create!(row.to_hash)
end
The price=4 is just a handy way to document the index value of the price element, it creates a variable called price assigns the value 4 to it, then immediately uses it as the array index.
Since Property.create! is already taking care of the string to integer conversion, we can perform an in-place substitution for the regular expression that contains a comma /,/ for an empty string ''.
Try:
"220,000".scan(/\d+/).join().to_i

Ruby on Rails script/console printing more than expected

I have a simple model setup in my Ruby on Rails app. (User {name, username, lat, lon}) and am writing a basic extension to the model. I would like the method to return users within a certain distance. It all works just fine in the page view, but as I am debugging I would like to work through some testing using the script/console.
My question: It seems to be printing to the screen the entire result set when I run it from the command line and script/console.
My model:
class User < ActiveRecord::Base
def distance_from(aLat, aLon)
Math.sqrt((69.1*(aLat - self.lat))**2 + (49*(aLon - self.lon))**2 )
end
def distance_from_user(aUser)
distance_from(aUser.lat, aUser.lon)
end
def users_within(distance)
close_users = []
users = User.find(:all)
users.each do |u|
close_users << u if u.distance_from_user(self) < distance
end
return close_users
end
end
and from the command line I am running
>> u = User.find_by_username("someuser")
>> print u.users_within(1)
So, I guess I would like to know why it's printing the whole result set, and if there is a way to suppress it so as to only print what I want?
you should handle it using a mysql query.
it's not good practise to pull all data from the database perofrm on them and show top 10 data out of them.
do something like following.
User.find_by_sql("select u.*,
(((69.1*(aLat - self.lat))**2 + (49*(aLon - self.lon))**2 ) < 100")) as distance
from users u
where u.username='someuser' and
distance > 100 order by distance DESC LIMIT 10)
Above query is just an example but it's always good practice to do something like above.
It appears that the console interface of the Ruby application evaluates each line as though it were entered from the console.
I would have expected it to some encapsulation of the scripts but it makes sense as it is being interpreted on the fly by the ruby interactive console line.
And to clarify as to the prefered sql base solution, doing find_by_sql is frowned upon in most refereces I have found. The SQL based solution that I reccomend is
def f
User.find(:all, :conditions => ["sqrt(pow(69.1*(lat - ?),2) + pow(49*(lon - ?),2)) < 1", lat.to_s,lon.to_s])
end

Resources