Problems using Thinking Sphinx in Ruby on Rails with multiple models - ruby-on-rails

I'm developing a website on Ruby on Rails with the search engine Sphinx (I'm using Thinking Sphinx). I have a model in which I want to make the searches and I'm using another models (I made the relationships in the models and in the tables) but I want to make additional INNER JOINS, so, I have something like this:
class Group < ActiveRecord::Base
belongs_to :person
has_many :categories, :dependent => :destroy
define_index do
indexes group_name
indexes person.fullnameindexes categories.category_name
indexes categories.category_name
end
end
It's ok to make something like this?
class Group < ActiveRecord::Base
belongs_to :person
has_many :categories, :dependent => :destroy
define_index do
indexes group_name
indexes person.fullnameindexes categories.category_name
indexes categories.category_name
indexes subcategories.subcategory_name #additional table
end
end
As you can see, I'm adding a new model (Subcategory) that has no relationship with the model Group, but it has a relationship with the model Category, is this ok? or what is the right way to do that?
Those are the links I'm following:
http://freelancing-god.github.com/ts/en/indexing.html
http://freelancing-gods.com/posts/a_concise_guide_to_using_thinking_sphinx

If subcategory is referenced in the Category model, you can do this:
indexes categories.subcategories.subcategory_name, :as => :subcategory_names
Thinking Sphinx will happily go through associations into deeper associations if you want it to.

I think the short answer to this is "no". ThinkingSphinx will try to reference an association on Group named subcategories, which won't exist, and you should get an error when indexing.
If Category has_many :subcategories, you can express this in Group with a :through option:
class Group < ActiveRecord::Base
belongs_to :person
has_many :categories, :dependent => :destroy
has_many :subcategories, :through => :categories
and then the index should recognize the association, though the docs point out that you need an explicit alias when doing this, so:
indexes subcategories.subcategory_name, :as => 'subcategory_name'

Related

Whats a best practice for implementing a hierarchical relation in Rails?

I'd like to model users that belong to groups, and groups that belong to groups, so I'm thinking along the lines of (forgive the newb syntax):
class Group < ActiveRecord::Base
attr_accessible :description, :group_id, :name
has_and_belongs_to_many :users
end
class User < ActiveRecord::Base
attr_accessible :email, :name
has_and_belongs_to_many :groups
end
Would the above be a preferred way to implement this? Is there a simple way of deleting a 'parent' group and have it delete it's children?
At the time of the writing, I'm learning with rails 3.2.x ...
I hate to be a gem-pusher, but I recently started using ancestry, and it works very well. It has a unique way of indexing ancestors and descendants for great performance.
There's also a Railscast that covers it.
Ancestry is a gem/plugin that allows the records of a Ruby on Rails
ActiveRecord model to be organised as a tree structure (or hierarchy).
It uses a single, intuitively formatted database column, using a
variation on the materialised path pattern. It exposes all the
standard tree structure relations (ancestors, parent, root, children,
siblings, descendants) and all of them can be fetched in a single SQL
query. Additional features are STI support, scopes, depth caching,
depth constraints, easy migration from older plugins/gems, integrity
checking, integrity restoration, arrangement of (sub)tree into hashes
and different strategies for dealing with orphaned records.
Source: https://github.com/stefankroes/ancestry#readme
I'm guessing here - but would users be able to be in multiple groups and groups would only be in one (a parent group)?
class Group < ActiveRecord::Base
attr_accessible :description, :name
has_many :groups, :dependent => :destroy
has_many :group_users, :dependent => :destroy
has_many :users, :through => :group_users
belongs_to :parent_group, :class_name => :group
end
class GroupUsers < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
class User < ActiveRecord::Base
attr_accessible :email, :name
has_many :group_users, :dependent => :destroy
has_many :groups, :through => :group_users
end

Rails: Using custom column to look up for has_many association

I have 3 models: User, List, Following
I'm trying to implement a system, where
A user can create many lists (list contains photos, but it's not relevant in this question)
A user can follow lists created by other users
Here's how I'm trying to build this system:
First we have a database table of lists:
lists: id, user_id
And specifying the models like the following:
class User < ActiveRecord::Base
has_many :lists
end
class List < ActiveRecord::Base
belongs_to :user
end
We can do User.first.lists without problem.
Now my challenge comes when trying to create the followership. I'd like a user to be able to find
All the lists he's following
All the lists being followed that are created by him
All the users following his lists (or, equivalently, all "followers")
Here's the database table I'm trying to use to fulfill the above function:
followings: user_id, list_id, list_user_id
In this table definition, user_id specifies who's following the list, list_id specifies the list being followed, and list_user_id specifies the owner of the list being followed. list_user_id is used here to speed up the database lookups, so that we don't have to join lists table with users table.
And now I'm stuck. I tried to change the user model to the following:
class User < ActiveRecord::Base
has_many :lists
has_many :followings
# Works
has_many :following_lists, :through => :followings, :class_name => "List", :source => :list
# Doesn't work
has_many :followed_lists, :through => :followings, :class_name => "List", :source => :list, :conditions => {:list_user_id => self.id}
# Doesn't work
has_many :followers, :through => :followings, :class_name => "User", :source => :user
end
The first goal, "Find all the lists he's following", is done, through has_many :following_lists, without problem. However, it seems it's difficult to get "all lists being followed" and "all followers" of a user.
The problem is that there seems to be no way to specify the key to use for lookup in the followings table. For example, when looking for user A's followers, I'll need to find all rows in followings table where the list_user_id equals A.id, but has_many method doesn't provide an option to do this, nor does the condition :conditions => {:list_user_id => self.id} work (it'll complain undefined method 'id').
So..how would you deal with this situation? Is there a better way to design the tables, or can we actually work out something based on the current table definitions?
Btw, here's how Following model is defined:
class Following < ActiveRecord::Base
attr_accessible :list_id, :list_user_id, :user_id
belongs_to :user
belongs_to :list
belongs_to :list_user, :class_name => "User"
end
You are trying to get these 2 things:
1.) All the lists being followed that are created by him
2.) All the users following his lists (or, equivalently, all "followers")
Both of these are filtering on top of lists owned by the user. Therefore the associations with :through => followings are incorrect. Since that scopes the lists to ones you follow, not ones you own.
One way to do want you want would be like this:
def followed_lists
# This essentially limits your owned lists to ones with an entry in the followings table
lists.join(:followings).all
end
def users_following_owned_lists
lists.followers.all
end
You will need to add the followers association to the List AR.
class List < ActiveRecord::Base
has_many :followings
has_many :followers, :through => :followings, :class_name => "User", :source => :user
end
Note also that list_user_id on followings table is not really needed.

