Module: StewEucen::Acts::FertileForest::Table::Finders

Defined in:
lib/fertile_forest/modules/finders.rb

Overview

This module is for extending into derived class by ActiveRecord.
The caption contains “Instance Methods”, but it means “Class Methods” of each derived class.

Instance Method Summary collapse

Instance Method Details

#ancestors(base_obj, columns = nil) ⇒ ActiveRecord::Relation? Also known as: superiors, forebears

Find all ancestor nodes from base node (without base node).

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding ancestor nodes.

  • (nil)

    No ancestor nodes.



86
87
88
# File 'lib/fertile_forest/modules/finders.rb', line 86

def ancestors(base_obj, columns = nil)
  trunk(base_obj, ANCESTOR_ALL, columns)
end

#cenancestors(objects, columns = nil) ⇒ Entity?

Find cenancestor nodes of given nodes from base node.

Parameters:

  • objects (Array)

    Array of base nodes|ids to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Grandparent node.

  • (nil)

    No grandparent node.

Since:

  • 1.2.0



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/fertile_forest/modules/finders.rb', line 157

def cenancestors(objects, columns = nil)
  base_nodes = ff_resolve_nodes(objects)
  return nil if base_nodes.blank?

  entities = base_nodes.values

  # if bases include null, can not find.
  return nil if entities.include? nil

  # check same grove.
  if has_grove?
    groves = entities.map {|n| n.ff_grove }
    return nil if groves.min != groves.max
  end

  eldist_node = entities.first;
  aim_grove = eldist_node.ff_grove  # When no grove, nil

  ffqq = arel_table[@_ff_queue]
  ffdd = arel_table[@_ff_depth]
  ffgg = arel_table[@_ff_grove]

  queues = entities.map {|n| n.ff_queue }
  head_queue = queues.min
  tail_queue = queues.max

  # create subquery to find top-depth in range of head-tail.
  top_depth_subquery = ff_usual_projection(aim_grove)
      .project(ffdd.minimum.as('top_depth'))
      .where(ffqq.gteq(head_queue))
      .where(ffqq.lteq(tail_queue))

  # create subquery to find queues of ancestors.
  aim_group = [ffdd]
  aim_group.unshift(ffgg) if has_grove?

  cenancestor_nodes_subquery = ff_usual_projection(aim_grove)
      .project(ffqq.maximum.as('ancestor_queue'))
      .where(ffqq.lt(head_queue))
      .where(ffdd.lt(top_depth_subquery))
      .group(aim_group)

  # find nodes by ancestor queues
  # must use IN(), because trunk() is for general purpose to find ancestors
  # When one row, can use "=". When plural, can not use "=".
  # Error: SQLSTATE[21000]: Cardinality violation: 1242 Subquery returns more than 1 row
  ff_required_columns_scope()
      .ff_usual_conditions_scope(aim_grove)
      .ff_usual_order_scope()
      .where(ffqq.in(cenancestor_nodes_subquery))
      .select(ff_all_optional_columns(columns))
end

#children(base_obj, columns = nil) ⇒ ActiveRecord::Relation?

Find child nodes from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding child nodes.

  • (nil)

    No child nodes.



306
307
308
# File 'lib/fertile_forest/modules/finders.rb', line 306

def children(base_obj, columns = nil)
  subtree(base_obj, DESCENDANTS_ONLY_CHILD, false, columns)
end

#cousins(base_obj, columns = nil) ⇒ ActiveRecord::Relation?

Find cousin nodes from base node. Note: Results includes siblngs nodes.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding cousin nodes.

  • (nil)

    No cousin nodes.

Since:

  • 1.1.0



513
514
515
# File 'lib/fertile_forest/modules/finders.rb', line 513

def cousins(base_obj, columns = nil)
  kinships(base_obj, KINSHIPS_BRANCH_LEVEL_TWO, KINSHIPS_SAME_DEPTH, columns)
end

#descendants(base_obj, columns = nil) ⇒ ActiveRecord::Relation? Also known as: inferiors, afterbears

Find descendant nodes from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding descendant nodes.

  • (nil)

    No descendant nodes.



294
295
296
# File 'lib/fertile_forest/modules/finders.rb', line 294

