Fork me on GitHub
#datascript
<
2023-05-31
>
DFST06:05:57

I'm wondering whether there's an idiomatic way to do recursive reverse lookups that I'm missing. Here's the schema I'm working with:

{ :code/label    {:db/cardinality :db.cardinality/one}
   :code/children {:db/cardinality :db.cardinality/many
                  		 :db/valueType   :db.type/ref
                 		  :db/isComponent true}
   :matter/codes  {:db/cardinality :db.cardinality/many
                 		  :db/isComponent true
                 		  :db/valueType   :db.type/ref}
}
Every matter can point to zero or more codes, and codes can be nested to an arbitrary depth. Here, when I want to find all of the codes that are a component of a given matter, I can do a simple pull: (ds/pull ds-db '[:matter/code] eid) => all the nested codes. And if I want to find the immediate parent of a code entity, I can use a reverse lookup like this: (ds/pull ds-db '[:matter/_codes :code/_children] eid) => db/id of the parent. But I don't know how to find the root node, i.e. the matter eid, given the eid of any given code. I tried (ds/pull ds-db '[{:code/_children ...}] eid) based on what I thought might work from the datalog documentation, but that just returns nil. The only other approach I could think of was perhaps manually doing some kind of (loop ...) solution that would contain a pull function in it. This isn't really ideal, given that I'm trying to do this in the context of re-posh, which doesn't really make the datascript database visible to subscriptions, as far as I can tell. Any pointers would be really appreciated!

Josh13:05:48

Given that (ds/pull ds-db '[:matter/_codes :code/_children] eid) was used to find the first parent, I’d guess that (ds/pull ds-db '[{:code/_children ...}{:matter/_codes ...}] eid) would be used to find all of your parents, but I’m not sure I understand your data structure, could you give an example?

DFST14:05:26

Sure, here's the setup:

(let [schema {:code/label    {:db/cardinality :db.cardinality/one}
              :code/children {:db/cardinality :db.cardinality/many
                  		      :db/valueType   :db.type/ref
                 		      :db/isComponent true}
              :matter/codes  {:db/cardinality :db.cardinality/many
                 		      :db/isComponent true
                 		      :db/valueType   :db.type/ref}}
      conn (ds/create-conn schema)
      db (ds/db-with @conn [{:db/id -1
                             :matter/codes -2}
                            {:db/id -2
                             :code/label "A"
                             :code/children -3}
                            {:db/id -3
                             :code/label "A-1"
                             :code/children -4}
                            {:db/id -4
                             :code/label "A-1-1"}])
      ]
    .......
  )
(ds/pull db '[:code/_children :matter/_codes] 4) => #:code{:_children #:db{:id 3}} 
(ds/pull db '[:code/_children :matter/_codes] 3) => #:code{:_children #:db{:id 2}}
(ds/pull db '[:code/_children :matter/_codes] 2) => #:code{:_children #:db{:id 1}}
(ds/pull db '[*] 1) => { :db/id 2, :code/children [ { :db/id 3, :code/children [ { :db/id 4, :code/label "A-1-1" } ], :code/label "A-1" } ], :code/label "A" }

BUT

(ds/pull db '[{:code/_children ...}] 4) => nil
(ds/pull db '[{:code/_children ...} {:matter/_codes ...}] 4) => nil

DFST14:05:29

Poking around I see people referencing recursive rules, so maybe that's the way to get what I want? Could you help me see what that would look like?

Josh14:05:12

What happens if you do (ds/pull db '[:db/id {:code/_children ...}] 4)

Josh14:05:31

Wondering if that is a bug where the db/id doesn’t get added right

Josh14:05:10

In my opinion recursive rules suck because they are really slow, but yes you could write a recursive query rule to do this. I’m not really well versed in them because I don’t write them, but I learned them from here https://www.learndatalogtoday.org/chapter/8

DFST15:05:59

Bingo! Thanks! For anyone who finds this thread in the future, (ds/pull '[:db/id {:code/_children ...}{:matter/_codes ...}] 4) did the trick.

Josh17:05:12

@U050UBKAA is this a bug with datascript? I would think his first example should work

Niki16:06:58

Sorry, I’m lost, what exactly works and what doesn’t but looks like it should, also?

Josh16:06:01

I think (ds/pull db '[:db/id {:code/_children ...}] 4) works but (ds/pull db '[{:code/_children ...}] 4) does not

Josh16:06:16

but not exactly sure

Niki16:06:24

So like adding :db/id makes it start working?

Niki16:06:44

@U011FLPNDD2 can you make a minimal repro and report it to github issues if that’s the case?

DFST21:06:48

Sorry, I missed these. Yes, Josh said it correctly. @U050UBKAA