Neo4j::Rails Lucene

subvertallchris edited this page Dec 23, 2013 · 11 revisions
Clone this wiki locally

You can use Lucene to find nodes. A common use case is to find one or more nodes/relationship and then traverse from them (using the Traversal methods or a Cypher Query).

See also Neo4j::Core-Cypher, Neo4j::Wrapper-Rules-and-Functions and Neo4j::Core-Traverse for alternative of finding nodes and relationships.

Neo4j also supports the orm_adaptor API which can be used for searching.

Declaring Lucene Index

You use the Neo4j::Rails::Model#property and Neo4j::Rails::Relationship#property to declare lucene index (same as for Neo4j::NodeMixin, see Neo4j::Wrapper-Lucene

Example:

class Role < Neo4j::Rails::Relationship
  property :since, :type => Fixnum, :index => :exact
end

Notice for the unique validations and unique entities (e.g. Neo4j::Rails::Model.get_or_create) method to work you must declare an index, see Neo4j::Rails-Persistence.

The find and all methods use the model’s Lucene index to search for either one or a set of nodes respectively. Make sure to index the properties that you’ll query on.

Notice 2 If you don’t declare the index it will try to use cypher to perform the query (this since neo4j version 2.2)

Another example:

class IceCream < Neo4j::Model
  property :flavour, :index => :exact
  property :cone, :index => :exact
end

See Neo4j::Wrapper-Lucene and Neo4j::Core-Lucene for more documentation how to declare and use a lucene index.

Find

The Neo4j::Rails::Model.find and Neo4j::Rails::Relationship.find method behaves different compared to Neo4j::Node.find and Neo4j::NodeMixin.find.
The find method will find the first node that matches its query or nil if one isn’t found.

Example:

IceCream.find('cone: sugar') #=> vanilla_sugar
IceCream.find('flavour: vanilla') #=> who knows?
IceCream.find(:flavour => 'vanilla') #=> same as above
IceCream.find('flavour: chocolate') #=> nil

The find method will also accept a Neo4j node id which is useful for finding a node in a rails controller.

class IceCreamController < ApplicationController
  
  def show
    @ice_cream = IceCream.find(params[:id])
    # . . .
  end

end

The Neo4j::Model also support Active Record like query syntax:

Model.find(:first, :conditions => { :name => "test" }) # works the same as find("name: \"test\"")
Model.find(:first, :conditions => { :id => 10 }) # works the same as find(10) or find("10")

It does also generate finder methods like IceCream.find_by_flavour or find_or_create_by_flavour, see below.

All

The all method will find a set of nodes or relationship. If none is find it will return an empty array.

Some examples:

IceCream.all #=> [vanilla_sugar, vanilla_waffle]
IceCream.all('flavour: vanilla') #=> [vanilla_sugar, vanilla_waffle]
IceCream.all('flavour: vanilla AND cone: sugar') #=> [vanilla_sugar]
IceCream.all('flavour: chocolate') #=> []
IceCream.all(:flavour => 'chocolate')
IceCream.find(:all, :conditions => {:flavour => 'chocolate'}, :sort => {:name => :asc})
IceCream.find(:all, 'name: choclate').asc(:name)

Tip: String parameter values with special characters, such as URI’s, should be escaped first before setting or querying to not conflict with the Lucene Query Parser Syntax: + – && || ! ( ) { } ^ " ~ * ? : \ . See Apache Lucene – Query Parser Syntax

escaped = URI.escape( "http://neo4j.rubyforge.org/guides", Regexp.new( "[^#{URI::PATTERN::UNRESERVED}]" ))
Website.find("uri: #{escaped}")

TIP: The find methods (e.g. all) also work for subclasses for Neo4j::Rails::Model (but as expected for subclasses of Neo4j::Rails::Relationship).

Fulltext Search

An example of using the lucene full text search index:

class BlogEntry < Neo4j::Rails::Model
  property :text, :index => :fulltext
end

e = BlogEntry.create(:text => 'some long text string')

BlogEntry.find('text: long', :type => :fulltext) #=> e
BlogEntry.all('text: long', :type => :fulltext).size #=> 1

Notice that you need to specify the :fulltext configuration property for both declaring the index and searching.

You can use Lucene syntax as described here with a fulltext query for more power.

BlogEntry.find(:first, 'text: "some string"~2', type: :fulltext) #=> e
BlogEntry.find(:first, 'text: zome~', type: :fulltext) #=> e
BlogEntry.find(:all, 'text: s?me', type: :fulltext).count #=> 1

Dynamic Finders

If you declare an index you neo4j.rb will generate finder methods for you.

Example:

class Person
  property :name
  index :name
end

Person.find_by_name 'andreas'  # with underscore of course, not visible

For a complete list of available methods see Neo4j::Rails::Finders