Skip to content

Commit

Permalink
* Less queries when checking for a subref
Browse files Browse the repository at this point in the history
* Defining neoid_enabled in a Middleware (to make sure Thread.current doesn't leak to next request, see rails/rails#8517)
* Model#search renamed to Model#neo_search to not conflict with Sunspot
* Return nil if node not found, rather than raising Neography's error
* A little bit of cleanup
* Better schema management for specs

Thanks @lemig for the pull request of the spec fixes
  • Loading branch information
elado committed Dec 17, 2012
1 parent 389ce34 commit fe249ad
Show file tree
Hide file tree
Showing 15 changed files with 144 additions and 85 deletions.
36 changes: 16 additions & 20 deletions lib/neoid.rb
Expand Up @@ -53,32 +53,30 @@ def ref_node
end

def reset_cached_variables
Neoid.models.each { |klass|
Neoid.models.each do |klass|
klass.instance_variable_set(:@_neo_subref_node, nil)
}
end
$neo_ref_node = nil
end

def clean_db(confirm)
puts "must call with confirm: Neoid.clean_db(:yes_i_am_sure)" and return unless confirm == :yes_i_am_sure
# RestClient.delete "#{Neoid.db.protocol}#{Neoid.db.server}:#{Neoid.db.port}/cleandb/secret-key"

Neoid::NeoDatabaseCleaner.clean_db
end

def enabled
return true unless @neoid_use_set
@neoid_use
end


def enabled=(flag)
@neoid_use = flag
@neoid_use_set = true
Thread.current[:neoid_enabled] = flag
end

def enabled
flag = Thread.current[:neoid_enabled]
# flag should be set by the middleware. in case it wasn't (non-rails app or console), default it to true
flag.nil? ? true : flag
end

alias enabled? enabled
def use(flag = true)

def use(flag=true)
old, self.enabled = enabled?, flag
yield if block_given?
ensure
Expand All @@ -97,7 +95,7 @@ def search(types, term, options = {})

query = []

types.each { |type|
types.each do |type|
query_for_type = []

query_for_type << "ar_type:#{type.name}"
Expand All @@ -108,13 +106,13 @@ def search(types, term, options = {})
next if search_in_fields.empty?
query_for_type << search_in_fields.map{ |field| generate_field_query(field, term, true) }.join(" OR ")
when Hash
term.each { |field, value|
term.each do |field, value|
query_for_type << generate_field_query(field, value, false)
}
end
end

query << "(#{query_for_type.join(") AND (")})"
}
end

query = "(#{query.join(") OR (")})"

Expand All @@ -135,8 +133,6 @@ def search(types, term, options = {})

results = Neoid.db.execute_script(gremlin_query)

# logger.info "[NEOID] > results: #{results}"

SearchSession.new(results, *types)
end

Expand Down
14 changes: 14 additions & 0 deletions lib/neoid/middleware.rb
@@ -0,0 +1,14 @@
module Ndoid
class Middleware
def initialize(app)
@app = app
end

def call(env)
old, Thread.current[:neoid_enabled] = Thread.current[:neoid_enabled], true
@app.call(env)
ensure
Thread.current[:neoid_enabled] = old
end
end
end
5 changes: 5 additions & 0 deletions lib/neoid/model_additions.rb
Expand Up @@ -37,6 +37,11 @@ def to_neo
end
end

def neo_resave
_reset_neo_representation
neo_update
end

protected
def neo_properties_to_hash(*attribute_list)
attribute_list.flatten.inject({}) { |all, property|
Expand Down
55 changes: 29 additions & 26 deletions lib/neoid/node.rb
Expand Up @@ -11,29 +11,31 @@ def neo_subref_node_rel_type
def neo_subref_node
@_neo_subref_node ||= begin
Neoid::logger.info "Node#neo_subref_node #{neo_subref_rel_type}"

subref_node_query = Neoid.ref_node.outgoing(neo_subref_rel_type)

if subref_node_query.to_a.blank?
Neoid::logger.info "Node#neo_subref_node not existing, creating #{neo_subref_rel_type}"

node = Neography::Node.create(type: self.name, name: neo_subref_rel_type)
Neography::Relationship.create(
neo_subref_rel_type,
Neoid.ref_node,
node
)
else
Neoid::logger.info "Node#neo_subref_node existing, fetching #{neo_subref_rel_type}"
node = subref_node_query.first
Neoid::logger.info "Node#neo_subref_node existing, fetched #{neo_subref_rel_type}"
end


gremlin_query = <<-GREMLIN
q = g.v(0).out(neo_subref_rel_type);
subref = q.hasNext() ? q.next() : null;
if (!subref) {
subref = g.addVertex([name: neo_subref_rel_type, type: name]);
g.addEdge(g.v(0), subref, neo_subref_rel_type);
}
g.createManualIndex(neo_index_name, Vertex.class);
subref
GREMLIN

Neoid.logger.info "subref query:\n#{gremlin_query}"

node = Neography::Node.load(Neoid.db.execute_script(gremlin_query, neo_subref_rel_type: neo_subref_rel_type, name: self.name, neo_index_name: self.neo_index_name))

node
end
end

def search(term, options = {})
def neo_search(term, options = {})
Neoid.search(self, term, options)
end
end
Expand All @@ -42,6 +44,8 @@ module InstanceMethods
def neo_find_by_id
Neoid::logger.info "Node#neo_find_by_id #{self.class.neo_index_name} #{self.id}"
Neoid.db.get_node_index(self.class.neo_index_name, :ar_id, self.id)
rescue Neography::NotFoundException
nil
end

def neo_create
Expand Down Expand Up @@ -90,13 +94,13 @@ def neo_search_index

Neoid.db.add_node_to_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, 'ar_type', self.class.name, neo_node.neo_id)

self.class.neoid_config.search_options.fulltext_fields.each { |field, options|
self.class.neoid_config.search_options.fulltext_fields.each do |field, options|
Neoid.db.add_node_to_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, "#{field}_fulltext", neo_helper_get_field_value(field, options), neo_node.neo_id)
}
end