def descendants(base_obj, columns = nil)
  subtree(base_obj, DESCENDANTS_ALL, false, columns)
end

#elder_sibling(base_obj, columns = nil) ⇒ Entity?

Find elder sibling node from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Elder sibling node of base node.

  • (nil)

    No elder sibling node.



571
572
573
# File 'lib/fertile_forest/modules/finders.rb', line 571

def elder_sibling(base_obj, columns = nil)
  offset_sibling(base_obj, -1, columns)
end

#genitor(base_obj, columns = nil) ⇒ Entity?

Find genitor (= parent) node from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Genitor node.

  • (nil)

    No genitor node.



98
99
100
101
102
103
104
# File 'lib/fertile_forest/modules/finders.rb', line 98

def genitor(base_obj, columns = nil)
  trunk_query = trunk(base_obj, ANCESTOR_ONLY_PARENT, columns)

  return nil if trunk_query.blank?

  trunk_query.first
end

#grandchildren(base_obj, columns = nil) ⇒ ActiveRecord::Relation?

Find grandchild nodes from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding grandchild nodes.

  • (nil)

    No grandchild nodes.



340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
# File 'lib/fertile_forest/modules/finders.rb', line 340

def grandchildren(base_obj, columns = nil)
  base_node = ff_resolve_nodes(base_obj)
  return [] if base_node.blank?

  grand_number = 2

  # return value
  subtree(
      base_node,
      grand_number,
      SUBTREE_WITHOUT_TOP_NODE,
      columns
    )
    .where(@_ff_depth => base_node.ff_depth + grand_number)
end

#grandparent(base_obj, columns = nil) ⇒ Entity?

Find grandparent node from base node.

Parameters:

  • base_obj (Entity|int)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Grandparent node.

  • (nil)

    No grandparent node.



134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/fertile_forest/modules/finders.rb', line 134

def grandparent(base_obj, columns = nil)
  base_node = ff_resolve_nodes(base_obj)
  return nil if base_node.blank?

  grand_number = 2

  grandparent_depth = base_node.ff_depth - grand_number
  return nil if grandparent_depth < ROOT_DEPTH

  trunk_query = trunk(base_node, grand_number, columns)
  return nil if trunk_query.blank?

  trunk_query.first
end

#grove_nodes(grove_id = nil, columns = nil) ⇒ ActiveRecord::Relation

TODO:

Pagination.

Find all nodes in grove.

Parameters:

  • grove_id (Integer|nil) (defaults to: nil)

    Grove ID to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Query for finding nodes.



700
701
702
703
# File 'lib/fertile_forest/modules/finders.rb', line 700

def grove_nodes(grove_id = nil, columns = nil)
  ff_usual_conditions_scope(grove_id)
    .ff_usual_order_scope()
end

#grovesActiveRecord::Relation

TODO:

Test.

Find grove informations that has enable (not soft deleted and not grove deleted).

Returns:

  • (ActiveRecord::Relation)

    Query for finding grove ids.



727
728
729
730
731
732
733
734
735
736
# File 'lib/fertile_forest/modules/finders.rb', line 727

def groves
  return nil unless has_grove?

  ffid = arel_table[@_ff_id]
  ffgg = arel_table[@_ff_grove]

  ff_usual_conditions_scope()
      .select([ffgg, ffid.count('*').as('ff_count')])
      .group([ffgg])
end

#internals(base_obj, columns = nil) ⇒ Array?

Find internal nodes from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Array)

    Basic query for finding internal nodes.

  • (nil)

    No internal nodes.



649
650
651
# File 'lib/fertile_forest/modules/finders.rb', line 649

def internals(base_obj, columns = nil)
  ff_features(base_obj, true, columns)
end

#kinships(base_obj, branch_level = KINSHIPS_BRANCH_LEVEL_ONE, depth_offset = KINSHIPS_SAME_DEPTH, columns = nil) ⇒ ActiveRecord::Relation?

Find any kind of kinship from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • branch_level (Integer) (defaults to: KINSHIPS_BRANCH_LEVEL_ONE)

    Branch distance of finding nodes from base nodes.

  • depth_offset (Integer) (defaults to: KINSHIPS_SAME_DEPTH)

    Offset of kinship level of finding nodes from base nodes.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding kinship nodes.

  • (nil)

    No kinship nodes.

