lexster 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e58713a9965ca82acd9d981adaa2451bbe1c85f4
|
4
|
+
data.tar.gz: 336d87ffd60736d34967f33edbd33b51d952d866
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1df55ed5565f0c84e31764481c1fafbfbc608fac158eefe6282c87906dcb7dafd13f0cdba678e66a70559337ee1cdb38e64745a30c51292f34d53fe0a7494075
|
7
|
+
data.tar.gz: a1303d614c49e858196be1a8cf33fea1c9408ae2d38a5f6e662b116ee1ddb9f88231dfb13ba1d99f719a27c8180ede559589ff7a3267e4bbe9fbaf5750f69f5d
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
## v0.1.1
|
2
|
+
|
3
|
+
* Added batch support, for much faster intiialization of current DB or reindexing all DB.
|
4
|
+
* Dropped indexes per model, instead, using `node_auto_index` and `relationship_auto_index`, letting Neo4j auto index objects.
|
5
|
+
* One `neo_save` method instead of `neo_create` and `neo_update`. It takes care of inserting or updating.
|
6
|
+
|
7
|
+
### Breaking changes:
|
8
|
+
|
9
|
+
Model indexes (such as `users_index`) are now turned off by default. Instead, Lexster uses Neo4j's auto indexing feature.
|
10
|
+
|
11
|
+
In order to have the model indexes back, use this in your configuration:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
Lexster.configure do |c|
|
15
|
+
c.enable_per_model_indexes = true
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
This will turn on for all models.
|
20
|
+
|
21
|
+
You can turn off for a specific model with:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class User < ActiveRecord::Base
|
25
|
+
include Lexster::Node
|
26
|
+
|
27
|
+
lexsterable enable_model_index: false do |c|
|
28
|
+
end
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
32
|
+
## v0.0.51
|
33
|
+
|
34
|
+
* Releasing Lexster as a gem.
|
35
|
+
|
36
|
+
## v0.0.41
|
37
|
+
|
38
|
+
* fixed really annoying bug caused by Rails design -- Rails doesn't call `after_destroy` when assigning many to many relationships to a model, like `user.movies = [m1, m2, m3]` or `user.update_attributes(params[:user])` where it contains `params[:user][:movie_ids]` list (say from checkboxes), but it DOES CALL after_create for the new relationships. the fix adds after_remove callback to the has_many relationships, ensuring neo4j is up to date with all changes, no matter how they were committed
|
39
|
+
|
40
|
+
## v0.0.4
|
41
|
+
|
42
|
+
* rewrote seacrch. one index for all types instead of one for type. please run neo_search_index on all of your models.
|
43
|
+
search in multiple types at once with `Lexster.search(types_array, term)
|
44
|
+
|
45
|
+
## v0.0.3
|
46
|
+
|
47
|
+
* new configuration syntax (backwards compatible)
|
48
|
+
* full text search index
|
49
|
+
|
50
|
+
## v0.0.2
|
51
|
+
|
52
|
+
* create node immediately after active record create
|
53
|
+
* logging
|
54
|
+
* bug fixes
|
55
|
+
|
56
|
+
## v0.0.1
|
57
|
+
|
58
|
+
* initial release
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2012 Elad Ossadon
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,467 @@
|
|
1
|
+
# Lexster
|
2
|
+
|
3
|
+
[![Build Status](https://secure.travis-ci.org/elado/lexster.png)](http://travis-ci.org/elado/lexster)
|
4
|
+
|
5
|
+
|
6
|
+
Make your ActiveRecords stored and searchable on Neo4j graph database, in order to make fast graph queries that MySQL would crawl while doing them.
|
7
|
+
|
8
|
+
Lexster to Neo4j is like Sunspot to Solr. You get the benefits of Neo4j speed while keeping your schema on your plain old RDBMS.
|
9
|
+
|
10
|
+
Lexster doesn't require JRuby. It's based on the great [Neography](https://github.com/maxdemarzi/neography) gem which uses Neo4j's REST API.
|
11
|
+
|
12
|
+
Lexster offers querying Neo4j for IDs of objects and then fetch them from your RDBMS, or storing all desired data on Neo4j.
|
13
|
+
|
14
|
+
**Important: Heroku Support is not available because Herokud doesn't support Gremlin. So until further notice, easiest way is to self host a Neo4j on EC2 in the same zone, and connect from your dyno to it**
|
15
|
+
|
16
|
+
## Changelog
|
17
|
+
|
18
|
+
[See Changelog](https://github.com/elado/lexster/blob/master/CHANGELOG.md). Including some breaking changes (and solutions) from previos versions.
|
19
|
+
|
20
|
+
|
21
|
+
## Installation
|
22
|
+
|
23
|
+
Add to your Gemfile and run the `bundle` command to install it.
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
gem 'lexster', '~> 0.1.1'
|
27
|
+
```
|
28
|
+
|
29
|
+
**Requires Ruby 1.9.2 or later.**
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
### Rails app configuration:
|
34
|
+
|
35
|
+
In an initializer, such as `config/initializers/01_neo4j.rb`:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
ENV["NEO4J_URL"] ||= "http://localhost:7474"
|
39
|
+
|
40
|
+
uri = URI.parse(ENV["NEO4J_URL"])
|
41
|
+
|
42
|
+
$neo = Neography::Rest.new(uri.to_s)
|
43
|
+
|
44
|
+
Neography.configure do |c|
|
45
|
+
c.server = uri.host
|
46
|
+
c.port = uri.port
|
47
|
+
|
48
|
+
if uri.user && uri.password
|
49
|
+
c.authentication = 'basic'
|
50
|
+
c.username = uri.user
|
51
|
+
c.password = uri.password
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Lexster.db = $neo
|
56
|
+
|
57
|
+
Lexster.configure do |c|
|
58
|
+
# should Lexster create sub-reference from the ref node (id#0) to every node-model? default: true
|
59
|
+
c.enable_subrefs = true
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
`01_` in the file name is in order to get this file loaded first, before the models (initializers are loaded alphabetically).
|
64
|
+
|
65
|
+
If you have a better idea (I bet you do!) please let me know.
|
66
|
+
|
67
|
+
|
68
|
+
### ActiveRecord configuration
|
69
|
+
|
70
|
+
#### Nodes
|
71
|
+
|
72
|
+
For nodes, first include the `Lexster::Node` module in your model:
|
73
|
+
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class User < ActiveRecord::Base
|
77
|
+
include Lexster::Node
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
This will help to create/update/destroy a corresponding node on Neo4j when changed are made a User model.
|
82
|
+
|
83
|
+
Then, you can customize what fields will be saved on the node in Neo4j, inside `lexsterable` configuration, using `field`. You can also pass blocks to save content that's not a real column:
|
84
|
+
|
85
|
+
```ruby
|
86
|
+
class User < ActiveRecord::Base
|
87
|
+
include Lexster::Node
|
88
|
+
|
89
|
+
lexsterable do |c|
|
90
|
+
c.field :slug
|
91
|
+
c.field :display_name
|
92
|
+
c.field :display_name_length do
|
93
|
+
self.display_name.length
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
```
|
98
|
+
|
99
|
+
#### Relationships
|
100
|
+
|
101
|
+
Let's assume that a `User` can `Like` `Movie`s:
|
102
|
+
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
# user.rb
|
106
|
+
|
107
|
+
class User < ActiveRecord::Base
|
108
|
+
include Lexster::Node
|
109
|
+
|
110
|
+
has_many :likes
|
111
|
+
has_many :movies, through: :likes
|
112
|
+
|
113
|
+
lexsterable do |c|
|
114
|
+
c.field :slug
|
115
|
+
c.field :display_name
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
# movie.rb
|
121
|
+
|
122
|
+
class Movie < ActiveRecord::Base
|
123
|
+
include Lexster::Node
|
124
|
+
|
125
|
+
has_many :likes
|
126
|
+
has_many :users, through: :likes
|
127
|
+
|
128
|
+
lexsterable do |c|
|
129
|
+
c.field :slug
|
130
|
+
c.field :name
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
|
135
|
+
# like.rb
|
136
|
+
|
137
|
+
class Like < ActiveRecord::Base
|
138
|
+
belongs_to :user
|
139
|
+
belongs_to :movie
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
|
144
|
+
Now let's make the `Like` model a Lexster, by including the `Lexster::Relationship` module, and define the relationship (start & end nodes and relationship type) options with `lexsterable` config and `relationship` method:
|
145
|
+
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
class Like < ActiveRecord::Base
|
149
|
+
belongs_to :user
|
150
|
+
belongs_to :movie
|
151
|
+
|
152
|
+
include Lexster::Relationship
|
153
|
+
|
154
|
+
lexsterable do |c|
|
155
|
+
c.relationship start_node: :user, end_node: :movie, type: :likes
|
156
|
+
end
|
157
|
+
end
|
158
|
+
```
|
159
|
+
|
160
|
+
Lexster adds the metohds `neo_node` and `neo_relationships` to instances of nodes and relationships, respectively.
|
161
|
+
|
162
|
+
So you could do:
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
user = User.create!(display_name: "elado")
|
166
|
+
user.movies << Movie.create("Memento")
|
167
|
+
user.movies << Movie.create("Inception")
|
168
|
+
|
169
|
+
user.neo_node # => #<Neography::Node…>
|
170
|
+
user.neo_node.display_name # => "elado"
|
171
|
+
|
172
|
+
rel = user.likes.first.neo_relationship
|
173
|
+
rel.start_node # user.neo_node
|
174
|
+
rel.end_node # user.movies.first.neo_node
|
175
|
+
rel.rel_type # 'likes'
|
176
|
+
```
|
177
|
+
|
178
|
+
#### Disabling auto saving to Neo4j:
|
179
|
+
|
180
|
+
If you'd like to save nodes manually rather than after_save, use `auto_index: false`:
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
class User < ActiveRecord::Base
|
184
|
+
include Lexster::Node
|
185
|
+
|
186
|
+
lexsterable auto_index: false do |c|
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
user = User.create!(name: "Elad") # no node is created in Neo4j!
|
191
|
+
|
192
|
+
user.neo_save # now there is!
|
193
|
+
```
|
194
|
+
|
195
|
+
## Querying
|
196
|
+
|
197
|
+
You can query with all [Neography](https://github.com/maxdemarzi/neography)'s API: `traverse`, `execute_query` for Cypher, and `execute_script` for Gremlin.
|
198
|
+
|
199
|
+
### Basics:
|
200
|
+
|
201
|
+
#### Finding a node by ID
|
202
|
+
|
203
|
+
Nodes and relationships are auto indexed in the `node_auto_index` and `relationship_auto_index` indexes, where the key is `Lexster::UNIQUE_ID_KEY` (which is 'lexster_unique_id') and the value is a combination of the class name and model id, `Movie:43`, this value is accessible with `model.neo_unique_id`. So use the constant and this method, never rely on assebling those values on your own because they might change in the future.
|
204
|
+
|
205
|
+
That means, you can query like this:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
Lexster.db.get_node_auto_index(Lexster::UNIQUE_ID_KEY, user.neo_unique_id)
|
209
|
+
# => returns a Neography hash
|
210
|
+
|
211
|
+
Lexster::Node.from_hash(Lexster.db.get_node_auto_index(Lexster::UNIQUE_ID_KEY, user.neo_unique_id))
|
212
|
+
# => returns a Neography::Node
|
213
|
+
```
|
214
|
+
|
215
|
+
#### Finding all nodes of type
|
216
|
+
|
217
|
+
If Subreferences are enabled, you can get the subref node and then get all attached nodes:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
Lexster.ref_node.outgoing('users_subref').first.outgoing('users').to_a
|
221
|
+
# => this, according to Neography, returns an array of Neography::Node so no conversion is needed
|
222
|
+
```
|
223
|
+
|
224
|
+
### Gremlin Example:
|
225
|
+
|
226
|
+
These examples query Neo4j using Gremlin for IDs of objects, and then fetches them from ActiveRecord with an `in` query.
|
227
|
+
|
228
|
+
Of course, you can store using the `lexsterable do |c| c.field ... end` all the data you need in Neo4j and avoid querying ActiveRecord.
|
229
|
+
|
230
|
+
|
231
|
+
**Most liked movies**
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
gremlin_query = <<-GREMLIN
|
235
|
+
m = [:]
|
236
|
+
|
237
|
+
g.v(0)
|
238
|
+
.out('movies_subref').out
|
239
|
+
.inE('likes')
|
240
|
+
.inV
|
241
|
+
.groupCount(m).iterate()
|
242
|
+
|
243
|
+
m.sort{-it.value}.collect{it.key.ar_id}
|
244
|
+
GREMLIN
|
245
|
+
|
246
|
+
movie_ids = Lexster.db.execute_script(gremlin_query)
|
247
|
+
|
248
|
+
Movie.where(id: movie_ids)
|
249
|
+
```
|
250
|
+
|
251
|
+
*Side note: the resulted movies won't be sorted by like count because the RDBMS won't necessarily do it as we passed a list of IDs. You can sort it yourself with array manipulation, since you have the ids.*
|
252
|
+
|
253
|
+
|
254
|
+
**Movies of user friends that the user doesn't have**
|
255
|
+
|
256
|
+
Let's assume we have another `Friendship` model which is a relationship with start/end nodes of `user` and type of `friends`,
|
257
|
+
|
258
|
+
```ruby
|
259
|
+
user = User.find(1)
|
260
|
+
|
261
|
+
gremlin_query = <<-GREMLIN
|
262
|
+
u = g.idx('node_auto_index').get(unique_id_key, user_unique_id).next()
|
263
|
+
movies = []
|
264
|
+
|
265
|
+
u
|
266
|
+
.out('likes').aggregate(movies).back(2)
|
267
|
+
.out('friends').out('likes')
|
268
|
+
.dedup
|
269
|
+
.except(movies).collect{it.ar_id}
|
270
|
+
GREMLIN
|
271
|
+
|
272
|
+
movie_ids = Lexster.db.execute_script(gremlin_query, unique_id_key: Lexster::UNIQUE_ID_KEY, user_unique_id: user.neo_unique_id)
|
273
|
+
|
274
|
+
Movie.where(id: movie_ids)
|
275
|
+
```
|
276
|
+
|
277
|
+
## Full Text Search
|
278
|
+
|
279
|
+
### Index for Full-Text Search
|
280
|
+
|
281
|
+
Using `search` block inside a `lexsterable` block, you can store certain fields.
|
282
|
+
|
283
|
+
```ruby
|
284
|
+
# movie.rb
|
285
|
+
|
286
|
+
class Movie < ActiveRecord::Base
|
287
|
+
include Lexster::Node
|
288
|
+
|
289
|
+
lexsterable do |c|
|
290
|
+
c.field :slug
|
291
|
+
c.field :name
|
292
|
+
|
293
|
+
c.search do |s|
|
294
|
+
# full-text index fields
|
295
|
+
s.fulltext :name
|
296
|
+
s.fulltext :description
|
297
|
+
|
298
|
+
# just index for exact matches
|
299
|
+
s.index :year
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
```
|
304
|
+
|
305
|
+
Records will be automatically indexed when inserted or updated.
|
306
|
+
|
307
|
+
### Querying a Full-Text Search index
|
308
|
+
|
309
|
+
```ruby
|
310
|
+
# will match all movies with full-text match for name/description. returns ActiveRecord instanced
|
311
|
+
Movie.neo_search("*hello*").results
|
312
|
+
|
313
|
+
# same as above but returns hashes with the values that were indexed on Neo4j
|
314
|
+
Movie.search("*hello*").hits
|
315
|
+
|
316
|
+
# search in multiple types
|
317
|
+
Lexster.neo_search([Movie, User], "hello")
|
318
|
+
|
319
|
+
# search with exact matches (pass a hash of field/value)
|
320
|
+
Movie.neo_search(year: 2013).results
|
321
|
+
```
|
322
|
+
|
323
|
+
Full text search with Lexster is very limited and is likely not to develop more than this basic functionality. I strongly recommend using gems like Sunspot over Solr.
|
324
|
+
|
325
|
+
## Batches
|
326
|
+
|
327
|
+
Lexster has a batch ability, that is good for mass updateing/inserting of nodes/relationships. It sends batched requests to Neography, and takes care of type conversion (neography batch returns hashes and other primitive types) and "after" actions (via promises).
|
328
|
+
|
329
|
+
A few examples, easy to complex:
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
Lexster.batch(batch_size: 100) do
|
333
|
+
User.all.each(&:neo_save)
|
334
|
+
end
|
335
|
+
```
|
336
|
+
With `then`:
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
User.first.name # => "Elad"
|
340
|
+
|
341
|
+
Lexster.batch(batch_size: 100) do
|
342
|
+
User.all.each(&:neo_save)
|
343
|
+
end.then do |results|
|
344
|
+
# results is an array of the script results from neo4j REST.
|
345
|
+
|
346
|
+
results[0].name # => "Elad"
|
347
|
+
end
|
348
|
+
```
|
349
|
+
|
350
|
+
*Nodes and relationships in the results are automatically converted to Neography::Node and Neography::Relationship, respectively.*
|
351
|
+
|
352
|
+
With individual `then` as well as `then` for the entire batch:
|
353
|
+
|
354
|
+
```ruby
|
355
|
+
Lexster.batch(batch_size: 30) do |batch|
|
356
|
+
(1..90).each do |i|
|
357
|
+
(batch << [:create_node, { name: "Hello #{i}" }]).then { |result| puts result.name }
|
358
|
+
end
|
359
|
+
end.then do |results|
|
360
|
+
puts results.collect(&:name)
|
361
|
+
end
|
362
|
+
```
|
363
|
+
|
364
|
+
When in a batch, `neo_save` adds gremlin scripts to a batch, instead of running them immediately. The batch flushes whenever the `batch_size` option is met.
|
365
|
+
So even if you have 20000 users, Lexster will insert/update in smaller batches. Default `batch_size` is 200.
|
366
|
+
|
367
|
+
|
368
|
+
## Inserting records of existing app
|
369
|
+
|
370
|
+
If you have an existing database and just want to integrate Lexster, configure the `lexsterable`s and run in a rake task or console.
|
371
|
+
|
372
|
+
Use batches! It's free, and much faster. Also, you should use `includes` to incude the relationship edges on relationship entities, so it doesn't query the DB on each relationship.
|
373
|
+
|
374
|
+
```ruby
|
375
|
+
Lexster.batch do
|
376
|
+
[ Like.includes(:user).includes(:movie), OtherRelationshipModel.includes(:from_model).includes(:to_model) ].each { |model| model.all.each(&:neo_save) }
|
377
|
+
|
378
|
+
NodeModel.all.each(&:neo_save)
|
379
|
+
end
|
380
|
+
```
|
381
|
+
|
382
|
+
This will loop through all of your relationship records and generate the two edge nodes along with a relationship (eager loading for better performance).
|
383
|
+
The second line is for nodes without relationships.
|
384
|
+
|
385
|
+
For large data sets use pagination.
|
386
|
+
Better interface for that in the future.
|
387
|
+
|
388
|
+
|
389
|
+
## Behind The Scenes
|
390
|
+
|
391
|
+
Whenever the `neo_node` on nodes or `neo_relationship` on relationships is called, Lexster checks if there's a corresponding node/relationship in Neo4j (with the auto indexes). If not, it does the following:
|
392
|
+
|
393
|
+
### For Nodes:
|
394
|
+
|
395
|
+
1. Ensures there's a sub reference node (read [here](http://docs.neo4j.org/chunked/stable/tutorials-java-embedded-index.html) about sub references), if that option is on.
|
396
|
+
2. Creates a node based on the ActiveRecord, with the `id` attribute and all other attributes from `lexsterable`'s field list
|
397
|
+
3. Creates a relationship between the sub reference node and the newly created node
|
398
|
+
4. Auto indexes a node in the auto index, for fast lookup in the future
|
399
|
+
|
400
|
+
Then, when it needs to find it again, it just seeks the auto index with that ActiveRecord id.
|
401
|
+
|
402
|
+
### For Relationships:
|
403
|
+
|
404
|
+
Like Nodes, it uses an auto index, to look up a relationship by ActiveRecord id
|
405
|
+
|
406
|
+
1. With the options passed in the `lexsterable`, it fetches the `start_node` and `end_node`
|
407
|
+
2. Then, it calls `neo_node` on both, in order to create the Neo4j nodes if they're not created yet, and creates the relationship with the type from the options.
|
408
|
+
3. Adds the relationship to the relationship index.
|
409
|
+
|
410
|
+
## Testing
|
411
|
+
|
412
|
+
In order to test your app or this gem, you need a running Neo4j database, dedicated to tests.
|
413
|
+
|
414
|
+
I use port 7574 for testing.
|
415
|
+
|
416
|
+
To run another database locally (read [here](http://docs.neo4j.org/chunked/1.9.M03/server-installation.html#_multiple_server_instances_on_one_machine) too):
|
417
|
+
|
418
|
+
Copy the entire Neo4j database folder to a different location,
|
419
|
+
|
420
|
+
**or**
|
421
|
+
|
422
|
+
symlink `bin`, `lib`, `plugins`, `system`, copy `conf` to a single folder, and create an empty `data` folder.
|
423
|
+
|
424
|
+
Then, edit `conf/neo4j-server.properties` and set the port (`org.neo4j.server.webserver.port`) from 7474 to 7574 and run the server with `bin/neo4j start`
|
425
|
+
|
426
|
+
## Testing Your App with Lexster (RSpec)
|
427
|
+
|
428
|
+
In `environments/test.rb`, add:
|
429
|
+
|
430
|
+
```ruby
|
431
|
+
ENV["NEO4J_URL"] = 'http://localhost:7574'
|
432
|
+
```
|
433
|
+
|
434
|
+
In your `spec_helper.rb`, add the following configurations:
|
435
|
+
|
436
|
+
```ruby
|
437
|
+
config.before :all do
|
438
|
+
Lexster.clean_db(:yes_i_am_sure)
|
439
|
+
end
|
440
|
+
|
441
|
+
config.before :each do
|
442
|
+
Lexster.reset_cached_variables
|
443
|
+
end
|
444
|
+
```
|
445
|
+
|
446
|
+
## Testing This Gem
|
447
|
+
|
448
|
+
Run the Neo4j DB on port 7574, and run `rake` from the gem folder.
|
449
|
+
|
450
|
+
## Contributing
|
451
|
+
|
452
|
+
Please create a [new issue](https://github.com/elado/lexster/issues) if you run into any bugs. Contribute patches via pull requests. Write tests and make sure all tests pass.
|
453
|
+
|
454
|
+
|
455
|
+
## Heroku Support
|
456
|
+
|
457
|
+
Unfortunately, as for now, Neo4j add-on on Heroku doesn't support Gremlin. Therefore, this gem won't work on Heroku's add on. You should self-host a Neo4j instance on an EC2 or any other server.
|
458
|
+
|
459
|
+
|
460
|
+
## TO DO
|
461
|
+
|
462
|
+
[TO DO](https://github.com/elado/lexster/blob/master/TODO.md)
|
463
|
+
|
464
|
+
|
465
|
+
---
|
466
|
+
|
467
|
+
Developed by [@elado](http://twitter.com/elado)
|