Neo4j - Cypher read-write-return query - python

I'm fairly new to neo4j. I've played a little bit with cypher and REST API. I want to be able to create a leaf node along certain path, consider these nodes are some types of events. I wouldn't know during run time the id of the node this event will be attached to. I need to either do a look-up and then get the id of the node and then create my new node.
So during run time I was hoping I can do a MATCH using cypher to get the node to which I can attach the event and CREATE new node along with the relationship to the existing node returned by MATCH. So I came across the cypher cheat sheet which has a read-write-return query which I thought would be good fit. But there is nothing much mentioned about it in documentation or may be I'm not a super googler!!
Could someone please tell me if this(read-write-return) is the right/valid approach?
Many Thanks!

Yep. That's a good approach. That's one of the nice things about how CREATE works in Cypher. You can also optionally use create unique which creates the rel/node at the same time. Something like:
start n=node(1)
create unique n-[:event]->(event {prop:"val"})
return n, event;
Or without create unique:
start n=node(1)
create (event {prop:"val"}), n-[:event]->event
return n, event;

Related

Add Label to all nodes in arbitrary cipher query

I am writing a web app with a single neo4j database as the datastore. Different users have their own separate charts. I have checked other documentation and found that neo4j doesn't have a concept of schema and instead it is suggested that labels are used against every node to indicate which graph it belongs to.
I am writing a middleware for the web app in Python which deals with user logins and sends queries onwards to the database. I am building a webservice in this middleware which accepts a cipher query string and returns the query result. Before it forwards the query string on to the database it needs to alter it, adding a label depending on the logged in user.
I need to build a function as follows:
def query_for_user(origional_query, users_label):
return XXX
My first idea on how to do this is to use string processing based on information here: https://neo4j.com/docs/getting-started/current/cypher-intro/patterns/
Every node in a query is surrounded by brackets so I could replace every part of the query inside brackets with a new version. The brackets always seem to start with a node identifier so I could read past this and add the required label. Seems like it would work but would be error prone and brittle because I don't know all the features of cpher.
Does anyone know of a more elegant way of achieving my goal? Is there a cypher parser somewhere that I could use instead of string manipulation?
Neo4j labels are not meant to be used to separate nodes into different "graphs".
A label is meant to be used as a node "type". For example, "Person" would be an example of a typical label, but having a huge number of labels like "User049274658188" or "Graph93458428834" would not be good practice.
As appropriate, nodes should have a "type"-like label, and can include a property (say, id) whose value is a unique identifier. Your queries can then start by matching a specific label/id combination in order to indicate which "graph" you want to work on. All other nodes in that "graph" would be linked to that start node via relationship paths.
And typically one would generate Cypher from raw data, not Cypher templates. However, if you really want to use Cypher templating, the templates could just contain Cypher parameters, as in:
MATCH (p:Person)-[:LIVES_AT]->(a:Address) WHERE p.id = $personId RETURN p, a;
Your client code will just need to provide the parameter values when invoking the query.

DELETE and CREATE Cypher statements in one transaction

I'm writing a python 3.6 application that uses Neo4j as a backend with the official python driver. I'm new to Neo4j and Cypher. Data coming into the database needs to replace previous 'versions' of that data. I'm attempting to do this by creating a root node, indicating version. Eg.
MATCH (root:root_node)-[*..]->(any_node:) DETACH DELETE root, any_node
CREATE(root:new_root_node)
...
...
... represents all the new data i'm attaching to the new_root_node
The above doesn't work. How do I incorporate DELETE and CREATE statements in one transaction?
Thanks!
There's not a problem with DELETE and CREATE statements in a single transaction.
There are two problems here needing fixing.
The first is with (anynode:). The : separates the variable from the node label. If : is present, then the node label must also be present, and because no label is supplied here you're getting an error. To fix this, remove : completely like so: (anynode)
The second problem is with CREATE(root:new_root_node). The problem here is that the root variable is already in scope from your previous MATCH, so you'll need to use a different variable.
Also, your :new_root_node label doesn't seem useful, as any previously created queries for querying data from the root node will need to change to use the new label. I have a feeling you might be misunderstanding something about Neo4j labels, so a quick review of the relevant documentation may be helpful.

Created Asana tasks via Python API in reverse order

