Is there way to set a transient property on nodes returned by a cypher query such that it is only visible to the user running the query.
This would allow us offload some controller logic directly into Neo4j and reduce business logic queries.
Currently I have a list that is returned by
List<Post> newsFeed (Long uid) {}
Post is a relationship between a User and News node.
I have two sub-classes of the Post object:
BroadcastedPost
MentionedPost
I have two cypher queries that return the posts that a user should see.
List broadcasts obtained from
MATCH (user:PlatformUser)-[:BROADCASTED]->post RETURN post;
List mentionedPost obtained from
MATCH (user:PlatformUser)-[:MENTIONED]->post RETURN post;
I then use Java instanceof to determine what kind of post this is. Depending on the type I am able to do some further application logic.
This however is inefficient because I should be able to combine both queries into one super query using the UNION operator
i.e List newsFeed is obtained directly by querying
MATCH (user:PlatformUser)-[:BROADCASTED]->post RETURN post UNION MATCH (user:PlatformUser)-[:MENTIONED]->post RETURN post;
However, how can I tell what kind of post this. I was hoping I could use the SET operator transiently to know which kind of post this is but I believe this is used to persist a property.
Neo4j 2.2 recently added authentication, which it had lacked in previous releases, but it's still only really one user; you set a login/password to secure access to the database, but adding additional users takes extra work and isn't something obvious to do out of the box.
Now what you're asking for has to do with securing per-user access to particular types of data. Since neo4j doesn't have much of a user management feature right now, what you're asking for can't be done inside of neo4j because in order to secure this data away from Joe or Bob, the DBMS would have to know that it's dealing with Joe or Bob.
What you're trying to do is usually enforced by the application layer by people writing neo4j applications right now. So it can be done, but it's done within your custom code and not by the database directly.
Related
In our company, we return a list of IDs to clients through a web service. The IDs are unique across the system. They invoke other web services passing the IDs. We don't always know the label of the ID we receive.
This does not perform:
MATCH(n {id:{my_id}) ...
While we have indexes on almost all label types, this query has no label and as thus does not use an index as far as I can tell.
Is it a bad idea to add a label called "GLOBAL" (or whatever) to all nodes so we can put a unique constraint on GLOBAL.id? Then the query above could be
MATCH(n: GLOBAL{id:{my_id}})...
and perform nicely.
Is there another way?
You can use Neo4j's internal ID to identify your resources, but it's not best practice, see Should we use the Neo4J internal id?
This is how to get a node using his neo4j's internal id:
START n=node({my_id}) return n
It's really faster than a MATCH clause, because here your query directly starts with one node, and doesn't have to filter a property accross a set of nodes, because it's internal id.
If you can handle the internal id limitations, it's the solution you are looking for.
I'd like to know just what the title says.
The reason I'd want this is to permit constrained read-only cypher queries to be executed; the data results would later be interpreted and serialized by a separate API layer.
I've seen code that makes basic assumptions in an attempt to mimic this behavior, e.g. the code might filter out any Cypher query that contains certain special words associated with write query structures (merge, create, delete, set, and so on).
This approach tends to be limited and naive though; if it very simply looks for those tokens, it would prevent a query like MATCH n WHERE n.label =~ '.*create.*' RETURN n even though it's a read-only query.
I'd really prefer not to do a full parse on a candidate query and then descend through the AST trying to figure out whether something is read-only or not (although I would gladly accept an answer that shows how to do this easily in java)
EDIT - I'm aware it's possible to start the entire database in read-only mode via the configuration property read_only=true, but this would be undesirable; no other aspect of the java API would be able to change the database.
EDIT 2 - I found another possible strategy, but I'm not sure of its advisability. Comments welcome on this, and potential downsides:
try (Transaction ignore = graphDb.beginTx()) {
ExecutionResult result = executionEngine.execute(query);
// Do nifty stuff with result, then...
// Force transaction to fail.
ignore.failure();
}
The idea here is that if queries happen within transactions and the transaction is always force-failed, then nothing can ever be written to the DB no matter what the result.
Read-only Cypher is (not yet) directly supported. However I can think of two workarounds for that:
1) assuming you're running a Neo4j enterprise cluster: you can set read_only=true on one instance. That instance is then used for the read only queries where the other cluster instances are used for r/w. A load balancer in front of the cluster can be set up to send the requests to the right instance.
2) Use a TransactionEventHandler that vetos a transaction if its TransactionData contains write operations. Just for fun I've invested some minutes to implement that, see https://github.com/sarmbruster/read-only-cypher - feedback is appreciated.
seems I am too tired to find the solution, maybe someone has a hint for me.
I have built a graph in Neo4J which I connect via neo4jphp (everyman). Using the java browser the graph looks ok, every user exists one time and might have several groups he belongs to.
While creating the user I use MERGE in order to avoid them to be doubled - like
MERGE (user:PERSON {
firstname: "Max",
name: "Muster",
password:"e52ddddddd9afb7b373f9da437",
title:"something",
login:"Nick",
status:"active"
})
ON CREATE SET user.uuid = "'.uniqid().'" // PHP function for a UUID
return user;
This works well as I see the correct number of users even when I resend the query or reload the page.
The users are connected with a query towards groups like this
MATCH (user:PERSON), (team:GROUP)
WHERE user.name= "Muster" AND user.firstname="Max" AND team.name="LOCAL_USER"
CREATE (user)-[:IS_MEMBER_OF {role:"user", status:"active"}]->(team);
Checking this in the GUI of Neo4J shows a correct graph (at least from what I can see). I have the right amount of users and their relations.
When I query the graph directly by Cypher in the browser GUI like this
MATCH (user:PERSON {status: "active"})-[relation:IS_MEMBER_OF{status:"active"}]->(team:GROUP {name:"LOCAL_USER"} )
RETURN user
ORDER BY user.name;
I get the correct number of users.
When I use the neo4jphp lib (everyman) I receive some users double - the resultset has several elements with the same user. I couldnt figure out why they behave differently but I assume that I might have messed up the relations somehow. But still I am wondering why the same cypher query returns different amount of records when you send it via GUI or via everyman lib and I would need a hint how to change maybe the queries to make sure that I only get one record per user as every user is only one time connected to the LOCAL_USER group.
Thanks for pushing me to the right direction.
I think you get doubles because you have multiple paths (e.g. multiple teams) for the user,
use RETURN distinct user
For your import statement, you do it the wrong way round, instead of your approach.
MERGE by unique id (e.g login in your case) and set the other properties with ON CREATE SET ...
Also use parameters not literal values in your query strings !!
MERGE (user:PERSON {login:{login}})
ON CREATE SET
user.firstname = {firstname}, user.name= {name}, user.password = {password},
user.title={title}, user.status = {status}, user.uuid = {uuid}
RETURN user;
I'm trying to wrap my head around how Neo4j works and how I can apply it to my problem. I thought it should be really easy and a matter of minutes, but I'm stuck.
I have data in MongoDB, say User and Item. What I want is connecting User and Item in a graph with a LIKE relationship (maybe with a score). Later I want to do things like recommending items based on connections, basic stuff.
But how do I get the data into Neo4j? Every document in MongoDB has an unique _id, so I though I could just throw both _ids into Neo4j and have them connected. What I found so far is that it's not even possible to have unique nodes based on the _id field (Neo4j has numeric incremented ids), which is only possible with some "hack" (https://github.com/jexp/app-net-graph/blob/master/lib/appnet.rb#L11) or using MERGE (I'm stuck on < 2.0). Even their examples on the website add the same node again if executed multiple times. I think I have a fundamental misunderstanding of how to use Neo4j. Maybe I'm too spoiled by redis, where I can put strings in and and it just works. Redis' sets aren't feasible though for complex graphs, only for simple connections.
Maybe someone can help me with a simple cypher example of how to add two nodes foo and bar and have them connected with a LIKE connection. And the operation should be idempotent, no matter if none or all of the nodes/relationships already existed before execution.
I'm accessing Neo4j via REST, in particular using this node module https://github.com/thingdom/node-neo4j
You could define your external ID as extra property on your nodes. Then depending on if your are using SpringData or not, you can insert the data.
If you are using SpringData, you can configure your external ID as unique index and then normally save you nodes(consider though, that inserting a duplicated ID will overwrite the existing one).
If you are using the plain java API, you can create unique nodes as described here:
http://docs.neo4j.org/chunked/stable/tutorials-java-embedded-unique-nodes.html#tutorials-java-embedded-unique-get-or-create
EDIT:
As for a sample query, does this help you?
http://console.neo4j.org/?id=b0z486
With the java api you would do it like this
firstNode = graphDb.createNode();
firstNode.setProperty( "externalID", "1" );
firstNode.setProperty( "name", "foo" );
secondNode = graphDb.createNode();
secondNode.setProperty( "externalID", "2" );
secondNode.setProperty( "name", "bar" );
relationship = firstNode.createRelationshipTo( secondNode, RelTypes.Likes );
I suggest you read some tutorials here: http://docs.neo4j.org/chunked/stable/tutorials-java-embedded-hello-world.html
Given you are using Neo4J1.9, have you tried creating a unique index on your _ID column?
Try this article from the docs
If you were using Neo4j2, then this article is helpful
I am using the Neo4j .NET Client ExecuteGetCypherResults to run cypher. It expects everything to come back in a single column. I have simple class JobType which contains a list of JobSpecialties on it. In the database this is modeled as the Types having a relationship to the Specialties.
I need a cypher query that returns the results as such, in a single column. The related Specialties should be a child property of the Type node I would expect the query to look like this:
start s=node:node_auto_index(StartType='JobTypes')
match s-[:starts]->t, t-[:SubTypes]->ts
return {Id: t.Id, Name: t.Name, JobSpecialties: ts}
But this doesn't work. I can't figure out from the docs if this is even possible. If there is a better way to get the result back to the .Net client, I am open to suggestions.
start s=node:node_auto_index(StartType='JobTypes')
match s-[:SubTypes]->js
return s.Id, s.Name, js;
ExecuteGetCypherResults does support multiple columns, you just need to kick our deserializer into a different mode. This is an implementation detail generally hidden behind our higher level APIs, which is why this isn't obvious.
When you call new CypherQuery, pass CypherResultMode.Projection instead of CypherResultMode.Set.
I actually can't remember why we have this. Sometime, I'll need to dig through the lower levels and try and kill it. Pull requests welcomed. :)
As a preference though, we always prefer people to use the higher level APIs (but we recognise there are some limitations).
It sounds like the .Net client needs some updating for cypher. Cypher doesn't support building maps on the fly yet, although it is something that is in the feature request list already...
You can create an array with your results (but as of 1.9.M04, they need to be the same type to be merged into the array):
http://console.neo4j.org/r/xo7voi
I've actually submitted a pull request (through back channels, since it broke some unit tests) to fix that (so you can have multiple types in an array built on the fly), but I think there are some concerns whether merging of different types is a good idea.
https://github.com/wfreeman/neo4j/commit/ca457ace0df4732376833b8694e4affac4143244
Update: This will be fixed in 1.9.M05/1.9.GA. Now you can build an array with any type mixed:
http://console.neo4j.org/r/vm4f83