I’ve got a hierarchical tree of data in a table named categories - each category can have a parent (as noted by the parent_id column, referring to the id column in that same table). I’m also using the PostgreSQL extension ltree and have a path column that keeps track of the branch of categories. And with all of this, what I’m trying to do is have an association that can be used via combine to get the ancestors of each category.
The statements of SQL that are being run are correct, and do return the correct records, but those records don’t get added to the entities. I’m very new to the ROM libraries, but from a bit of spelunking I’m wondering if the mapper that’s used to combine the results is involved - I think it’s comparing ids to parent ids to load up the associated data. Given I’m not just returning the direct parent, but also all parents, that’s probably not helpful - is it possible to customise the mapper’s behaviour somehow? But also, given it’s not even returning the direct parent, perhaps my guess here is completely off the mark and it’s something other than the mapper that I should be looking to change.
Any thoughts on how to better do this? Is what I’m trying to do even possible?
My current workaround is to manage the association query and respective mapping myself. Most of the code from the gist remains the same, but the entity and repository have changed:
module Types
include Dry.Types()
end
module Entities
class Category < ROM::Struct
attribute? :ancestors, Types::Array.of(self)
end
end
class CategoryRepo < ROM::Repository[:categories]
struct_namespace Entities
def with_ancestors
initial = categories.to_a
return [] if initial.empty?
collected_ancestors = ancestors(initial.map(&:id)).uniq
initial.map { |category|
category.new(ancestors: collected_ancestors.select { |ancestor|
category.path.value.split(".").include?(ancestor.id.to_s)
})
}
end
def with_ancestors_by_pk(id)
initial = categories.by_pk(id).one
initial.new(ancestors: ancestors(initial.id))
end
private
def ancestors(ids)
categories.full_hierarchy(id: ids).to_a
end
end
This works, and it’s probably fine in my particular case given I’m not running this code often. It’d be nice if I could wrap the LTree data type into something custom that split up the path into an array of labels (and memoized the value), but my first attempts at that didn’t work out, and I’ve had other priorities.
This solution does keep things to single separate queries for the core categories and the ancestor categories - which is what I was hoping for from the association initially. Granted, if there’s a way to wrap this up into the association logic anyway, that’d be even better.