From tolsen718 at gmail.com Wed Oct 11 18:45:36 2006 From: tolsen718 at gmail.com (Tim Olsen) Date: Wed, 11 Oct 2006 18:45:36 -0400 Subject: [Betternestedset-talk] :scope appears to be broken Message-ID: <4be80d840610111545k3e5abcnabc014ced6b4d372@mail.gmail.com> There appears to be two bugs with :scope, one of which is conceptual. The regular bug is that it doesn't look like the scope condition is being evaluated. The ternary operator ? : is still visible here in this output from MySQL: ./db/../config/../vendor/rails/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb:120:in `log': Mysql::Error: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '? ? "root_id IS NULL" : "root_id = #{root_id}")' at line 1: SELECT max(rgt) AS max_rgt FROM dav_acl_inheritance WHERE (root_id.nil? ? "root_id IS NULL" : "root_id = #{root_id}") (ActiveRecord::StatementInvalid) The conceptual bug is that we are using the :scope condition in some class methods: def root self.find(:first, :conditions => "(#{acts_as_nested_set_options[:scope]} AND #{acts_as_nested_set_options[:parent_column]} IS NULL)") end # Returns roots when multiple roots (or virtual root, which is the same) def roots self.find(:all, :conditions => "(#{acts_as_nested_set_options[:scope]} AND #{acts_as_nested_set_options[:parent_column]} IS NULL)", :order => "#{acts_as_nested_set_options[:left_column]}") end end where the :scope may depend on evaluating an instance variable which won't exist in this case. Of course, it may be possible that the :scope only restricts a column to a constant, in which case using :scope in a class method may make sense. Maybe we should have a :scope_class (I'm open to a better name) that gets applied inside both class and instance methods, whereas :scope will only get applied inside instance methods? Also, looking at root() and roots(), it's kind of odd that they're in the same module as acts_as_nested_set() . We should probably put those in a module called SingletonMethods and have acts_as_nested_set extend() that. -Tim From jc.michel at symetrie.com Thu Oct 12 13:14:55 2006 From: jc.michel at symetrie.com (Jean-Christophe Michel) Date: Thu, 12 Oct 2006 19:14:55 +0200 Subject: [Betternestedset-talk] Fwd: Welcome all Message-ID: <4e0fccc3114ed040c338f9e5f678bb5c@symetrie.com> Hi, You are subscribed to the better nested set mailing list. We just moved to rubyforge. here are our todo list: 1. make sure rubyforge svn works correctly with script/install plugin 2. build gems 3. add tests to svn for all bugs in trac 4. correct the bugs ;-) Please report any error. The trac system is now protected, see at the bottom of the wiki page to discover login and passwd. Jean-Christophe Michel -- symetrie.com Better Nested Set for rails: http://opensource.symetrie.com/trac/better_nested_set From tolsen718 at gmail.com Fri Oct 13 14:24:01 2006 From: tolsen718 at gmail.com (Tim Olsen) Date: Fri, 13 Oct 2006 14:24:01 -0400 Subject: [Betternestedset-talk] tests Message-ID: <4be80d840610131124h4abb87f4k7b566c5cdf07a247@mail.gmail.com> Jean-Christophe, In order to apply your diff from the Rails ticket and to make sure that mixin_nested_set_test still works, I imported the ActiveRecord source code into a vendor directory in repository and copied the test directory into test/activerecord. But then when I looked at the Rakefile, I noticed the following task. desc 'Test the better_nested_set plugin.' Rake::TestTask.new(:test) do |t| t.libs << 'lib' t.test_files = ['test/preload_active_support.rb'] + FileList['test/t_*.rb'] + FileList['lib/*.rb'] t.verbose = true end How does your approach to running the tests compare to mine? Do you have the test files referenced in the task? thanks, Tim From jc.michel at symetrie.com Wed Oct 18 02:48:56 2006 From: jc.michel at symetrie.com (Jean-Christophe Michel) Date: Wed, 18 Oct 2006 08:48:56 +0200 Subject: [Betternestedset-talk] better nested set In-Reply-To: <469dcb990610091727k581440c5u2d91da789db4403b@mail.gmail.com> References: <469dcb990610091727k581440c5u2d91da789db4403b@mail.gmail.com> Message-ID: <88a459d7254b204655fae2b3f6bd04b7@symetrie.com> Hi, Sorry for the late reply. Le 10 oct. 06, ? 02:27, Harlan Crystal a ?crit : > I'm trying to use your "better nested set" plugin.? I've only > accessed the database through the "children" and "move_to_child_of" > function --- and it has managed to put the lft and rgt columns into an > inconsistant state. > > For instance, there are negative numbers in the lft/rgt columns in > some cases.? In others, there are overlapping ranges for sets which > unrelated.? In other cases, the value of lft is greater than the value > of rgt on the same row. > > > +----+----------------------+-----------+-----+-----+ > | id | name???????????????? | parent_id | lft | rgt | > +----+----------------------+-----------+-----+-----+ > |? 6 | Battery Park City??? |??????? 32 |?? 8 |?? 9 | > |? 7 | Chelsea????????????? |??????? 34 |? 10 |? 28 | > |? 8 | Chinatown??????????? |??????? 32 |?? 6 |?? 7 | > |? 9 | East Village???????? |??????? 32 |? -4 |? -3 | > | 10 | Financial/Battery Pk |??????? 32 |? -2 |? -1 | > | 11 | Flatiron???????????? |??????? 34 |? 31 |? 32 | > | 12 | Gramercy???????????? |??????? 34 |?? 3 |?? 4 | > | 13 | Harlem?????????????? |??????? 33 |? 45 |? 46 | > | 14 | Hells Kitchen??????? |??????? 34 |? 39 |? 40 | > | 15 | Little Italy???????? |??????? 32 |?? 0 |?? 1 | > | 16 | Lower East Side????? |??????? 32 |?? 2 |?? 5 | > | 17 | Meatpacking District |??????? 34 |? 29 |? 30 | > | 18 | Midtown East???????? |??????? 34 |? 33 |? 34 | > | 19 | Midtown West???????? |??????? 34 |? 11 |? 12 | > | 20 | Morningside Hts????? |??????? 33 |? 53 |? 54 | > | 21 | Murray Hill/Kips Bay |??????? 34 |? -9 |? 11 | > | 22 | Nolita?????????????? |??????? 32 |? -8 |? -7 | > | 23 | Other??????????????? |??????? 32 |? 42 |? 43 | > | 24 | Soho???????????????? |??????? 35 |? 43 |? 12 | > | 25 | Times Square???????? |??????? 34 |? 35 |? 36 | > | 26 | Tribeca????????????? |??????? 32 |? -6 |? -5 | > | 27 | Union Square???????? |??????? 34 |? 37 |? 38 | > | 28 | Upper East Side????? |??????? 33 |? 47 |? 48 | > | 29 | Upper West Side????? |??????? 33 |? 49 |? 50 | > | 30 | Wash Hts/Inwood????? |??????? 33 |? 51 |? 52 | > | 31 | West Village???????? |??????? 35 |?? 3 |?? 4 | > | 32 | Manhattan??????????? |????? NULL |?? 1 |? 56 | > | 33 | Uptown?????????????? |??????? 32 |? 44 |? 55 | > | 34 | Midtown????????????? |??????? 32 |? 10 |? 41 | > | 35 | Downtown???????????? |??????? 32 |?? 2 |? -9 | > +----+----------------------+-----------+-----+-----+ > Obviously there's a problem. Could you send us, to betternestedset-talk at rubyforge.org, the code you use to add or update your entries ? and the original consistent data you had when starting editing ? Jean-Christophe Michel -- symetrie.com Better Nested Set for rails: http://opensource.symetrie.com/trac/better_nested_set From tolsen718 at gmail.com Wed Oct 18 08:21:49 2006 From: tolsen718 at gmail.com (Tim Olsen) Date: Wed, 18 Oct 2006 08:21:49 -0400 Subject: [Betternestedset-talk] better nested set In-Reply-To: <88a459d7254b204655fae2b3f6bd04b7@symetrie.com> References: <469dcb990610091727k581440c5u2d91da789db4403b@mail.gmail.com> <88a459d7254b204655fae2b3f6bd04b7@symetrie.com> Message-ID: <4be80d840610180521t1cce8db8w8dcf8f59465775d2@mail.gmail.com> Hi Harlan, Are you resaving any of these objects to the database manually? It's very easy to cause inconsistencies with better_nested_set if you don't reload the parent, lft, and rgt values before saving the object. The better_nested_set plugin does direct sql commands (to gain the benefits of a nested_set implementation), so object values can change out from under you. -Tim On 10/18/06, Jean-Christophe Michel wrote: > Hi, > > Sorry for the late reply. > > Le 10 oct. 06, ? 02:27, Harlan Crystal a ?crit : > > I'm trying to use your "better nested set" plugin. I've only > > accessed the database through the "children" and "move_to_child_of" > > function --- and it has managed to put the lft and rgt columns into an > > inconsistant state. > > > > For instance, there are negative numbers in the lft/rgt columns in > > some cases. In others, there are overlapping ranges for sets which > > unrelated. In other cases, the value of lft is greater than the value > > of rgt on the same row. > > > > > > +----+----------------------+-----------+-----+-----+ > > | id | name | parent_id | lft | rgt | > > +----+----------------------+-----------+-----+-----+ > > | 6 | Battery Park City | 32 | 8 | 9 | > > | 7 | Chelsea | 34 | 10 | 28 | > > | 8 | Chinatown | 32 | 6 | 7 | > > | 9 | East Village | 32 | -4 | -3 | > > | 10 | Financial/Battery Pk | 32 | -2 | -1 | > > | 11 | Flatiron | 34 | 31 | 32 | > > | 12 | Gramercy | 34 | 3 | 4 | > > | 13 | Harlem | 33 | 45 | 46 | > > | 14 | Hells Kitchen | 34 | 39 | 40 | > > | 15 | Little Italy | 32 | 0 | 1 | > > | 16 | Lower East Side | 32 | 2 | 5 | > > | 17 | Meatpacking District | 34 | 29 | 30 | > > | 18 | Midtown East | 34 | 33 | 34 | > > | 19 | Midtown West | 34 | 11 | 12 | > > | 20 | Morningside Hts | 33 | 53 | 54 | > > | 21 | Murray Hill/Kips Bay | 34 | -9 | 11 | > > | 22 | Nolita | 32 | -8 | -7 | > > | 23 | Other | 32 | 42 | 43 | > > | 24 | Soho | 35 | 43 | 12 | > > | 25 | Times Square | 34 | 35 | 36 | > > | 26 | Tribeca | 32 | -6 | -5 | > > | 27 | Union Square | 34 | 37 | 38 | > > | 28 | Upper East Side | 33 | 47 | 48 | > > | 29 | Upper West Side | 33 | 49 | 50 | > > | 30 | Wash Hts/Inwood | 33 | 51 | 52 | > > | 31 | West Village | 35 | 3 | 4 | > > | 32 | Manhattan | NULL | 1 | 56 | > > | 33 | Uptown | 32 | 44 | 55 | > > | 34 | Midtown | 32 | 10 | 41 | > > | 35 | Downtown | 32 | 2 | -9 | > > +----+----------------------+-----------+-----+-----+ > > > > Obviously there's a problem. Could you send us, to > betternestedset-talk at rubyforge.org, > the code you use to add or update your entries ? and the original > consistent data you had when starting editing ? > > > > Jean-Christophe Michel > -- > symetrie.com > > Better Nested Set for rails: > http://opensource.symetrie.com/trac/better_nested_set > > _______________________________________________ > Betternestedset-talk mailing list > Betternestedset-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/betternestedset-talk > From jc.michel at symetrie.com Wed Oct 18 17:42:37 2006 From: jc.michel at symetrie.com (Jean-Christophe Michel) Date: Wed, 18 Oct 2006 23:42:37 +0200 Subject: [Betternestedset-talk] better nested set In-Reply-To: <4be80d840610180521t1cce8db8w8dcf8f59465775d2@mail.gmail.com> References: <469dcb990610091727k581440c5u2d91da789db4403b@mail.gmail.com> <88a459d7254b204655fae2b3f6bd04b7@symetrie.com> <4be80d840610180521t1cce8db8w8dcf8f59465775d2@mail.gmail.com> Message-ID: <8d020f85ce00474c5f87485c44d79c54@symetrie.com> Hi Tim, Le 18 oct. 06, ? 14:21, Tim Olsen a ?crit : > Are you resaving any of these objects to the database manually? It's > very easy to cause inconsistencies with better_nested_set if you don't > reload the parent, lft, and rgt values before saving the object. The > better_nested_set plugin does direct sql commands (to gain the > benefits of a nested_set implementation), so object values can change > out from under you. In order to prevent any inconsistencies, we could try to add a class variable datetime that would be compared to an instance var datetime: @@updated would be set by move_to_* methods @last_reloaded would be set by the autoreload method, and checkreload would be called before any read or write change to instance data, and would call autoreload if necessary. would probably be a bit heavy... Maybe a consistency_check method would be a good start... Would check that [all left]+[all right] === (1..(node count)*2).to_a Jean-Christophe Michel -- symetrie.com Better Nested Set for rails: http://opensource.symetrie.com/trac/better_nested_set From tolsen718 at gmail.com Thu Oct 19 12:04:45 2006 From: tolsen718 at gmail.com (Tim Olsen) Date: Thu, 19 Oct 2006 12:04:45 -0400 Subject: [Betternestedset-talk] better nested set In-Reply-To: <8d020f85ce00474c5f87485c44d79c54@symetrie.com> References: <469dcb990610091727k581440c5u2d91da789db4403b@mail.gmail.com> <88a459d7254b204655fae2b3f6bd04b7@symetrie.com> <4be80d840610180521t1cce8db8w8dcf8f59465775d2@mail.gmail.com> <8d020f85ce00474c5f87485c44d79c54@symetrie.com> Message-ID: <4be80d840610190904t37c237cfj7d2b1e833689d7e3@mail.gmail.com> I think such a strategy may accidentally overwrite any changes to other attributes that are not related to the nested set representation. Also, I'm not sure if we can depend 100% on the precision of datetime. In addition, I'm not sure how well this would work in the face of concurrency where class variables are not necessarily shared across different invocations of Ruby by mongrel or FastCGI. And then there's the case where Rails is split across several machines, where the only shared data is the session store and the database. I think a possibly better approach would be to assume that we always write to the better_nested_set attributes via direct SQL. Then we can add a before_update callback that reloads just those attributes. The number of SQL queries to do a save remains constant. I think having a consistency check is an excellent idea. We could provide an option to turn it on or off. We should put the check at the end of every one of our methods that does a SQL update or insert. To stay consistent, we can also call the consistency check inside an after_save callback. What do you think? Cheers, Tim On 10/18/06, Jean-Christophe Michel wrote: > Hi Tim, > > Le 18 oct. 06, ? 14:21, Tim Olsen a ?crit : > > Are you resaving any of these objects to the database manually? It's > > very easy to cause inconsistencies with better_nested_set if you don't > > reload the parent, lft, and rgt values before saving the object. The > > better_nested_set plugin does direct sql commands (to gain the > > benefits of a nested_set implementation), so object values can change > > out from under you. > > In order to prevent any inconsistencies, we could try to add a class > variable datetime that would be compared to an instance var datetime: > > @@updated would be set by move_to_* methods > @last_reloaded would be set by the > autoreload method, and > checkreload would be called before any read or write change to instance > data, and would call autoreload if necessary. > > > would probably be a bit heavy... > > Maybe a > consistency_check method would be a good start... > Would check that [all left]+[all right] === (1..(node count)*2).to_a > > Jean-Christophe Michel > -- > symetrie.com > > Better Nested Set for rails: > http://opensource.symetrie.com/trac/better_nested_set > > _______________________________________________ > Betternestedset-talk mailing list > Betternestedset-talk at rubyforge.org > http://rubyforge.org/mailman/listinfo/betternestedset-talk > From jc.michel at symetrie.com Thu Oct 19 13:03:35 2006 From: jc.michel at symetrie.com (Jean-Christophe Michel) Date: Thu, 19 Oct 2006 19:03:35 +0200 Subject: [Betternestedset-talk] a bit of theory and vocabulary Message-ID: <95d0fd2765a09e2f47bf84aef111ee15@symetrie.com> Hi, What does 'roots' concept mean ? In nested sets theory, we have one root for each tree. Example: menu (1,14) + Item 1 (2,3) + Item 2 (4,9) +SubItem1 (5,6) +SubItem2 (7,8) +Item3 (10,13) +SubItem 3 (11,12) In some cases you want to store multiple trees in the same table (call it a forrest ;-) The :scope is there to uniquely define one tree among the others. Another problem is related to menus or forum entries (threaded), where the root is never used. We could simply ignore it, and use root.children as displayed entries when all threads are closed. To avoid storing this useless item, I propose to use directly main items/entries, and store them in the tree without any parent. (As if I dropped the 'menu' node in the example above). This gives: (menu) = virtual root + Item 1 (1,2) + Item 2 (3,8) +SubItem1 (4,5) +SubItem2 (6,7) +Item3 (9,12) +SubItem 3 (10,11) If 'root' means 'that has no parent', Item 1, 2 and 3 are roots in this case. Of course it's not a perfect name (though some indian trees like banians have multiple roots afaik :-) but it leads to simpler code. Maybe changing this name and use the concept of 'virtual root' would be better ? We could have: def virtual_root? last = self.find(:first, :order => 'rgt DESC') # catch exceptions return last.lft != 1 end and rename roots to branches ;-) Jean-Christophe Michel -- symetrie.com Better Nested Set for rails: http://opensource.symetrie.com/trac/better_nested_set From jc.michel at symetrie.com Thu Oct 19 13:41:46 2006 From: jc.michel at symetrie.com (Jean-Christophe Michel) Date: Thu, 19 Oct 2006 19:41:46 +0200 Subject: [Betternestedset-talk] a bit of theory and vocabulary Message-ID: Forgot another point: What is exactly to be named a tree ? I propose that in nested set, a tree is a group of records that all together constitute a continuous serial with their left and right numbers, ranging from 1 to 2*(count(records)). WIth this definition, each scope defines a new tree, while virtual roots can be member of the same tree. From jc.michel at symetrie.com Thu Oct 19 18:03:35 2006 From: jc.michel at symetrie.com (Jean-Christophe Michel) Date: Fri, 20 Oct 2006 00:03:35 +0200 Subject: [Betternestedset-talk] check consistency Message-ID: <72708abe5224b2dcd60529fdb6b1270e@symetrie.com> Here is the sql to check some consistency in a tree: # check each lft and rgt values are unique SELECT * FROM mytree m1, mytree m2 WHERE (m1.lft=m2.lft OR m1.rgt=m2.rgt OR m1.lft=m2.rgt) AND m1.id<>m2.id; # check boundaries : 1..maxr, maxr = 2*count(leaves in tree) SELECT * FROM mytree m WHERE (m.lft NOT BETWEEN 1 AND 30) OR (m.rgt NOT BETWEEN 1 AND 30); Jean-Christophe Michel -- symetrie.com Better Nested Set for rails: http://opensource.symetrie.com/trac/better_nested_set