self.class.neoid_config.search_options.index_fields.each { |field, options|
self.class.neoid_config.search_options.index_fields.each do |field, options|
Neoid.db.add_node_to_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, field, neo_helper_get_field_value(field, options), neo_node.neo_id)
}
end

neo_node
end
Expand All @@ -120,6 +124,7 @@ def neo_node
def neo_destroy
return unless neo_node
Neoid.db.remove_node_from_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, neo_node)

Neoid.db.remove_node_from_index(self.class.neo_index_name, neo_node)
neo_node.del
_reset_neo_representation
Expand Down Expand Up @@ -148,11 +153,9 @@ def self.included(receiver)
receiver.send :include, InstanceMethods
Neoid.node_models << receiver

receiver.neo_subref_node # ensure

receiver.after_create :neo_create
receiver.after_update :neo_update
receiver.after_destroy :neo_destroy
end
end
end
end
6 changes: 6 additions & 0 deletions lib/neoid/railtie.rb
@@ -1,9 +1,15 @@
require 'neoid/middleware'

module Neoid
class Railtie < Rails::Railtie
initializer "neoid.configure_rails_initialization" do
config.after_initialize do
Neoid.initialize_all
end
end

initializer 'neoid.inject_middleware' do |app|
app.middleware.use Ndoid::Middleware
end
end
end
7 changes: 3 additions & 4 deletions lib/neoid/relationship.rb
Expand Up @@ -3,6 +3,8 @@ module Relationship
module InstanceMethods
def neo_find_by_id
Neoid.db.get_relationship_index(self.class.neo_index_name, :ar_id, self.id)
rescue Neography::NotFoundException
nil
end

def neo_create
Expand Down Expand Up @@ -51,7 +53,7 @@ def neo_destroy
end

def neo_update
Neoid.db.set_relationship_properties(neo_relationship, self.to_neo)
Neoid.db.set_relationship_properties(neo_relationship, self.to_neo) if neo_relationship
end

def neo_relationship
Expand Down Expand Up @@ -88,7 +90,6 @@ def self.initialize_relationship(rel_model)
# e.g. User has_many :likes, after_remove: ...
full_callback_name = "after_remove_for_#{this_has_many.name}"
belongs_to.klass.send(full_callback_name) << :neo_after_relationship_remove if belongs_to.klass.method_defined?(full_callback_name)
# puts " CALLBACK SET #{belongs_to.klass.name} #{belongs_to.name} | #{full_callback_name} << :neo_after_relationship_remove"
# belongs_to.klass.send(:has_many, this_has_many.name, this_has_many.options.merge(after_remove: :neo_after_relationship_remove))

# has_many (with through) on the side of the relationship that removes a relationship. e.g. User has_many :movies, through :likes
Expand All @@ -114,14 +115,12 @@ def self.initialize_relationship(rel_model)
# e.g. User has_many :movies, through: :likes, before_remove: ...
full_callback_name = "before_remove_for_#{many_to_many.name}"
belongs_to.klass.send(full_callback_name) << :neo_before_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name)
# puts " CALLBACK SET #{belongs_to.klass.name} #{belongs_to.name} | #{full_callback_name} << :neo_before_relationship_through_remove"
# belongs_to.klass.send(:has_many, many_to_many.name, many_to_many.options.merge(before_remove: :neo_after_relationship_remove))