Since:

  • 1.1.0



367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
# File 'lib/fertile_forest/modules/finders.rb', line 367

def kinships(
  base_obj,
  branch_level = KINSHIPS_BRANCH_LEVEL_ONE,
  depth_offset = KINSHIPS_SAME_DEPTH,
  columns = nil
)
  base_node = ff_resolve_nodes(base_obj)
  return nil if base_node.blank?

  aim_queue = base_node.ff_queue
  aim_depth = base_node.ff_depth
  aim_grove = base_node.ff_grove  # When no grove, nil

  top_depth = aim_depth - branch_level

  # Impossible to find.
  return nil if top_depth < ROOT_DEPTH

  return nil if branch_level + depth_offset < 0

  ffqq = arel_table[@_ff_queue]
  ffdd = arel_table[@_ff_depth]
  ffgg = arel_table[@_ff_grove]

  # create subquery
  before_nodes_subquery = ff_usual_projection(aim_grove)
      .project(ffqq.maximum.to_sql + " + 1 AS head_queue")
      .where(ffqq.lt(aim_queue))
      .where(ffdd.lteq(top_depth))

  after_nodes_subquery = ff_usual_projection(aim_grove)
      .project(ffqq.minimum.to_sql + " - 1 AS tail_queue")
      .where(ffqq.gt(aim_queue))
      .where(ffdd.lteq(top_depth))

  func_maker = Arel::Nodes::NamedFunction

  before_coalesce_condition = func_maker.new(
      'COALESCE',
      [before_nodes_subquery, 0]
  )

  after_coalesce_condition = func_maker.new(
      'COALESCE',
      [after_nodes_subquery, QUEUE_MAX_VALUE]
  )

  #
  # create column of ff_branch_level
  #
  increment_branch_level_case = ff_create_case_expression(
    [ffqq.eq(aim_queue).to_sql, 0],
    1
  )

  branch_for_level_subqueries = [increment_branch_level_case]
  ((top_depth + 1) .. (aim_depth - 1)).each do |d|
    before_nodes_branch_for_lavel_subquery = ff_usual_projection(aim_grove)
        .project("#{ffqq.maximum.to_sql} AS head_queue_#{d}")
        .where(ffqq.lt(aim_queue))
        .where(ffdd.lteq(d))

    after_nodes_branch_far_level_subquery = ff_usual_projection(aim_grove)
        .project("#{ffqq.minimum.to_sql} - 1 AS tail_queue_#{d}")
        .where(ffqq.gt(aim_queue))
        .where(ffdd.lteq(d))

    before_condition = func_maker.new(
        'COALESCE',
        [before_nodes_branch_for_lavel_subquery, 0]
    )
    after_condition = func_maker.new(
        'COALESCE',
        [after_nodes_branch_far_level_subquery, QUEUE_MAX_VALUE]
    )

    branch_for_level_subqueries << ff_create_case_expression(
      [ffqq.lt(before_condition).to_sql, 1],
      [ffqq.gt(after_condition).to_sql, 1],
      0
    )
  end

  ff_branch_level = branch_for_level_subqueries.join(' + ') + " AS #{@_ff_branch_level}";

  #
  # create columns
  #
  base_columns = ff_all_optional_columns(columns);
  base_columns << ff_branch_level

  # find nodes by ancestor queues
  ff_required_columns_scope()
      .ff_usual_conditions_scope(aim_grove)
      .ff_usual_order_scope()
      .where(ffdd.eq(aim_depth + depth_offset))
      .where(ffqq.gteq(before_coalesce_condition))
      .where(ffqq.lteq(after_coalesce_condition))
      .select(base_columns)
end

#leaves(base_obj, columns = nil) ⇒ Array? Also known as: externals, terminals

TODO:

Pagination, Create limit as desc.

Find leaf nodes from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Array)

    Basic query for finding leaf nodes.

  • (nil)

    No leaf nodes.



637
638
639
# File 'lib/fertile_forest/modules/finders.rb', line 637

def leaves(base_obj, columns = nil)
  ff_features(base_obj, false, columns)
end

#nested_ids(haystack_nodes) ⇒ Array

Create nested IDs

Parameters:

  • haystack_nodes (ActiveRecord::Relation|Array)

    Iteratorable nodes data.

