Reputation: 21596
I am using neo4j and vertx.
I am using neo4j-jdbc.
I managed to create user but how I can "upgrade" this query to create new users(with unique Index) only if that user isnt existed. The userId should be the primary key.
private final Logger logger = Logger.getLogger(Neo4jRepo.class);
private Connection conn = null;
public Neo4jRepo() {
logger.debug("Neo4jRepo, Init");
try {
Class.forName("org.neo4j.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:neo4j://localhost:7474/");
} catch (SQLException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
..
public boolean addDistanceUserListByForUserId(String userId) {
try {
final PreparedStatement ps = conn.prepareStatement( "create (n:User {name:{1}})" );
ps.setString( 1, "userId" );
ps.execute();
} catch (SQLException e) {
throw new RuntimeException(e);
}
return true;
}
Thank you, ray.
Upvotes: 2
Views: 2251
Reputation: 66999
Use MERGE
instead of CREATE
:
"MERGE (n:User {name:{1}})"
[UPDATE 1]
The following is an example of how to modify addDistanceUserListByForUserId()
so that it returns a Map<String, Object>
containing the properties of the created/existing User
.
public Map<String, Object> addDistanceUserListByForUserId(String userId) {
try {
final PreparedStatement ps = conn.prepareStatement(
"MERGE (n:User {name:{1}}) RETURN n" );
ps.setString( 1, "userId" );
ResultSet rs = ps.executeQuery();
if (rs.next()) {
return (Map<String, Object>) rs.getObject("n");
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
return null;
}
[UPDATE 2]
In your specific case, since you have a uniqueness constraint on :User(name)
, and existing User
nodes can have properties other than name
-- the simple query MERGE (n:User {name:{1}})
can cause a ConstraintViolation
error if an existing User
with the same name
exists but also has other properties.
To get around that, try replacing the MERGE
statement with this more complex query:
OPTIONAL MATCH (n:User { name:{1} })
WITH (CASE WHEN n IS NULL THEN [1] ELSE [] END ) AS todo
FOREACH (x IN todo | CREATE (:User { name:{1} }))
WITH todo
MATCH (n:User { name:{1} })
RETURN n;
Here is an explanation of this query:
OPTIONAL MATCH
so that if the User
with the specified name
is not found, the rest of the query is not skipped (but n
will be NULL
).todo
collection that will tell the FOREACH
clause whether it should create the new User
node. The FOREACH
clause does nothing if the todo
collection is empty.WITH
clause between a modifying clause (like FOREACH
) and a subsequent MATCH
clause. We don't really need to pass anything forward, so we just use todo
since we already have it handy.MATCH
query because we want to return the node that was either found, or that we just created. But there is no way to get the node that was created by the FOREACH
unless we do another MATCH
.Upvotes: 3
Reputation: 1997
You should use MERGE
instead of CREATE
. This will create node if it doesn't exists. If node already exists - then MERGE
will act like normal MATCH
operation.
Important: you should use only properties with unique constraint in {}
properties syntax. Neo4j will merge nodes which have same properties in {}
.
If you have additional data to be set on node after merge, then:
MERGE (user:User {name: "John", age: 20})
- WRONG way. Because there are no node in database with such name
and age
property combination and database will try to create that node. And this will result in ConstraintViolation
because such name
already exists.
Correct version:
MERGE (user:User {name: "John"})
SET user.age = 20
This version will search node (and create if needed) at first, and only then then set property on that node.
Upvotes: 4