fast_tree 0.1.0 → 0.2.0
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 +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
|