Ruby on Rails: Find values based on has_many relations - ruby-on-rails

I have 2 models: nation (table nations) and majors (table majors).
nations contains all the nations of the world and majors contains all the educational branches like Computer Science, Mechanical Engineering etc.
Both models have id and name fields and contain the following relationships.
Nation model (Nation.rb)
has_many :majors
Major model (Major.rb)
has_many :nations
I want to run a query like: Find all the majors where nation_id = x (where x is the id of nation).
How do I do this in rails?
I feel there should exist a table which contains a mapping like:
id major_id nation_id
1 1 1
2 1 2
3 2 1
.
.
.
where each major is mapped to the country to which it belongs and vice versa.
I am new to rails and not sure how to do this.

You're right about creating a third table for the mapping between the two. You have two options. You can either create a third table in case you want fields in it other than major_id and nation_id.
If not, you should go with creation of the join table. Feel free to try it and let me know if their are other issues.This article explains it in a very simple way.

Related

Rails should relation be polymorphic?

I have 3 tables Injury, Sport, Exercise. I'm trying to wrap my head around if I should create a relation between them. I want to be able to list Injuries in the Sports and Exercises tables; this might expand to more tables in the future, so I'm thinking using a Polymorphic relation. However, that would create a multiple Injuries for both tables, i.e. Injurable_Type would be Exercises or Sports each with there own Injurable_id; if my understanding is correct.
Example:
Injury_id : 1
name = acl tear
injurable_id = 1
type = Sport
Injury_id: 2
name = acl tear
id = 1
type = Exercise
Ideally(though this might be impossible)
Injury_id : 1
name = acl tear
injurable_id = 1
injurable_type = Sport and Exercise
I could keep them separate and store Injury_ids into columns on both Exercises and Sports tables but that will lead to querying issues/headaches down the line I believe. Further more, my understanding of polymorphic associations is to get rid of the Injury_id column.
How should I approach this problem? Leave the tables separate and deal with the possibility of multiple queries down the road or make them polymorphic or something else?
My thought process is this:
Sport / Exercise table would have access to all injuries in Injury table
What I have though of so far:
Injury
belongs_to :injurable, polymorphic: true
Sport
has_many :injuries, as: :injurable
Exercise
has_many :injuries, as: :injurable
Or just leave them separate
Injuries_id: 1, 2 ,3 etc.
Sports table has column
injuries_id: 1, 2
Exercises table has column
injuries_id: 1, 3
Then query them (polymorphic) like:
Sports controller:
sport = Sport.includes(:injuries).find(1)
Exercises controller
exercise = Exercise.includes(injuries).find(1)
Then query them (Separate way) like:
Sports controller:
sport = Sports.find(1)
injuries = sport.pluck(:injuries_id)
injury_name = Injuries.find(injuries).pluck(:name)
Exercises controller:
exercise = Exercises.find(1)
injuries = exercise.pluck(:injuries_id)
injury_name = Injuries.find(injuries).pluck(:name)
Something like that.
My concern with the polymorphic approach is that you won't be able to share the same injury "definition" across different activities. You'll have to duplicate the same injury to attach it to other sports and exercises; this may cause more troubles down the line(and goes against database normalization rules). For example, answering a question such as "how many total ACL tear injuries across all activities?" would not be quickly done(you'll end up matching by text -- that's not ideal!). Also, what if you decided to change the name of the ACL tear injury? You'll end up updating multiple rows in the Injury table.
I would suggest a third approach(similar to your 2nd approach): Injuries should not be directly linked to sports or exercises. The Injury model should do one thDefinehould do it well: Defining what an injury is. This will serve you better as you expand your data schema and add new use cases. Now, associating a sport or exercise activity can be done with a bridge model, i.e.:
class SportInjury
belongs_to :injury
belongs_to :sport
end
class ExerciseInjury
belongs_to :injury
belongs_to :exercise
Then, you're able to associate a Sport model to an Injury model as the following:
class Sport
has_many :injuries, through: :sport_injuries
end
Ruby on Rails should be smart enough to produce optimized SQL when accessing a sport's list of injuries when you call sport.injuries.
This approach has a couple of benefits:
Injuries are not duplicated, and updates to them are done once.
Linking new activities to injuries is straightforward.
You can attach additional data in the bridge entities(e.g., time of injury during a sports match can be stored as an attribute in SportInjury)
Finally, in comparison to your 2nd approach, this is the "RDMS Way." Generally in an RDMS, you encode associations in tables rather than as array columns(which is a practice you may see in a document-based DB such as MongoDB).

Why does `has_one` in rails require a foreign key?

