Iam a Rails developer and working on a project which needs to a show collection set of different images. I want to implement an algorithm where I want to fetch images in different timespan , for instance, 30% images from current month and 20% images from previous month and so on and shuffle them all together and create a set and then show it on the website. Thus it makes me to target created_at attribute for Image model and fetch images on the basis of the creation date.
I looked for various possible solutions. One of them is Thinking Sphinx. As per the doc you can give field weight to an attribute. But my problem is I want to give weightage within the attribute i.e. 30 % to first month, 20% to second month. So I didnt find it a good solution.
Next I thought to achieve the same using scopes
Image model
class Image < ActiveRecord::Base
scope :current_month, where("created_at < ? and created_at > ?", Time.now, 1.month.ago).shuffle.take(6)
scope :previous_month, where("created_at < ? and created_at > ?", 1.month.ago, 2.month.ago).shuffle.take(5)
scope :last_month, where("created_at < ? and created_at > ?", 2.month.ago, 3.month.ago).shuffle.take(4)
end
Image Controller
class ImagesController < ApplicationController
#total_images = Image.current_month + Image.previous_month + Image.last_month
end
But this fetches the same set of Images for each request. Whereas I need different images everytime a request is made. Moreover this will make my website's performance really down since a single request hits my database so many times.
Please guide me a better and correct way to implement this. Thanks in advance.
Related
I am struggling with the following problem:
I want to have two different tabs, one that displays all recent chugs (Done), and one that displays the chugs that are the fastest per person.
However, this needs to remain an ActiveRecord, since I need to use it with link_to and gravatar, thus restraining me from group_by, as far as I understand it.
AKA: If there are three users who each have three chugs, I want to show 1 chug per user, which contains the fastest time of that particular user.
The current code looks like this, where chugs_unique should be edited:
def show
#pagy, #chugs_all_newest = pagy(#chugtype.chugs.order('created_at DESC'), items: 10, page: params[:page])
#chugs_unique = #chugtype.chugs.order('secs ASC, milis ASC, created_at DESC').uniq
breadcrumb #chugtype.name, chugtypes_path(#chugtype)
end
In this case, a chug belongs to both a chugtype and user, and the chugtype has multiple chugs.
Thanks in advance!
We build a video-section for our users. The user can filter the videos by rating/views/date.
Also the user can decide to hide already seen videos. This is where i struggle a little bit.
right now i have a solution, which is working, but doesnt seem to perform great.
if #filter == "newest"
if #unseen
ids = Videothek::Video.where(videothek_category_id: categories).pluck(:id)
views = Videothek::Video::View.where(user_id: current_user.id).pluck(:video_id)
unseen = ids - views #ids der ungesehenen videos
#videos = Videothek::Video.where(id: unseen).order("created_at DESC")
else
#videos = Videothek::Video.where(videothek_category_id: categories).order("created_at DESC")
end
end
i thought it must be possible to do with a scope, like Videothek::Video.unseen(current_user).order(.....)
A Video has_many Views, but i struggle to get the join running, as i just want the videos, that DONT have an association with an videothek_video_view, where user_id = 1 (or current_user.id).
can somebody help me out?
btw: we are on RoR3
You may use where.not(video_id: [ids]) to make database filter videos user already seen. This method is added since rails 4.
https://robots.thoughtbot.com/activerecords-wherenot
instead of pluck(:id) you may use .ids. I would also move the code somewhere out of controller.
Probably you question would fit better to https://codereview.stackexchange.com/ since you already have working version.
I need to persist an array in my rails language learning app. Here is the situation/reason I need to do this (if there is a better way please let me know). The user is presented, one at a time, 10 words to learn. After the words are learned they get quized on the same set of 10 words.
For the 'quiz' portion I would like to randomize the order the words appear in (for example: currently if you learn the words 1.fish 2.cat 3.dog... you will be quized in the same order 1.fish 2.cat 3.dog... which can make the quiz easier.
I need to persist it in case the user were to log off or navigate away. In this instance I want to return them to the exact word they left off on the quiz.
Here is the controller code I currently have:
def index
.
.
#quiz = Lang.limit(10).offset(current_user.bookmark - 11)
exercise_bank
.
.
end
private
def exercise_bank
current_user.random_exercise_array = (0..9).step(1).shuffle
current_user.save
#index2 = current_user.random_exercise_array[#index]
##index starts at 0 and increments on each call to index action
##index2 grabs the random number and uses it to reference a word in #quiz
#something like this: #quiz[#index2].spanish_to_english---grabs a english word
end
end
The idea of the above is to pick a random number 0-9 and use it to reference a word in my DB. The above results in something like the following in my DB for the random_exercise_array attribute:
random_exercise_array: "---\n- 6\n- 0\n- 1\n- 7\n- 8\n- 5\n- 9\n- 3\n- 2\n- 4\n"
Here is the relevant User DB code:
class DeviseCreateUsers < ActiveRecord::Migration
serialize :random_exercise_array
end
Relevant migration file:
class AddRandomExerciseArrayToUsers < ActiveRecord::Migration
def change
add_column :users, :random_exercise_array, :text
end
end
Is this the best way to solve this problem? If so, can you explain how to get back an integer from random_exercise_array without all of the (-)'s and (\n')s?
If you want get an array back from DB text column, you should declare the type in your user model like this:
class DeviseCreateUsers < ActiveRecord::Base #why your original code inherit from ActiveRecord::Migration?
serialize :random_exercise_array, Array
end
Then you can use current_user.random_exercise_array as an array instead of a string.
Is there a better way to update more record in one query with different values in Ruby on Rails? I solved using CASE in SQL, but is there any Active Record solution for that?
Basically I save a new sort order when a new list arrive back from a jquery ajax post.
#List of product ids in sorted order. Get from jqueryui sortable plugin.
#product_ids = [3,1,2,4,7,6,5]
# Simple solution which generate a loads of queries. Working but slow.
#product_ids.each_with_index do |id, index|
# Product.where(id: id).update_all(sort_order: index+1)
#end
##CASE syntax example:
##Product.where(id: product_ids).update_all("sort_order = CASE id WHEN 539 THEN 1 WHEN 540 THEN 2 WHEN 542 THEN 3 END")
case_string = "sort_order = CASE id "
product_ids.each_with_index do |id, index|
case_string += "WHEN #{id} THEN #{index+1} "
end
case_string += "END"
Product.where(id: product_ids).update_all(case_string)
This solution works fast and only one query, but I create a query string like in php. :) What would be your suggestion?
You should check out the acts_as_list gem. It does everything you need and it uses 1-3 queries behind the scenes. Its a perfect match to use with jquery sortable plugin. It relies on incrementing/decrementing the position (sort_order) field directly in SQL.
This won't be a good solution for you, if your UI/UX relies on saving the order manually by the user (user sorts out the things and then clicks update/save). However I strongly discourage this kind of interface, unless there is a specific reason (for example you cannot have intermediate state in database between old and new order, because something else depends on that order).
If thats not the case, then by all means just do an asynchronous update after user moves one element (and acts_as_list will be great to help you accomplish that).
Check out:
https://github.com/swanandp/acts_as_list/blob/master/lib/acts_as_list/active_record/acts/list.rb#L324
# This has the effect of moving all the higher items down one.
def increment_positions_on_higher_items
return unless in_list?
acts_as_list_class.unscoped.where(
"#{scope_condition} AND #{position_column} < #{send(position_column).to_i}"
).update_all(
"#{position_column} = (#{position_column} + 1)"
)
end
This should be fairly simply...
I have Recommendations has_many Assets.
I want to limit the user to adding 3 Assets per Recommendation, and I can do this simply by limiting the number of fields show.
In my new action in the controller I am doing a very simple:
3.times {#recommendation.assets.build}
In my edit action I am trying to build the logic to decide how many fields to show:
#assets = #recommendation.assets.all
if #assets.empty?
3.times {#recommendation.assets.build}
else
asset_loop = #assets.count - 3
asset_loop.times {#recommendation.assets.build}
end
The if works but the else does not. How can I make this work?
If I understand your goal, you just need to change
asset_loop = #assets.count - 3
to
asset_loop = 3 - #assets.count
Do make sure to validate the incoming data if you want to truly enforce the limit. Otherwise you're at the mercy of anyone with a web console and curl.