stefankroes/ancestry

Последовательное нахождение...

dastanabeuov opened this issue · 6 comments

Здравствуйте! Подскажите пожалуйста. Как найти нужный узел?
Есть набор кодов условно full_code = 'BABAB' через которых созданы записи в базе.

Например:
#<Xclass id: 1957, title: "MyString", synonym: "MyString", description: "MyText", code: "A", ancestry: nil, user_id: 1, created_at: "2021-06-14 04:35:34", updated_at: "2021-06-14 04:35:34">

A

  • A
  • B
  • C
    • A
    • B
      full_code = 'AABCAB'

B

  • A
  • B
    • A
    • B
      full_code = 'BABAB'

Как мне найти нужный мне узел через коды? Сравнить и определить нужный мне узел (full_code == path || ancestors == full_code)

Я пытаюсь сделать
root = Model.roots.find_by_code(full_code[0])
child = root.children.find_by_code(full_code.[1])
child.children.each do |child|
child.children
end

Hello! Please tell me. How do I find the right node?
There is a set of codes conditionally full_code = 'BABAB' through which records are created in the database.

How can I find the node I need using the codes? Compare and determine the node I want (full_code == path || ancestors == full_code)

I understand this:

  def siblings_before
    found_me = false
    siblings.select do |node|
      found_me ||= (node == self)
      node unless found_me
    end.compact 
  end

  def full_code
    "#{path.map(&:name).join}#{siblings_before.map(:name).join}"
   end

ancestry does not do a good job with ordering among siblings. It does not work with duplicate ids either.
ancestry only works with unique ids in the path.

I'm guessing that you have a unique integer id (not shown) and you have a name (shown as a letter).

You may want to store a name_path (full_code[1]) that taps into ancestry and updates when the ancestry updates. I have done this before in other code, have not yet implemented this here. It would be updated when the depth cache is updated.

Not sure how to use full_code[0] -- it seems you would want to use a unique id for that first lookup rather than concatenate all the keys before it.

I'm having trouble understanding your use case. I'm sure you have a good reason for concatenating keys together. Since I don't understand, I'm having trouble coming up with a good way of solving it.

root = Model.roots.find_by(:name => full_code[0].last)
child = root.children..find_by(:name_path => full_code[0][0..-2], :name => full_code[1].last)

Good option, thanks for the answer. If I come across such a problem in the future it will definitely help.

But my problem is pretty simple. I have a banal import from an Excelx file.
I would like to just find only the desired node (by comparison through code) and from that node I would get the last object for which I will already create a child.

I can easily determine the root object

full_code = 'CAVYHK'
root = Model.roots.find_by_code(full_code[0])

Then I need to somehow automatically select all other descendants in depth by matching with the code

child_depth_1 = root.children.find_by_code(full_code[1])
child_depth_2 = child_depth_1.children.find_by_code(full_code[2])
child_depth_3 = child_depth_2.children.find_by_code(full_code[3])
child_depth_4 = child_depth_3.children.find_by_code(full_code[4])
and so on until the code runs out.........

Then, create at last object child - child_depth_4.children.create(code: full_code[5], title: 'MyString', synonym: 'MyString', description: 'MyText')

But further, finding the node corresponding to the code sequence remains an unsolved problem.
My import file - https://drive.google.com/file/d/1dTNSVkAc_Mnn2IMY-RRq8O4LRKX0NGs7/view?usp=sharing

Hello again! Today I tried another option using the cache in depth and it works for me)

I'm assuming, can do the type...
root = Model.roots.find_by_code(full_code[0])
parent = root.descendants.at_depth(full_code.length - 2).find_by_code(full_code[-2])
parent.children.create(code: full_code[-1], title: 'MyString', synonym: 'MyString', description: 'MyText')

But there are doubts that it is fast(

I agree. Something doesn't look right there.

If code was a unique field, you could just look up the parent by If the code field was unique, then you could just use find_by_code to look up the parent without any of this code.

You probably understand what ancestry does, but let me explain the why.

Ancestry uses "materialized path" which basically puts all the parents' ids into a single field with a slash between them. That way you can look up records with a single query. And look up all children with a single query and a wildcard (parent/%)

But this is optimized for lookups with a unique internal id. You are trying to look up trees by an external id. If you insist on looking up trees by a non-unique external id, then you probably want to materialize the path of the external ids so you can look them up in a single shot. code_ancestry = path.map(&:code). That way you know what the external value is on insert. Or store the full_code in each record.

Did you solve your problem?

Let me know if you need more help