generic categories for my rails models

I am working in a rails app which main points are articles and products.
Someone has implemented categories for articles.
class ArticleCategory < MainSchemaBase
belongs_to :user
has_many :articles, :through => :article_category_articles, :conditions => 'articles.deleted_at IS NULL'
has_many :article_category_articles, :conditions => 'article_category_articles.deleted_at IS NULL'
And I have been asked to do basically the same thing for products.
Of course I want to DRY but products belongs to brand instead of user, and I have many products instead of many articles
The model is almost empty (some named scopes), controller and views also very dependent of the context (article)
Can this be DRY? Or should I just copy the implemntation ?
Make it polymorphic: The best way, i think, is to set up a polymorphic many-to-many relationship using has_many_polymorphs (https://github.com/Nielsomat/has_many_polymorphs) so that a single category could be applied to a product and an article.
class Category
#doesn't have any association fields
has_many_polymorphs :categorizables, :from => [:products, :articles], :through => :categorizations, :dependent => :destroy
end
class Categorization < ActiveRecord::Base
#has fields categorizable_id, categorizable_type, :category_id
belongs_to :categorizable, :polymorphic => true
belongs_to :category
end
class Product < ActiveRecord::Base
#doesn't need anything to set up the association
end
class Article < ActiveRecord::Base
#doesn't need anything to set up the association
end
"Categorizable" is a bit of a mouthful but you won't actually be using it. You'll be saying #product.categories or #category.articles etc.

How do I specify associations in Rails that pass through several models

I'm writing some tricky polymorphic relationships to handle tagging.
I have a Tag model, and a Tagging model which belongs_to a polymorphic taggable.
I have an Item model, which has_many :taggings, :as => :taggable, and has_many :tags, :through => :taggings, so that I can call #item.tags.
This is all working ok.
I want to bring another model into the mix - a Store which has_many :items. I want to be able to find all tags associated with all items in the store using #store.tags.
Here's what I have:
class Store < AR::Base
has_many :items
has_many :tags, :through => :items, :source => :taggings
However, this returns all of the taggings associated with items in the store, not the actual tags.
How do I get specify that the store has_many tags, through items, through taggings?
Can post more info if needed - trying to prevent information overload! Thanks :)
The source for a has_many association must be a belongs_to, has_one, or has_many association without a :through option (thanks to this answer for info).
There is a plugin that some people have had success with, but in my case it didn't seem to work correctly with my taggable polymorphic association.
At the moment, my solution is to pass a finder_sql option to has_many:
class Store < ActiveRecord::Base
has_many :items
has_many :tags, :finder_sql =>
'SELECT tags.* from tags
INNER JOIN taggings on tags.id = taggings.tag_id
INNER JOIN items on items.id = taggings.taggable_id AND taggings.taggable_type = "Item"
WHERE items.store_id = #{id}'
end
You can do in plain Ruby:
site.wares.map(&:tags).flatten.uniq
This will be inefficient though, unless you have a low number of tags / wares / items.

Use ActiveRecord to Find Result of 5 Nested Tables

I have a User Model(:name, :password, :email), and Event model(:name, :etc) and Interest model (:name)
Then I created two join tables -> UsersInterests and EventsInterests; each not containing a primary key and only comprised of the user_id/interest_id and event_id/interest_id respectively.
I'm trying to use ActiveRecord to query a list all the events where the interest.id of EventsInterests= interest.id of UsersInterests
I'm using has_many and belongs_to relationships with the Nested Loop Plugin
My models look like so =>
user.rb
has_many :users_interests
has_many :interests, :through => :users_interests
event.rb
has_many :events_interests
has_many :interests, :through => :events_interests
interest.rb
belongs_to :users , :through => :users_interests
belongs_to :events , :through => :events_interests
users_interests.rb
belongs_to :users
belongs_to :interests
events_interests.rb
belongs_to :interests
belongs_to :events
If the #user= User.find(1), How would I query the events a user would be interested in?
I came up with this =>
#events.find(:all, :conditions => EventsInterests.interest_id = UsersInterests.interest_id) ??
but I get the error
undefined method `interest_id' for UsersInterests(user_id: integer, interest_id: integer):Class
umm..wtf? any help guys....I've been at this for like 4 days
First, hop into the console and make sure all of your relationships work:
User.first.events
User.first.interests
Events.first.users
Interests.first.users
Interests.first.events
# ... and so
Just to clarify, a User lists his Interests, and you want to get a list of the Events matching those interests?
User.first.interests.collect { |interest| interest.events }.uniq
Not particular efficient, but effective and easy to comprehend.
You could use User.first.interests_singular_ids to get the ids and pass that to a find() with interest_id IN(...) that list, too. I'm not sure how much faster it would be, though.

Resources