Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using with Neo4j 3.x #17

Open
pmackay opened this issue Feb 8, 2017 · 8 comments
Open

Using with Neo4j 3.x #17

pmackay opened this issue Feb 8, 2017 · 8 comments

Comments

@pmackay
Copy link

pmackay commented Feb 8, 2017

Please could I clarify, is there a configuration of neo4j, neo4j-spatial, neo4jrb and neo4jrb_spatial that works for Neo4J 3.x? This issue suggests there isnt because indexes were changes for spatial procedures. Would #14 resolve this compatibility problem?

@ProGM would appreciate your input.

@ProGM
Copy link
Member

ProGM commented Feb 8, 2017

Unfortunately this repo does not work with new procedures.
However I had written a minimal set of methods to monkey-patch neo4jrb_spatial so it could work with neo4j 3.x.

Here's it:

First of all, install neo4j_spatial like this:

gem 'neo4jrb_spatial', github: 'neo4jrb/neo4jrb_spatial', branch: 'neo4j-8.x'

Create a neo4j3_spatial.rb file in your "app/models/concern"

module Neo4j3Spatial
  extend ActiveSupport::Concern
  module ClassMethods
    def create_index!(type: 'SimplePoint')
      return if layer?(spatial_index_name)
      _query.call('spatial.addLayer({name}, {type}, "lon:lat")')
            .params(name: spatial_index_name, type: type).to_a
    end

    def layer?(name)
      _query.call('spatial.layers() YIELD name, signature')
            .with(:name).where(name: name).return(:name).any?
    end

    private

    def _query
      Neo4j::ActiveBase.new_query
    end
  end

  included do
    scope :within_distance, lambda { |options|
      Neo4j::ActiveBase
        .new_query
        .call('spatial.withinDistance({layer}, {coordinate}, {distance}) YIELD node, distance')
        .params(layer: spatial_index_name,
                coordinate: { lon: options[:lon], lat: options[:lat] },
                distance: options[:distance])
        .with(:node).where("(node:#{mapped_label_name})")
        .pluck(:node)
    }

    scope :bbox, lambda { |box|
      Neo4j::ActiveBase
        .new_query
        .call('spatial.bbox({layer}, {x1}, {x2}) YIELD node')
        .params(layer: spatial_index_name, x1: { lon: box[0], lat: box[1] }, x2: { lon: box[2], lat: box[3] })
        .with(:node).pluck(:node)
    }
    create_index!
  end

  delegate :layer?, to: :class

  def add_to_spatial_index
    return if !lat? || !lon? || !layer?(self.class.spatial_index_name)
    Neo4j::ActiveBase
      .new_query
      .match_nodes(n: neo_id)
      .with(:n).call('spatial.addNode({layer}, n) YIELD node')
      .params(layer: self.class.spatial_index_name).pluck(:n)
  end
end

Here's a sample usage:

class YourModel
  include Neo4j::ActiveNode
  include Neo4j::ActiveNode::Spatial
  spatial_index 'your_index'
  include Neo4j3Spatial
end

YourModel.within_distance(...)
ecc...

@pmackay
Copy link
Author

pmackay commented Feb 9, 2017

@ProGM hi, really appreciate your answer. I'm testing this out but getting Neo.ClientError.Schema.IndexNotFound: Index places does not exist (places being my index name). What is the right way to setup an index now (or layer, but am assuming the index terminology is still being used here)?

@ProGM
Copy link
Member

ProGM commented Feb 9, 2017

I can't really test right now, but the index creation should be provided when including the module. (check the last line of the "included" block)

You can manually trigger it by using YourModel.create_index!.

Don't forgot to call node_instance. add_to_spatial_index after creating a new node.

Let me know if this helps :)

@pmackay
Copy link
Author

pmackay commented Feb 11, 2017

Thanks for the help :) I wasnt calling within_distance properly as a scope method, re-reviewing it after your comments helped. I think the error response was confusing me a bit. So I've got basic spatial query working!

BTW, I had to get a local copy of the gem and increase the dependency version on neo4j so it allowed v8, otherwise bundle wouldnt work. Perhaps that needs tweaking in your branch?

Do you know of any example code of how to chain queries so a spatial and other kinds of queries can be combined? Is it actually possible to chain these scopes? The return value from the within_distance scope is an array so it cannot accept further scopes.

@ProGM
Copy link
Member

ProGM commented Feb 13, 2017

@pmackay
Not sure about it.

To make the query chainable, just remove the pluck(:node) from the scope chain ;)

    scope :within_distance, lambda { |options|
      Neo4j::ActiveBase
        .new_query
        .call('spatial.withinDistance({layer}, {coordinate}, {distance}) YIELD node, distance')
        .params(layer: spatial_index_name,
                coordinate: { lon: options[:lon], lat: options[:lat] },
                distance: options[:distance])
        .with(:node).where("(node:#{mapped_label_name})")
    }

Example:

Yourmodel. within_distance('...').where(node: { some_property: 'some_value' }).pluck(:node)

@kroyagis
Copy link

kroyagis commented Apr 25, 2017

I get a following error when I run create_index!

NameError: uninitialized constant Neo4j::ActiveBase
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/aws-s3-0.6.3/lib/aws/s3/extensions.rb:212:in `const_missing_from_s3_library'
   from (irb):2
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.1/lib/rails/commands/console.rb:110:in `start'
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.1/lib/rails/commands/console.rb:9:in `start'
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.1/lib/rails/commands/commands_tasks.rb:68:in `console'
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.1/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/railties-4.2.1/lib/rails/commands.rb:17:in `<top (required)>'
   from /Users/jiyou/Desktop/tilr/tilr-api/bin/rails:9:in `<top (required)>'
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
   from /Users/jiyou/.rbenv/versions/2.2.3/lib/ruby/2.2.0/rubygems/core_ext/kernel_require.rb:54:in `require'
   from -e:1:in `<main>'

@ProGM
Copy link
Member

ProGM commented May 10, 2017

@kroyagis Are you using the neo4j gem 8.0+?

@kroyagis
Copy link

Sorry, I was using 7.2.x at the time I posted this. I upgraded to 8.0.x and it does recognize ActiveBase. I'm currently solving different problems so this can be closed! Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants