please i can't check and create a node plus a relationship to pre-existing node.
i need to check if node pre-existing in my Neo4j database and create a node plus relationship to this node.
Thank you
Here's how you'd merge existing data with data that you create on the fly using Cypher 2.0, the native language to Neo4j:
MERGE ({ name: "Jane" })-[r:KNOWS]->({ name: "John" })
In fact none of these data need pre-exist. It will all be created on the fly, relationships and all. However there's not much checking going on, and if you make a small mistake, it's easy to create duplicate entries this way. Thus it's good practice to check things beforehand.
Here's how you'd use Cypher to check whether a node already exists:
MATCH (n:SomeNode {some_field: "some_discrete_data"}) RETURN n;
In your case it would probably be something like:
MATCH (n {id: 1}) RETURN n;
If there is a node with a field containing the data you need (in this case I've assumed it is {id: 1}, but you might of course want something else), it will be returned. And if not, it will return 0 nodes.
Warning: If you do not describe the data you need, i.e. {id: 1}, all the nodes in the database will be returned, and you might not want that.
However you can't make a relationship with just one node. You need at least two nodes to create a relationship between them. So we'll create another:
CREATE (:OtherNode {id: 2});
To make a relationship with this new node to the pre-existing one, you need to find them with MATCH, and then create the relationship. Here's an example:
MATCH (node1 {id: 1}), (node2 {id: 2})
CREATE (node1)<-[:LIKES]-(node2);
The <- denotes that node2 will have a directional relationship to the already pre-existing node1.
However you can do this process in just one step; that is check if a node already exists, and add a new node plus relationship between the new and the pre-existing node all at the same time! You can do this with the UNIQUE keyword:
MATCH (node1 {id: 1})
CREATE UNIQUE (node1)<-[:LIKES]-(node2 {id: 2});
As long as node1 already exists, node2 and its relationship to node1 will be created if they don't already exist. However if node1 doesn't exist, nothing will happen. IMHO that's a nice safety feature if you want to avoid duplicates.
As for the Python-spesific API, you'd have to check the documentation for the language driver relevant to the programming language you use. Most drivers accept Cypher in some form or another. Here's the relevant part regarding Python: http://book.py2neo.org/en/latest/cypher/
Check out the py2neo docs for the Cypher support and use just a merge query like this:
MERGE (user:User {name:"John"}) // get-or-crate
MERGE (friend:User {name:"Jane"}) // create friend
MERGE (user)-[r:KNOWS]->(friend) // create relationship
RETURN user,r,friend
Cypher's MERGE is it: http://docs.neo4j.org/chunked/stable/query-merge.html. And http://www.catb.org/esr/faqs/smart-questions.html might also be a good read.
Related
I started looking into DynamoDB, but got stuck reading this part about the materialized graph pattern: Best Practices for Managing Many-to-Many Relationships.
I guess I get some ideas, but don't understand the whole thing yet.
As far as I understand the pattern the main table stores edges and each edge can have properties (the data-attribute).
For example (taken from the shown tables):
Node 1 (PK 1) has an edge to Node 2 which is of type DATE, and the edge is of type BIRTH (SK DATE|2|BIRTH).
I guess this would somewhat be the same as ()-[:BIRTH]->(:DATE { id: 2 }) in Cipher, right?
But after this it becomes unclear how everything fits together.
For example:
Can the data attribute be a map?
Does the data attribute have to be written to two places on writes? E.g. under (1, DATE|2|BIRTH) and (2, DATE|2)?
If I want to add a new person that is born 1980-12-19, do I have to look up the corresponding node first?
How can I get all properties associated with a node? How to get all properties associated with an edge?
How can I query adjacent nodes?
...
Can someone explain to me how everything fits together by walking through a few use cases?
Thanks in advance.
Hopefully this answers all of your questions. Here's a couple of introductory things. I'll be using a generic table for all of my examples. The hash key is node_a and the sort key is node_b. There is a reverse lookup GSI where node_b is the hash key and node_a is the sort key.
1. Can the data attribute be a map?
The data attribute can be any of the supported data types in DynamoDB, including a map.
2. Does the data attribute have to be written to two places on writes?
The data attribute should be written to only one place. For the example of birthdate, you could do either one of these DynamoDB entries:
node_a | node_b | data
----------|-----------|---------------
user-1 | user-1 | {"birthdate":"2000-01-01", "firstname": "Bob", ...}
user-1 | birthdate | 2000-01-01
In the first row, we created an edge from the user-1 node that loops back on itself. In the second row, we created an edge from user-1 to birthdate. Either way is fine, and the best choice depends on how you will be accessing your data. If you need to be able to find users with a birthdate in a given range, then you should create a birthdate node. If you just need to look up a user's information from their user ID, then you can user either strategy, but the first row will usually be a more efficient use of your table's throughput.
3. If I want to add a new person that is born 1980-12-19, do I have to look up the corresponding node first?
No. Just insert one of the rows from the example above.
You only have to look up the node if there is a more complex access pattern, such as "update the name of the person who was born on 1980-12-19". In that case, you would need to look up by birthdate to get the person node, and then modify something related to the person node. However, that use case is really two different operations. You can rephrase that sentence as "Find the person who was born on 1980-12-19, and update the name", which makes the two operations more apparent.
4.(a) How can I get all properties associated with a node?
Suppose you want to find all the edges for "myNode". You would query the main table with the key condition expression of node_a="myNode" and query the reverse lookup GSI with the key condition expression of node_b="myNode". This is the equivalent of SELECT * FROM my_table WHERE node_a="myNode" OR node_b="myNode".
4.(b) How to get all properties associated with an edge?
All of the properties of an edge are stored directly in the attributes of the edge, but you may still run into a situation where you don't know exactly where the data is. For example:
node_a | node_b | data
----------|-----------|---------------
thing-1 | thing-2 | Is the data here?
thing-2 | thing-1 | Or here?
If you know the ordering of the edge nodes (ie. which node is node_a and node_b) then you need only one GetItem operation to retrieve the data. If you don't know which order the nodes are in, then you can use BatchGetItems to look up both of the rows in the table (only one of the rows should exist unless you're doing something particularly complex involving a directed graph).
5. How can I query adjacent nodes?
Adjacent nodes are simply two nodes that have an edge connecting them. You would use the same query as 4a, except that instead of being interested in the data attribute, you're interested in the IDs of the other nodes.
Some more examples
Using a graph pattern to model a simple social network
Using a graph pattern to model user-owned resources
How to model a circular relationship between actors and films in DynamoDB (answer uses a graph pattern)
Modeling many-to-many relationships in DynamoDB
From relational DB to single DynamoDB table: a step-by-step exploration. This is a killer piece. It's got an AWS re:Invent talk embedded in it, and the author of this blog post adds his own further explanation on top of it.
Let's imagine these 2 entities:
ShoppingCart
creationDate
Item
name
shoppingCart # ManyToOne
I'm managing a ShoppingCart in a form with a CollectionType of Items
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder
// ...
->add('items', CollectionType::class, array(
'entry_type' => ItemFormType::class,
// ...
}
I'd like my users to be able to add/remove items, which seems to me like a quite common need.
If I understand well, here is what I have to do:
Define the reverse OneToMany side of the ShoppingCart relation to Item
Make sure this relation have the cascade={"persist"} and orphanRemoval=true options
Initialize this items Collection into the constructor
Set the allow_add and allow_delete form option to true
Set the by_reference form option to false
Make sure andItem() & removeItem() functions are implemented into ShoppingCart
Set/unset the owning side relation to the ShoppingCart in addition to add/remove the Item from the ShoppingCart items collection
Keep the collection indexes within javascript so Doctrine can keep track of references
Am I forgetting something ?
I find it a big pain in the arse for me compared to the need and to other frameworks/ORMs
Is there a way to do it simpler ?
As I understand, your question is "why is it so complicated", isn't it?
Well, it's not. You've listed things multiple layers in your project. To exaggerate the point a little bit - why didn't you wrote that in order to create this form you need to install Symfony or even turn on your computer. ;-)
Anyway, most of these things are not required, others should be done before anyway.
Let's start with your OneToMany relation (Doctrine level).
ShoppingCart is your aggregate root (DDD term, but it doesn't matter if you follow DDD idea), which means you probably will do most of work on this object which will handle Items inside. If so, then you should have done inversed side of the relation, which covers these points (I think you went into details so much to make your list longer ;-), they all should be in one list item), but it takes a minute to do this stuff:
Define the reverse OneToMany side of the ShoppingCart relation to Item
Make sure this relation have the cascade={"persist"} and orphanRemoval=true options
Initialize this items Collection into the constructor
Make sure andItem() & removeItem() functions are implemented into ShoppingCart
Set/unset the owning side relation to the ShoppingCart in addition to add/remove the Item from the ShoppingCart items collection
This has nothing to do with forms. It's about how you want your entities to work with Doctrine.
Following two are not required:
Set the allow_add and allow_delete form option to true
Set the by_reference form option to false
They are about enabling/disabling features. You want them, so you need to use them. That's obvious and still not so much to do.
Last one:
Keep the collection indexes within javascript so Doctrine can keep track of references
This is the one that I actually don't understand. But what you need (in your case, because you're using allow_add/delete feature), is to provide JavaScript code that will create HTML for new row or delete existing one. But that's also just about copying prototype code. Also there are ready examples in Symfony's doc.
Let me answer myself in a simpler manner:
No I am not forgetting anything
No we can't do simpler
Some reasons are explained in #dragoste answer
I'm reviewing some code and I came across a line that does the following:
Person.find_by(name: "Tom").id
The above code gets the FIRST record with a name of "Tom", then builds a model, then gets the id of that model. Since we only want the id, the process of retreiving all data in the model and initializing the model is unneeded. What's the best way to optimize this using active record queries?
I'd like to avoid a raw sql solution. So far I have been able to come up with:
Person.where(name: "Tom").pluck(:id).first
This is faster in some situations since pluck doesn't build the actual model object and doesn't load all the data. However, pluck is going to build an array of records with name "Tom", whereas the original statement only ever returns a single object or nil - so this technique could potentially be worse depending on the where statement. I'd like to avoid the array creation and potential for having a very long list of ids returned from the server. I could add a limit(1) in the chain,
Person.where(name: "Tom").limit(1).pluck(:id).first
but is seems like I'm making this more complicated than it should be.
With Rails 6 you can use the new pick method:
Person.where(name: 'Tom').pick(:id)
This is a little verbose, but you can use select_value from the ActiveRecord connection like this:
Person.connection.select_value(Person.select(:id).where(name: 'Tom').limit(1))
This might work depending on what you're looking for.
Person.where(name: "Tom").minimum(:id)
Person.where(name: "Tom").maximum(:id)
These will sort by id value while the Person.where(name: "Tom").first.id will sort off of your default sort. Which could be id, created_at, or primary_key.
eitherway test and see if it works for you
How to add multiple node to relations here is my query
MATCH (n:Customer{name:"motoM"})-[:RECENT {default:TRUE}]-(l:Location{name:"Adugodi"}) return l how to write a query to add one more "location" node to the relation "recent" if location node is not found and setting default to true to newly created realtion
What about this?
MATCH (n:Customer{name:"motoM"})-[:RECENT {default:TRUE}]-(l:Location{name:"Adugodi"})
MERGE (n)-[:RECENT]->(l2:Location)
ON CREATE SET l2.default = true
RETURN l, l2
The direction needs to be specified so I made it up, but it might need to go the other way.
Well, I don't know if I understood what you were looking for, but this might help you :)
Try with this query:
MATCH (n:Customer{name:"motoM"})-[r:RECENT {default:TRUE}]-(:Location{name:"Adugodi"})
CREATE (l2:Location{name:"Wherever You need"})
With r,n,l,l2
Set r.default = false
With n,l2
CREATE (n)-[r2:RECENT{default:TRUE}]->(l2)
I'm using Withto make the query easier to read, but you can do it in a single query.
In fact, I think your problem is your Graph model.
You should probably do something like a Customer node, related to Location nodes with a "VISITED" relation, and when you create your VISITED relation, you set date property to timestamp. Then, when you get your relations, you can simply compare timestamps to get the closest one, and you know which one is the one your need. Also, if you need a default property, set it on the node, it'll be easier to match.
Tell me if you need a code example for match, create and set data with this graph model.
I'm using Ben Firshman's fork of django-MPTT (hat tip to Daniel Roseman for the recommendation).
I've got stuck trying to re-order nodes which share a common parent. I've got a list of primary keys, like this:
ids = [5, 9, 7, 3]
All of these nodes have a parent, say with primary key 1.
At present, these nodes are ordered [5, 3, 9, 7], how can I re-order them to [5, 9, 7, 3]?
I've tried something like this:
last_m = MyModel.get(pk = ids.pop(0))
last_m.move_to(last_m.parent, position='first-child')
for id in ids:
m = MyModel.get(pk = id)
m.move_to(last_m, position='right')
Which I'd expect to do what I want, per the docs on move_to, but it doesn't seem to change anything. Sometimes it seems to move the first item in ids to be the first child of its parent, sometimes it doesn't.
Am I right in my reading of the docs for move_to that calling move_to on a node n with position=right and a target which is a sibling of n will move n to immediately after the target?
It's possible I've screwed up my models table in trying to figure this out, so maybe the code above is actually right. It's also possible there's a much more elegant way of doing this (perhaps one that doesn't involve O(n) selects and O(n) updates).
Have I misunderstood something?
Bonus question: is there a way of forcing django-MPTT to rebuild lft and rght values for all instances of a given model?
I think this is an artefact of a failure in MPTT that I've mentioned before - when you move nodes around, it correctly updates the instance of the node you're moving, but it doesn't update the instance of the target (although it does get updated in the database).
The consequence of this is that in your code, each m gets moved to the right of last_m - but the values in the last_m still reflect the position before the move, so the next move uses the original lft/right values instead of the new post-move ones.
The solution is to reload last_m each time:
for id in ids:
last_m = MyModel.objects.get(pk=last_m.id)
m = MyModel.get(pk = id)
m.move_to(last_m, position='right')