Consider two tables Foo and Bar and consider models based on them. Now consider a one-to-one relationship between them.
Foo contains has_one :bar in it's declaration so that we're able to access Bar from Foo's objects. But then what I don't understand is why Bar needs a foreign key referencing Foo?
Shouldn't it be easier if they just compare both the ids to get the result?
I'm assuming that there will be problems with comparing both ids and I want to know what the problems are.
The problem with ids is that they store auto-incremented values. Let's consider 2 tables students and projects.
Let's assume a student can have at most 1 project. Which means he can either have a project or not.
Now consider 2 students A & B.
students table
id name
1 A
2 B
now projects table
id name
1 P1
2 NULL
in this case A has a project named as P1 but B doesn't and we're creating a null entry just to maintain and match the id of records present in projects with the students but this is not feasible in the long term. If in a school there are 1000 students then we'll have may be 500 empty rows for 500 students who are not working on a project.
That's why adding a column in projects table is a feasible solution to reduce the size of the table and maintain relationships as well and also if you're going to delete a record then the new id won't be same as the previous one as id's are auto-incremented.
now projects table
id name student_id
1 P1 1
is more feasible and flexible as well. You can make it has_many as well because a student can work on multiple projects as well.
I hope this helps you.
You can't assume that the DB engine will add the same IDs to rows in different tables. You can (I would not recommend) make an app with such behavior and implement it with triggers and constraints, but this would be a very creative (in a negative sense) approach to relational databases.

Neo4J Grandchild Relationships Linked to Nodes

