Neo4j spatial cypher query withinDistance doesn

2019-02-18 23:26发布

问题:

I am using the spatial server plugin for Neo4j 2.0 and have followed the guide at http://neo4j.github.io/spatial/ to add a node with name Stockholm.

:POST http://localhost:7475/db/data/ext/SpatialPlugin/graphdb/addSimplePointLayer
{
  "layer" : "geom",
  "lat" : "lat",
  "lon" : "lon"
}
:POST http://localhost:7475/db/data/index/node/
{
  "name" : "geom",
  "config" : {
    "provider" : "spatial",
    "geometry_type" : "point",
    "lat" : "lat",
    "lon" : "lon"
  }
}
:POST http://localhost:7475/db/data/node
{
  "lat" : 60.1,
  "lon" : 15.2,
  "name" : "Stockholm"
}
:POST http://localhost:7475/db/data/ext/SpatialPlugin/graphdb/addNodeToLayer
{
  "layer" : "geom",
  "node" : "http://localhost:7475/db/data/node/4"
}

I am able to retrieve the node via REST with:

:POST http://localhost:7475/db/data/ext/SpatialPlugin/graphdb/findGeometriesWithinDistance
{
  "layer" : "geom",
  "pointX" : 15.0,
  "pointY" : 60.0,
  "distanceInKm" : 100
}

but not with the cypher query below. Why is that? Am I doing any obvious mistake here?

START n=node:geom('withinDistance:[60.0,15.0, 100.0]') RETURN n;

回答1:

In order to query using Cypher you'll need to add each node to an index:

    :POST http://localhost:7474/db/data/index/node/geom
    {
      'value': 'dummy', 
      'key': 'dummy', 
      'uri': 'http://localhost:7474/db/data/node/NODE_ID_HERE'
    }

I wrote a blog post about getting starting with Neo4j Spatial recently that covers this: http://lyonwj.com/mapping-the-worlds-airports-with-neo4j-spatial-and-openflights-part-1/



回答2:

I just discovered that you don't have to add the node to the index as described above, at least not for Neo4j 2.1.2 and Neo4j Spatial 0.13-neo4j-2.1.2.

After you create the node, add a property that sets as user "id" property to the Neo4j node id value. The cypher spatial query will now work. So, if you add the cypher command

start n=node(4) n.id = id(n)

the query will work.

There are clearly other ways to form the Cypher statement. In fact, you can do all this in a few bulk steps. You can add all your nodes to the RTree graph (using REST, Java, bulk shp file loader, etc), create self-referential user "id" properties on each node, then create the spatial index (the second REST command in the question post).

The problem you are seeing appears to come from a disconnect between the REST addNodeToLayer process and the Cypher "add node to index" process. The Cypher process creates a second node that contains only the geometry properties (lat/lon, wkt, etc) from the original node, and adds that node to the RTree graph. That node has a user property named "id" which has a value of the Neo4j node id of the original node. The REST (or Java) addNodeToLayer process directly adds the original node to the RTree graph and does not create a copy. It also does not set a user property named "id" on the node.

If you added the node to the RTree graph using the Cypher method, you will discover that the node returned to you by a REST query is the copy node, not the original one. When you do the same query using the Cypher method, you get the original node. The underlying code in the Cypher query returns the original node by using the user "id" property on the copy node found by the query to get the original node. When the node initially found by the Cypher query does not contain an id property that can be dereferenced, the Cypher query silently fails and you get 0 results.

By adding a self-referential id property to each data node in the RTree graph, the Cypher query is able to successfully find a node to return.

Using the REST method described in the previous answer works, but it ends up doubling the geometry storage cost because it makes a copy of the geometry information held on the original node. When using that method, the results returned by Cypher and REST queries are also different from each other. Using the method I described saves space and unifies the Cypher and REST query behaviors.