lib/neoid.rb in neoid-0.0.2 vs lib/neoid.rb in neoid-0.0.5.alpha

- old
+ new

@@ -1,26 +1,159 @@ -require "neoid/version" -require "neoid/model_config" -require "neoid/model_additions" -require "neoid/node" -require "neoid/relationship" +require 'neoid/version' +require 'neoid/model_config' +require 'neoid/model_additions' +require 'neoid/search_session' +require 'neoid/node' +require 'neoid/relationship' +require 'neoid/database_cleaner' +require 'neoid/railtie' if defined?(Rails) module Neoid + DEFAULT_FULLTEXT_SEARCH_INDEX_NAME = 'neoid_default_search_index' + class << self attr_accessor :db attr_accessor :logger attr_accessor :ref_node + attr_accessor :env_loaded + def models + @models ||= [] + end + + def node_models + @node_models ||= [] + end + + def relationship_models + @relationship_models ||= [] + end + + def config + @config ||= {} + end + + def initialize_all + @env_loaded = true + relationship_models.each do |rel_model| + Relationship.initialize_relationship(rel_model) + end + end + def db raise "Must set Neoid.db with a Neography::Rest instance" unless @db @db end def logger - @logger ||= Logger.new(ENV['NEOID_LOG'] ? $stdout : '/dev/null') + @logger ||= Logger.new(ENV['NEOID_LOG'] ? ENV['NEOID_LOG_FILE'] || $stdout : '/dev/null') end def ref_node @ref_node ||= Neography::Node.load(Neoid.db.get_root['self']) end + + def reset_cached_variables + 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 + Neoid::NeoDatabaseCleaner.clean_db + end + + + def enabled=(flag) + 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) + old, self.enabled = enabled?, flag + yield if block_given? + ensure + self.enabled = old + end + + # create a fulltext index if not exists + def ensure_default_fulltext_search_index + Neoid.db.create_node_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, 'fulltext', 'lucene') unless (indexes = Neoid.db.list_node_indexes) && indexes[DEFAULT_FULLTEXT_SEARCH_INDEX_NAME] + end + + def search(types, term, options = {}) + options = options.reverse_merge(limit: 15) + + types = [*types] + + query = [] + + types.each do |type| + query_for_type = [] + + query_for_type << "ar_type:#{type.name}" + + case term + when String + search_in_fields = type.neoid_config.search_options.fulltext_fields.keys + 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 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 (")})" + + logger.info "Neoid query #{query}" + + gremlin_query = <<-GREMLIN + #{options[:before_query]} + + idx = g.getRawGraph().index().forNodes('#{DEFAULT_FULLTEXT_SEARCH_INDEX_NAME}') + hits = idx.query('#{sanitize_query_for_gremlin(query)}') + + hits = #{options[:limit] ? "hits.take(#{options[:limit]})" : "hits"} + + #{options[:after_query]} + GREMLIN + + logger.info "[NEOID] search:\n#{gremlin_query}" + + results = Neoid.db.execute_script(gremlin_query) + + SearchSession.new(results, *types) + end + + private + def sanitize_term(term) + # TODO - case sensitive? + term.downcase + end + + def sanitize_query_for_gremlin(query) + # TODO - case sensitive? + query.gsub("'", "\\\\'") + end + + def generate_field_query(field, term, fulltext = false) + term = term.to_s if term + return "" if term.nil? || term.empty? + + fulltext = fulltext ? "_fulltext" : nil + + "(" + term.split(/\s+/).reject(&:empty?).map{ |t| "#{field}#{fulltext}:#{sanitize_term(t)}" }.join(" AND ") + ")" + end end end