仅当neo4j中不存在节点时如何创建新节点
How to create new node only if node is not existed in neo4j
我正在使用 neo4j 和 vertx。
我正在使用 neo4j-jdbc。
我设法创建了用户,但我如何才能 "upgrade" 此查询仅在该用户不存在的情况下创建新用户(具有唯一索引)。
userId 应该是主键。
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;
}
谢谢,
雷.
使用MERGE
代替CREATE
:
"MERGE (n:User {name:{1}})"
[更新 1]
以下示例说明如何修改 addDistanceUserListByForUserId()
,使其 return 成为包含 created/existing User
属性的 Map<String, Object>
。
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;
}
[更新 2]
在您的特定情况下,由于您对 :User(name)
有唯一性约束,并且现有 User
节点可以具有 name
以外的属性 - 简单查询 MERGE (n:User {name:{1}})
可能会导致 ConstraintViolation
错误,如果存在具有相同 name
的现有 User
但还具有其他属性。
要解决这个问题,请尝试用这个更复杂的查询替换 MERGE
语句:
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;
下面是对这个查询的解释:
- 它使用
OPTIONAL MATCH
这样如果没有找到具有指定 name
的 User
,则不会跳过其余的查询(但 n
将NULL
).
- 然后它创建一个
todo
集合,它将告诉 FOREACH
子句它是否应该创建新的 User
节点。如果 todo
集合为空,FOREACH
子句不执行任何操作。
- 修饰子句(如
FOREACH
)和后续的MATCH
子句之间需要有一个WITH
子句。我们真的不需要向前传递任何东西,所以我们只使用 todo
因为我们已经有了它。
- 我们需要另一个
MATCH
查询,因为我们想要 return 找到或刚刚创建的节点。但是没有办法得到由 FOREACH
创建的节点,除非我们再做一个 MATCH
.
您应该使用 MERGE
而不是 CREATE
。如果它不存在,这将创建节点。如果节点已经存在 - 那么 MERGE
将像正常的 MATCH
操作一样。
重要提示: 您应该在 {}
属性语法中仅使用具有唯一约束的属性。 Neo4j 将合并 {}
中具有相同属性的节点。
如果在 合并 节点后要在节点 上设置其他数据,则:
MERGE (user:User {name: "John", age: 20})
- 错误的方式。因为数据库中没有这样的 name
和 age
属性 组合的节点,数据库将尝试创建该节点。这将导致 ConstraintViolation
因为这样的 name
已经存在。
正确版本:
MERGE (user:User {name: "John"})
SET user.age = 20
这个版本首先会搜索节点(如果需要的话会创建),然后才在那个节点上设置属性。
我正在使用 neo4j 和 vertx。
我正在使用 neo4j-jdbc。
我设法创建了用户,但我如何才能 "upgrade" 此查询仅在该用户不存在的情况下创建新用户(具有唯一索引)。 userId 应该是主键。
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;
}
谢谢, 雷.
使用MERGE
代替CREATE
:
"MERGE (n:User {name:{1}})"
[更新 1]
以下示例说明如何修改 addDistanceUserListByForUserId()
,使其 return 成为包含 created/existing User
属性的 Map<String, Object>
。
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;
}
[更新 2]
在您的特定情况下,由于您对 :User(name)
有唯一性约束,并且现有 User
节点可以具有 name
以外的属性 - 简单查询 MERGE (n:User {name:{1}})
可能会导致 ConstraintViolation
错误,如果存在具有相同 name
的现有 User
但还具有其他属性。
要解决这个问题,请尝试用这个更复杂的查询替换 MERGE
语句:
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;
下面是对这个查询的解释:
- 它使用
OPTIONAL MATCH
这样如果没有找到具有指定name
的User
,则不会跳过其余的查询(但n
将NULL
). - 然后它创建一个
todo
集合,它将告诉FOREACH
子句它是否应该创建新的User
节点。如果todo
集合为空,FOREACH
子句不执行任何操作。 - 修饰子句(如
FOREACH
)和后续的MATCH
子句之间需要有一个WITH
子句。我们真的不需要向前传递任何东西,所以我们只使用todo
因为我们已经有了它。 - 我们需要另一个
MATCH
查询,因为我们想要 return 找到或刚刚创建的节点。但是没有办法得到由FOREACH
创建的节点,除非我们再做一个MATCH
.
您应该使用 MERGE
而不是 CREATE
。如果它不存在,这将创建节点。如果节点已经存在 - 那么 MERGE
将像正常的 MATCH
操作一样。
重要提示: 您应该在 {}
属性语法中仅使用具有唯一约束的属性。 Neo4j 将合并 {}
中具有相同属性的节点。
如果在 合并 节点后要在节点 上设置其他数据,则:
MERGE (user:User {name: "John", age: 20})
- 错误的方式。因为数据库中没有这样的 name
和 age
属性 组合的节点,数据库将尝试创建该节点。这将导致 ConstraintViolation
因为这样的 name
已经存在。
正确版本:
MERGE (user:User {name: "John"})
SET user.age = 20
这个版本首先会搜索节点(如果需要的话会创建),然后才在那个节点上设置属性。