Returns:

  • (Array)

    Nested IDs data.



826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
# File 'lib/fertile_forest/modules/finders.rb', line 826

def nested_ids(haystack_nodes)
  res = {}
  if haystack_nodes.present?
    if has_grove?
      haystack_nodes.each_pair do |grove_id, grove_nodes|
        res[grove_id] = {}
        grove_nodes.each_pair do |the_id, the_node|
          res[grove_id].merge!(ff_nest_children(the_id, grove_nodes))
        end
      end
    else
      haystack_nodes.each_pair do |the_id, the_node|
        res.merge!(ff_nest_children(the_id, haystack_nodes))
      end
    end
  end

  # return value
  res
end

#nested_nodes(haystack_nodes) ⇒ Hash|Array

Create nested nodes from subtree nodes.

Parameters:

  • haystack_nodes (ActiveRecord::Relation|Array)

    Iteratorable nodes data.

Returns:

  • (Hash|Array)

    When has grove is Hash, otherwise Array.



744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
# File 'lib/fertile_forest/modules/finders.rb', line 744

def nested_nodes(haystack_nodes)
  return {} if haystack_nodes.blank?

  #
  # pick up nodes by iterator
  #  (1) array
  #  (2) query
  #  (3) ResultSet
  # return by hash [id => node]
  #
  sorted_nodes = ff_queue_sorted_nodes(haystack_nodes)
  return {} if sorted_nodes.blank?

  # ネストがroot(=1)からじゃない場合の対応
  # queueで並べた先頭のdepthを暫定root depthとする
  the_root_depth = sorted_nodes[0].ff_depth

  sorted_nodes.each do |node|
    node.nest_unset_parent
    node.nest_unset_children
  end

  # 遡って見えているnodesを格納する配列
  retro_nodes = {}
  if has_grove?
    sorted_nodes.each do |node|
      retro_nodes[node.ff_grove] = []
    end
  else
    retro_nodes[:singular] = []
  end

  # 戻り値の生成用の、IDをキーとしたhashによるnest情報
  res_nested_nodes = {}

  grove_key = :singular
  sorted_nodes.each do |node|
    the_id = node.id
    depth = node.ff_depth

    grove_key = node.ff_grove if has_grove?

    res_nested_nodes[the_id] = node;  # 今回のnodesを登録

    depth_index = depth - the_root_depth   # ネストがroot(=1)からじゃない場合の対応
    parent_depth_index = depth_index - 1

    # このnodeに親があれば、親子関係を登録
    if 0 <= parent_depth_index && retro_nodes[grove_key][parent_depth_index].present?
      parent_id = retro_nodes[grove_key][parent_depth_index]

      res_nested_nodes[parent_id].nest_set_child_id(the_id)
      res_nested_nodes[the_id].nest_set_parent_id(parent_id)
    end

    # 今回の深度のところまで親リストを消した上で自分を登録する
    retro_nodes[grove_key] = retro_nodes[grove_key].slice(0, depth_index)
    retro_nodes[grove_key][depth_index] = the_id
  end

  return res_nested_nodes unless has_grove?

  # set grove hash
  grove_res = {}

  retro_nodes.keys.each do |grove|
    grove_res[grove] = {}
  end

  res_nested_nodes.each_pair do |id, node|
    grove_res[node.ff_grove][id] = node
  end

  grove_res
end

#niblings(base_obj, columns = nil) ⇒ ActiveRecord::Relation? Also known as: nephews, nieces

Find nibling nodes from base node. Note: Results includes siblngs nodes.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding nibling nodes.

  • (nil)

    No nibling nodes.

Since:

  • 1.1.0



539
540
541
# File 'lib/fertile_forest/modules/finders.rb', line 539

def niblings(base_obj, columns = nil)
  kinships(base_obj, KINSHIPS_BRANCH_LEVEL_ONE, KINSHIPS_CHILD_DEPTH, columns)
end

#nth_child(base_obj, nth = 0, columns = nil) ⇒ Entity?

Find nth-child node from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • nth (Integer) (defaults to: 0)

    Order in child nodes.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Nth-child node.

  • (nil)

    No nth-child node.



319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/fertile_forest/modules/finders.rb', line 319

