无法从 Neo4j 空间索引中删除节点
Unable to remove a node from Neo4j Spatial index
在我的 TransactionEventHandler.beforeCommit()
中,我试图在成功添加节点后从空间索引中删除该节点。但该节点仍保留在索引中,我仍然能够使用空间 Cypher 查询找到它。
这是我的代码的摘录:
Index<Node> index = getGraphDatabaseService().index().forNodes("locations", SpatialIndexProvider.SIMPLE_POINT_CONFIG);
if (node.hasProperty("lat") && node.hasProperty("lon")) {
index.add(node, null, null); // it works perfectly
} else {
index.remove(node); // it doesn't work
}
这是 Neo4j Spatial 中的已知错误吗?无论如何,我怎样才能实现我的 objective?
PS:我使用 Neo4j 2.3.2 和 Neo4j Spatial 0.15-neo4j-2.3.1。
我找到了解决方案(解决方法):
William Lyon 阐明了一些情况:
When a node is added to the spatial index using the spatial
IndexProvider interface it creates a proxy node and adds that node to
the spatial index, keeping the original node separate from the
in-graph RTree index.
我发现代理节点总是包含一个"id" 属性。它指向原始节点。我们甚至不需要手动添加它(正如 William 所建议的)。使用它我们可以找到代理节点以便手动删除它。
有时我们的图表可能是这样的:
有时它可能会变得有点复杂:
图片上:
- 标记为“1”的空间根(带有"ReferenceNode"标签的节点)
- 选择代理节点
因此,我们可以使用以下 Cypher 查询来查找和删除代理节点:
MATCH (:ReferenceNode)-[:LAYER]-()-[:RTREE_ROOT]-()-[*..]-(n {id:{id}}) MATCH (n)-[r:RTREE_REFERENCE]-() DELETE r, n
这是我目前在 TransactionEventHandler
:
中使用的完整解决方案
private static final String INDEX_NAME = "locations";
private static final Map<String, String> CONFIG = SpatialIndexProvider.SIMPLE_POINT_CONFIG;
private static final String LAT = CONFIG.get(LayerNodeIndex.LAT_PROPERTY_KEY);
private static final String LON = CONFIG.get(LayerNodeIndex.LON_PROPERTY_KEY);
@Override
public Void beforeCommit(TransactionData data) throws Exception {
Index<Node> index = getGraphDatabaseService().index().forNodes(INDEX_NAME, CONFIG);
Node originalNode = <...>;
if (originalNode.hasProperty(LAT) && originalNode.hasProperty(LON)) {
index.add(originalNode, null, null);
} else {
deleteProxyNode(originalNode.getId());
}
return null;
}
private void deleteIndexedProxyNode(long originalNodeId) {
String query = "" +
"MATCH (:ReferenceNode)-[:LAYER]-()-[:RTREE_ROOT]-()-[*..]-(n {id:{id}}) " +
"MATCH (n)-[r:RTREE_REFERENCE]-() " +
"DELETE r, n";
Map<String, Object> parameters = new HashMap<>();
parameters.put("id", originalNodeId);
getGraphDatabaseService().execute(query, parameters);
}
当使用空间 IndexProvider 接口将节点添加到空间索引时,它会创建一个 proxy 节点并将 that 节点添加到空间索引,使原始节点与图中 RTree 索引分开。
这与空间库的其他界面不一致,造成了一些混乱。有关详细信息,请参阅 this SO post。
您可以将一个 id 属性 添加到您正在索引的节点,以便在您要从索引中删除它时能够找到代理节点,或者使用空间 Java API 到空间层中的 add/remove 个节点,以避免创建代理节点。
在我的 TransactionEventHandler.beforeCommit()
中,我试图在成功添加节点后从空间索引中删除该节点。但该节点仍保留在索引中,我仍然能够使用空间 Cypher 查询找到它。
这是我的代码的摘录:
Index<Node> index = getGraphDatabaseService().index().forNodes("locations", SpatialIndexProvider.SIMPLE_POINT_CONFIG);
if (node.hasProperty("lat") && node.hasProperty("lon")) {
index.add(node, null, null); // it works perfectly
} else {
index.remove(node); // it doesn't work
}
这是 Neo4j Spatial 中的已知错误吗?无论如何,我怎样才能实现我的 objective?
PS:我使用 Neo4j 2.3.2 和 Neo4j Spatial 0.15-neo4j-2.3.1。
我找到了解决方案(解决方法):
William Lyon 阐明了一些情况:
When a node is added to the spatial index using the spatial IndexProvider interface it creates a proxy node and adds that node to the spatial index, keeping the original node separate from the in-graph RTree index.
我发现代理节点总是包含一个"id" 属性。它指向原始节点。我们甚至不需要手动添加它(正如 William 所建议的)。使用它我们可以找到代理节点以便手动删除它。
有时我们的图表可能是这样的:
有时它可能会变得有点复杂:
图片上:
- 标记为“1”的空间根(带有"ReferenceNode"标签的节点)
- 选择代理节点
因此,我们可以使用以下 Cypher 查询来查找和删除代理节点:
MATCH (:ReferenceNode)-[:LAYER]-()-[:RTREE_ROOT]-()-[*..]-(n {id:{id}}) MATCH (n)-[r:RTREE_REFERENCE]-() DELETE r, n
这是我目前在 TransactionEventHandler
:
private static final String INDEX_NAME = "locations";
private static final Map<String, String> CONFIG = SpatialIndexProvider.SIMPLE_POINT_CONFIG;
private static final String LAT = CONFIG.get(LayerNodeIndex.LAT_PROPERTY_KEY);
private static final String LON = CONFIG.get(LayerNodeIndex.LON_PROPERTY_KEY);
@Override
public Void beforeCommit(TransactionData data) throws Exception {
Index<Node> index = getGraphDatabaseService().index().forNodes(INDEX_NAME, CONFIG);
Node originalNode = <...>;
if (originalNode.hasProperty(LAT) && originalNode.hasProperty(LON)) {
index.add(originalNode, null, null);
} else {
deleteProxyNode(originalNode.getId());
}
return null;
}
private void deleteIndexedProxyNode(long originalNodeId) {
String query = "" +
"MATCH (:ReferenceNode)-[:LAYER]-()-[:RTREE_ROOT]-()-[*..]-(n {id:{id}}) " +
"MATCH (n)-[r:RTREE_REFERENCE]-() " +
"DELETE r, n";
Map<String, Object> parameters = new HashMap<>();
parameters.put("id", originalNodeId);
getGraphDatabaseService().execute(query, parameters);
}
当使用空间 IndexProvider 接口将节点添加到空间索引时,它会创建一个 proxy 节点并将 that 节点添加到空间索引,使原始节点与图中 RTree 索引分开。
这与空间库的其他界面不一致,造成了一些混乱。有关详细信息,请参阅 this SO post。
您可以将一个 id 属性 添加到您正在索引的节点,以便在您要从索引中删除它时能够找到代理节点,或者使用空间 Java API 到空间层中的 add/remove 个节点,以避免创建代理节点。