Relationship between two db entities - erd

My relationship
I have a Place table and Tour table in DB.
In Tour, I have a start-place and end-place.
I don't know what's exactly true realation between them.
Thanks for watch!

You have two relationships, represented by (tour_id, start_place_id) and (tour_id, end_place_id). These are both many-to-one binary relationships, since each tour has a single start place and end place, while a place can be the starting point or ending point of multiple tours.

Related

From a given node get reachable node following unidirectional relationship and display that sub-graph as a tree

here is my problem:
I've got a graph where I have :Item with one relationship :CRAFTED_WITH to one :RECIPE and those :RECIPEhave one or more relationship :COMPOSED_OF{quantity} to ingredients that are :Item.
As you can imagine you can have several level of relationship to get from a high tier :Item to the most basic of components.
I want to be able to find all nodes that are reachable from a specific node while following only one direction. That part was easy I used the apoc procedure apoc.path.subgraphAll.
But now my next step is to have the result display as a tree and not a graph. In a graph I will ended up with multiple :Item on the receiving end of :COMPOSED_OF relationship. I want :Item to be "duplicated" so they are linked by a single :COMPOSED_OF relationship.
Is it even feasible only in cypher ? Or will I have to use another language to handle a graph to turn it into that "tree" structure ?
There is an apoc function to do that. The cypher below illustrates what is does.
MATCH treePath=(root:Thing)-[:CHILD*0..]->(leaf:Thing)
WHERE NOT (leaf)-[:CHILD]->()
AND NOT ()-[:CHILD]->(root)
WITH COLLECT(treePath) AS treePaths
CALL apoc.convert.toTree(treePaths) yield value AS tree
RETURN tree

How to get a path from one node to another including all other nodes and relationships involved in between

I have designed a model in Neo4j in order to get paths from one station to another including platforms/legs involved. The model is depicted down here. Basically, I need a query to take me from NBW to RD. also shows the platforms and legs involved. I am struggling with the query. I get no result. Appreciate if someone helps.
Here is my cypher statement:
MATCH p = (a:Station)-[r:Goto|can_board|can_alight|has_platfrom*0..]->(c:Station)
WHERE (a.name='NBW')
AND c.name='RD'
RETURN p
Model:
As mentioned in the comments, in Cypher you can't use a directed variable-length relationship that uses differing directions for some of the relationships.
However, APOC Procedures just added the ability to expand based on sequences of relationships. You can give this a try:
MATCH (start:station), (end:station)
WHERE start.name='NBW' AND end.name='THT'
CALL apoc.path.expandConfig(start, {terminatorNodes:[end], limit:1,
relationshipFilter:'has_platform>, can_board>, goto>, can_alight>, <has_platform'}) YIELD path
RETURN path
I added a limit so that only the first (and shortest) path to your end station will be returned. Removing the limit isn't advisable, since this will continue to repeat the relationships in the expansion, going from station to station, until it finds all possible ways to get to your end station, which could hang your query.
EDIT
Regarding the new model changes, the reason the above will not work is because relationship sequences can't contain a variable-length sequence within them. You have 2 goto> relationships to traverse, but only one is specified in the sequence.
Here's an alternative that doesn't use sequences, just a whitelisting of allowed relationships. The spanningTree() procedure uses NODE_GLOBAL uniqueness so there will only be a single unique path to each node found (paths will not backtrack or revisit previously-visited nodes).
MATCH (start:station), (end:station)
WHERE start.name='NBW' AND end.name='RD'
CALL apoc.path.spanningTree(start, {terminatorNodes:[end], limit:1,
relationshipFilter:'has_platform>|can_board>|goto>|can_alight>|<has_platform'}) YIELD path
RETURN path
Your query is directed --> and not all of the relationships between your two stations run in the same direction. If you remove the relationship direction you will get a result.
Then once you have a result I think something like this could get you pointed in the right direction on extracting the particular details from the resulting path once you get that working.
Essentially I am assuming that everything you are interested in is in your path that is returned you just need to filter out the different pieces that are returned.
As #InverseFalcon points out this query should be limited in a larger graph or it could easily run away.
MATCH p = (a:Station)-[r:Goto|can_board|can_alight|has_platfrom*0..]-(c:Station)
WHERE (a.name='NBW')
AND c.name='THT'
RETURN filter( n in nodes(p) WHERE 'Platform' in labels(n)) AS Platforms

