WARNING: calling search on a relation is deprecated - ruby-on-rails

I keep getting this error when we upgraded our codebase to searchkick 4.4. I have tried to search online but to no avail.
So we have an Opportunity Model where we're calling search on a collection of ActiveRecord objects like so to return our search results:
results = r.search q, fields: [:search_term], match: :word_middle, order: { created_at: :desc}
r is the said collection we're calling search on. Is this the reason why this error is throwing? if it is, how do i go around it, we're doing a lot conditional checks and queries on the queries before we run the search.

Yes, that's exactly the reason why you see this warning. There will not be option to search on relation in next major version. It's breaking change so you need to update your code to follow where section if you are going to update the gem to higher version in the future. You simply need to translate your relation to be included in where part.

Related

Using combined models with pagy, getting undefined method 'offset' error

I am using pagy. I combined two models into one, and I used pagy on that combined model. I am getting this error:
undefined method `offset' for #<Array:0x00007f886f88b3b0>
With the last line of the code below highlighted.
My code:
#problems = Problem.me_and_friends(current_user)
#activities = Activity.me_and_friends(current_user)
#combine = (#problems + #activities).sort{|a,b| a.created_at <=> b.created_at }
#pagy, #combined = pagy_countless(#combine, items:100, link_extra: 'class="" style="color:black; margin:3px;"')
It worked fine with using pagination on #problems alone.
I'd appreciate any help.
As soon as you call the (#problems + #activities), you transform the ActiveRecord::Relation into an array (which is also not good because you are loading all the database rows into memory, sorting and then paginating them). Pagy expects an ActiveRecord::Relation to work, hence the error.
You can consider multiple solutions,
Change your UI to show problems and activities in separate UIs, then you can paginate them separately
Update your models to store both problems and activities in the same table (maybe just a reference table which points to either a Problem or an Activity)
If either of these is not feasible, you can consider rolling out a custom solution for the pagination, but it will be tricky.
Update: June 21, 2021
If you are using Rails 6, it introduces the concept of Delegated Types which fits well into this scenario. The example given in the link mentions the issue of pagination across different tables.

How to access dynamic attribute 'geo_near_distance' with Mongoid

I am using Mongoid 3.1.6 with Rails 4. I need to find all the objects 'near' a certain co-ordinate. For each result from the search, I will need to display the distance from the search co-orodinate. According to Mongoid Documentation
...each instantiated document from a $geoNear query will get a special
dynamic attribute geo_near_distance that will be available as long as
the document is in memory.
But I am not able to access the Object.geo_near_distance
My query inside controller...
#objects = Object.geo_near([-118.4451, 34.0633]).max_distance(10)
Edit#1
Some additional details
If the use the following query in MongoDB
db.runCommand( { geoNear: "objects",
near: [ -73.95269,40.77578],
spherical: true
})
I see an array of 100 elements. Each element has 2 attributes. The first one, 'dis' has values like '0.000123' (Note: this is not in Km or Mile) and the second attribute is the result Object itself.
Now I have changed the query to Mongoid to...
#objects = Object.geo_near([-118.4451, 34.0633]).spherical.max_distance(10)
still no result.
Thanks in advance for your help.
After more than 2 years, the issue ticket is still open on mongodb jira tracker.
The quick fix is not use the hash notation instead of the dot notation to access the attribute:
Instead of
Object.geo_near_distance
Use
Object['geo_near_distance']
Tested on mongoid 6
Are you accessing the field while you are iterating the documents? You can see by the specs that this field is in fact there when the document is in memory and is being part of the iteration of the criteria result.
https://github.com/mongoid/mongoid/blob/master/spec/mongoid/contextual/geo_near_spec.rb#L167

AcitveRecord where method that matches just one record

Program.where(name: "xxyyzz123") will return a collection, even if there's just one record that matches which forces me to do ugly things like:
puts Program.where(name: "xxyyzz123").first.age
or
puts Program.where(name: "xxyyzz123")[0].age
When I know for sure only one record will match, is there a shorter way to grab a property from that one record?
ActiveRecord's dynamic attribute-based finders (find_by_x) allow you to select the first record that matches in your database. For example:
Program.find_by_name('xxyyzz123')
will return the first record with name = 'xxyyzz123'
Note that these finders are 'mildly deprecated' in Rails 4. Using
Program.find_by(name: 'xxyyzz123")
achieves the same thing and may make it easier when needing to update to the next version of Rails if they ever remove the former's functionality.
See ActiveRecord::Base in the API for more.
Yes, you will have to access that with Program.where(name: "xxyyzz123").first.age, however, in Rails 3, it is usually recommended to do that type of query with: Program.find_by_name('xxyyzz123').age.
Rails 4 deprecates the above syntax and recommends you to use the following syntax for that:
Program.find_by(name: 'xxyyzz123')
If you have multiple conditions, then simply : Program.find_by(name: 'xxyyzz123', lang: 'ruby')
Behind the scene, it does the same tomfoolery - where clause and returns first object.

Rails: Getting column value from query

Seems like it should be able to look at a simple tutorial or find an aswer with a quick google, but I can't...
codes = PartnerCode.find_by_sql "SELECT * from partner_codes where product = 'SPANMEX' and isused = 'false' limit 1"
I want the column named code, I want just the value. Tried everything what that seems logical. Driving me nuts because everything I find shows an example without referencing the actual values returned
So what is the object returned? Array, hash, ActiveRecord? Thanks in advance.
For Rails 4+ (and a bit earlier I think), use pluck:
Partner.where(conditions).pluck :code
> ["code1", "code2", "code3"]
map is inefficient as it will select all columns first and also won't be able to optimise the query.
You need this one
Partner.where( conditions ).map(&:code)
is shorthand for
Partner.where( conditions ).map{|p| p.code}
PS
if you are often run into such case you will like this gem valium by ernie
it gives you pretty way to get values without instantiating activerecord object like
Partner.where( conditions ).value_of :code
UPDATED:
if you need access some attribute and after that update record
save instance first in some variable:
instance=Partner.where( conditions ).first
then you may access attributes like instance.code and update some attribute
instance.update_attribute || instance.update_attributes
check documentation at api.rubyonrails.org for details

Removing duplicates from array before saving

I periodically fetch the latest tweets with a certain hashtag and save them locally. In order to prevent saving duplicates, I use the method below. Unfortunately, it does not seem to be working... so what's wrong with this code:
def remove_duplicates
before = #tweets.size
#tweets.delete_if {|tweet| !((Tweet.all :conditions => { :twitter_id => tweet.twitter_id}).empty?) }
duplicates = before - #tweets.size
puts "#{duplicates} duplicates found"
end
Where #tweets is an array of Tweet objects fetched from twitter. I'd appreciate any solution that works and especially one that might be more elegant...
you can validate_uniqueness_of :twitter_id in the Tweet model (where this code should be). This will cause duplicates to fail to save.
Since it sounds like you're using the Twitter search API, a better solution is to use the since_id parameter. Keep track of the last twitter status id you got from your previous query and use that as the since_id parameter on your next query.
More information is available at Twitter Search API Method: search
array.uniq!
Removes duplicate elements from self. Returns nil if no changes are made (that is, no duplicates are found).
Ok, turns out the problem was a bit of different nature: When looking closer into it, I found out that multipe Tweets were saved with the twitter_id 2147483647... This is the upper limit for integer fields :)
Changing the field to bigint solved the problem. It took me very long to figure out since MySQL did silently fail and just reverted to the maximum value as long as it could. (until I added the unique index). I quickly tried it out with postgres, which returned a nice "Integer out of range" error, which then pointed me to the real cause of the problem here.
Thanks Ben for the validation and indexing tips, as they lead to much cleaner code now!

Resources