def nth_child(base_obj, nth = 0, columns = nil)
  children_query = children(base_obj, columns)

  nth = nth.to_i
  if nth < 0
    sibling_count = children_query.all.length
    nth = sibling_count - 1
    return nil if nth < 0
  end

  children_query.offset(nth).first
end

#nth_sibling(base_obj, nth = 0, columns = nil) ⇒ Entity?

Find nth-sibling node from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • nth (Integer) (defaults to: 0)

    Order in child nodes.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Nth-sibling node.

  • (nil)

    No nth-sibling node.



552
553
554
555
556
557
558
559
560
561
# File 'lib/fertile_forest/modules/finders.rb', line 552

def nth_sibling(base_obj, nth = 0, columns = nil)
  base_node = ff_resolve_nodes(base_obj)
  return nil if base_node.blank?

  parent_node = genitor(base_node)
  return nil if parent_node.blank?

  nth = nth.to_i
  nth_child(parent_node, nth, columns)
end

#offset_sibling(base_obj, offset, columns = nil) ⇒ Entity?

Find offsetted sibling node from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • offset (Integer)

    Order in child nodes.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Offsetted sibling node from base node.

  • (nil)

    No offsetted sibling node.



596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
# File 'lib/fertile_forest/modules/finders.rb', line 596

def offset_sibling(base_obj, offset, columns = nil)
  base_node = ff_resolve_nodes(base_obj)
  return nil if base_node.blank?

  parent_node = genitor(base_node)
  return nil if parent_node.blank?

  sibling_nodes = children(parent_node, [@_id]).all
  return nil if sibling_nodes.blank?

  base_id = base_node.id

  nth = nil
  Array(sibling_nodes).each.with_index do |node, i|
    if node.id == base_id
      nth = i
      break
    end
  end

  return nil if nth.nil?

  offset = offset.to_i

  # OFFSET -1 make an error
  # Error: SQLSTATE[42000]: Syntax error or access violation: 1064 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 '-1' at line 1
  return nil if nth + offset < 0

  nth_child(parent_node, nth + offset, columns)
end

#piblings(base_obj, columns = nil) ⇒ ActiveRecord::Relation? Also known as: auncles, aunts, uncles

Find aunt|uncle nodes from base node. Note: Results includes siblngs nodes.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding aunt|uncle nodes.

  • (nil)

    No aunt|uncle nodes.

Since:

  • 1.1.0



526
527
528
# File 'lib/fertile_forest/modules/finders.rb', line 526

def piblings(base_obj, columns = nil)
  kinships(base_obj, KINSHIPS_BRANCH_LEVEL_TWO, KINSHIPS_PARENT_DEPTH, columns)
end

#root(base_obj, columns = nil) ⇒ Entity, Enil

Find root node from base node.

Parameters:

  • base_obj (Entity|int)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Root node. When base node is root, return base node.

  • (Enil)

    No root node.



114
115
116
117
118
119
120
121
122
123
124
# File 'lib/fertile_forest/modules/finders.rb', line 114

def root(base_obj, columns = nil)
  base_node = ff_resolve_nodes(base_obj)
  return nil if base_node.blank?

  return base_node if base_node.ff_depth == ROOT_DEPTH

  trunk_query = trunk(base_node, ANCESTOR_ONLY_ROOT, columns)
  return nil if trunk_query.blank?

  trunk_query.first
end

#roots(grove_id = nil, columns = nil) ⇒ ActiveRecord::Relation

TODO:

Pagination.

Find all root nodes in grove.

Parameters:

  • grove_id (Integer|nil) (defaults to: nil)

    Grove ID to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Query for finding nodes.



713
714
715
716
717
718
719
# File 'lib/fertile_forest/modules/finders.rb', line 713

def roots(grove_id = nil, columns = nil)
  grove_id = grove_id.to_i
  ffdd = arel_table[@_ff_depth]
  ff_usual_conditions_scope(grove_id)
      .ff_usual_order_scope()
      .where(ffdd.eq(ROOT_DEPTH))
end

#siblings(base_obj, columns = nil) ⇒ ActiveRecord::Relation?

Find sibling nodes from base node. Note: Results includes base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding sibling nodes.

  • (nil)

    No sibling nodes.

Version:

  • 1.1.0 Replace to use kinships()



