# * George Moschovitis # (c) 2004-2005 Navel, all rights reserved. # $Id: tree.rb 1 2005-04-11 11:04:30Z gmosx $ # NOT WORKING YET !!! raise 'This is not working yet, do not require this file.' require 'glue/attribute' module Og # A useful encapsulation of the nested intervals pattern for # hierarchical SQL queries. Slightly adapted from the original # article (http://www.dbazine.com/tropashko4.shtml) module TreeTraversal # The default prefix for the tree traversal helpers. cattr_accessor :prefix, 'tree' def self.child(sum, n) power = 2 ** n return sum * power end end end __END__ def xcoord(numer, denom) num = numer + 1 den = denom * 2 while (num / 2).floor == (num / 2) num /= 2 den /= 2 end return num, den end #-- # TODO: optimize this #++ def ycoord(numer, denom) num, den = xcoord(numer, denom) while den < denom num *= 2 den *= 2 end num = numer - num while (num / 2).floor == (num / 2) num /= 2 den /= 2 end return num, den end def parent(numer, denom) return nil if numer == 3 num = (numer - 1) / 2 den = denom / 2 while ((num-1)/4).floor == ((num-1)/4) num = (num + 1) / 2 den = den / 2 end return num, den end def sibling(numer, denom) return nil if numer == 3 num = (numer - 1) / 2 den = denom / 2 sib = 1 while ((num-1)/4).floor == ((num-1)/4) return sib if num == 1 and den == 1 num = (num + 1) / 2 den /= 2 sib += 1 end return sib end def child(numer, denom, n) power = 2 ** n num = (numer * power) + 3 - power den = denom * power return num, den end def path(numer, denom) return '' if numer == nil n, d = parent(numer, denom) return "#{path(n, d)}.#{sibling(numer, denom)}" end def encode(path) num = den = 1 postfix = ".#{path}." while postfix.length > 1 sibling, postfix = postfix.split('.', 2) num, den = child(num, den, sibling.to_i) end return num, den end require 'og' class Comment property :path, String property :x, :y, Float def initialize(path = nil, x = nil, y = nil) @path, @x, @y = path, x, y end end Og::Database.new( :database => 'test', :adapter => 'psql', :user => 'postgres', :password => 'navelrulez', :connection_count => 1, :drop => true ) def dp(path) n, d = encode(path) n, d = Float(n), Float(d) # puts "#{path} -> n: #{n} d: #{d} c: #{n/d}" p = path(n, d) # puts "=== #{p}" xn, xd = xcoord(n, d) yn, yd = ycoord(n, d) Comment.create(path, xn/xd, yn/yd) end dp '1.1' dp '1.2' dp '1.3' dp '1.1.2.1' dp '1.5.2.1' dp '1.2.1.1.1' dp '1.4.1.1' dp '1.4.3' dp '1.4.1' dp '1.2.1' dp '1.2.1.2' dp '1.5' dp '1.5.1' for c in Comment.all('ORDER BY x DESC, y ASC') puts "#{c.path.ljust(16)}#{c.inspect}" end class Article property :title, :body, String has_many: :comments, Comment, :tree => true end class Comment property :body, String belongs_to :article, Article belongs_to :parent, Comment has_many :children, Comment, :tree => true has_many :roles, Role, :list => true end article.comments_tree comment.add_child(Comment.new('hello')) comment.children_tree comment.children if options[:tree] code << %{ property :#{prefix}_x, Fixnum property :#{prefix}_y, Fixnum sql_index '#{prefix}_x, #{prefix}_y' } end if options[:tree] code << %{ def #{name}_tree(extrasql = nil) Og.db.select("SELECT * FROM #{Og::Adapter.table(klass)} WHERE #{linkback}=\#\@oid \#\{extrasql\} ORDER BY #{prefix}_x DESC, #{prefix}_y ASC", #{klass}) end } elsif options[:list] else end } if options[:tree] code << %{ n = Og.db.count("#{linkback}=\#\@oid", #{klass}) ptx = @#{prefix}_x || 0 pty = @#{prefix}_y || 0 obj.#{prefix}_x, obj.#{prefix}_y = TreeTraversal.child(ptx + pty, n) } end code << %{