Using fuzzily with more than one field RAILS - ruby-on-rails

Rails 4.2
Ruby 2.1
Fuzzily (0.3.3)
I am using the fuzzily gem for searches and it's working. In my controller, I would have something like this:
#notes = Note.find_by_fuzzy_description("%#{item}%")
the notes table has the following fields:
user_id
description
contents
What I would like to do is return only the fields where the user_id = 5 (for example). Any idea how to do this with Fuzzily?

As stated in github open issues it's well-known requested feature that is not resolved. You can try to test Note.where(user_id: user_id).find_by_fuzzy_description("%#{item}%") and look if it works correctly, but I doubt it does.
As workaround you can try fuzzy search with big limit and then select results that belongs for user and apply standart limit. But it will be slower, so i wouldn't recommend to use it if you have many users.

Related

Rails - ActiveModel::Serializer virtual attribute error

I'm using the active_model_serializers gem for a RoR API. Versions:
Rails: 4.2.8
Ruby: 2.2.5
active_model_serializers: 0.10.0
I'm using a virtual attribute in a serializer. I get it by using a sub query when I retrieve the objects from the database.
You can find the code here: Github Gist
This is the error I'm getting:
undefined method 'number_of_reservations' for DiscountSchedule...
This field isn't defined in the table nor in the model (attr_accessor)
I'm not sure why it doesn't work, I have a very similar serializer and it's working OK.
Any help will be appreciated.
EDIT:
I have another serializer where the virtual/calculated field is working OK. My guess on this is that since AR is making a bunch of LEFT OUTER JOINS and the SELECT list of the query is very big at some point something is getting broke.
The link won't work for me as I don't have access at my work place, however, from the error I can recommend you to check if you have defined the attributes like this in your serializer attributes :number_of_reservations and have an action in the serializer that says
def number_of_reservations
// Your logic goes here.
end
I suspect this question has to be about ActiveRecord, rather than AMS. You're trying to use select and alias to collect some computed (aggregate) attribute along with objects themselves. This, unfortunately, won't work in ActiveRecord, at least not in versions below 4.2.X. And this is why you're observing this behavior, there is no number_of_reservations in your models.
To see what's going on, try to inspect #objects here: https://gist.github.com/LuisDeHaro/ebf92781b449aa1ee7b85f8f552dd672#file-some_controller-rb-L17
Indeed: the issue was by the big amount of LEFT JOINS that the includes(:table_name) is generating. The serializer then does not know what to do.
I found a monkey-patch gem that works for AR (Rails 4 & 5) that fix this.
https://github.com/alekseyl/rails_select_on_includes
So, the virtual field number_of_reservations is picked up by the serializer like a charm.
And, you might be wondering: why do you want to retrieve a field that is not in the table definition in the database. A: well, in some scenarios you will need a calculated field for EVERY row you are retrieving. A SQL sub query is one of the most efficient ways to do so.
It's working now for me.

How would you get a list of best sellers from Spree?

I'm working on Spree 2.3.1 and I've been trying to get a list of "best sellers" based on qty for 2 days...any ideas?
So far I've tried to come up with a query using ActiveRecords helpers which I'm pretty new at:
Spree::Product.includes(variants: {line_items: :order}).group('spree_products.id')
I'm not sure what else to do. Seems like this would be something available out of the box.
This gem seems to do what you are looking for https://github.com/sylvinho81/spree_best_sellers
I extracted the query from the code and this is what gets a list of products in order of how many have been sold.
Spree::Product.active.select("spree_products.*, SUM(spree_line_items.quantity) as total_qty, spree_line_items.variant_id").
joins(:line_items).joins("INNER JOIN spree_orders ON spree_orders.id = spree_line_items.order_id").
where("spree_orders.state = 'complete'").
group("spree_line_items.variant_id, spree_products.id").order("total_qty DESC")
This is the approach that I would use:
Step 1 - SQL/Rails query
As suggested by Sumeet in his comment. I would expand the query to limit the Orders by date.
easy to implement.
very heavy on the database.
Use it only if you need it for e.g.: Admin Panel that is rarely being viewed or the site itself is not so popular.
Step 2 - Expand the functionality by adding task to update the product position in the Taxon
If you would like to provide a bestselling view for the frontend User on a site that has a lot of Users it wouldn't be a good idea to perform heavy counting SQL operations every time User displays product list. Instead, you could make a use of the existing position field that Products have in the relation to the Taxons, perform the query from the previous point and update the positions accordingly. You could trigger the position update also after each Order in the scope of the Taxons that line items belong to.
You can use the query that Qwertie provided you. Instead of doing that you can add the gem in you Gemfile and use it in your code:
gem "spree_best_sellers", :git =>
"https://github.com/sylvinho81/spree_best_sellers.git", :branch =>
'3-0-stable'
Since you are using Spree 2.3.1, you will need to adapt the gem.

ActiveRecord: Search multiple models for a list of words

