Searching through a collection of indexed model objects using Thinking Sphinx - ruby-on-rails-3

I have a Technique model which belongs_to User and is indexed by Thinking Sphinx.
I also have a method in my model that returns an array of Technique objects:
def possible_children(user)
user.techniques - (self.children + [self])
end
This just takes the techniques that a user has, subtracts out those of the techniques that are already the children of the 'self' technique object, along with 'self' itself, and returns the remaining technique objects.
Then in a controller I instantiate a collection of possible children like so:
#possible_children = #technique.possible_children(current_user).search params[:search]
This returns an "undefined method 'search' for #"
Not sure if this is relevant but the controller this takes place in is not the TechniquesController.
What I am trying to do is search an arbitrary collection returned by a Model method.
Any ideas?
Let me know if I need to provide more information. Thank you.

I'm afraid this isn't possible with Thinking Sphinx - at least, not that simply. What you could do is use the objects you want to search across, grab their ids, and use that in a filter:
possible_children = #technique.possible_children(current_user)
Technique.search params[:search],
:with => {:sphinx_internal_id => possible_children.collect(&:id)}
Sphinx has its own id, but the primary key from the database is stored as the attribute sphinx_internal_id by Thinking Sphinx.

Related

When do you use dynamic finders vs scopes and scope methods in Rails?

I saw in the documentation that in ROR 3.2 it's generally better to use scope methods in the model for retrieving records with arguments instead of lamda scopes
Using a class method is the preferred way to accept arguments for
scopes. These methods will still be accessible on the association
objects
But when do you use dynamic finders (such as find_by_* instead of scope and scope methods? dynamic finders can also receive arguments and they seem to be simpler to use directly from the view.
If I want to retrieve all the records that have
attribute1 == [boolean] AND attribute2 IN [array]
which method is advisable (scope, scope method, or dynamic finder?)
You shouldn't be using any finders in your views.
I would probably write this using a class method.
something like this:
class Dog < ActiveRecord::Base
attr_accessible :chewed, :color
def self.toys chewed = false, colors = []
where chewed: chewed, color: colors
end
end

How to manually instantiate ActiveRecord truncated objects?

Working with ActiveRecord and JRuby, I try to invoke a stored procedure on a Database. Using the underlying Java Library I reached a point where I have a hash with the columns specified in the select.
Now I'd like to use this hash to have ActiveRecord models, but I'd like them to look like if I did a classic Model.select(columns).all (with only the columns values, errors when trying to reach the other ones and readonly).
There must be something inside of AR to do this but I can't find anything and all my search leads to all the basic "fetch" tutorials ...
OK so I kept digging in Rails code and figured out my answer was the instantiate method.
The idea is if you are inside a model called MyModel and do this
object = instantiate(value1: 1, value2: 'ok')
you will have an instance of the MyModel class with theses attributes defined. If the model is supposed to have more columns, they are not defined. The object is readonly.

Override model attribute

Here is the thing. I have a Country model with two columns "languages" and "default_language". So for instance, for switzerland these columns are set respectively to "de,fr,it" and "de".
Now, if I do Country.languages I get the "de,fr,it" string. How can I override this so when I get Country.languages I get an array ["de","fr","it"] ?
Sure I could create a function def available_languages, but still, I don't want languages to be public.
For simple arrays in this case, it is probably better to write your own solution.
This can be done by overriding the getter/setter methods:
In your model:
class Country < ActiveRecord::Base
def languages
read_attribute(:languages).split(',')
end
def languages=(array)
write_attribute(:languages,array.join(','))
end
end
For hashes, you can use ActiveRecord::Store, see http://api.rubyonrails.org/classes/ActiveRecord/Store.html
For more general objects (not just arrays, you can use serialize), see http://duanesbrain.blogspot.co.nz/2007/04/ruby-on-rails-persist-array-to-database.html

Kaminari doesn't work If my query is in the model

I have my mongoid query in the model
def self.get_result collection_name, hash_params, page, per_page
self.collection_name = collection_name
#result_pg = self.where(hash_params).page(page).per(per_page)
end
I have it here because my collection names are passed as parameters and I don't have models for all my collection. Instead I have one model and I set its name dynamically based on the request parameter.
My controller code
Collection.get_result params[:state], hash_param, params[:page], params[:per]
My View code
<%= paginate #result_pg %>
When I use the above code I get
undefined method `current_page' for nil:NilClass ERROR
I think the variable is being lost when you call it in the model. Instead, have it return the object back to you like so:
return self.where(hash_params).page(page).per(per_page) // put this in your model
Then, I'm the controller, connect the pieces:
#result_pg = Collection.get_result params[:state], hash_param, params[:page], params[:per]
// in your controller like so
Right now, the controller isn't setting #result_pg, that is why it is nil and you are getting the nil error. Try this and let me know if that worked out for you.
Updated
Ok, as for the reason this happens. When Rails loads a controller, the variables that you declare with a # symbol, like #bologna will be passed to the view and something will be done with them.
On the other hand, you are not technically declaring and instantiating that variable in the controller, you are doing it in the model, as per your source code that you posted above. Declaring the variable there can be done but it isn't useful because the controller has no idea that it even happened. When you tell the Class Collection to perform a method, usually you have that method return something back to you, if you don't, then the variable is lost.
It is similar to me asking someone to go to the store and buy me some groceries, you did the right things, giving the method all the information it needed, passing it the right variables to do its job correctly, but not telling it to actually come back with them, is where the error occurs. In your method, the groceries get purchased at the store and left there. The revised function I wrote for you tells it return with the groceries and put them in the variable #result_pg. The variable is declared in the Controller like it is supposed to be.
So in short, anything you want to have accessible in the view, needs to be declared in the controller. And anytime you want something back from a method, always have it return the information to you.
Also, it isn't entirely necessary to even have the method call to the Class Collection. In a project of my own where I use Kaminari, I just simply do the whole call from in the controller like so:
#notes = current_user.notes.page params[:page]
// My Application has :users that have_many :notes
So you could simplify it that way if you want, but the method that I suggested that fixes it the way you are doing it will work too, however you prefer.
I hope my explanation helped and wan't too long winded.

Atomic arithmetic in Rails

I need to perform some atomic arithmetic in Rails but the only way I've found to do it for single objects is via the crude update_all class method, e.g.:
Account.update_all(["debits = debits + ?", amount], :id => id)
With collection associations, the update_all class method should be usable as an association method, since the collection will pass missing method calls on to the class with the relevant scope:
accounts.update_all(["debits = debits + ?", amount])
When dealing with collections, this is much nicer and less repetitive. However this doesn't work for singleton associations, i.e. belongs_to and has_one. The method_missing for AssociationProxy passes through to the target instance, which won't have an update_all instance method (naturally).
Is there a more elegant way to perform this arithmetic? Or is update_all as good as it gets?
I think what you are looking for is the update_counters method. It allows you to specify an arbitrary increment value and still performs an atomic operation:
Account.update_counters id, :debits => amount
It also supports performing the operation on collections of ids without a loop:
Account.update_counters [1, 2, 3, 4, 5], :debits => amount
Check out the most recent Rails 3.0.x documentation:
http://api.rubyonrails.org/classes/ActiveRecord/CounterCache.html#method-i-update_counters
You're not going to beat the performance of update_all on a large number of records by a longshot. Any reason not to use that and wrap it in a transaction?