neoid 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE +1 -1
- data/README.md +27 -25
- data/Rakefile +2 -2
- data/lib/neoid.rb +74 -61
- data/lib/neoid/batch.rb +27 -27
- data/lib/neoid/database_cleaner.rb +1 -1
- data/lib/neoid/middleware.rb +1 -1
- data/lib/neoid/model_additions.rb +13 -11
- data/lib/neoid/model_config.rb +13 -13
- data/lib/neoid/node.rb +21 -21
- data/lib/neoid/railtie.rb +1 -1
- data/lib/neoid/relationship.rb +78 -78
- data/lib/neoid/search_session.rb +3 -3
- data/lib/neoid/version.rb +1 -1
- data/spec/factories.rb +12 -0
- data/spec/neoid/batch_spec.rb +68 -89
- data/spec/neoid/config_spec.rb +6 -6
- data/spec/neoid/model_config_spec.rb +14 -13
- data/spec/neoid/node_spec.rb +60 -79
- data/spec/neoid/relationship_spec.rb +39 -37
- data/spec/neoid/search_spec.rb +71 -46
- data/spec/neoid_spec.rb +11 -4
- data/spec/spec_helper.rb +23 -4
- data/spec/support/database.yml +1 -1
- data/spec/support/models.rb +16 -16
- data/spec/support/schema.rb +1 -2
- metadata +84 -31
- data/.gitignore +0 -5
- data/.rspec +0 -1
- data/.travis.yml +0 -4
- data/CHANGELOG.md +0 -58
- data/Gemfile +0 -4
- data/TODO.md +0 -4
- data/neoid.gemspec +0 -28
@@ -1,49 +1,50 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Neoid::Relationship do
|
4
|
-
let(:user) { User.create!(name:
|
5
|
-
let(:movie) { Movie.create!(name:
|
6
|
-
|
7
|
-
it
|
8
|
-
Like.
|
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
|
+
expect_any_instance_of(Like).to receive(:neo_save)
|
9
9
|
user.like! movie
|
10
10
|
end
|
11
11
|
|
12
|
-
it
|
12
|
+
it 'should create a neo_relationship for like' do
|
13
13
|
like = user.like! movie
|
14
14
|
like = user.likes.last
|
15
15
|
|
16
|
-
like.neo_find_by_id.
|
16
|
+
expect(like.neo_find_by_id).to_not be_nil
|
17
|
+
|
18
|
+
expect(like.neo_relationship).to_not be_nil
|
17
19
|
|
18
|
-
like.neo_relationship.
|
19
|
-
|
20
|
-
like.neo_relationship.
|
21
|
-
like.neo_relationship.end_node.should == movie.neo_node
|
22
|
-
like.neo_relationship.rel_type.should == 'likes'
|
20
|
+
expect(like.neo_relationship.start_node).to eq(user.neo_node)
|
21
|
+
expect(like.neo_relationship.end_node).to eq(movie.neo_node)
|
22
|
+
expect(like.neo_relationship.rel_type).to eq('likes')
|
23
23
|
end
|
24
|
-
|
25
|
-
it
|
24
|
+
|
25
|
+
it 'should delete a relationship on deleting a record' do
|
26
26
|
user.like! movie
|
27
27
|
like = user.likes.last
|
28
|
-
|
28
|
+
|
29
29
|
relationship_neo_id = like.neo_relationship.neo_id
|
30
30
|
|
31
|
-
Neography::Relationship.load(relationship_neo_id).
|
32
|
-
|
31
|
+
expect(Neography::Relationship.load(relationship_neo_id)).to_not be_nil
|
32
|
+
|
33
33
|
user.unlike! movie
|
34
|
-
|
34
|
+
|
35
35
|
expect { Neography::Relationship.load(relationship_neo_id) }.to raise_error(Neography::RelationshipNotFoundException)
|
36
36
|
end
|
37
37
|
|
38
|
-
it
|
38
|
+
it 'should update neo4j on manual set of a collection' do
|
39
|
+
pending
|
39
40
|
movies = [
|
40
|
-
Movie.create(name:
|
41
|
-
Movie.create(name:
|
42
|
-
Movie.create(name:
|
43
|
-
Movie.create(name:
|
41
|
+
Movie.create(name: 'Memento'),
|
42
|
+
Movie.create(name: 'The Prestige'),
|
43
|
+
Movie.create(name: 'The Dark Knight'),
|
44
|
+
Movie.create(name: 'Spiderman')
|
44
45
|
]
|
45
46
|
|
46
|
-
user.neo_node.outgoing(:likes).length.
|
47
|
+
expect(user.neo_node.outgoing(:likes).length).to eq(0)
|
47
48
|
|
48
49
|
expect {
|
49
50
|
user.movies = movies
|
@@ -59,44 +60,45 @@ describe Neoid::Relationship do
|
|
59
60
|
}.to change{ user.neo_node.outgoing(:likes).length }.to(0)
|
60
61
|
|
61
62
|
expect {
|
62
|
-
user.movie_ids = movies[0...2].
|
63
|
+
user.movie_ids = movies[0...2].map(&:id)
|
63
64
|
}.to change{ user.neo_node.outgoing(:likes).length }.to(2)
|
64
65
|
end
|
65
66
|
|
66
|
-
it
|
67
|
+
it 'should update a relationship after relationship model update' do
|
67
68
|
like = user.like! movie
|
68
69
|
|
69
|
-
like.neo_relationship.rate.
|
70
|
+
expect(like.neo_relationship.rate).to be_nil
|
70
71
|
|
71
72
|
like.rate = 10
|
72
73
|
like.save!
|
73
74
|
|
74
|
-
like.neo_relationship.rate.
|
75
|
+
expect(like.neo_relationship.rate).to eq(10)
|
75
76
|
end
|
76
77
|
|
77
|
-
context
|
78
|
-
let(:user) { User.create(name:
|
78
|
+
context 'polymorphic relationship' do
|
79
|
+
let(:user) { User.create(name: 'Elad Ossadon', slug: 'elado') }
|
79
80
|
|
80
|
-
it
|
81
|
+
it 'should create relationships with polymorphic items' do
|
82
|
+
pending
|
81
83
|
followed = [
|
82
|
-
User.create(name:
|
83
|
-
Movie.create(name:
|
84
|
-
Movie.create(name:
|
84
|
+
User.create(name: 'Some One', slug: 'someone'),
|
85
|
+
Movie.create(name: 'The Prestige'),
|
86
|
+
Movie.create(name: 'The Dark Knight')
|
85
87
|
]
|
86
88
|
|
87
89
|
expect {
|
88
90
|
followed.each do |item|
|
89
91
|
user.user_follows.create!(item: item)
|
90
92
|
end
|
91
|
-
}.to change{ user.neo_node.outgoing(:follows).length }.to(followed.length)
|
93
|
+
}.to change { user.neo_node.outgoing(:follows).length }.to(followed.length)
|
92
94
|
|
93
95
|
expect {
|
94
96
|
user.user_follows = user.user_follows[0...1]
|
95
|
-
}.to change{ user.neo_node.outgoing(:follows).length }.to(1)
|
97
|
+
}.to change { user.neo_node.outgoing(:follows).length }.to(1)
|
96
98
|
|
97
99
|
expect {
|
98
100
|
user.user_follows = []
|
99
|
-
}.to change{ user.neo_node.outgoing(:follows).length }.to(0)
|
101
|
+
}.to change { user.neo_node.outgoing(:follows).length }.to(0)
|
100
102
|
end
|
101
103
|
end
|
102
104
|
end
|
data/spec/neoid/search_spec.rb
CHANGED
@@ -1,30 +1,30 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Neoid::ModelAdditions do
|
4
|
-
context
|
4
|
+
context 'search' do
|
5
5
|
let(:index_name) { "articles_search_index_#{Time.now.to_f.to_s.gsub('.', '')}" }
|
6
|
-
|
7
|
-
it
|
8
|
-
Neoid.db.create_node_index(index_name,
|
9
|
-
|
10
|
-
n = Neography::Node.create(name:
|
11
|
-
Neoid.db.add_node_to_index(index_name,
|
12
|
-
Neoid.db.add_node_to_index(index_name,
|
13
|
-
|
6
|
+
|
7
|
+
it 'should index and find node in fulltext' do
|
8
|
+
Neoid.db.create_node_index(index_name, 'fulltext', 'lucene')
|
9
|
+
|
10
|
+
n = Neography::Node.create(name: 'test hello world', year: 2012)
|
11
|
+
Neoid.db.add_node_to_index(index_name, 'name', n.name, n)
|
12
|
+
Neoid.db.add_node_to_index(index_name, 'year', n.year, n)
|
13
|
+
|
14
14
|
[
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
'name:test',
|
16
|
+
'year:2012',
|
17
|
+
'name:test AND year:2012'
|
18
18
|
].each { |q|
|
19
19
|
results = Neoid.db.find_node_index(index_name, q)
|
20
|
-
results.length.
|
21
|
-
Neoid.db.send(:get_id, results).
|
20
|
+
expect(results.length).to eq(1)
|
21
|
+
expect(Neoid.db.send(:get_id, results)).to eq(n.neo_id)
|
22
22
|
}
|
23
23
|
end
|
24
|
-
|
25
|
-
it
|
24
|
+
|
25
|
+
it 'should index item on save' do
|
26
26
|
r = rand(1000000)
|
27
|
-
article = Article.create!(title: "Hello world #{r}", body:
|
27
|
+
article = Article.create!(title: "Hello world #{r}", body: 'Lorem ipsum dolor sit amet', year: r)
|
28
28
|
|
29
29
|
[
|
30
30
|
"title:#{r}",
|
@@ -33,58 +33,83 @@ describe Neoid::ModelAdditions do
|
|
33
33
|
].each do |q|
|
34
34
|
results = Neoid.db.find_node_index(Neoid::DEFAULT_FULLTEXT_SEARCH_INDEX_NAME, q)
|
35
35
|
|
36
|
-
results.
|
37
|
-
results.length.
|
38
|
-
Neoid.db.send(:get_id, results).
|
36
|
+
expect(results).to_not be_nil
|
37
|
+
expect(results.length).to eq(1)
|
38
|
+
expect(Neoid.db.send(:get_id, results)).to eq(article.neo_node.neo_id)
|
39
39
|
end
|
40
40
|
end
|
41
|
-
|
42
|
-
context
|
43
|
-
it
|
44
|
-
Article.neo_search(
|
41
|
+
|
42
|
+
context 'search session' do
|
43
|
+
it 'should return a search session' do
|
44
|
+
expect(Article.neo_search('hello')).to be_a(Neoid::SearchSession)
|
45
45
|
end
|
46
|
-
|
47
|
-
it
|
48
|
-
article = Article.create!(title:
|
49
|
-
|
50
|
-
Article.neo_search(
|
46
|
+
|
47
|
+
it 'should find hits' do
|
48
|
+
article = Article.create!(title: 'Hello world', body: 'Lorem ipsum dolor sit amet', year: 2012)
|
49
|
+
|
50
|
+
expect(Article.neo_search('hello').hits).to eq([article.neo_node])
|
51
51
|
end
|
52
|
-
|
53
|
-
it "should find results with a search string" do
|
54
|
-
article = Article.create!(title: "Hello world", body: "Lorem ipsum dolor sit amet", year: 2012)
|
55
52
|
|
56
|
-
|
53
|
+
it 'should find results with a search string' do
|
54
|
+
article = Article.create!(title: 'Hello world', body: 'Lorem ipsum dolor sit amet', year: 2012)
|
55
|
+
|
56
|
+
expect(Article.neo_search('hello').results).to eq([article])
|
57
57
|
end
|
58
|
-
|
59
|
-
it
|
58
|
+
|
59
|
+
it 'should find results with a hash' do
|
60
60
|
articles = [
|
61
|
-
Article.create!(title:
|
62
|
-
Article.create!(title:
|
61
|
+
Article.create!(title: 'How to draw manga', body: 'Lorem ipsum dolor sit amet', year: 2012),
|
62
|
+
Article.create!(title: 'Manga x', body: 'Lorem ipsum dolor sit amet', year: 2013)
|
63
63
|
]
|
64
64
|
|
65
65
|
|
66
|
-
Article.neo_search(year: 2012).results.
|
66
|
+
expect(Article.neo_search(year: 2012).results).to eq([articles[0]])
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
|
-
context
|
70
|
+
context 'in multiple types' do
|
71
71
|
before :each do
|
72
72
|
@articles = [
|
73
|
-
Article.create!(title:
|
74
|
-
Article.create!(title:
|
73
|
+
Article.create!(title: 'How to draw manga', body: 'Lorem ipsum dolor sit amet', year: 2012),
|
74
|
+
Article.create!(title: 'Manga x', body: 'Lorem ipsum dolor sit amet', year: 2012)
|
75
75
|
]
|
76
76
|
|
77
77
|
@movies = [
|
78
|
-
Movie.create!(name:
|
78
|
+
Movie.create!(name: 'Anime is not Manga', slug: 'anime')
|
79
79
|
]
|
80
80
|
end
|
81
81
|
|
82
|
-
it
|
83
|
-
Neoid.search([Article, Movie],
|
82
|
+
it 'should search in multiple types' do
|
83
|
+
expect(Neoid.search([Article, Movie], 'manga').results).to match_array(@articles + @movies)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should search in single type when specified' do
|
87
|
+
expect(Neoid.search([Article], 'manga').results).to match_array(@articles)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'matching types' do
|
92
|
+
before :each do
|
93
|
+
Article.create!(title: 'Comics: How to draw manga', body: 'Lorem ipsum dolor sit amet', year: 2012)
|
94
|
+
Article.create!(title: 'Manga x', body: 'Lorem ipsum dolor sit amet', year: 2012)
|
95
|
+
Article.create!(title: 'hidden secrets of comics masters', body: 'Lorem ipsum dolor sit amet', year: 2012)
|
96
|
+
Article.create!(title: 'hidden secrets of manga comics artists', body: 'Lorem ipsum dolor sit amet', year: 2012)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'will match by keyword' do
|
100
|
+
expect(Neoid.search([Article], 'manga comics').results.size).to eq(1)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'will match with AND' do
|
104
|
+
expect(Neoid.search([Article], 'manga comics', match_type: 'AND').results.size).to eq(1)
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'will match with OR' do
|
108
|
+
expect(Neoid.search([Article], 'manga comics', match_type: 'OR').results.size).to eq(4)
|
84
109
|
end
|
85
110
|
|
86
|
-
it
|
87
|
-
Neoid.search([Article],
|
111
|
+
it 'will fail with wrong match_type' do
|
112
|
+
expect { Neoid.search([Article], 'manga comics', match_type: 'MAYBE') }.to raise_error('Invalid match_type option. Valid values are AND,OR')
|
88
113
|
end
|
89
114
|
end
|
90
115
|
end
|
data/spec/neoid_spec.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Neoid do
|
4
|
-
context
|
5
|
-
|
4
|
+
context 'subrefs' do
|
5
|
+
before(:each) do
|
6
|
+
# first we have to enable subrefs
|
7
|
+
Neoid.config.enable_subrefs = true
|
8
|
+
# now that its enabled, we re-initialize the module
|
9
|
+
Neoid.initialize_all
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'should create all subrefs on initialization' do
|
6
13
|
Neoid.node_models.each do |klass|
|
7
|
-
klass.instance_variable_get(:@neo_subref_node).
|
14
|
+
expect(klass.instance_variable_get(:@neo_subref_node)).to_not be_nil
|
8
15
|
end
|
9
16
|
end
|
10
17
|
end
|
11
|
-
end
|
18
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,10 +2,23 @@ require 'neoid'
|
|
2
2
|
require 'active_record'
|
3
3
|
require 'neography'
|
4
4
|
require 'rest-client'
|
5
|
+
require 'codeclimate-test-reporter'
|
6
|
+
require 'factory_girl'
|
7
|
+
require 'rspec/its'
|
5
8
|
|
6
|
-
|
9
|
+
CodeClimate::TestReporter.start if ENV['CODECLIMATE_REPO_TOKEN']
|
7
10
|
|
8
|
-
|
11
|
+
require 'simplecov'
|
12
|
+
|
13
|
+
require "factories.rb"
|
14
|
+
|
15
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
16
|
+
SimpleCov::Formatter::HTMLFormatter,
|
17
|
+
]
|
18
|
+
|
19
|
+
SimpleCov.start
|
20
|
+
|
21
|
+
uri = URI.parse('http://localhost:7474')
|
9
22
|
$neo = Neography::Rest.new(uri.to_s)
|
10
23
|
|
11
24
|
Neography.configure do |c|
|
@@ -21,9 +34,13 @@ end
|
|
21
34
|
|
22
35
|
Neoid.db = $neo
|
23
36
|
|
37
|
+
Neoid.configure do |config|
|
38
|
+
config.enable_subrefs = false
|
39
|
+
end
|
40
|
+
|
24
41
|
logger, ActiveRecord::Base.logger = ActiveRecord::Base.logger, Logger.new('/dev/null')
|
25
42
|
ActiveRecord::Base.configurations = YAML::load(IO.read(File.join(File.dirname(__FILE__), 'support/database.yml')))
|
26
|
-
ActiveRecord::Base.establish_connection(
|
43
|
+
ActiveRecord::Base.establish_connection(:sqlite3)
|
27
44
|
|
28
45
|
require 'support/schema'
|
29
46
|
require 'support/models'
|
@@ -31,11 +48,13 @@ require 'support/models'
|
|
31
48
|
ActiveRecord::Base.logger = logger
|
32
49
|
|
33
50
|
RSpec.configure do |config|
|
51
|
+
config.include FactoryGirl::Syntax::Methods
|
52
|
+
|
34
53
|
config.mock_with :rspec
|
35
54
|
|
36
55
|
config.before(:all) do
|
37
56
|
end
|
38
|
-
|
57
|
+
|
39
58
|
config.before(:each) do
|
40
59
|
Neoid.node_models.each(&:destroy_all)
|
41
60
|
Neoid.clean_db(:yes_i_am_sure)
|
data/spec/support/database.yml
CHANGED
data/spec/support/models.rb
CHANGED
@@ -1,26 +1,26 @@
|
|
1
1
|
class User < ActiveRecord::Base
|
2
2
|
include ActiveModel::Validations::Callbacks
|
3
|
-
|
3
|
+
|
4
4
|
has_many :likes
|
5
5
|
has_many :movies, through: :likes
|
6
6
|
|
7
7
|
has_many :user_follows
|
8
|
-
|
8
|
+
|
9
9
|
def likes?(movie)
|
10
10
|
likes.where(movie_id: movie.id).exists?
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def like!(movie)
|
14
14
|
movies << movie unless likes?(movie)
|
15
15
|
likes.where(movie_id: movie.id).first
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def unlike!(movie)
|
19
|
-
likes.where(movie_id: movie.id, user_id:
|
19
|
+
likes.where(movie_id: movie.id, user_id: id).destroy_all
|
20
20
|
end
|
21
|
-
|
21
|
+
|
22
22
|
include Neoid::Node
|
23
|
-
|
23
|
+
|
24
24
|
neoidable do |c|
|
25
25
|
c.field :name
|
26
26
|
c.field :slug
|
@@ -29,12 +29,12 @@ end
|
|
29
29
|
|
30
30
|
class Movie < ActiveRecord::Base
|
31
31
|
include ActiveModel::Validations::Callbacks
|
32
|
-
|
32
|
+
|
33
33
|
has_many :likes
|
34
34
|
has_many :users, through: :likes
|
35
|
-
|
35
|
+
|
36
36
|
include Neoid::Node
|
37
|
-
|
37
|
+
|
38
38
|
neoidable do |c|
|
39
39
|
c.field :name
|
40
40
|
c.field :slug
|
@@ -55,9 +55,9 @@ class UserFollow < ActiveRecord::Base
|
|
55
55
|
|
56
56
|
belongs_to :user
|
57
57
|
belongs_to :item, polymorphic: true
|
58
|
-
|
58
|
+
|
59
59
|
include Neoid::Relationship
|
60
|
-
|
60
|
+
|
61
61
|
neoidable do |c|
|
62
62
|
c.relationship start_node: :user, end_node: :item, type: :follows
|
63
63
|
end
|
@@ -65,12 +65,12 @@ end
|
|
65
65
|
|
66
66
|
class Like < ActiveRecord::Base
|
67
67
|
include ActiveModel::Validations::Callbacks
|
68
|
-
|
68
|
+
|
69
69
|
belongs_to :user
|
70
70
|
belongs_to :movie
|
71
|
-
|
71
|
+
|
72
72
|
include Neoid::Relationship
|
73
|
-
|
73
|
+
|
74
74
|
neoidable do |c|
|
75
75
|
c.relationship start_node: :user, end_node: :movie, type: :likes
|
76
76
|
c.field :rate
|
@@ -84,7 +84,7 @@ class Article < ActiveRecord::Base
|
|
84
84
|
c.field :title
|
85
85
|
c.field :year
|
86
86
|
c.field :title_length do
|
87
|
-
|
87
|
+
title ? title.length : 0
|
88
88
|
end
|
89
89
|
|
90
90
|
c.search do |s|
|