I have a search controller where I want to search a number of different models.
So say I have this query: "Foo bar baz". Then I want to have the following queries:
Model1.where("name like '%foo%' or name like '%bar%' or name like '%baz'").offset(o).limit(l)
Model2.where("title like '%foo%' or title like '%bar%' or title like '%baz'").offset(o).limit(l)
Model3.joins(:sources).where("sources.name" => [source1, source2]).where("name like '%foo%' or name like '%bar%' or name like '%baz'").offset(o).limit(l)
Most important is that I want to do it safe (no SQL injections) but it would be nice if I could keep it DRY and pretty as well. I am using Rails 4 but I am very new to it. I have seen that there's something called "Concerns" which may be something to use?
Thanks!
Dependencies
You'll probably be best using one of the search extensions for Ruby stacks:
Sunspot Solr
ElasticSearch
Sphynx
As a disclaimer, I've never used any of these in production, but from my understanding, each of them will "index" data in your application, and then perform a search based on that indexed data
--
Indexing
The bottom line is if you want to search multiple models, you'll be best harnessing an index-based search system. This will take all the data you want to index, and put it into a manageable, searchable format that you can then call when you need.
As far as I know, sunspot works very well in this regard (indexing your data). The trick is to include all your models in the Sunspot settings, from which you can then pull the data you need.
There's a great Railscast about this here:
I don't have any live code for you I'm afraid - if you need more ideas, though, I'll gladly come back with them

Versioning for Rails i18n translations

I'm in the process of building a volunteer based translation engine for a new site built in Rails 4.0. Since it's volunteer based, there is always the possibility that a user may enter a translation that others do not agree with, accidentally remove a translation, etc. In such an event, I would like to give users the option to revert to a previous translation.
I did some searching around but have yet to find a solution aside from writing my own I18n backend. Is there a simpler way of storing previous versions of translations?
I'm currently using Sven Fuchs' Active Record as a backend, however I'm seriously thinking about switching due to possible performance issues later on down the road.
We had a very successful experience using Globalize (github page: https://github.com/globalize/globalize) and as for the versioning part we didn't try it but Globalize does have support for that in a seperate gem github page: (https://github.com/globalize/globalize-versioning)
After tens of painful gem experiences, i found that comparing gems by last update date and how frequent is new releases, bugs fixes and support is a major factor to decide which one will make your life easier and which one won't.
Update:
You can use Globalize to dynamically translate views (check tutorial) but i came across a github project called iye. I think it suits your needs best (github page: https://github.com/firmafon/iye)
I used Nimir's help to find this solution. Like Globalize Versioning, you can add Paper Trail support for the Active Record's Translation class, however there are a few pitfalls to this method currently.
First you need to include the gems in your Gemfile:
gem "i18n-active_record"
gem "paper_trail"
Then you need to make sure your Translation model class is inheriting from I18n Active Record::Translation and call and calls has_paper_trail:
class Translation < I18n::Backend::ActiveRecord::Translation
has_paper_trail
end
This should really be enough, however the store_translations method in I18n Active Record do not update existing records. Instead, each time a record is added, all records with the given key are deleted and a new record is created. This causes confusion for Paper Trail since it relied on an id.
To get around this issue, I created my own store_translation method, which will update records if they exist:
def store_translations(locale, data, options = {})
escape = options.fetch(:escape, true)
I18n.backend.flatten_translations(locale, data, escape, false).each do |key, value|
t = Translation.find_or_create_by!(locale: locale.to_s, key: key.to_s)
t.value = value
t.save
end
I18n.backend.reload!
end
Notice I also included I18n.backend.reload!, this is because I am running Memoize to cache translations but it seems that it needs to be told to recache whenever a record is updated.
Now I can simply call:
store_translations(lang, {key => key}, :escape => false)
to a store a new translation and ensure we keep a record of the old one and who made the change.

Ransack: Can page size be customized?

I'm using Spree and in the products/search controller action it uses Ransack. The Spree documentation says that Spree provides no way to customize page size, so I thought I'd customize the action itself to use Ransack to turn off pagination entirely. Does Ransack support a way to either not paginate results or to at least make the pagination huge?
This is the code in the existing controller action:
#products = product_scope.ransack(params[:q]).result.page(params[:page])
When I puts the type of #products after that, it is ActiveRecord::Relation. When I puts of
#products = product_scope.ransack(params[:q]).result
it is also ActiveRecord::Relation but in the first case there's a method 'total_count' used in the rabl template that is not present in the second one. I have dug through the source for ransack and I can't find where the .page is defined. I'm wondering if perhaps it's included in some class_eval of ActiveRecord::Relation in one of the gems that Spree pulls in.
Someone somewhere has to have faced this problem and come up with a solution. I'm hoping against hope to find that person :P
jd
The .page method comes from the Kaminari gem, and not Ransack.
You can make the pagination "huge" by changing the default per_page value in Kaminari:
Kaminari.config.default_per_page = 1000
Ryan's already got you, but I'm not sure if there isn't another way to do it as well. I can't tell from your question if you're talking about just the number of products shown per page, but it sounds like you are.
If so, I've done the same modification by using the built-in Spree preference products_per_page. When Spree provides a preference, I prefer (no pun intended) to use that over modifying gem parameters since it's part of their design already.
I fired up the console with bundle exec rails c, then ran the following command to set the preference on my store to 16 products per page: Spree::Config.products_per_page = 16
You can also put it in one of your initialization files so it doesn't get accidentally erased if you rebuild your database from scratch...I learned about it from this post in the Spree group. I also found details on setting Spree preferences here.
You want to use .per, for example:
collection.per(100)
collection.page(3).per(100)
collection.per(100).per(3)
Any of these will work.

Resources