I have a python script which imports a csv file into an Asana project, one task per row.
It works fine, except that it seems to create tasks in the opposite order from the web interface - ie: online, you create new tasks at the end of the list, but the API appears to create new tasks at the top.
Since the csv's are annoyingly structured (ie: sorted by presentation not by due date, broken into sections which I'm parsing at runtime, etc) the order does actually matter more than it ideally should.
So: is there an easy way to reverse the order so the API appends tasks at the end rather than inserting them at the top? Right now, I'm just reversing the csv when I import it, and it works okay, but it's ugly and inefficient and I wonder if I'm not missing something incredibly obvious :)
Any suggestions?
Tasks added to a project in the UI via the omni button, multi-homing, etc will also appear at the top of the project unless you are explicitly in the project its self, focused on the center pane task list and press "Enter" to add the new task. A bit confusing, I agree.
As for task ordering in the API, you are correct that new tasks added to a project will appear at the top of the project. You can alter the position of a task by calling POST /tasks/task-id/addProject and providing insert_before, insert_after, or section parameters with the ID of the task or section to move this task next to.
With the Asana python client the call looks like this:
`client.tasks.add_project(<TASK_ID>, { 'project' : <PROJECT_ID>, 'insert_after' : <PRECEDING_TASK> })`
You can also specify the memberships property of a task when creating in order to dictate the section a task should appear in.
To be honest, your current solution is probably the best, in the future we may allow you to specify the neighboring task upon creation. In that case you could specify the ID from each response in the insert_after of each create to get reverse order. I will add that to our list of requests.

Reset Index in neo4j using Python

Is there a possibility to reset the indices once I deleted the nodes just as if deleted the whole folder manually?
I am deleting the whole database with node.delete() and relation.delete() and just want the indices to start at 1 again and not where I had actually stopped...
I assume you are referring to the node and relationship IDs rather than the indexes?
Quick answer: You cannot explicitly force the counter to reset.
Slightly longer answer: Generally speaking, these IDs should not carry any relevance within your application. There have been a number of discussions about this within the Neo4j mailing list and Stack Overflow as the ID is an internal artifact and should not be used like a primary key. It's purpose is more akin to an in-memory address and if you require unique identifiers, you are better off considering something like a UUID.
You can stop your database, delete all the files in the database folder, and start it again.
This way, the ID generation will start back from 1.
This procedure completely wipes your data, so handle with care.
Now you certainly can do this using Python.
see https://stackoverflow.com/a/23310320

Select list of Primary Key objects in SQLAlchemy

First off, this is my first project using SQLAlchemy, so I'm still fairly new.
I am making a system to work with GTFS data. I have a back end that seems to be able to query the data quite efficiently.
What I am trying to do though is allow for the GTFS files to update the database with new data. The problem that I am hitting is pretty obvious, if the data I'm trying to insert is already in the database, we have a conflict on the uniqueness of the primary keys.
For Efficiency reasons, I decided to use the following code for insertions, where model is the model object I would like to insert the data into, and data is a precomputed, cleaned list of dictionaries to insert.
for chunk in [data[i:i+chunk_size] for i in xrange(0, len(data), chunk_size)]:
engine.execute(model.__table__.insert(),chunk)
There are two solutions that come to mind.
I find a way to do the insert, such that if there is a collision, we don't care, and don't fail. I believe that the code above is using the TableClause, so I checked there first, hoping to find a suitable replacement, or flag, with no luck.
Before we perform the cleaning of the data, we get the list of primary key values, and if a given element matches on the primary keys, we skip cleaning and inserting the value. I found that I was able to get the PrimaryKeyConstraint from Table.primary_key, but I can't seem to get the Columns out, or find a way to query for only specific columns (in my case, the Primary Keys).
Either should be sufficient, if I can find a way to do it.
After looking into both of these for the last few hours, I can't seem to find either. I was hoping that someone might have done this previously, and point me in the right direction.
Thanks in advance for your help!
Update 1: There is a 3rd option I failed to mention above. That is to purge all the data from the database, and reinsert it. I would prefer not to do this, as even with small GTFS files, there are easily hundreds of thousands of elements to insert, and this seems to take about half an hour to perform, which means if this makes it to production, lots of downtime for updates.
With SQLAlchemy, you simply create a new instance of the model class, and merge it into the current session. SQLAlchemy will detect if it already knows about this object (from cache or the database) and will add a new row to the database if needed.
newentry = model(chunk)
session.merge(newentry)
Also see this question for context: Fastest way to insert object if it doesn't exist with SQLAlchemy

Categories

Resources