JohnSmall-acts-as-hausdorff-space 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.document +5 -0
  2. data/.gitignore +10 -0
  3. data/VERSION.yml +1 -1
  4. data/acts-as-hausdorff-space.gemspec +97 -0
  5. data/design_idea.rdoc +27 -0
  6. data/doc/LICENSE.html +113 -0
  7. data/doc/MindoroMarine.html +153 -0
  8. data/doc/MindoroMarine/Acts.html +150 -0
  9. data/doc/MindoroMarine/Acts/HausdorffSpace.html +160 -0
  10. data/doc/MindoroMarine/Acts/HausdorffSpace/ActMethods.html +230 -0
  11. data/doc/MindoroMarine/Acts/HausdorffSpace/ClassMethods.html +466 -0
  12. data/doc/MindoroMarine/Acts/HausdorffSpace/Gap.html +193 -0
  13. data/doc/MindoroMarine/Acts/HausdorffSpace/HSArray.html +234 -0
  14. data/doc/MindoroMarine/Acts/HausdorffSpace/InstanceMethods.html +1152 -0
  15. data/doc/MindoroMarine/Acts/HausdorffSpace/VirtualRoot.html +261 -0
  16. data/doc/README_rdoc.html +386 -0
  17. data/doc/created.rid +1 -0
  18. data/doc/images/brick.png +0 -0
  19. data/doc/images/brick_link.png +0 -0
  20. data/doc/images/bug.png +0 -0
  21. data/doc/images/bullet_black.png +0 -0
  22. data/doc/images/bullet_toggle_minus.png +0 -0
  23. data/doc/images/bullet_toggle_plus.png +0 -0
  24. data/doc/images/date.png +0 -0
  25. data/doc/images/find.png +0 -0
  26. data/doc/images/loadingAnimation.gif +0 -0
  27. data/doc/images/macFFBgHack.png +0 -0
  28. data/doc/images/package.png +0 -0
  29. data/doc/images/page_green.png +0 -0
  30. data/doc/images/page_white_text.png +0 -0
  31. data/doc/images/page_white_width.png +0 -0
  32. data/doc/images/plugin.png +0 -0
  33. data/doc/images/ruby.png +0 -0
  34. data/doc/images/tag_green.png +0 -0
  35. data/doc/images/wrench.png +0 -0
  36. data/doc/images/wrench_orange.png +0 -0
  37. data/doc/images/zoom.png +0 -0
  38. data/doc/index.html +140 -0
  39. data/doc/js/darkfish.js +116 -0
  40. data/doc/js/jquery.js +32 -0
  41. data/doc/js/quicksearch.js +114 -0
  42. data/doc/js/thickbox-compressed.js +10 -0
  43. data/doc/lib/acts-as-hausdorff-space_rb.html +55 -0
  44. data/doc/lib/acts_as_hausdorff_space_rb.html +53 -0
  45. data/doc/rdoc.css +696 -0
  46. data/lib/acts_as_hausdorff_space.rb +22 -14
  47. data/test/acts_as_hausdorff_space_test.rb +13 -0
  48. metadata +48 -4