I'm trying to model contractor relationships in Neo4J and I'm struggling with how to conceptualize subcontracts. I have nodes for Government Agencies (label: Agency) and Contractors (label:Company). Each of these have geospatial Office nodes with the HAS_OFFICE relationship. I'm thinking of creating a node that represents a Government Contract (label: Contract).
Here's what I'm struggling with: A Contract has a Government Agency (I'm thinking this is a "HAS CONTRACT" relationship) and one or more prime contractor(s) (I'm thinking this is a "PRIME" relationship). Here's where it gets complicated. Each of those primes contractors can have subcontractors under the prime contract only. Graphically, this is:
(Agency) -[HAS_CONTRACT]-> (Contract) -[PRIME]-> (Company 1) -[SUB]-> (Company 2)
The problem I'm struggling with is that the [SUB] relationship is only for certain contracts -- not all. For example:
Agency 1 -HAS-> Contract ABC -P-> Company 1 -S-> Company 2
Agency 1 -HAS-> Contract ABC -P-> Company 3 -S-> Company 4
Agency 2 -HAS-> Contract XYZ -P-> Company 1
Agency 2 -HAS-> Contract XYZ -P-> Company 4 -S-> Company 2
I want some way to search on that so I can ask cypher questions like "Find ways Agency 2 can put money on contract with Company 2." It should come back with the XYZ contract through Company 4, and NOT the XYZ contract through Company 1.
It seems like maybe storing and filtering on data within the relationship would work, but I'm struggling with how. Can I say Prima and Sub relationships have a property, "contract_id" that must match Contract['id']? If so, how?
Edit: I don't want to have to specify the contract name for the query. Based on #MarkM's reply, I'm thinking something like:
MATCH (a:Agency)-[:HAS]-(c:Contract)-[:PRIME {contract_id:c.id}]
-(p:Company)-[:SUB {contract_id:c.id}]-(s:Company)
RETURN s
I'd also like to be able to use things like shortestPath to find the shortest path between an agency and a contractor that follows a single contract ID.
I'd create the subcontractor by having two relationships, one to the contractor and one to the contract.
(:Agency)-[:ISSUES]->(con:Contract)-[:PRIMARY]->(contractor:Company)
(con:Contract)-[:SECONDARY]->(subContractor:Company)<-[:SUBCONTRACTS]-(contractor:Company)
Perhaps you can mode your use-case as a graph-gist, which is a good way of documenting and discussing modeling issues.
This seems pretty simple; I apologize if I've misunderstood the question.
If you want subcontractors you can simply query:
MATCH (a:Agency)-[:HAS]-(:Contract)-[:PRIME]-(p:Company)-[:SUB]-(s:Company) RETURN s
This will return all companies that are subcontractors. The query matches the whole pattern. So if you want XYZ contract subcontractors you simply give it the parameter:
MATCH (a:Agency)-[:HAS]-(:Contract {contractID: XYZ})-[:PRIME]-(p:Company)-[:SUB]-(s:Company) RETURN s
You'll only get company 2.
EDIT: based on your edit:
"Find ways Agency 2 can put money on contract with Company 2"
This seems to require some domain-specific knowledge which I don't have. I assume Agency 2 can only put money on subcontractors but not primes?? I might help if you reword so we know exactly what your trying to get from the graph. From my reading it looks like you want all companies that are subcontractors under Company 2's contracts. Is that right?
If that's what you want, again you just give Neo the path:
MATCH (a:Agency: {AgencyID: 2)-[:HAS]
-(c:Contract)-[:PRIME]-(:Company)-[:SUB]-(s:Company: {companyID: 2)
RETURN c, s
This will give you a list of all contracts under XYZ for which Company 2 is a subcontractor. With the current example, it will one row: [c:Contract XYZ, s:Company 2]. If Agency 2 had more contracts under which Company 2 subcontracted, you would get more rows.
You can't do this: [:PRIME {contract_id:c.id}] [:SUB {contract_id:c.id}] because Prime and Sub relationships shouldn't have contract_id properties. They don't need them — the very fact that they are connected to a contract is enough.
One thing that might make this a little more complicated is if the subcontractors also have subcontractors, but that's not evident.
Okay take 2:
So the problem isn't captured well in the original example data — sorry for missing it. A better example is:
Agency 1 -HAS-> Contract ABC -P-> Company 1 -S-> Company 2
Agency 1 -HAS-> Contract XYZ -P-> Company 1 -S-> Company 3
Now if I ask
MATCH (a:Agency)-[:HAS_CONTRACT]-(ABC:Contract {id:ABC})-[:PRIME]
-(c:Company)-[:SUBS]-(c2) RETURN c2
I'll get both Company 2 and 3 even though only 2 is on ABC, Right?
The problem here is the data model not the query. There's no way to distinguish a company's subs because they are all connected directly to the company node. You could put a property on the sub relationship with the prime ID, but a better way that really captures the information is to add another contract node under company. Whether you label this as a different type depends on your situation.
Company1 then [:HAS] a contract which the subs are connected to. The contract can then point back to the prime contract with a relationship of something like [:PARENT] or [:PRIME] or maybe from the prime to the sub with a [:SUBCONTRACT] relationship
Now everything becomes much easier. You can find all subcontracts under a particular contract, all subcontracts a particular company [:HAS], etc. To find all subcontractors under a particular contract you could query something like this:
MATCH (contract:Contract { id:"ContractABC" })-[:PRIME]-(c:Company)
-[:HAS]->(subcontract:Contract)-[:PARENT]-(contract)
WITH c, subcontract
MATCH (subcontract)-[:SUBS]-(subcontractor:Company)
RETURN c, subcontractor
This should give you a list of all companies and their subcontractors under contract ABC. In this case Company 1, Company 2 (but not company 3).
Here's a console example: http://console.neo4j.org/?id=flhv8e
I've left the original [:SUB] relationships but you might not need them.

How to display queried records in a specific order

I have HABTM relationships between Topic and Chapter Now I want to display chapters in a specific order under topics.
For Eg: A particular chapter might be 2nd chapter under one topic, and might be 8th chapter under another topic.
How Should I do it? Thanks in advance.
In this case you have a non-trivial many-to-many relationship, the join table needs to contain more than just foreign keys to each of main tables.
As a result, you'll want to move from HABTM to has_many :through on both Topic and Chapter, and change the join table to be its own model that not only relates the two main tables to each other, but also stores the order in a column (which, by the way, you should not name "order", since it will confuse SQL and Rails :-).
Converting from a standard Rails HABTM model is pretty easy, but one bit of advice: think of what it is that makes what used to be just a join table a real model ... and use that as a name for the new model (and associated new table). In a simple HABTM, you would create a table chapters_topics, when it becomes a model it might be "ChapterTopic" (resulting in a table chapter_topics).

How to combine two models in one RoR acts_as_tree treeview?

I have two simple models each with acts_as_tree, say Departments and Employees.
My goal is to create a treeview combining both models in to one overall tree, like so:
Department 1
SubDepartment 1.1
Employee A
Employee B
SubDepartment 1.2
Department 2
Subdepartment 2.1
Employee C
Department 3
SubDepartment 3.1
Employee D
Employee E
Subdepartment 3.2
etc
I found this already: Acts as Tree with Multiple Models but I'm afraid I could use a little more pointers in the right direction.
Thanks!
So your schema is like this?
Department
acts_as_tree #requires departments.parent_id field
has_many :employees
Employee
belongs_to :department #requires employees.department_id field
I would just stick with this rather than trying to make the tree 'know' about employees. The only things that have the tree relationship are the departments. The employees belong to a department but they're not part of the tree structure.
As far as editing goes, then, when you change a department you set parent_id to be the id of its parent in the tree, and when you move an employee you set department_id to be the id of its 'parent'.
What's your actual problem? I mean what are you trying to do?

Resources