How to do Exclusion Queries in OrientDB

I'm wondering if it's possible to use a sub-selection as an exclusion query in orient db (v2.0). Or if it's necessary to export separate queries and process in Java/PHP/etc.
For instance, say we have the following graph for Hogwarts.
Vertices
People, Houses, Classes
Edges
is_at (subclasses is_student, is_faculty), was_at (alumni), is_taking, is_teaching, belongs_to
How would we find all the alumni who aren't also faculty? Is it possible to do so as a single query or using LET somehow?
How would we find all the faculty who are teaching a course on, say, time travel, that have no students who belong to the house gryffindor?
Thanks,
Lindsay
The .size() operator should work: http://orientdb.com/docs/2.0/orientdb.wiki/SQL-Methods.html#size
select from People where out('is_faculty').size() = 0
Use out('...') or in('...') based on your graph.
How would we find all the faculty you are teaching a course on, say, time travel, that have no students who belong to the house gryffindor?
I don't have much information on your graph and classes, but that could be something like:
select from Classes where ClassName='time travel' and in('is_teaching')[Id=yourId] and in('is_taking').out('belongs_to')[Name='gryffindor'].size() = 0
Again, use in() or out() accordingly to your graph.

Adding a new user to neo4j

A totally neo4j noob is talking here,
I like to create a graph to store a set of users, a typical user is as follows:
CREATE
(node_1 {FullName:"Peter Parker",FirstName:"peter",FamilyName:"parker"}),
(node_2 {Address:"Newyork",CountryCode:"US"}),
(node_3 {Location:"Hidden"}),
(node_4 {phoneNumber:11111}),
(node_5 {InternetEmailAddress:"peter#peterland.com")
now the problem is,
Every time I execute this I add 5 more nodes.
I know I need to use a unique key, but all example I saw can use a unique key for a specific node. So how can I make sure a user doesn't get added if it already exists(I can use email address as unique key).
how do I update the nodes if some changes occur. for example, after a week I want to update the graph to contain the following instead of the previous one.(no duplicates)
CREATE(node_1 {FullName:"Peter Parker",FirstName:"peter",FamilyName:"parker"}),(node_2 {Address:"Newyork",CountryCode:"US"}),(node_3 {Location:"public"}),(node_4 {phoneNumber:11111}),(node_5 {InternetEmailAddress:"peter#peterland.com"),(node_6 {status:"Jailed"})
(NOTE the new update changed location to "public" and added a new node for peter
Seeing as you had a load of nodes anyway.
Some of the data you have modelled as Nodes are probably properties as the other answer suggests, some are possibly correctly modelled as Nodes and one could probably form the or a part of the relationship.
Location public/hidden can be modelled in one of three ways, as a property on the Person, as a property between the Person and the Location or as the relationship type. To understand that first you need to have a relationship.
Your address at the moment is another Node, I think this is correct, but possibly you would want two nodes, related something like this:
(s:State)-[:IN_COUNTRY]-(c:Country)
YMMV and clearly that a US centric model, but you can extend it easilly enough.
Now you could create Peter with a LIVES_IN relationship:
CREATE (p:Person{fullName:"Peter Parker"}), (s:State{name:"New York"}), (c:Country{code:"US"}),
(p)-[:LIVES_IN]->(s), (s)-[:IN_COUNTRY]->(c)
For speed you are better off modelling two relationships which could be LIVES_IN_PUBLIC and LIVES_IN_HIDDEN which means to perform that update that you want above then you have to delete the one and create the other. However, if speed is not of the essence, it is common also to use properties on the relationship.
CREATE (p:Person{fullName:"Peter Parker"}), (s:State{name:"New York"}), (c:Country{code:"US"}),
(p)-[:LIVES_IN{public:false}]->(s), (s)-[:IN_COUNTRY]->(c)
So your complete Q&A:
CREATE (p:Person {fullName:"Peter Parker",firstName:"peter",familyName:"parker", phoneNumber:1111, internetEmailAddress:"peter#peterland.com"}),
(s:State {name:"New York"}), (c:Country {code:"US"}),
(p)-[:LIVES_IN{public:false}]->(s), (s)-[:IN_COUNTRY]-(c)
MATCH (p:Person {internetEmailAddress:"peter#peterland.com"})-[li:LIVES_IN]->()
SET li.public = true, p.status = "jailed"
When adding other People you probably do not want to recreate States and Countries, rather you want to match them, and possibly Merge them, but we'll stick to Create.
MATCH (s:State{name:"New York"})
CREATE (p:Person{name:"John Smith", internetEmailAddress:"john#google.com"})-[:LIVES_IN{public:false}]->(s)
John Smith now implicitly lives in the US too as you can follow the relationship through the State Node.
Treatise complete.
I think you're modeling your data incorrectly here - you're setting up each property of the person as a separate node, which is not a good idea. You don't have any linkages between those nodes, so with this data pattern, later on you won't be able to tell what Peter Parker's address is. You're also not using node labels, which I think could really help here.
The quick question to your answer about updating nodes is that you have to MATCH them, then use SET to modify a property. So if you had a person, you might do this:
MATCH (p:Person { FullName: "Peter Parker" })
SET p.Address = "123 Fake Street"
RETURN p;
But notice I'm making assumptions about the way your data is structured. I'll take that same data you provided, this might be a better way of creating it:
CREATE (node_1:Person {FullName:"Peter Parker",
FirstName:"peter",
FamilyName:"parker",
Address:"Newyork",CountryCode:"US",
Location:"Hidden",
phoneNumber:11111,
InternetEmailAddress:"peter#peterland.com"});
The difference with this suggestion is that I'm putting all the properties into a single node (instead of one property per node) and I'm applying the Person label to the node.
If you structured the data like this, then the update query I provided would work. Structuring the data like you have it, it's not possible to update Peter Parker's address, because there's no relationship between your node_1 and node_2

Drupal create views involving LEFT JOIN Sub-Select with non-existent node

i'm using Drupal 6
I have this table relation and I've translated into CCK complete with it's relation.
Basically when I view a Period node, I have tabs to display ALL Faculty nodes combined with Presence Number.
here's the table diagram: http://i.stack.imgur.com/7Y5cU.png
Translated into CCK like these:
CCK Faculty (name),
CCK Period (desc,from,to) and
CCK Presence(node-reference-faculty, node-reference-period, presence_number)
Here's my simple manual SQL query that achieve this result: http://i.stack.imgur.com/oysd3.png
SELECT faculty.name, presence.presence_number FROM Faculty AS faculty
LEFT JOIN (SELECT * FROM Presence WHERE Period_id=1) AS presence ON faculty.id=presence.Faculty_id
The value of 1 for Period_id will be given by the Period Node ID from the url argument.
Now the hardest part, is simulating simple SQL query above into Views. How can I make such query into Views in Drupal-6 or Drupal-7 ?
thanks for any helps.
The main issue, which I think you've noticed, is that if you treat Faculty as the base for your join, then there is no way to join on the Presence nodes. Oppositely, if you treat Presence as the base, then you will not see faculties that have no presence number.
There is no easy way, using your currently defined structure, to do these joins in views.
I would say your easiest option is to remove the 'node-reference-faculty' field from the presence node and add a node-reference-presence field to the faculty. Since CCK fields can have multiple values, you can still have your one-to-many relationship properly.
The one downside of this is that then you need to manage the presence-faculty relationship from the faculty nodes instead of the presence nodes. If that's a show stopper, which it could be depending on your workflow, you could have BOTH node-reference fields, and use a module like http://drupal.org/project/backreference to keep them in sync.
Once you have your reference from faculty -> presence, you will need to add a relationship in Views. Just like adding a field or a filter, open the list of relationships and find the one for your node-reference field.
Next, you will need to add an argument for period id and set it up to use a node id from the url. The key thing is that when you add the argument, it will ask which relationship to use in its options. You will want to tell it to use your newly added presence relationship.
You don't really need to do a subquery in your SQL like that. This should be the same thing and won't make mysql try to create a temporary table. I mention it because you can't really do subqueries in Views unless you are writing a very custom Views handler, but in this case you don't really need the subquery anyway.
Ex.
SELECT f.name, p.presence_number
FROM Faculty AS f
LEFT JOIN Presence AS p ON f.id=p.Faculty_id
WHERE p.Period_id=1;
I wrote an article about how to achieve a similar outcome here. http://scottanderson.com.au/#joining-a-views-query-to-a-derived-table-or-subquery
Basically how to alter a Views query to left join on a sub-query.

Resources