fast_tree 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +73 -2
- data/lib/fast_tree/model/subtree/traverse.rb +20 -0
- data/lib/fast_tree/model.rb +43 -6
- data/lib/fast_tree/version.rb +1 -1
- data/lib/fast_tree.rb +7 -5
- data/lib/generators/active_record/fast_tree_generator.rb +1 -0
- data/lib/generators/active_record/templates/migration.rb +1 -0
- data/lib/generators/active_record/templates/migration_existing.rb +1 -0
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a2408d73a49d03de109154c5bc8897bfcbeadaf
|
4
|
+
data.tar.gz: 48bda26709f050ccc1b56ac535220aaf89169696
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59bd83c3ee0bb38e65dfe8ce9152619a18c65b06eddedfb65b765581e7afd354fc4687f82d25d86058c4d12894e20ad278c8eaa48802eafd4d2e4f30c78ee74f
|
7
|
+
data.tar.gz: c89f948f54b279fc8a857764995353abac0bf56e0bbb38e1ba6ef5afcb6e952757df66db150a1ce5c92211a813f2cd77feecb115d7036100c4baf7e71a210796
|
data/README.md
CHANGED
@@ -24,18 +24,30 @@ $ gem install fast_tree
|
|
24
24
|
## Usage
|
25
25
|
|
26
26
|
`fast_tree` provides a generator which adds left and right pointers used in nested sets model to your model class.
|
27
|
-
Even if you have created a class or not, execute following commands in
|
27
|
+
Even if you have created a target class or not, execute following commands in your terminal:
|
28
28
|
|
29
29
|
```bash
|
30
30
|
$ bin/rails g fast_tree YOUR_MODEL_NAME
|
31
31
|
```
|
32
32
|
|
33
|
-
|
33
|
+
and, execute migration by `bundle exec rake db:migrate`.
|
34
|
+
|
35
|
+
After that, add the following line into your model:
|
34
36
|
|
35
37
|
```ruby
|
36
38
|
include FastTree::Model
|
37
39
|
```
|
38
40
|
|
41
|
+
It seems like:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
class YOUR_MODEL_NAME < ApplicationRecord
|
45
|
+
include FastTree::Model
|
46
|
+
|
47
|
+
...
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
39
51
|
Finally, you can use several methods as class methods and instance methods.
|
40
52
|
|
41
53
|
If you are interested in how it works, see the section "How It Works" below.
|
@@ -137,6 +149,63 @@ target = YOUR_MODEL_NAME.second
|
|
137
149
|
root_of_subtree.move_to(targe)
|
138
150
|
```
|
139
151
|
|
152
|
+
### Find root
|
153
|
+
|
154
|
+
To get the root node from the tree,
|
155
|
+
|
156
|
+
```
|
157
|
+
root = YOUR_MODEL_NAME.find_root
|
158
|
+
```
|
159
|
+
|
160
|
+
### Deal with subtrees
|
161
|
+
|
162
|
+
To get subtree from a root node,
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
# root can be any node in the tree
|
166
|
+
root = YOUR_MODEL_NAME.take
|
167
|
+
root.subtree.each do |node|
|
168
|
+
# do something
|
169
|
+
end
|
170
|
+
```
|
171
|
+
|
172
|
+
NOTE: `subtree` method returns ActiveRelation, so that you can use `each`, `map` or anything you want!
|
173
|
+
|
174
|
+
### Tree traverse algorithms
|
175
|
+
|
176
|
+
In `fast_tree`, several tree-traverse algorithms, such as DFS and BFS, are provided.
|
177
|
+
|
178
|
+
#### DFS (Depth First Search)
|
179
|
+
|
180
|
+
To get nodes by DFS,
|
181
|
+
|
182
|
+
```ruby
|
183
|
+
root = YOUR_MODEL_NAME.take
|
184
|
+
root.subtree.dfs.each do |node|
|
185
|
+
# do something
|
186
|
+
end
|
187
|
+
```
|
188
|
+
|
189
|
+
#### BFS (Breadth First Search)
|
190
|
+
|
191
|
+
To get nodes by BFS,
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
root = YOUR_MODEL_NAME.take
|
195
|
+
root.subtree.bfs.each do |node|
|
196
|
+
# do something
|
197
|
+
end
|
198
|
+
```
|
199
|
+
|
200
|
+
### Boolean methods
|
201
|
+
|
202
|
+
`fast_tree` provides several boolean methods, such as:
|
203
|
+
|
204
|
+
- root?
|
205
|
+
- leaf?
|
206
|
+
- has_children?
|
207
|
+
|
208
|
+
, as instance methods.
|
140
209
|
|
141
210
|
## How It Works
|
142
211
|
The migration file will create a migration file, such as:
|
@@ -148,11 +217,13 @@ class AddFastTreeToTestTrees < ActiveRecord::Migration[5.0]
|
|
148
217
|
## Pointers
|
149
218
|
t.integer :l_ptr
|
150
219
|
t.integer :r_ptr
|
220
|
+
t.integer :depth
|
151
221
|
|
152
222
|
end
|
153
223
|
|
154
224
|
add_index :test_trees, :l_ptr
|
155
225
|
add_index :test_trees, :r_ptr
|
226
|
+
add_index :test_trees, :depth
|
156
227
|
end
|
157
228
|
|
158
229
|
def self.down
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module FastTree
|
2
|
+
module Model
|
3
|
+
module Subtree
|
4
|
+
module Traverse
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
scope :bfs, -> { order(depth: :asc, l_ptr: :asc) }
|
9
|
+
scope :dfs, -> { order(l_ptr: :asc) }
|
10
|
+
end
|
11
|
+
|
12
|
+
def subtree
|
13
|
+
self.class.where(self.class.arel_table[:l_ptr].gteq(l_ptr))
|
14
|
+
.where(self.class.arel_table[:r_ptr].lteq(r_ptr))
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/fast_tree/model.rb
CHANGED
@@ -2,6 +2,11 @@ module FastTree
|
|
2
2
|
module Model
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
included do
|
6
|
+
include FastTree::Model::Subtree::Traverse
|
7
|
+
end
|
8
|
+
|
9
|
+
|
5
10
|
# =================
|
6
11
|
# Class Methods
|
7
12
|
# =================
|
@@ -13,22 +18,28 @@ module FastTree
|
|
13
18
|
# ====================
|
14
19
|
|
15
20
|
def add_parent(parent, children, &block)
|
21
|
+
parent_depth = children.first.depth
|
22
|
+
|
16
23
|
# create space for parent
|
17
24
|
ptrs = _create_parent_embedded_space(children)
|
18
25
|
|
19
26
|
# parent node's pointer
|
20
27
|
parent.l_ptr = ptrs[:l_ptr]
|
21
28
|
parent.r_ptr = ptrs[:r_ptr]
|
29
|
+
parent.depth = parent_depth
|
22
30
|
parent.save
|
23
31
|
end
|
24
32
|
|
25
33
|
def create_parent(attributes = {}, children, &block)
|
34
|
+
parent_depth = children.first.depth
|
35
|
+
|
26
36
|
# create space for parent
|
27
37
|
ptrs = _create_parent_embedded_space(children)
|
28
38
|
|
29
39
|
# parent node's pointer
|
30
40
|
attributes[:l_ptr] = ptrs[:l_ptr]
|
31
41
|
attributes[:r_ptr] = ptrs[:r_ptr]
|
42
|
+
attributes[:depth] = parent_depth
|
32
43
|
|
33
44
|
self.create(attributes, &block)
|
34
45
|
end
|
@@ -40,6 +51,7 @@ module FastTree
|
|
40
51
|
else
|
41
52
|
attributes[:l_ptr] = 0
|
42
53
|
attributes[:r_ptr] = 1
|
54
|
+
attributes[:depth] = 0
|
43
55
|
self.create(attributes, &block)
|
44
56
|
end
|
45
57
|
end
|
@@ -69,6 +81,12 @@ UPDATE #{self.to_s.underscore.pluralize}
|
|
69
81
|
WHEN l_ptr > #{right}
|
70
82
|
THEN r_ptr + 2
|
71
83
|
ELSE r_ptr
|
84
|
+
END,
|
85
|
+
depth = CASE
|
86
|
+
WHEN l_ptr >= #{left}
|
87
|
+
AND r_ptr <= #{right}
|
88
|
+
THEN depth + 1
|
89
|
+
ELSE depth
|
72
90
|
END
|
73
91
|
WHERE r_ptr > #{left}
|
74
92
|
EOS
|
@@ -134,6 +152,7 @@ UPDATE #{self.to_s.underscore.pluralize}
|
|
134
152
|
# child node's pointer
|
135
153
|
node.l_ptr = r_ptr
|
136
154
|
node.r_ptr = r_ptr + 1
|
155
|
+
node.depth = depth + 1
|
137
156
|
node.save
|
138
157
|
end
|
139
158
|
|
@@ -144,6 +163,7 @@ UPDATE #{self.to_s.underscore.pluralize}
|
|
144
163
|
# create child
|
145
164
|
attributes[:l_ptr] = r_ptr
|
146
165
|
attributes[:r_ptr] = r_ptr + 1
|
166
|
+
attributes[:depth] = depth + 1
|
147
167
|
self.class.create(attributes, &block)
|
148
168
|
end
|
149
169
|
|
@@ -154,19 +174,17 @@ UPDATE #{self.to_s.underscore.pluralize}
|
|
154
174
|
_update_nodes(node.l_ptr, node.r_ptr, "r_ptr >= #{r_ptr}", width + 1)
|
155
175
|
|
156
176
|
bias = node.l_ptr + 1 - l_ptr
|
177
|
+
base_depth = depth
|
157
178
|
subtree.each do |st_node|
|
158
179
|
attributes = st_node.attributes.to_h
|
159
180
|
attributes.delete("id")
|
160
|
-
attributes["l_ptr"]
|
161
|
-
attributes["r_ptr"]
|
181
|
+
attributes["l_ptr"] += bias
|
182
|
+
attributes["r_ptr"] += bias
|
183
|
+
attributes["depth"] += node.depth - base_depth + 1
|
162
184
|
self.class.create(attributes)
|
163
185
|
end
|
164
186
|
end
|
165
187
|
|
166
|
-
def depth
|
167
|
-
path.size - 1
|
168
|
-
end
|
169
|
-
|
170
188
|
def move_to(node)
|
171
189
|
# NOTE:
|
172
190
|
# copy_to and remove change node ids
|
@@ -184,9 +202,11 @@ UPDATE #{self.to_s.underscore.pluralize}
|
|
184
202
|
|
185
203
|
# move subtree under the given node
|
186
204
|
bias = node.l_ptr + 1 - l_ptr
|
205
|
+
base_depth = depth
|
187
206
|
subtree.each do |st_node|
|
188
207
|
st_node.l_ptr += bias
|
189
208
|
st_node.r_ptr += bias
|
209
|
+
st_node.depth += node.depth - base_depth + 1
|
190
210
|
st_node.save
|
191
211
|
end
|
192
212
|
end
|
@@ -217,6 +237,23 @@ UPDATE #{self.to_s.underscore.pluralize}
|
|
217
237
|
end
|
218
238
|
|
219
239
|
|
240
|
+
#
|
241
|
+
# Boolean Methods
|
242
|
+
#
|
243
|
+
|
244
|
+
def root?
|
245
|
+
self.l_ptr == 0
|
246
|
+
end
|
247
|
+
|
248
|
+
def leaf?
|
249
|
+
l_ptr == r_ptr - 1
|
250
|
+
end
|
251
|
+
|
252
|
+
def has_children?
|
253
|
+
l_ptr != r_ptr - 1
|
254
|
+
end
|
255
|
+
|
256
|
+
|
220
257
|
protected
|
221
258
|
|
222
259
|
def _update_nodes(left, right, condition, diff = 2)
|
data/lib/fast_tree/version.rb
CHANGED
data/lib/fast_tree.rb
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
require 'rails'
|
2
2
|
|
3
|
-
module FastTree
|
4
|
-
|
5
|
-
# =====================================
|
6
|
-
# Implementation of Fast Tree Structure
|
7
|
-
# =====================================
|
8
3
|
|
4
|
+
module FastTree
|
9
5
|
autoload :Model, 'fast_tree/model'
|
10
6
|
|
7
|
+
module Model
|
8
|
+
module Subtree
|
9
|
+
autoload :Traverse, 'fast_tree/model/subtree/traverse'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
11
13
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fast_tree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chisato Hasegawa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -64,6 +64,7 @@ files:
|
|
64
64
|
- Rakefile
|
65
65
|
- lib/fast_tree.rb
|
66
66
|
- lib/fast_tree/model.rb
|
67
|
+
- lib/fast_tree/model/subtree/traverse.rb
|
67
68
|
- lib/fast_tree/version.rb
|
68
69
|
- lib/generators/active_record/fast_tree_generator.rb
|
69
70
|
- lib/generators/active_record/templates/migration.rb
|
@@ -91,7 +92,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
91
92
|
version: '0'
|
92
93
|
requirements: []
|
93
94
|
rubyforge_project:
|
94
|
-
rubygems_version: 2.5.
|
95
|
+
rubygems_version: 2.5.2
|
95
96
|
signing_key:
|
96
97
|
specification_version: 4
|
97
98
|
summary: '["Rails plugin for Fast Tree Structure", "fast_tree is an implementation
|