neoid 0.0.2 → 0.0.5.alpha
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +4 -0
- data/CHANGELOG.md +14 -0
- data/README.md +216 -121
- data/Rakefile +1 -0
- data/TODO.md +6 -0
- data/lib/neoid/database_cleaner.rb +12 -0
- data/lib/neoid/middleware.rb +14 -0
- data/lib/neoid/model_additions.rb +43 -13
- data/lib/neoid/model_config.rb +49 -5
- data/lib/neoid/node.rb +101 -25
- data/lib/neoid/railtie.rb +15 -0
- data/lib/neoid/relationship.rb +81 -6
- data/lib/neoid/search_session.rb +28 -0
- data/lib/neoid/version.rb +1 -1
- data/lib/neoid.rb +139 -6
- data/neoid.gemspec +7 -5
- data/spec/neoid/model_additions_spec.rb +61 -79
- data/spec/neoid/model_config_spec.rb +24 -0
- data/spec/neoid/search_spec.rb +92 -0
- data/spec/spec_helper.rb +20 -4
- data/spec/support/database.yml +6 -0
- data/spec/support/models.rb +97 -0
- data/spec/support/schema.rb +40 -0
- metadata +75 -23
data/lib/neoid/node.rb
CHANGED
@@ -11,50 +11,108 @@ module Neoid
|
|
11
11
|
def neo_subref_node
|
12
12
|
@_neo_subref_node ||= begin
|
13
13
|
Neoid::logger.info "Node#neo_subref_node #{neo_subref_rel_type}"
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
14
|
+
|
15
|
+
gremlin_query = <<-GREMLIN
|
16
|
+
q = g.v(0).out(neo_subref_rel_type);
|
17
|
+
|
18
|
+
subref = q.hasNext() ? q.next() : null;
|
19
|
+
|
20
|
+
if (!subref) {
|
21
|
+
subref = g.addVertex([name: neo_subref_rel_type, type: name]);
|
22
|
+
g.addEdge(g.v(0), subref, neo_subref_rel_type);
|
23
|
+
}
|
24
|
+
|
25
|
+
g.createManualIndex(neo_index_name, Vertex.class);
|
26
|
+
|
27
|
+
subref
|
28
|
+
GREMLIN
|
29
|
+
|
30
|
+
Neoid.logger.info "subref query:\n#{gremlin_query}"
|
31
|
+
|
32
|
+
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))
|
33
|
+
|
28
34
|
node
|
29
35
|
end
|
30
36
|
end
|
37
|
+
|
38
|
+
def neo_search(term, options = {})
|
39
|
+
Neoid.search(self, term, options)
|
40
|
+
end
|
31
41
|
end
|
32
42
|
|
33
43
|
module InstanceMethods
|
34
44
|
def neo_find_by_id
|
35
45
|
Neoid::logger.info "Node#neo_find_by_id #{self.class.neo_index_name} #{self.id}"
|
36
46
|
Neoid.db.get_node_index(self.class.neo_index_name, :ar_id, self.id)
|
47
|
+
rescue Neography::NotFoundException
|
48
|
+
nil
|
37
49
|
end
|
38
50
|
|
39
51
|
def neo_create
|
52
|
+
return unless Neoid.enabled?
|
53
|
+
|
40
54
|
data = self.to_neo.merge(ar_type: self.class.name, ar_id: self.id)
|
41
55
|
data.reject! { |k, v| v.nil? }
|
42
56
|
|
43
57
|
node = Neography::Node.create(data)
|
44
58
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
59
|
+
retires = 2
|
60
|
+
begin
|
61
|
+
Neography::Relationship.create(
|
62
|
+
self.class.neo_subref_node_rel_type,
|
63
|
+
self.class.neo_subref_node,
|
64
|
+
node
|
65
|
+
)
|
66
|
+
rescue
|
67
|
+
# something must've happened to the cached subref node, reset and retry
|
68
|
+
@_neo_subref_node = nil
|
69
|
+
retires -= 1
|
70
|
+
retry if retires > 0
|
71
|
+
end
|
72
|
+
|
51
73
|
Neoid.db.add_node_to_index(self.class.neo_index_name, :ar_id, self.id, node)
|
52
74
|
|
53
75
|
Neoid::logger.info "Node#neo_create #{self.class.name} #{self.id}, index = #{self.class.neo_index_name}"
|
76
|
+
|
77
|
+
neo_search_index
|
54
78
|
|
55
79
|
node
|
56
80
|
end
|
57
81
|
|
82
|
+
def neo_update
|
83
|
+
Neoid.db.set_node_properties(neo_node, self.to_neo)
|
84
|
+
neo_search_index
|
85
|
+
end
|
86
|
+
|
87
|
+
def neo_search_index
|
88
|
+
return if self.class.neoid_config.search_options.blank? || (
|
89
|
+
self.class.neoid_config.search_options.index_fields.blank? &&
|
90
|
+
self.class.neoid_config.search_options.fulltext_fields.blank?
|
91
|
+
)
|
92
|
+
|
93
|
+
Neoid.ensure_default_fulltext_search_index
|
94
|
+
|
95
|
+
Neoid.db.add_node_to_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, 'ar_type', self.class.name, neo_node.neo_id)
|
96
|
+
|
97
|
+
self.class.neoid_config.search_options.fulltext_fields.each do |field, options|
|
98
|
+
Neoid.db.add_node_to_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, "#{field}_fulltext", neo_helper_get_field_value(field, options), neo_node.neo_id)
|
99
|
+
end
|
100
|
+
|
101
|
+
self.class.neoid_config.search_options.index_fields.each do |field, options|
|
102
|
+
Neoid.db.add_node_to_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, field, neo_helper_get_field_value(field, options), neo_node.neo_id)
|
103
|
+
end
|
104
|
+
|
105
|
+
neo_node
|
106
|
+
end
|
107
|
+
|
108
|
+
def neo_helper_get_field_value(field, options = {})
|
109
|
+
if options[:block]
|
110
|
+
options[:block].call
|
111
|
+
else
|
112
|
+
self.send(field) rescue (raise "No field #{field} for #{self.class.name}")
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
58
116
|
def neo_load(node)
|
59
117
|
Neography::Node.load(node)
|
60
118
|
end
|
@@ -65,21 +123,39 @@ module Neoid
|
|
65
123
|
|
66
124
|
def neo_destroy
|
67
125
|
return unless neo_node
|
126
|
+
Neoid.db.remove_node_from_index(DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, neo_node)
|
127
|
+
|
68
128
|
Neoid.db.remove_node_from_index(self.class.neo_index_name, neo_node)
|
69
129
|
neo_node.del
|
130
|
+
_reset_neo_representation
|
131
|
+
end
|
132
|
+
|
133
|
+
def neo_after_relationship_remove(relationship)
|
134
|
+
relationship.neo_destroy
|
135
|
+
end
|
136
|
+
|
137
|
+
def neo_before_relationship_through_remove(record)
|
138
|
+
rel_model, foreign_key_of_owner, foreign_key_of_record = Neoid.config[:relationship_meta_data][self.class.name.to_s][record.class.name.to_s]
|
139
|
+
rel_model = rel_model.to_s.constantize
|
140
|
+
@__neo_temp_rels ||= {}
|
141
|
+
@__neo_temp_rels[record] = rel_model.where(foreign_key_of_owner => self.id, foreign_key_of_record => record.id).first
|
142
|
+
end
|
143
|
+
|
144
|
+
def neo_after_relationship_through_remove(record)
|
145
|
+
@__neo_temp_rels.each { |record, relationship| relationship.neo_destroy }
|
146
|
+
@__neo_temp_rels.delete(record)
|
70
147
|
end
|
71
148
|
end
|
72
149
|
|
73
150
|
def self.included(receiver)
|
74
|
-
receiver.
|
75
|
-
receiver.send :include, Neoid::ModelAdditions::InstanceMethods
|
151
|
+
receiver.send :include, Neoid::ModelAdditions
|
76
152
|
receiver.extend ClassMethods
|
77
153
|
receiver.send :include, InstanceMethods
|
78
|
-
|
79
|
-
receiver.neo_subref_node # ensure
|
154
|
+
Neoid.node_models << receiver
|
80
155
|
|
81
156
|
receiver.after_create :neo_create
|
157
|
+
receiver.after_update :neo_update
|
82
158
|
receiver.after_destroy :neo_destroy
|
83
159
|
end
|
84
160
|
end
|
85
|
-
end
|
161
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'neoid/middleware'
|
2
|
+
|
3
|
+
module Neoid
|
4
|
+
class Railtie < Rails::Railtie
|
5
|
+
initializer "neoid.configure_rails_initialization" do
|
6
|
+
config.after_initialize do
|
7
|
+
Neoid.initialize_all
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
initializer 'neoid.inject_middleware' do |app|
|
12
|
+
app.middleware.use Ndoid::Middleware
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/neoid/relationship.rb
CHANGED
@@ -3,20 +3,29 @@ module Neoid
|
|
3
3
|
module InstanceMethods
|
4
4
|
def neo_find_by_id
|
5
5
|
Neoid.db.get_relationship_index(self.class.neo_index_name, :ar_id, self.id)
|
6
|
+
rescue Neography::NotFoundException
|
7
|
+
nil
|
6
8
|
end
|
7
9
|
|
8
10
|
def neo_create
|
9
|
-
|
11
|
+
return unless Neoid.enabled?
|
12
|
+
@_neo_destroyed = false
|
13
|
+
|
14
|
+
options = self.class.neoid_config.relationship_options
|
10
15
|
|
11
16
|
start_node = self.send(options[:start_node])
|
12
17
|
end_node = self.send(options[:end_node])
|
13
18
|
|
14
19
|
return unless start_node && end_node
|
20
|
+
|
21
|
+
data = self.to_neo.merge(ar_type: self.class.name, ar_id: self.id)
|
22
|
+
data.reject! { |k, v| v.nil? }
|
15
23
|
|
16
24
|
relationship = Neography::Relationship.create(
|
17
|
-
options[:type],
|
25
|
+
options[:type].is_a?(Proc) ? options[:type].call(self) : options[:type],
|
18
26
|
start_node.neo_node,
|
19
|
-
end_node.neo_node
|
27
|
+
end_node.neo_node,
|
28
|
+
data
|
20
29
|
)
|
21
30
|
|
22
31
|
Neoid.db.add_relationship_to_index(self.class.neo_index_name, :ar_id, self.id, relationship)
|
@@ -31,23 +40,89 @@ module Neoid
|
|
31
40
|
end
|
32
41
|
|
33
42
|
def neo_destroy
|
43
|
+
return if @_neo_destroyed
|
44
|
+
@_neo_destroyed = true
|
34
45
|
return unless neo_relationship
|
35
46
|
Neoid.db.remove_relationship_from_index(self.class.neo_index_name, neo_relationship)
|
36
47
|
neo_relationship.del
|
48
|
+
_reset_neo_representation
|
49
|
+
|
50
|
+
Neoid::logger.info "Relationship#neo_destroy #{self.class.name} #{self.id}, index = #{self.class.neo_index_name}"
|
51
|
+
|
52
|
+
true
|
37
53
|
end
|
38
54
|
|
55
|
+
def neo_update
|
56
|
+
Neoid.db.set_relationship_properties(neo_relationship, self.to_neo) if neo_relationship
|
57
|
+
end
|
58
|
+
|
39
59
|
def neo_relationship
|
40
60
|
_neo_representation
|
41
61
|
end
|
42
62
|
end
|
43
63
|
|
44
64
|
def self.included(receiver)
|
45
|
-
receiver.
|
46
|
-
receiver.send :include, Neoid::ModelAdditions::InstanceMethods
|
65
|
+
receiver.send :include, Neoid::ModelAdditions
|
47
66
|
receiver.send :include, InstanceMethods
|
48
67
|
|
49
68
|
receiver.after_create :neo_create
|
50
69
|
receiver.after_destroy :neo_destroy
|
70
|
+
receiver.after_update :neo_update
|
71
|
+
|
72
|
+
if Neoid.env_loaded
|
73
|
+
initialize_relationship receiver
|
74
|
+
else
|
75
|
+
Neoid.relationship_models << receiver
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.initialize_relationship(rel_model)
|
80
|
+
rel_model.reflect_on_all_associations(:belongs_to).each do |belongs_to|
|
81
|
+
return if belongs_to.options[:polymorphic]
|
82
|
+
|
83
|
+
# e.g. all has_many on User class
|
84
|
+
all_has_many = belongs_to.klass.reflect_on_all_associations(:has_many)
|
85
|
+
|
86
|
+
# has_many (without through) on the side of the relationship that removes a relationship. e.g. User has_many :likes
|
87
|
+
this_has_many = all_has_many.find { |o| o.klass == rel_model }
|
88
|
+
next unless this_has_many
|
89
|
+
|
90
|
+
# e.g. User has_many :likes, after_remove: ...
|
91
|
+
full_callback_name = "after_remove_for_#{this_has_many.name}"
|
92
|
+
belongs_to.klass.send(full_callback_name) << :neo_after_relationship_remove if belongs_to.klass.method_defined?(full_callback_name)
|
93
|
+
# belongs_to.klass.send(:has_many, this_has_many.name, this_has_many.options.merge(after_remove: :neo_after_relationship_remove))
|
94
|
+
|
95
|
+
# has_many (with through) on the side of the relationship that removes a relationship. e.g. User has_many :movies, through :likes
|
96
|
+
many_to_many = all_has_many.find { |o| o.options[:through] == this_has_many.name }
|
97
|
+
next unless many_to_many
|
98
|
+
|
99
|
+
return if many_to_many.options[:as] # polymorphic are not supported here yet
|
100
|
+
|
101
|
+
# user_id
|
102
|
+
foreign_key_of_owner = many_to_many.through_reflection.foreign_key
|
103
|
+
|
104
|
+
# movie_id
|
105
|
+
foreign_key_of_record = many_to_many.source_reflection.foreign_key
|
106
|
+
|
107
|
+
(Neoid.config[:relationship_meta_data] ||= {}).tap do |data|
|
108
|
+
(data[belongs_to.klass.name.to_s] ||= {}).tap do |model_data|
|
109
|
+
model_data[many_to_many.klass.name.to_s] = [rel_model.name.to_s, foreign_key_of_owner, foreign_key_of_record]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# puts Neoid.config[:relationship_meta_data].inspect
|
114
|
+
|
115
|
+
# e.g. User has_many :movies, through: :likes, before_remove: ...
|
116
|
+
full_callback_name = "before_remove_for_#{many_to_many.name}"
|
117
|
+
belongs_to.klass.send(full_callback_name) << :neo_before_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name)
|
118
|
+
# belongs_to.klass.send(:has_many, many_to_many.name, many_to_many.options.merge(before_remove: :neo_after_relationship_remove))
|
119
|
+
|
120
|
+
|
121
|
+
# e.g. User has_many :movies, through: :likes, after_remove: ...
|
122
|
+
full_callback_name = "after_remove_for_#{many_to_many.name}"
|
123
|
+
belongs_to.klass.send(full_callback_name) << :neo_after_relationship_through_remove if belongs_to.klass.method_defined?(full_callback_name)
|
124
|
+
# belongs_to.klass.send(:has_many, many_to_many.name, many_to_many.options.merge(after_remove: :neo_after_relationship_remove))
|
125
|
+
end
|
51
126
|
end
|
52
127
|
end
|
53
|
-
end
|
128
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Neoid
|
2
|
+
class SearchSession
|
3
|
+
def initialize(response, *models)
|
4
|
+
@response = response || []
|
5
|
+
@models = models
|
6
|
+
end
|
7
|
+
|
8
|
+
def hits
|
9
|
+
@response.map { |x| Neography::Node.new(x) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def ids
|
13
|
+
@response.collect { |x| x['data']['ar_id'] }
|
14
|
+
end
|
15
|
+
|
16
|
+
def results
|
17
|
+
models_by_name = @models.inject({}) { |all, curr| all[curr.name] = curr; all }
|
18
|
+
|
19
|
+
ids_by_klass = @response.inject({}) do |all, curr|
|
20
|
+
klass_name = curr['data']['ar_type']
|
21
|
+
(all[models_by_name[klass_name]] ||= []) << curr['data']['ar_id']
|
22
|
+
all
|
23
|
+
end
|
24
|
+
|
25
|
+
ids_by_klass.map { |klass, ids| klass.where(id: ids) }.flatten
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/neoid/version.rb
CHANGED
data/lib/neoid.rb
CHANGED
@@ -1,14 +1,43 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require 'neoid/version'
|
2
|
+
require 'neoid/model_config'
|
3
|
+
require 'neoid/model_additions'
|
4
|
+
require 'neoid/search_session'
|
5
|
+
require 'neoid/node'
|
6
|
+
require 'neoid/relationship'
|
7
|
+
require 'neoid/database_cleaner'
|
8
|
+
require 'neoid/railtie' if defined?(Rails)
|
6
9
|
|
7
10
|
module Neoid
|
11
|
+
DEFAULT_FULLTEXT_SEARCH_INDEX_NAME = 'neoid_default_search_index'
|
12
|
+
|
8
13
|
class << self
|
9
14
|
attr_accessor :db
|
10
15
|
attr_accessor :logger
|
11
16
|
attr_accessor :ref_node
|
17
|
+
attr_accessor :env_loaded
|
18
|
+
|
19
|
+
def models
|
20
|
+
@models ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def node_models
|
24
|
+
@node_models ||= []
|
25
|
+
end
|
26
|
+
|
27
|
+
def relationship_models
|
28
|
+
@relationship_models ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
def config
|
32
|
+
@config ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize_all
|
36
|
+
@env_loaded = true
|
37
|
+
relationship_models.each do |rel_model|
|
38
|
+
Relationship.initialize_relationship(rel_model)
|
39
|
+
end
|
40
|
+
end
|
12
41
|
|
13
42
|
def db
|
14
43
|
raise "Must set Neoid.db with a Neography::Rest instance" unless @db
|
@@ -16,11 +45,115 @@ module Neoid
|
|
16
45
|
end
|
17
46
|
|
18
47
|
def logger
|
19
|
-
@logger ||= Logger.new(ENV['NEOID_LOG'] ? $stdout : '/dev/null')
|
48
|
+
@logger ||= Logger.new(ENV['NEOID_LOG'] ? ENV['NEOID_LOG_FILE'] || $stdout : '/dev/null')
|
20
49
|
end
|
21
50
|
|
22
51
|
def ref_node
|
23
52
|
@ref_node ||= Neography::Node.load(Neoid.db.get_root['self'])
|
24
53
|
end
|
54
|
+
|
55
|
+
def reset_cached_variables
|
56
|
+
Neoid.models.each do |klass|
|
57
|
+
klass.instance_variable_set(:@_neo_subref_node, nil)
|
58
|
+
end
|
59
|
+
$neo_ref_node = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def clean_db(confirm)
|
63
|
+
puts "must call with confirm: Neoid.clean_db(:yes_i_am_sure)" and return unless confirm == :yes_i_am_sure
|
64
|
+
Neoid::NeoDatabaseCleaner.clean_db
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
def enabled=(flag)
|
69
|
+
Thread.current[:neoid_enabled] = flag
|
70
|
+
end
|
71
|
+
|
72
|
+
def enabled
|
73
|
+
flag = Thread.current[:neoid_enabled]
|
74
|
+
# flag should be set by the middleware. in case it wasn't (non-rails app or console), default it to true
|
75
|
+
flag.nil? ? true : flag
|
76
|
+
end
|
77
|
+
alias enabled? enabled
|
78
|
+
|
79
|
+
def use(flag=true)
|
80
|
+
old, self.enabled = enabled?, flag
|
81
|
+
yield if block_given?
|
82
|
+
ensure
|
83
|
+
self.enabled = old
|
84
|
+
end
|
85
|
+
|
86
|
+
# create a fulltext index if not exists
|
87
|
+
def ensure_default_fulltext_search_index
|
88
|
+
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]
|
89
|
+
end
|
90
|
+
|
91
|
+
def search(types, term, options = {})
|
92
|
+
options = options.reverse_merge(limit: 15)
|
93
|
+
|
94
|
+
types = [*types]
|
95
|
+
|
96
|
+
query = []
|
97
|
+
|
98
|
+
types.each do |type|
|
99
|
+
query_for_type = []
|
100
|
+
|
101
|
+
query_for_type << "ar_type:#{type.name}"
|
102
|
+
|
103
|
+
case term
|
104
|
+
when String
|
105
|
+
search_in_fields = type.neoid_config.search_options.fulltext_fields.keys
|
106
|
+
next if search_in_fields.empty?
|
107
|
+
query_for_type << search_in_fields.map{ |field| generate_field_query(field, term, true) }.join(" OR ")
|
108
|
+
when Hash
|
109
|
+
term.each do |field, value|
|
110
|
+
query_for_type << generate_field_query(field, value, false)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
query << "(#{query_for_type.join(") AND (")})"
|
115
|
+
end
|
116
|
+
|
117
|
+
query = "(#{query.join(") OR (")})"
|
118
|
+
|
119
|
+
logger.info "Neoid query #{query}"
|
120
|
+
|
121
|
+
gremlin_query = <<-GREMLIN
|
122
|
+
#{options[:before_query]}
|
123
|
+
|
124
|
+
idx = g.getRawGraph().index().forNodes('#{DEFAULT_FULLTEXT_SEARCH_INDEX_NAME}')
|
125
|
+
hits = idx.query('#{sanitize_query_for_gremlin(query)}')
|
126
|
+
|
127
|
+
hits = #{options[:limit] ? "hits.take(#{options[:limit]})" : "hits"}
|
128
|
+
|
129
|
+
#{options[:after_query]}
|
130
|
+
GREMLIN
|
131
|
+
|
132
|
+
logger.info "[NEOID] search:\n#{gremlin_query}"
|
133
|
+
|
134
|
+
results = Neoid.db.execute_script(gremlin_query)
|
135
|
+
|
136
|
+
SearchSession.new(results, *types)
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
def sanitize_term(term)
|
141
|
+
# TODO - case sensitive?
|
142
|
+
term.downcase
|
143
|
+
end
|
144
|
+
|
145
|
+
def sanitize_query_for_gremlin(query)
|
146
|
+
# TODO - case sensitive?
|
147
|
+
query.gsub("'", "\\\\'")
|
148
|
+
end
|
149
|
+
|
150
|
+
def generate_field_query(field, term, fulltext = false)
|
151
|
+
term = term.to_s if term
|
152
|
+
return "" if term.nil? || term.empty?
|
153
|
+
|
154
|
+
fulltext = fulltext ? "_fulltext" : nil
|
155
|
+
|
156
|
+
"(" + term.split(/\s+/).reject(&:empty?).map{ |t| "#{field}#{fulltext}:#{sanitize_term(t)}" }.join(" AND ") + ")"
|
157
|
+
end
|
25
158
|
end
|
26
159
|
end
|
data/neoid.gemspec
CHANGED
@@ -18,9 +18,11 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
-
s.add_development_dependency
|
22
|
-
s.add_development_dependency
|
23
|
-
s.add_development_dependency
|
24
|
-
s.add_development_dependency
|
25
|
-
s.
|
21
|
+
s.add_development_dependency 'rake'
|
22
|
+
s.add_development_dependency 'rspec'
|
23
|
+
s.add_development_dependency 'rest-client'
|
24
|
+
s.add_development_dependency 'activerecord'
|
25
|
+
s.add_development_dependency 'sqlite3'
|
26
|
+
|
27
|
+
s.add_runtime_dependency 'neography'
|
26
28
|
end
|