lexster 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +58 -0
- data/Gemfile +4 -0
- data/LICENSE +19 -0
- data/README.md +467 -0
- data/Rakefile +7 -0
- data/TODO.md +4 -0
- data/lexster.gemspec +28 -0
- data/lib/lexster.rb +239 -0
- data/lib/lexster/batch.rb +168 -0
- data/lib/lexster/config.rb +6 -0
- data/lib/lexster/database_cleaner.rb +12 -0
- data/lib/lexster/middleware.rb +16 -0
- data/lib/lexster/model_additions.rb +121 -0
- data/lib/lexster/model_config.rb +57 -0
- data/lib/lexster/node.rb +210 -0
- data/lib/lexster/railtie.rb +15 -0
- data/lib/lexster/relationship.rb +163 -0
- data/lib/lexster/search_session.rb +28 -0
- data/lib/lexster/version.rb +3 -0
- data/spec/lexster/batch_spec.rb +170 -0
- data/spec/lexster/config_spec.rb +13 -0
- data/spec/lexster/model_config_spec.rb +24 -0
- data/spec/lexster/node_spec.rb +131 -0
- data/spec/lexster/relationship_spec.rb +102 -0
- data/spec/lexster/search_spec.rb +117 -0
- data/spec/lexster_spec.rb +11 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/database.yml +6 -0
- data/spec/support/models.rb +106 -0
- data/spec/support/schema.rb +44 -0
- metadata +172 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module Lexster
|
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
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lexster::ModelAdditions do
|
4
|
+
context "promises" do
|
5
|
+
it "should run scripts in a batch and return results" do
|
6
|
+
Lexster.batch do |batch|
|
7
|
+
batch << [:execute_script, "1"]
|
8
|
+
batch << [:execute_script, "2"]
|
9
|
+
end.then do |results|
|
10
|
+
results.should == [1, 2]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should run scripts in a batch with batch_size and flush batch when it's full" do
|
15
|
+
Lexster.batch(batch_size: 3) do |batch|
|
16
|
+
(0...9).each do |i|
|
17
|
+
batch.count.should == i % 3
|
18
|
+
batch << [:execute_script, i.to_s]
|
19
|
+
if i % 3 == 0
|
20
|
+
batch.results.count.should == i
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should run scripts in a batch with batch_size and return all results" do
|
27
|
+
Lexster.batch(batch_size: 2) do |batch|
|
28
|
+
(1..6).each do |i|
|
29
|
+
batch << [:execute_script, i.to_s]
|
30
|
+
end
|
31
|
+
end.then do |results|
|
32
|
+
results.should == [1, 2, 3, 4, 5, 6]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return results then process them" do
|
37
|
+
node_1 = Lexster.db.create_node
|
38
|
+
node_2 = Lexster.db.create_node
|
39
|
+
rel = Lexster.db.create_relationship(:related, node_1, node_2)
|
40
|
+
|
41
|
+
Lexster.batch do |batch|
|
42
|
+
batch << [:execute_script, "g.v(neo_id)", neo_id: node_1['self'].split('/').last.to_i]
|
43
|
+
batch << [:execute_script, "g.v(neo_id)", neo_id: node_2['self'].split('/').last]
|
44
|
+
batch << [:execute_script, "g.e(neo_id)", neo_id: rel['self'].split('/').last]
|
45
|
+
end.then do |results|
|
46
|
+
results[0].should be_a(Neography::Node)
|
47
|
+
results[1].should be_a(Neography::Node)
|
48
|
+
results[2].should be_a(Neography::Relationship)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should remember what to do after each script has executed, and perform it when batch is flushed" do
|
53
|
+
then_results = []
|
54
|
+
|
55
|
+
Lexster.batch do |batch|
|
56
|
+
(batch << [:execute_script, "1"]).then { |res| then_results << res }
|
57
|
+
(batch << [:execute_script, "2"]).then { |res| then_results << res }
|
58
|
+
batch << [:execute_script, "3"]
|
59
|
+
(batch << [:execute_script, "4"]).then { |res| then_results << res }
|
60
|
+
end.then do |results|
|
61
|
+
results.should == [1, 2, 3, 4]
|
62
|
+
then_results.should == [1, 2, 4]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "nodes" do
|
68
|
+
it "should not execute until batch is done" do
|
69
|
+
u1 = u2 = nil
|
70
|
+
|
71
|
+
res = Lexster.batch do
|
72
|
+
u1 = User.create!(name: "U1")
|
73
|
+
u2 = User.create!(name: "U2")
|
74
|
+
|
75
|
+
u1.neo_find_by_id.should be_nil
|
76
|
+
u2.neo_find_by_id.should be_nil
|
77
|
+
end
|
78
|
+
|
79
|
+
res.length.should == 2
|
80
|
+
|
81
|
+
u1.neo_find_by_id.should_not be_nil
|
82
|
+
u2.neo_find_by_id.should_not be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should update nodes in batch" do
|
86
|
+
u1 = User.create!(name: "U1")
|
87
|
+
u2 = User.create!(name: "U2")
|
88
|
+
|
89
|
+
res = Lexster.batch do
|
90
|
+
u1.name = "U1 update"
|
91
|
+
u2.name = "U2 update"
|
92
|
+
|
93
|
+
u1.save!
|
94
|
+
u2.save!
|
95
|
+
|
96
|
+
u1.neo_find_by_id.name.should == "U1"
|
97
|
+
u2.neo_find_by_id.name.should == "U2"
|
98
|
+
end
|
99
|
+
|
100
|
+
res.length.should == 2
|
101
|
+
|
102
|
+
u1.neo_find_by_id.name.should == "U1 update"
|
103
|
+
u2.neo_find_by_id.name.should == "U2 update"
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# Not working yet because Neography can't delete a node and all of its realtionships in a batch, and deleting a node with relationships results an error
|
108
|
+
# it "should delete nodes in batch" do
|
109
|
+
# u1 = User.create!(name: "U1")
|
110
|
+
# u2 = User.create!(name: "U2")
|
111
|
+
|
112
|
+
# res = Lexster.batch do
|
113
|
+
# u1_node_id = u1.neo_find_by_id.neo_id
|
114
|
+
# u2_node_id = u2.neo_find_by_id.neo_id
|
115
|
+
|
116
|
+
# u1.destroy
|
117
|
+
# u2.destroy
|
118
|
+
|
119
|
+
# Lexster.db.get_node(u1_node_id).should_not be_nil
|
120
|
+
# Lexster.db.get_node(u2_node_id).should_not be_nil
|
121
|
+
# end
|
122
|
+
|
123
|
+
# res.length.should == 2
|
124
|
+
|
125
|
+
# Lexster.db.get_node(u1_node_id).should be_nil
|
126
|
+
# Lexster.db.get_node(u2_node_id).should be_nil
|
127
|
+
# end
|
128
|
+
end
|
129
|
+
|
130
|
+
context "relationships" do
|
131
|
+
let(:user) { User.create(name: "Elad Ossadon", slug: "elado") }
|
132
|
+
let(:movie) { Movie.create(name: "Memento", slug: "memento-1999", year: 1999) }
|
133
|
+
|
134
|
+
it "should not execute until batch is done" do
|
135
|
+
# ensure user and movie nodes are inserted
|
136
|
+
user
|
137
|
+
movie
|
138
|
+
|
139
|
+
res = Lexster.batch do |batch|
|
140
|
+
user.like! movie
|
141
|
+
|
142
|
+
user.likes.last.neo_find_by_id.should be_nil
|
143
|
+
end
|
144
|
+
|
145
|
+
res.length.should == 1
|
146
|
+
|
147
|
+
user.likes.last.neo_find_by_id.should_not be_nil
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should not execute until batch is done" do
|
151
|
+
# ensure user and movie nodes are inserted
|
152
|
+
user
|
153
|
+
movie
|
154
|
+
|
155
|
+
# then destroy the nodes, allow the relationship do that in the batch
|
156
|
+
user.neo_destroy
|
157
|
+
movie.neo_destroy
|
158
|
+
|
159
|
+
res = Lexster.batch do |batch|
|
160
|
+
user.like! movie
|
161
|
+
|
162
|
+
user.likes.last.neo_find_by_id.should be_nil
|
163
|
+
end
|
164
|
+
|
165
|
+
res.length.should == 3
|
166
|
+
|
167
|
+
user.likes.last.neo_find_by_id.should_not be_nil
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lexster::Config do
|
4
|
+
context "config" do
|
5
|
+
it "should store and read config" do
|
6
|
+
Lexster.configure do |config|
|
7
|
+
config.enable_subrefs = false
|
8
|
+
end
|
9
|
+
|
10
|
+
Lexster.config.enable_subrefs.should == false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
|
5
|
+
describe Lexster::ModelConfig do
|
6
|
+
context "config on a model" do
|
7
|
+
it "should store search fields" do
|
8
|
+
Article.lexster_config.search_options.should_not be_nil
|
9
|
+
Article.lexster_config.search_options.index_fields.keys.should =~ [ :title, :body, :year ]
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should store stored fields" do
|
13
|
+
Article.lexster_config.stored_fields.should_not be_nil
|
14
|
+
Article.lexster_config.stored_fields.keys.should =~ [ :title, :year, :title_length ]
|
15
|
+
Article.lexster_config.stored_fields[:title_length].should be_a(Proc)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should store stored fields based on blocks" do
|
19
|
+
article = Article.create! title: "Hello", year: 2012
|
20
|
+
|
21
|
+
article.neo_node.title_length.should == article.title.length
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lexster::Node do
|
4
|
+
context "create graph nodes" do
|
5
|
+
it "should call neo_save after model creation" do
|
6
|
+
user = User.new(name: "Elad Ossadon")
|
7
|
+
user.should_receive(:neo_save)
|
8
|
+
user.save!
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should create a node for user" do
|
12
|
+
user = User.create!(name: "Elad Ossadon", slug: "elado")
|
13
|
+
|
14
|
+
user.neo_node.should_not be_nil
|
15
|
+
|
16
|
+
user.neo_node.ar_id.should == user.id
|
17
|
+
user.neo_node.name.should == user.name
|
18
|
+
user.neo_node.slug.should == user.slug
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should create a neo_node for movie" do
|
22
|
+
movie = Movie.create!(name: "Memento", slug: "memento-1999", year: 1999)
|
23
|
+
|
24
|
+
movie.neo_node.should_not be_nil
|
25
|
+
|
26
|
+
movie.neo_node.ar_id.should == movie.id
|
27
|
+
movie.neo_node.name.should == movie.name
|
28
|
+
movie.neo_node.year.should == movie.year
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "update graph nodes" do
|
33
|
+
it "should call neo_save after model update" do
|
34
|
+
user = User.create!(name: "Elad Ossadon")
|
35
|
+
user.should_receive(:neo_save)
|
36
|
+
user.name = "John Doe"
|
37
|
+
user.save!
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should update a node after model update" do
|
41
|
+
user = User.create!(name: "Elad Ossadon")
|
42
|
+
user.neo_node.name.should == "Elad Ossadon"
|
43
|
+
|
44
|
+
user.name = "John Doe"
|
45
|
+
user.save!
|
46
|
+
|
47
|
+
user.neo_node.name.should == "John Doe"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "find by id" do
|
52
|
+
it "should find a neo_node for user" do
|
53
|
+
user = User.create!(name: "Elad Ossadon", slug: "elado")
|
54
|
+
|
55
|
+
user.neo_node.should_not be_nil
|
56
|
+
user.neo_find_by_id.should_not be_nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "no auto_index" do
|
61
|
+
it "should not index a node if option :auto_index is set to false" do
|
62
|
+
model = NoAutoIndexNode.new(name: "Hello")
|
63
|
+
model.should_not_receive(:neo_save)
|
64
|
+
model.save!
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "subrefs" do
|
69
|
+
it "should connect subrefs to reference node" do
|
70
|
+
old, Lexster.config.enable_subrefs = Lexster.config.enable_subrefs, true
|
71
|
+
|
72
|
+
Lexster.send(:initialize_subrefs)
|
73
|
+
|
74
|
+
begin
|
75
|
+
Lexster.ref_node.rel(:outgoing, :users_subref).should_not be_nil
|
76
|
+
ensure
|
77
|
+
Lexster.config.enable_subrefs = old
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should create a relationship with a subref node" do
|
82
|
+
old, Lexster.config.enable_subrefs = Lexster.config.enable_subrefs, true
|
83
|
+
|
84
|
+
Lexster.send(:initialize_subrefs)
|
85
|
+
|
86
|
+
begin
|
87
|
+
user = User.create!(name: "Elad")
|
88
|
+
user.neo_node.rel(:incoming, :users).should_not be_nil
|
89
|
+
ensure
|
90
|
+
Lexster.config.enable_subrefs = old
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not create a relationship with a subref node if disabled" do
|
95
|
+
old, Lexster.config.enable_subrefs = Lexster.config.enable_subrefs, false
|
96
|
+
|
97
|
+
begin
|
98
|
+
user = User.create!(name: "Elad")
|
99
|
+
user.neo_node.rel(:incoming, :users_subref).should be_nil
|
100
|
+
ensure
|
101
|
+
Lexster.config.enable_subrefs = old
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "per_model_indexes" do
|
107
|
+
it "should create a relationship with a subref node" do
|
108
|
+
old, Lexster.config.enable_per_model_indexes = Lexster.config.enable_per_model_indexes, true
|
109
|
+
|
110
|
+
Lexster.send(:initialize_per_model_indexes)
|
111
|
+
|
112
|
+
begin
|
113
|
+
user = User.create!(name: "Elad")
|
114
|
+
Lexster.db.get_node_index(User.neo_model_index_name, 'ar_id', user.id).should_not be_nil
|
115
|
+
ensure
|
116
|
+
Lexster.config.enable_per_model_indexes = old
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should not create a relationship with a subref node if disabled" do
|
121
|
+
old, Lexster.config.enable_per_model_indexes = Lexster.config.enable_per_model_indexes, false
|
122
|
+
|
123
|
+
begin
|
124
|
+
user = User.create!(name: "Elad")
|
125
|
+
expect { Lexster.db.get_node_index(User.neo_model_index_name, 'ar_id', user.id) }.to raise_error(Neography::NotFoundException)
|
126
|
+
ensure
|
127
|
+
Lexster.config.enable_per_model_indexes = old
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Lexster::Relationship do
|
4
|
+
let(:user) { User.create!(name: "Elad Ossadon", slug: "elado") }
|
5
|
+
let(:movie) { Movie.create!(name: "Memento", slug: "memento-1999", year: 1999) }
|
6
|
+
|
7
|
+
it "should call neo_save after relationship model creation" do
|
8
|
+
Like.any_instance.should_receive(:neo_save)
|
9
|
+
user.like! movie
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should create a neo_relationship for like" do
|
13
|
+
like = user.like! movie
|
14
|
+
like = user.likes.last
|
15
|
+
|
16
|
+
like.neo_find_by_id.should_not be_nil
|
17
|
+
|
18
|
+
like.neo_relationship.should_not be_nil
|
19
|
+
|
20
|
+
like.neo_relationship.start_node.should == user.neo_node
|
21
|
+
like.neo_relationship.end_node.should == movie.neo_node
|
22
|
+
like.neo_relationship.rel_type.should == 'likes'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should delete a relationship on deleting a record" do
|
26
|
+
user.like! movie
|
27
|
+
like = user.likes.last
|
28
|
+
|
29
|
+
relationship_neo_id = like.neo_relationship.neo_id
|
30
|
+
|
31
|
+
Neography::Relationship.load(relationship_neo_id).should_not be_nil
|
32
|
+
|
33
|
+
user.unlike! movie
|
34
|
+
|
35
|
+
expect { Neography::Relationship.load(relationship_neo_id) }.to raise_error(Neography::RelationshipNotFoundException)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should update neo4j on manual set of a collection" do
|
39
|
+
movies = [
|
40
|
+
Movie.create(name: "Memento"),
|
41
|
+
Movie.create(name: "The Prestige"),
|
42
|
+
Movie.create(name: "The Dark Knight"),
|
43
|
+
Movie.create(name: "Spiderman")
|
44
|
+
]
|
45
|
+
|
46
|
+
user.neo_node.outgoing(:likes).length.should == 0
|
47
|
+
|
48
|
+
expect {
|
49
|
+
user.movies = movies
|
50
|
+
}.to change{ user.neo_node.outgoing(:likes).length }.to(movies.length)
|
51
|
+
|
52
|
+
expect { expect {
|
53
|
+
user.movies -= movies[0..1]
|
54
|
+
}.to change{ user.movies.count }.by(-2)
|
55
|
+
}.to change{ user.neo_node.outgoing(:likes).length }.by(-2)
|
56
|
+
|
57
|
+
expect {
|
58
|
+
user.movies = []
|
59
|
+
}.to change{ user.neo_node.outgoing(:likes).length }.to(0)
|
60
|
+
|
61
|
+
expect {
|
62
|
+
user.movie_ids = movies[0...2].collect(&:id)
|
63
|
+
}.to change{ user.neo_node.outgoing(:likes).length }.to(2)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should update a relationship after relationship model update" do
|
67
|
+
like = user.like! movie
|
68
|
+
|
69
|
+
like.neo_relationship.rate.should be_nil
|
70
|
+
|
71
|
+
like.rate = 10
|
72
|
+
like.save!
|
73
|
+
|
74
|
+
like.neo_relationship.rate.should == 10
|
75
|
+
end
|
76
|
+
|
77
|
+
context "polymorphic relationship" do
|
78
|
+
let(:user) { User.create(name: "Elad Ossadon", slug: "elado") }
|
79
|
+
|
80
|
+
it "should create relationships with polymorphic items" do
|
81
|
+
followed = [
|
82
|
+
User.create(name: "Some One", slug: "someone"),
|
83
|
+
Movie.create(name: "The Prestige"),
|
84
|
+
Movie.create(name: "The Dark Knight")
|
85
|
+
]
|
86
|
+
|
87
|
+
expect {
|
88
|
+
followed.each do |item|
|
89
|
+
user.user_follows.create!(item: item)
|
90
|
+
end
|
91
|
+
}.to change{ user.neo_node.outgoing(:follows).length }.to(followed.length)
|
92
|
+
|
93
|
+
expect {
|
94
|
+
user.user_follows = user.user_follows[0...1]
|
95
|
+
}.to change{ user.neo_node.outgoing(:follows).length }.to(1)
|
96
|
+
|
97
|
+
expect {
|
98
|
+
user.user_follows = []
|
99
|
+
}.to change{ user.neo_node.outgoing(:follows).length }.to(0)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|