@@ -0,0 +1,261 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
7
+
8
+ <title>Class: MindoroMarine::Acts::HausdorffSpace::VirtualRoot</title>
9
+
10
+ <link rel="stylesheet" href="../../../rdoc.css" type="text/css" media="screen" />
11
+
12
+ <script src="../../../js/jquery.js" type="text/javascript"
13
+ charset="utf-8"></script>
14
+ <script src="../../../js/thickbox-compressed.js" type="text/javascript"
15
+ charset="utf-8"></script>
16
+ <script src="../../../js/quicksearch.js" type="text/javascript"
17
+ charset="utf-8"></script>
18
+ <script src="../../../js/darkfish.js" type="text/javascript"
19
+ charset="utf-8"></script>
20
+
21
+ </head>
22
+ <body class="class">
23
+
24
+ <div id="metadata">
25
+ <div id="file-metadata">
26
+ <div id="file-list-section" class="section">
27
+ <h3 class="section-header">In Files</h3>
28
+ <div class="section-body">
29
+ <ul>
30
+
31
+ <li><a href="../../../lib/acts_as_hausdorff_space.rb.html?TB_iframe=true&amp;height=550&amp;width=785"
32
+ class="thickbox" title="lib/acts_as_hausdorff_space.rb">lib/acts_as_hausdorff_space.rb</a></li>
33
+
34
+ </ul>
35
+ </div>
36
+ </div>
37
+
38
+
39
+ </div>
40
+
41
+ <div id="class-metadata">
42
+
43
+ <!-- Parent Class -->
44
+
45
+ <div id="parent-class-section" class="section">
46
+ <h3 class="section-header">Parent</h3>
47
+
48
+ <p class="link">Object</p>
49
+
50
+ </div>
51
+
52
+
53
+ <!-- Namespace Contents -->
54
+
55
+
56
+ <!-- Method Quickref -->
57
+
58
+ <div id="method-list-section" class="section">
59
+ <h3 class="section-header">Methods</h3>
60
+ <ul class="link-list">
61
+
62
+ <li><a href="#M000001">::new</a></li>
63
+
64
+ </ul>
65
+ </div>
66
+
67
+
68
+ <!-- Included Modules -->
69
+
70
+ </div>
71
+
72
+ <div id="project-metadata">
73
+
74
+
75
+ <div id="fileindex-section" class="section project-section">
76
+ <h3 class="section-header">Files</h3>
77
+ <ul>
78
+
79
+ <li class="file"><a href="../../../LICENSE.html">LICENSE</a></li>
80
+
81
+ <li class="file"><a href="../../../README_rdoc.html">README.rdoc</a></li>
82
+
83
+ </ul>
84
+ </div>
85
+
86
+
87
+ <div id="classindex-section" class="section project-section">
88
+ <h3 class="section-header">Class Index
89
+ <span class="search-toggle"><img src="../../../images/find.png"
90
+ height="16" width="16" alt="[+]"
91
+ title="show/hide quicksearch" /></span></h3>
92
+ <form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
93
+ <fieldset>
94
+ <legend>Quicksearch</legend>
95
+ <input type="text" name="quicksearch" value=""
96
+ class="quicksearch-field" />
97
+ </fieldset>
98
+ </form>
99
+
100
+ <ul class="link-list">
101
+
102
+ <li><a href="../../../MindoroMarine.html">MindoroMarine</a></li>
103
+
104
+ <li><a href="../../../MindoroMarine/Acts/HausdorffSpace/ActMethods.html">MindoroMarine::Acts::HausdorffSpace::ActMethods</a></li>
105
+
106
+ <li><a href="../../../MindoroMarine/Acts/HausdorffSpace/ClassMethods.html">MindoroMarine::Acts::HausdorffSpace::ClassMethods</a></li>
107
+
108
+ <li><a href="../../../MindoroMarine/Acts/HausdorffSpace/Gap.html">MindoroMarine::Acts::HausdorffSpace::Gap</a></li>
109
+
110
+ <li><a href="../../../MindoroMarine/Acts/HausdorffSpace/HSArray.html">MindoroMarine::Acts::HausdorffSpace::HSArray</a></li>
111
+
112
+ <li><a href="../../../MindoroMarine/Acts/HausdorffSpace/InstanceMethods.html">MindoroMarine::Acts::HausdorffSpace::InstanceMethods</a></li>
113
+
114
+ <li><a href="../../../MindoroMarine/Acts/HausdorffSpace/VirtualRoot.html">MindoroMarine::Acts::HausdorffSpace::VirtualRoot</a></li>
115
+
116
+ </ul>
117
+ <div id="no-class-search-results" style="display: none;">No matching classes.</div>
118
+ </div>
119
+
120
+
121
+ </div>
122
+ </div>
123
+
124
+ <div id="documentation">
125
+ <h1 class="class">MindoroMarine::Acts::HausdorffSpace::VirtualRoot</h1>
126
+
127
+ <div id="description">
128
+ <p>
129
+ An instance of <a href="VirtualRoot.html">VirtualRoot</a> is a hidden root
130
+ that &#8220;owns&#8221; the actual roots. It&#8217;s only here so we can
131
+ use the same code for real roots and children. Otherwise we have to write
132
+ special code for the top level parents. Note: This is very different from
133
+ the concept of virtual roots in better_nested_set which is the equivalent
134
+ of <Klass>.root.children to get the immediate children of a top level root
135
+ </p>
136
+
137
+ </div>
138
+
139
+ <!-- Constants -->
140
+
141
+
142
+ <!-- Attributes -->
143
+
144
+ <div id="attribute-method-details" class="method-section section">
145
+ <h3 class="section-header">Attributes</h3>
146
+
147
+
148
+ <div id="left-col-val-attribute-method" class="method-detail">
149
+ <a name="left_col_val"></a>
150
+
151
+ <a name="left_col_val="></a>
152
+
153
+ <div class="method-heading attribute-method-heading">
154
+ <span class="method-name">left_col_val</span><span
155
+ class="attribute-access-type">[RW]</span>
156
+ </div>
157
+
158
+ <div class="method-description">
159
+
160
+ <p class="missing-docs">(Not documented)</p>
161
+
162
+ </div>
163
+ </div>
164
+
165
+ <div id="right-col-val-attribute-method" class="method-detail">
166
+ <a name="right_col_val"></a>
167
+
168
+ <a name="right_col_val="></a>
169
+
170
+ <div class="method-heading attribute-method-heading">
171
+ <span class="method-name">right_col_val</span><span
172
+ class="attribute-access-type">[RW]</span>
173
+ </div>
174
+
175
+ <div class="method-description">
176
+
177
+ <p class="missing-docs">(Not documented)</p>
178
+
179
+ </div>
180
+ </div>
181
+
182
+ <div id="children-attribute-method" class="method-detail">
183
+ <a name="children"></a>
184
+
185
+ <div class="method-heading attribute-method-heading">
186
+ <span class="method-name">children</span><span
187
+ class="attribute-access-type">[R]</span>
188
+ </div>
189
+
190
+ <div class="method-description">
191
+
192
+ <p class="missing-docs">(Not documented)</p>
193
+
194
+ </div>
195
+ </div>
196
+
197
+ </div>
198
+
199
+
200
+ <!-- Methods -->
201
+
202
+ <div id="public-class-method-details" class="method-section section">
203
+ <h3 class="section-header">Public Class Methods</h3>
204
+
205
+
206
+ <div id="new-method" class="method-detail ">
207
+ <a name="M000001"></a>
208
+
209
+ <div class="method-heading">
210
+
211
+ <span class="method-name">new</span><span
212
+ class="method-args">( left_val,right_val)</span>
213
+ <span class="method-click-advice">click to toggle source</span>
214
+
215
+ </div>
216
+
217
+ <div class="method-description">
218
+
219
+ <p class="missing-docs">(Not documented)</p>
220
+
221
+
222
+
223
+ <div class="method-source-code"
224
+ id="new-source">
225
+ <pre>
226
+ <span class="ruby-comment cmt"># File lib/acts_as_hausdorff_space.rb, line 50</span>
227
+ <span class="ruby-keyword kw">def</span> <span class="ruby-identifier">initialize</span>( <span class="ruby-identifier">left_val</span>,<span class="ruby-identifier">right_val</span>)
228
+ <span class="ruby-keyword kw">self</span>.<span class="ruby-identifier">left_col_val</span> = <span class="ruby-identifier">left_val</span>
229
+ <span class="ruby-keyword kw">self</span>.<span class="ruby-identifier">right_col_val</span> = <span class="ruby-identifier">right_val</span>
230
+ <span class="ruby-ivar">@children</span> = <span class="ruby-constant">HSArray</span>.<span class="ruby-identifier">new</span>
231
+ <span class="ruby-ivar">@children</span>.<span class="ruby-identifier">parent</span> = <span class="ruby-keyword kw">self</span>
232
+ <span class="ruby-keyword kw">end</span></pre>
233
+ </div>
234
+
235
+ </div>
236
+
237
+
238
+ </div>
239
+
240
+
241
+ </div>
242
+
243
+
244
+ </div>
245
+
246
+
247
+ <div id="rdoc-debugging-section-dump" class="debugging-section">
248
+
249
+ <p>Disabled; run with --debug to generate this.</p>
250
+
251
+ </div>
252
+
253
+ <div id="validator-badges">
254
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
255
+ <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
256
+ Rdoc Generator</a> 1.1.6</small>.</p>
257
+ </div>
258
+
259
+ </body>
260
+ </html>
261
+
@@ -0,0 +1,386 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
3
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
6
+ <head>
7
+ <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
8
+
9
+ <title>File: README.rdoc [RDoc Documentation]</title>
10
+
11
+ <link type="text/css" media="screen" href="./rdoc.css" rel="stylesheet" />
12
+
13
+ <script src="./js/jquery.js" type="text/javascript"
14
+ charset="utf-8"></script>
15
+ <script src="./js/thickbox-compressed.js" type="text/javascript"
16
+ charset="utf-8"></script>
17
+ <script src="./js/quicksearch.js" type="text/javascript"
18
+ charset="utf-8"></script>
19
+ <script src="./js/darkfish.js" type="text/javascript"
20
+ charset="utf-8"></script>
21
+ </head>
22
+
23
+ <body class="file">
24
+ <div id="metadata">
25
+ <div id="project-metadata">
26
+
27
+
28
+ <div id="fileindex-section" class="section project-section">
29
+ <h3 class="section-header">Files</h3>
30
+ <ul>
31
+
32
+ <li class="file"><a href="./LICENSE.html">LICENSE</a></li>
33
+
34
+ <li class="file"><a href="./README_rdoc.html">README.rdoc</a></li>
35
+
36
+ </ul>
37
+ </div>
38
+
39
+
40
+ <div id="classindex-section" class="section project-section">
41
+ <h3 class="section-header">Class Index
42
+ <span class="search-toggle"><img src="./images/find.png"
43
+ height="16" width="16" alt="[+]"
44
+ title="show/hide quicksearch" /></span></h3>
45
+ <form action="#" method="get" accept-charset="utf-8" class="initially-hidden">
46
+ <fieldset>
47
+ <legend>Quicksearch</legend>
48
+ <input type="text" name="quicksearch" value=""
49
+ class="quicksearch-field" />
50
+ </fieldset>
51
+ </form>
52
+
53
+ <ul class="link-list">
54
+
55
+ <li><a href="./MindoroMarine.html">MindoroMarine</a></li>
56
+
57
+ <li><a href="./MindoroMarine/Acts/HausdorffSpace/ActMethods.html">MindoroMarine::Acts::HausdorffSpace::ActMethods</a></li>
58
+
59
+ <li><a href="./MindoroMarine/Acts/HausdorffSpace/ClassMethods.html">MindoroMarine::Acts::HausdorffSpace::ClassMethods</a></li>
60
+
61
+ <li><a href="./MindoroMarine/Acts/HausdorffSpace/Gap.html">MindoroMarine::Acts::HausdorffSpace::Gap</a></li>
62
+
63
+ <li><a href="./MindoroMarine/Acts/HausdorffSpace/HSArray.html">MindoroMarine::Acts::HausdorffSpace::HSArray</a></li>
64
+
65
+ <li><a href="./MindoroMarine/Acts/HausdorffSpace/InstanceMethods.html">MindoroMarine::Acts::HausdorffSpace::InstanceMethods</a></li>
66
+
67
+ <li><a href="./MindoroMarine/Acts/HausdorffSpace/VirtualRoot.html">MindoroMarine::Acts::HausdorffSpace::VirtualRoot</a></li>
68
+
69
+ </ul>
70
+ <div id="no-class-search-results" style="display: none;">No matching classes.</div>
71
+ </div>
72
+
73
+
74
+ </div>
75
+ </div>
76
+
77
+ <div id="documentation">
78
+ <p>
79
+ <h2>acts_as_hausdorff_space</h2>
80
+ </p>
81
+ <p>
82
+ acts_as_hausdorff_space is a gemified mixin for Rails ActiveRecord. It
83
+ implements the nested set model for maintaining recursive trees in a
84
+ database, but using real numbers rather than integers because real numbers
85
+ are a <a href="http://en.wikipedia.org/wiki/Hausdorff_space">Hausdorff
86
+ Space</a> and the integers aren&#8217;t
87
+ </p>
88
+ <p>
89
+ <h2> Installation </h2>
90
+ </p>
91
+ <ol>
92
+ <li>Make sure you have <a href="http://gems.github.com">gems.github.com</a> set
93
+ up in your gem sources
94
+
95
+ </li>
96
+ <li>sudo gem install acts-as-hausdorff-space
97
+
98
+ </li>
99
+ <li>In environment.rb add <tt> config.gem
100
+ &quot;acts-as-hausdorff-space&quot;</tt>
101
+
102
+ </li>
103
+ <li>Create a migration with left and right columns using the biggest decimal
104
+ representations available on your database.
105
+
106
+ </li>
107
+ <li>Add
108
+
109
+ <pre>
110
+ &lt;tt&gt; acts_as_hausdorff_space &lt;/tt&gt;
111
+ </pre>
112
+ </li>
113
+ </ol>
114
+ <p>
115
+ to your model.
116
+ </p>
117
+ <ol>
118
+ <li>It&#8217;s not a drop in replacement for acts_as_nested_set so watch out.
119
+
120
+ </li>
121
+ </ol>
122
+ <p>
123
+ Other references
124
+ </p>
125
+ <ol>
126
+ <li>Source is at git@github.com:JohnSmall/acts-as-hausdorff-space.git
127
+
128
+ </li>
129
+ <li>Wiki is at
130
+
131
+ </li>
132
+ <li>Lighthouse bugtracking is at
133
+
134
+ </li>
135
+ <li>Google group for discussion is at <a
136
+ href="http://groups.google.com/group/acts_as_hausdorff_space">groups.google.com/group/acts_as_hausdorff_space</a>
137
+
138
+ </li>
139
+ <li>I&#8217;m at <a href="http://mindoro-marine.co.uk">John Small</a>
140
+
141
+ </li>
142
+ </ol>
143
+ <p>
144
+ <h2>A Problem with Nested Sets - Mega Peformance Issues</h2>
145
+ </p>
146
+ <p>
147
+ I was using the better_nested_set plugin in a territories model and trying
148
+ to load all the countries in the world and all the regions and sub-regions
149
+ and it was taking utterly ages. So I set about thinking what could be done
150
+ to improve performance. I&#8217;d written the code for nested sets before a
151
+ long time ago using Delphi &amp; Interbase, so I knew the problems. But in
152
+ the meantime I&#8217;d learned some topology theory so I had a name and a
153
+ concept to put to what must have been starting everyone in the face .
154
+ Hausdorff Spaces! Integers aren&#8217;t a Hausdorff space but we&#8217;re
155
+ trying to implement a concept, namely nested sets, which are the defining
156
+ characteristic of a Hausdorff space, so we have to write extra code to
157
+ fudge integers to behave like they are a Hausdorff space and that is the
158
+ source of the performance hit. So the obvious thing to do is to use real
159
+ numbers which are a Hausdorff space and then the extra code and associated
160
+ performance hit melts away. After some fussing about version 0.1.0 is now
161
+ released. For small trees the overhead of using BigDecimal rather than
162
+ integers makes this method slower, but as the trees get bigger the relative
163
+ performance soon switches in favour of using BigDecimals. check the table
164
+ at the bottom to see how big the performance improvements can be.
165
+ </p>
166
+ <p>
167
+ <h2>A bit of history</h2>
168
+ </p>
169
+ <p>
170
+ Joe Celko, an SQL guru, popularized the idea of using nested sets for
171
+ databases back in his 1996 article reproduced <a
172
+ href="http://www.dbmsmag.com/9603d06.html">here</a>. Though the earliest
173
+ description is in <a href="http://www.kamfonas.com/id3.html">Kamfonas</a>.
174
+ Since it&#8217;s usually a bad idea to go against the advice of an SQL guru
175
+ every implementation I&#8217;ve seen follows his example in using integers
176
+ to set the nesting boundaries. So every implementation has had to deal with
177
+ the awkwardness that comes with using integers in a way they can&#8217;t
178
+ inherently be used. The description in <a
179
+ href="http://www.kamfonas.com/id3.html">Kamfonas</a> recomends using a
180
+ SKIP-VALUE to make sure you&#8217;ve got some space to put new entries in.
181
+ Though the code for that is obviously going to be complicated because
182
+ you&#8217;d need to work out a skip value for each node if you&#8217;re
183
+ adding a collection of nodes inside an already existing node. This is the
184
+ kind of coding fudge people have to do to make nested sets maintainable.
185
+ </p>
186
+ <p>
187
+ <h2>The Basic Idea</h2> This is copied from <a
188
+ href="http://threebit.net/tutorials/nestedset/tutorial1.html">ThreeBit</a>
189
+ and is the example used in better_nested_sets. The example uses integers.
190
+ </p>
191
+ <p>
192
+ An easy way to visualize how a nested set works is to think of a parent
193
+ entity surrounding all of its children, and its parent surrounding it, etc.
194
+ So this tree:
195
+ </p>
196
+ <pre>
197
+ root
198
+ |_ Child 1
199
+ |_ Child 1.1
200
+ |_ Child 1.2
201
+ |_ Child 2
202
+ |_ Child 2.1
203
+ |_ Child 2.2
204
+ </pre>
205
+ <p>
206
+ Could be visualized like this:
207
+ </p>
208
+ <pre>
209
+ ___________________________________________________________________
210
+ | Root |
211
+ | ____________________________ ____________________________ |
212
+ | | Child 1 | | Child 2 | |
213
+ | | __________ _________ | | __________ _________ | |
214
+ | | | C 1.1 | | C 1.2 | | | | C 2.1 | | C 2.2 | | |
215
+ 1 2 3_________4 5________6 7 8 9_________10 11_______12 13 14
216
+ | |___________________________| |___________________________| |
217
+ |___________________________________________________________________|
218
+ </pre>
219
+ <p>
220
+ The numbers represent the left and right boundaries. The table then might
221
+ look like this:
222
+ </p>
223
+ <pre>
224
+ id | parent_id | lft | rgt | data
225
+ 1 | | 1 | 14 | root
226
+ 2 | 1 | 2 | 7 | Child 1
227
+ 3 | 2 | 3 | 4 | Child 1.1
228
+ 4 | 2 | 5 | 6 | Child 1.2
229
+ 5 | 1 | 8 | 13 | Child 2
230
+ 6 | 5 | 9 | 10 | Child 2.1
231
+ 7 | 5 | 11 | 12 | Child 2.2
232
+ </pre>
233
+ <p>
234
+ To get all children of an entry <tt>parent</tt>, you
235
+ </p>
236
+ <pre>
237
+ SELECT * WHERE lft IS BETWEEN parent.lft AND parent.rgt
238
+ </pre>
239
+ <p>
240
+ To get the number of children, it&#8217;s
241
+ </p>
242
+ <pre>
243
+ (right - left - 1)/2
244
+ </pre>
245
+ <p>
246
+ To get a node and all its ancestors going back to the root, you
247
+ </p>
248
+ <pre>
249
+ SELECT * WHERE node.lft IS BETWEEN lft AND rgt
250
+ </pre>
251
+ <h4></h4>
252
+ <p>
253
+ Notes. Pretty obviously if you wanted to add a new child with C.1.1 as
254
+ parent you&#8217;ll have to update the entire tree from 4 onwards because
255
+ there is no integer between 3 and 4.
256
+ </p>
257
+ <p>
258
+ <h2>A bit of topological theory</h2>
259
+ </p>
260
+ <p>
261
+ There is no integer between 3 and 4, but there are an uncountable infinity
262
+ of real numbers between 3 and 4. That is the crux of the problem. Between
263
+ any two real numbers there is always another real number, no matter how
264
+ close they are. This property was first described by Archimedes and
265
+ it&#8217;s called the <a
266
+ href="http://en.wikipedia.org/wiki/Archimedean_property#Archimedean_property_of_the_real_numbers">Archimedean
267
+ property of real numbers</a>
268
+ </p>
269
+ <p>
270
+ What that means is that for real numbers we can surround any two of them
271
+ with disjoint open neighbourhoods and that is the defining property of a <a
272
+ href="http://en.wikipedia.org/wiki/Hausdorff_space">Hausdorff Space</a>.
273
+ That property of being able to put any two points inside disjoint sets of
274
+ nearby points means you can have infinite levels of nesting. The notion of
275
+ nested sets is inherently part of a Hausdorff space, but not of the
276
+ integers, which is why you have to write lots of awkward slow code to
277
+ maintain nested sets implemented with integers
278
+ </p>
279
+ <p>
280
+ <h2>The Implementation in Abstract</h2>
281
+ </p>
282
+ <p>
283
+ The implementation is the same as above, with two differences. I&#8217;m
284
+ not using parent_id because that defeats the main advantage of the nested
285
+ set model over the adjaceny list model and I&#8217;m using real numbers.
286
+ That means if we want to add a record between 3 and 4, we can make lft =
287
+ 3.25 and rgt = 3.5, and we can keep on adding records to any depth or width
288
+ of tree we like, without having to update the rest of the tree.
289
+ </p>
290
+ <p>
291
+ <h2>The Real Life Implementation Constraints</h2>
292
+ </p>
293
+ <p>
294
+ Computers don&#8217;t use real numbers, they use binary arithmetic to
295
+ emulate real numbers. That means the lovely idea has to get ugly when it
296
+ meets the real world. There are maximum sizes for the real numbers used and
297
+ also minimum sizes depending on the real number precision limits. But
298
+ within those constraints we can implement the concept of nested sets using
299
+ real numbers fairly easily. We just have to keep our calculations away from
300
+ bumping up against the lower and upper limits. It means that there&#8217;s
301
+ going to be a limit to the number of children a parent node can own, and
302
+ also a limit to the depth of the tree. Most trees used in nested sets in
303
+ relational databases aren&#8217;t very deep, but they can be very wide.
304
+ When we add a new node into a gap in the tree we have to make sure it
305
+ leaves a gap between itself and its siblings or the parent lefts and
306
+ rights. That way we will always have some space to put a new record in the
307
+ gap, as long as the gap isn&#8217;t so small that we&#8217;re bumping up
308
+ against the limits of decimal precision. So when you set up your tables
309
+ always choose the maximum precision for that database.
310
+ </p>
311
+ <p>
312
+ The other issue when we add a new record, we want to leave space for more
313
+ records but we don&#8217;t know in advance how many records to leave space
314
+ for. We could do what&#8217;s done in the integer implementations of nested
315
+ sets, move every lft and rgt along a bit, but that is where the performance
316
+ hit comes from. So we have to leave gaps and fill them as best we can. If
317
+ each new node were to take up half of the remaining space between its
318
+ siblings and the boundaries of its parent, then we&#8217;d pretty soon bump
319
+ up against the decimal precision limit. To avoid that I&#8217;ve set things
320
+ up so that there&#8217;s a number called the bias, which is a division
321
+ factor. It works like this, I add a new child to a parent, I use the bias
322
+ to work out where to place the child inside what ever gap remains, roughly
323
+ (remaining gap)/bias. So the bigger the bias the less of the remaining gap
324
+ is taken up. The default bias is one million, to allow for wide and shallow
325
+ trees.
326
+ </p>
327
+ <p>
328
+ <h2> Relative Performance </h2>
329
+ </p>
330
+ <p>
331
+ For small trees integers are faster, for large trees
332
+ acts_as_hausdorff_space using BigDecimals are faster by a large margin. To
333
+ get a rough estimate of performance I wrote a test which adds 1000 children
334
+ to a root node, then goes through each child node adding children. This
335
+ really hammers the integer implementation of nested sets since every new
336
+ insertion requires updating every node to the right of the new insertion.
337
+ The full test description and updated results are <a
338
+ href="http://mindoro-marine.co.uk/nested_sets_tests">here</a>.
339
+ </p>
340
+ <p>
341
+ No. of children added is the number of child nodes added to each of the
342
+ 1000 children of the root. aahs = acts_as_hausdorff_space, bns = using the
343
+ better_nested_set mixin.
344
+ </p>
345
+ <pre>
346
+ | SQLite3 | MySQL |
347
+ No of | aahs | bns | aahs | bns |
348
+ children
349
+ added
350
+ 0 | 28 secs | 15 secs | 28 secs | 9 secs |
351
+ 1 | 39 | 55 | 40 | 150 |
352
+ 2 | 51 | 116 | 51 | 7,692 |
353
+ 3 | 62 | 203 | 63 | 24,555 |
354
+ 4 | 73 | 312 | 75 | I gave up |
355
+ 5 | 84 | 444 | 87 | |
356
+ 6 | 96 | 596 | 99 | |
357
+ 7 | 109 | 765 | 113 | |
358
+ 8 | 121 | 952 | 129 | |
359
+ 9 | 131 | 1156 | 144 | |
360
+ </pre>
361
+ <p>
362
+ As you can see adding children into large trees using
363
+ acts_as_hausdorff_space (aahs) is nice and linear in the number of children
364
+ added. Using the integer method implemented by better_nested_set things are
365
+ much slower and on MySQL the time taken blows up quite quickly.
366
+ </p>
367
+ <p>
368
+ I&#8217;ll be testing Postgres and trying to work out why MySQL blows up so
369
+ badly.
370
+ </p>
371
+ <h2>Copyright</h2>
372
+ <p>
373
+ Copyright &#169; 2009 John Small. See <a href="LICENSE.html">LICENSE</a>
374
+ for details.
375
+ </p>
376
+
377
+ </div>
378
+
379
+ <div id="validator-badges">
380
+ <p><small><a href="http://validator.w3.org/check/referer">[Validate]</a></small></p>
381
+ <p><small>Generated with the <a href="http://deveiate.org/projects/Darkfish-Rdoc/">Darkfish
382
+ Rdoc Generator</a> 1.1.6</small>.</p>
383
+ </div>
384
+ </body>
385
+ </html>
386
+