# e.g. User has_many :movies, through: :likes, after_remove: ...
full_callback_name = "after_remove_for_#{many_to_many.name}"
belongs_to.klass.send(full_callback_name) << :neo_after_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name)
# puts " CALLBACK SET #{belongs_to.klass.name} #{belongs_to.name} | #{full_callback_name} << :neo_after_relationship_through_remove"
# belongs_to.klass.send(:has_many, many_to_many.name, many_to_many.options.merge(after_remove: :neo_after_relationship_remove))
end
end
Expand Down
8 changes: 3 additions & 5 deletions lib/neoid/search_session.rb
Expand Up @@ -6,9 +6,7 @@ def initialize(response, *models)
end

def hits
@response.map { |x|
Neography::Node.new(x)
}
@response.map { |x| Neography::Node.new(x) }
end

def ids
Expand All @@ -18,11 +16,11 @@ def ids
def results
models_by_name = @models.inject({}) { |all, curr| all[curr.name] = curr; all }

ids_by_klass = @response.inject({}) { |all, curr|
ids_by_klass = @response.inject({}) do |all, curr|
klass_name = curr['data']['ar_type']
(all[models_by_name[klass_name]] ||= []) << curr['data']['ar_id']
all
}
end

ids_by_klass.map { |klass, ids| klass.where(id: ids) }.flatten
end
Expand Down
2 changes: 1 addition & 1 deletion lib/neoid/version.rb
@@ -1,3 +1,3 @@
module Neoid
VERSION = "0.0.41.alpha"
VERSION = "0.0.5.alpha"
end
10 changes: 5 additions & 5 deletions spec/neoid/model_additions_spec.rb
Expand Up @@ -7,12 +7,12 @@
it "should call neo_create on a neo_node for user" do
User.any_instance.should_receive(:neo_create)

User.create(name: "Elad Ossadon")
User.create!(name: "Elad Ossadon")
end

it "should create a neo_node for user" do
user = User.create(name: "Elad Ossadon", slug: "elado")
user = User.create!(name: "Elad Ossadon", slug: "elado")

user.neo_node.should_not be_nil

user.neo_node.ar_id.should == user.id
Expand All @@ -21,7 +21,7 @@
end

it "should create a neo_node for movie" do
movie = Movie.create(name: "Memento", slug: "memento-1999", year: 1999)
movie = Movie.create!(name: "Memento", slug: "memento-1999", year: 1999)

movie.neo_node.should_not be_nil

Expand All @@ -33,7 +33,7 @@

context "find by id" do
it "should find a neo_node for user" do
user = User.create(name: "Elad Ossadon", slug: "elado")
user = User.create!(name: "Elad Ossadon", slug: "elado")

user.neo_node.should_not be_nil
user.neo_find_by_id.should_not be_nil
Expand Down
13 changes: 7 additions & 6 deletions spec/neoid/search_spec.rb
Expand Up @@ -31,29 +31,30 @@
"title:#{r}",
"year:#{r}",
"title:#{r} AND year:#{r}"
].each { |q|
].each do |q|
results = Neoid.db.find_node_index(Neoid::DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, q)

results.should_not be_nil
results.length.should == 1
Neoid.db.send(:get_id, results).should == article.neo_node.neo_id
}
end
end

context "search session" do
it "should return a search session" do
Article.search("hello").should be_a(Neoid::SearchSession)
Article.neo_search("hello").should be_a(Neoid::SearchSession)
end

it "should find hits" do
article = Article.create!(title: "Hello world", body: "Lorem ipsum dolor sit amet", year: 2012)

Article.search("hello").hits.should == [ article.neo_node ]
Article.neo_search("hello").hits.should == [ article.neo_node ]
end

it "should find results with a search string" do
article = Article.create!(title: "Hello world", body: "Lorem ipsum dolor sit amet", year: 2012)

Article.search("hello").results.should == [ article ]
Article.neo_search("hello").results.should == [ article ]
end

it "should find results with a hash" do
Expand All @@ -63,7 +64,7 @@
]


Article.search(year: 2012).results.should == [ articles[0] ]
Article.neo_search(year: 2012).results.should == [ articles[0] ]
end
end

Expand Down

0 comments on commit fe249ad

Please sign in to comment.