500
501
502
# File 'lib/fertile_forest/modules/finders.rb', line 500

def siblings(base_obj, columns = nil)
  kinships(base_obj, KINSHIPS_BRANCH_LEVEL_ONE, KINSHIPS_SAME_DEPTH, columns)
end

#subtree(base_obj, range = DESCENDANTS_ALL, with_top = true, columns = nil) ⇒ ActiveRecord::Relation?

Find subtree nodes from base node with ordered range.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • range (Integer) (defaults to: DESCENDANTS_ALL)

    Ordered range of trunk nodes. -1:To designate as root node.

  • withTop (boolean)

    Include base node in return query.

  • fields (Array)

    Fields for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding subtree nodes.

  • (nil)

    No subtree nodes.



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/fertile_forest/modules/finders.rb', line 222

def subtree(base_obj, range = DESCENDANTS_ALL, with_top = true, columns = nil)
  base_node = ff_resolve_nodes(base_obj)
  return [] if base_node.blank?

  aim_query = ff_required_columns_scope(columns)
    .ff_subtree_scope(
      base_node,
      with_top,
      true   # use COALESCE() must be false for children().count
    )

  ffdd = arel_table[@_ff_depth]
  limited = ff_limited_subtree_depth(
    base_node.ff_depth,
    range,
    aim_query
  )

  aim_query.where!(ffdd.lteq(limited)) if 0 < limited

  aim_query
    .select(ff_all_optional_columns(columns))
    .ff_usual_order_scope()
end

#trunk(base_obj, range = ANCESTOR_ALL, columns = nil) ⇒ ActiveRecord::Relation?

Find trunk (= ancestor) nodes from base node in ordered range.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • range (Integer) (defaults to: ANCESTOR_ALL)

    Ordered range of trunk nodes. -1:To designate as root node.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (ActiveRecord::Relation)

    Basic query for finding trunk nodes.

  • (nil)

    No trunk nodes.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/fertile_forest/modules/finders.rb', line 35

def trunk(base_obj, range = ANCESTOR_ALL, columns = nil)
  base_node = ff_resolve_nodes(base_obj)
  return nil if base_node.blank?

  aim_queue = base_node.ff_queue
  aim_depth = base_node.ff_depth
  aim_grove = base_node.ff_grove  # When no grove, nil

  return nil if aim_depth == ROOT_DEPTH

  ffqq = arel_table[@_ff_queue]
  ffdd = arel_table[@_ff_depth]
  ffgg = arel_table[@_ff_grove]

  # create subquery to find queues of ancestor
  aim_subquery = ff_usual_projection(aim_grove)
      .where(ffqq.lt(aim_queue))

  if range < 0
    aim_subquery = aim_subquery.where(ffdd.eq(ROOT_DEPTH))
  else
    aim_subquery = aim_subquery.where(ffdd.lt(aim_depth))
    aim_subquery = aim_subquery.where(ffdd.gteq(aim_depth - range)) \
        if 0 < range
  end

  aim_group = [ffdd]
  aim_group.unshift(ffgg) if has_grove?

  aim_subquery = aim_subquery.project(ffqq.maximum.as('ancestor_queue'))
    .group(aim_group)

  # find nodes by ancestor queues
  # must use IN(), because trunk() is for general purpose to find ancestors
  # When one row, can use "=". When plural, can not use "=".
  # Error: SQLSTATE[21000]: Cardinality violation: 1242 Subquery returns more than 1 row
  ff_required_columns_scope()
      .ff_usual_conditions_scope(aim_grove)
      .ff_usual_order_scope()
      .where(ffqq.in(aim_subquery))
      .select(ff_all_optional_columns(columns))
end

#younger_sibling(base_obj, columns = nil) ⇒ Entity?

Find younger sibling node from base node.

Parameters:

  • base_obj (Entity|Integer)

    Base node|id to find.

  • columns (Array) (defaults to: nil)

    Columns for SELECT clause.

Returns:

  • (Entity)

    Younger sibling node of base node.

  • (nil)

    No younger sibling node.



583
584
585
# File 'lib/fertile_forest/modules/finders.rb', line 583

def younger_sibling(base_obj, columns = nil)
  offset_sibling(base_obj, 1, columns)
end