Is there a way to traverse relationships through their lineage?
In other words, if you have a hierarchy of three or more polymorphic-ally related objects, is there any relative way to retrieve objects that are two degrees away, since I won't know its class?
For example:
Let's say I have images, which is polymorphic to students and teachers. Let's say students and teachers is polymorphic to different objects, like church and university.
How would I easily grab the "church" or "university" object with the images one loaded?
The short answer is that you can't do this. That is, you can't make a has-many-through association across a polymorphic association.
You can, however, make such an association if you limit the scope to a specific class/table. So you can find (and eager load) all teachers' universities for a specific image or image relation, for example. But you can't broaden the query to look at multiple potential tables in the way you seem to be hoping.
Of course, you can always write a query to do this by hand if so inclined...
Related
I have three models, let's call them Product, TemplateProduct and ReadyProduct. There is only one table for these, the products table, and both TemplateProduct and ReadyProduct inherit from the Product model. There is a has_many/belongs_to association between TemplateProduct and ReadyProduct. Templates are used to lay out general characteristics for products, Readys are used to customize the products and what are actually made available for view by the customer. Each TemplateProduct has an id and each ReadyProduct has a template_product_id which ties to it's template.
The project is built using Rails 5.
What I want to be able to do is to gather a list of TemplateProducts, then get a count of each templates associated ReadyProducts and do so in such a way that won't hammer the database. I understand ActiveRecord associations but my SQL is weak and I have only a limited understanding of joins. I can gather a list of TemplateProducts with a simple Product.where(conditions) but I don't know what to once I have this. For the sake of flexability sake I want to be able to base my ReadyProduct count off of this initial collection as sometimes I'll need the additional count and sometimes I won't. I'm sure there must be a simple way to do this but I haven't found a solution.
If you just need a mapping of TemplateProduct ids to ReadyProduct counts, then all you need is:
TemplateProduct.joins(:ready_products).group(:id).count
If you want TemplateProduct instances with a baked in ReadyProduct count, then you'll need this instead:
tps = TemplateProduct.joins(:ready_products).select('products.*, COUNT(ready_products_products.id) ready_product_count').group(:id)
tps.first.ready_product_count
#=> 6
ready_products_products gets defined by Rails, it prefixes the actual table name (products) with the model name's "table form" (pluralized, snake case, lower case, ready_products), joined with an underscore.
I want to categorize objects in multiple trees to reflect their characteristics and to build a navigation on.
So, given the following trees:
Category1
-Category-1-1
-Category-1-2
Category2
-Category-2-1
-Category-2-2
--Category-2-2-1
An object could e.g. belong to both Category-1-2 and to Category-2-2-1.
The goal is to be able to fetch all objects from the database
that belong to a certain category
that belong to a certain category or its decendants
A more practical example:
A category might have a hierarchy of 'Tools > Gardening Tools > Cutters'.
A second category: 'Hard objects > Metal objects > Small metal objects'
An object 'Pruners' would be categorized as belonging to 'Cutters' as well as 'Small metal objects'.
I want to be able to
retrieve all 'Gardening Tools' -> 'Pruners'
retrieve all Category children of 'Gardening Tools' -> 'Cutters'
retrieve all 'Hard objects' -> 'Pruners'
retrieve all 'Hard objects' that are also 'Cutters' -> 'Pruners'
retrieve all 'Soft objects' that are also 'Cutters' -> []
Any pointers? I have briefly looked at closure_tree, awesome_nested_sets etc., but I am not sure they are a good match.
Please note that the code here is all pseudo code.
I would use ancestry gem and would model your data with three model classes.
This way your data is normalized and it's a good base to build on.
Category - ancestry tree
has_may Memberships
has_may Products through Memberships
Membership
belongs_to Category
belongs_to Products
Products
has_may Memberships
has_may Categories through Memberships
From there on you need to figure out how to perform the equerries efficiently.
My way of doing this is to understand how to do it with SQL and then figure out how to express the queries with activercord's DSL.
Some resources:
http://railsantipatterns.com/ This book has some examples of complex SQL queries turned into reusable scopes and helpers
http://guides.rubyonrails.org/active_record_querying.html#joining-tables Rails's documentation, see section on joins and includes
http://stackoverflow.com/questions/38549/difference-between-inner-and-outer-join A great explanation of SQL joins
Queries examples:
Find a category.
Category.find(category_id)
Find a category and include it's products inside the specified category.
Category.find(category_id).join(:memberships => :products)
Find a category's sub-tree ind include products
Category.subtree_of(category_id).join(:memberships => :products)
Find all categories a products belongs to.
Product.find(product_id).categories
I just did this and I chose not to use ancestry, but closure_tree because the author says it is faster and I agree with him. Know you need a `has_and_belongs_to_many' between Categories (which I like to call tags whenever I add multiple to a single object) and Objects.
Now the finders, the bad news is that without your own custom query you might not be able to do it with one. Using the gems methods you will do something like:
Item.joins(:tags).where(tags: {id: self_and_descendant_ids })
The code is clean and it executes two queries, one for the descendant_ids and another one in Objects. Slight variations of this, should give you what you need for all except the last. That one is tough and I haven't implemented it (I'm in the process).
For now, you will have to call tag.self_and_ancestor_ids on both (Query count: 2), all items in those tags (Query count: 4) and intersect. After this, some serious refactoring is needed. I think we need to write SQL to reduce the number of queries, I don't think Rails query interface will be enough.
Another reason I chose *closure_tree* was the use of parent_id, all siblings share it (just like any other Rails association) so it made it easier to interface with other gems (for example RankedModel to sort).
I think you could go for one of the tree gems, personally I like Ancestry. Then make an association for each category to have many objects and each object can belong to many categories.
Have you stumbled on any problems already or are you just researching your options?
The mongoid documentation told me that n-n relations should be used with caution
I understand his but don't have an idea how to solve my problem a better way using pure mongoid:
A course has many participants and a participant could participate with many courses. So wouldn't it be faster to store the participant on the course model and do a search over all courses when all courses of a participant are needed?
Your model should be reflective of your use cases.
One way to do this would be to have one model for the courses, one for participants and a 3rd that maps students to courses (with a unique index on course & student to prevent duplicates). This way there is a single model referring to the other 2. This may or may not be ideal based on your access patterns.
I think this is probably a good use case for embedding documents. See the sample syntax on the front page for embeds_many and embedded_in: http://mongoid.org/en/mongoid/
The main downside here is that if you have participants in more than one course, you will have duplicate participants in each of those courses.
Make sure you put an index on the fields you plan to do your lookups for participants with.
I've read so many Rails books/tutorials, but when it comes time to actually make the app, I keep tripping over myself, so I'm trying this little exercise to help me get it better.
I have an app with 3 classes (Link, Url, Visit) that have associations defined between them, such as has_one, belongs_to etc. This allows me to call methods like Link.url
If I were to convert this into an app with a single model, is it possible to create the convenience methods (such as Link.url) even though there are no relationships between models, because there is only one model.
This might not be the 'Rails way' but if you answer this question it'll help me get it more.
I guess another way to ask this is, do the Rails associations only exist because the tab
Thanks
Models exist to represent tables in a database. If you have 3 different conceptual objects, then you need 3 different models. Keeping those objects separate and in different tables/models is essential to good programming in any language. The relations are there to help you understand the correlation of each object to the others.
If you think all of data from each of the models can be represented in one table sensibly, then combine them in to one model with a name that encompasses all of the data. If you choose this option, you'll use columns for that one table which represent each of the pieces of data you need. Those column names come free in the model when you create the migration. A table with a column named "url" on a model named "Hit" could be used like this:
Hit.first.url
I have this app I am writing in Rails 3.1, I am wondering the best way to model this.
Would it be best if I created a model called "Movie" with a "title" and then create a new model for each "movie asset" such as "poster, trailer, screener" etc and relate it to the "Movie" by associations? Or would it be best if I just created this as one and do-away with the of associations of each asset to "Movie"?
My assumption is to just make it as one as it will remove all the overhead of the FK's and joins to get retrieve the data related to the movie but I am looking for opinions/suggestions. Thanks
There can be three types of attributes(columns) for movies.
Which have exactly one value, and are present in every movie e.g. title, year, official trailer etc.
Keep them in the movie table.
Which have exactly one value, but are present in few of the movies e.g. total Academy Awards.
Keep them in separate table, and use has_one+belongs_to association.
Which have multiple values e.g. trailers
Keep them in separate table, and use has_many+belongs_to association.
More suggestions:
For many key-value attributes, it is easier to use one json/yaml column using serialize instead of creating one column for each key.
Do not store images in DB, keep them in file-system or cloud storage.
You can create your models in the order you want since you have to fill in both models to create a unique association (such as belongs_to and has_many) so I think it doesn't really matter !