This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-09-12
Channels
- # announcements (3)
- # babashka (6)
- # beginners (84)
- # biff (1)
- # cider (7)
- # cljsrn (1)
- # clojure (18)
- # clojure-australia (3)
- # clojure-dev (21)
- # clojure-france (1)
- # clojure-spec (6)
- # clojurescript (78)
- # datomic (34)
- # emacs (5)
- # exercism (32)
- # graalvm (1)
- # helix (2)
- # hyperfiddle (3)
- # lsp (36)
- # malli (4)
- # missionary (3)
- # off-topic (54)
- # re-frame (14)
- # releases (2)
- # sql (31)
- # vim (9)
I have the following query, which find all the quantities of products in storage and subtracts any reservations:
(defn find-quantities-for-product [product-eid]
(d/q '[:find ?sid ?pid ?q
:keys storage/id product/id quantity
:in $ [?p ...]
:where
[?sp :storage.product/product ?p]
[?s :storage/products ?sp]
[?s :storage/id ?sid]
[?p :product/id ?pid]
[?sp :storage.product/quantity ?sq]
[?r :reservation/product ?p]
[?r :reservation/storage ?s]
[?r :reservation/quantity ?rq]
[(- ?sq ?rq) ?q]]
@db
product-eid))
since Datalog unifies everything, this means that if something is in storage somewhere, but does not have a reservation, it does not get included in the result. I’ve tried every imaginable combination of or
, or-join
and get-else
to accomplish something like “if there is a reservation, get the amount for it, otherwise consider it 0”. Is there a way to do this or do I need to do two queries and manually combine the results?Would something like this work?
(def reservations-rules
'[[(reservation-quantity ?p ?s ?rq)
[?r :reservation/product ?p]
[?r :reservation/storage ?s]
[?r :reservation/quantity ?rq]]
[(reservation-quantity ?p ?s ?rq)
(not-join [?p]
[?r :reservation/product ?p]
[?r :reservation/storage ?s])
[(ground 0) ?rq]]])
(defn find-quantities-for-product [product-eid]
(d/q '[:find ?sid ?pid ?q
:keys storage/id product/id quantity
:in $ % [?p ...]
:where
[?sp :storage.product/product ?p]
[?s :storage/products ?sp]
[?s :storage/id ?sid]
[?p :product/id ?pid]
[?sp :storage.product/quantity ?sq]
(reservation-quantity ?p ?s ?rq)
[(- ?sq ?rq) ?q]]
(d/db conn-mem2) reservations-rules product-eid))
Assuming I got your schema right and trusting a few tests, I think this should work. But I'd also consider splitting it up into multiple queries and move the logic from the query into the code.
Sooner or later, all variables inside a not
will need unification. If I understand the semantics correctly, then in this case, the difference between not
and not-join
is that when invoking the second version of the rule reservation-quantity
, we don't want to (indeed cannot) unify ?r
.
depends on his schema, but it looks like a reservation is found by matching a storage and a product
So either the first version of the rule succeeds, with a unified reservation, or the second rule succeeds, but (and this is why using or
wouldn't work) both versions of the rule never succeed at the same time
Your observation on the schema is the assumption I made when testing this too. As an aside, if the product entities referenced the reservations directly, this query would have been a lot easier to write.
adding ?s
to the not-join
gives exactly what I need! for my understanding, is it possible to write this without rules? or is this “either or” behavior exclusive to rules? thank you very much @U024X3V2YN4 and @U09R86PA4! 🙏
@U3L6TFEJF where did you need to add the ?s
? Inside not-join [?p ?s]
?
gotcha, I’ll commit the not-join pattern to my brain! one more question for my understanding: why doesn’t or-join
work here? intuitively I’m asking for “this value or 0”, so it seems I’m not understanding what or-join means in Datomic fully :thinking_face:
ahh: > With or clauses, you can express that one or more logic variables inside a query satisfy *at least one* of a set of predicates.
You are wondering why this doesn't work?
(or-join [rq]
(and [?r :reservation/product ?p]
[?r :reservation/storage ?s]
[?r :reservation/quantity ?rq])
[(ground 0) ?rq])
If a product has a reservation, it will return the quantity both with and without the reservation applied
yeah, I was thinking about or
as “this or that, but not both”, but reading the docs it’s clear that it matches at least one
for whatever reason, without ?s
in the not-join
products in storages with no reservation don’t get included in the result
Of course, that makes sense. Not having ?s
in the vector means the unification of ?s
to only those storages with reservations "escapes" the not-join
.
always always always pass the db as the primary argument to a function that queries a database.
@U050ECB92 yessir, this is still very much at the sketching stage so I’m taking some liberties :thumbsup:
Is there a way to use fulltext search on a heterogeneous tuple with some string types? I can transact the following schema, but can't figure out how to get fulltext to match the entity
{ :db/ident :patient/name
:db/valueType :db.type/tuple
:db/cardinality :db.cardinality/many
:db/fulltext true
:db/tupleTypes [
:db.type/long
:db.type/keyword
:db.type/string
:db.type/string
:db.type/string
:db.type/string
:db.type/instant
:db.type/instant]}
My tests would seem to indicate dispite the succesful schema txn, there is no fulltext index generated for entities with :patient/name
I hope others can come up with a better workaround, but if you want to keep the tuple model I can suggest something like this:
(def schema [{:db/ident :
:db/valueType :db.type/long
:db/cardinality :db.cardinality/one}
{:db/ident :
:db/valueType :db.type/keyword
:db/cardinality :db.cardinality/one}
{:db/ident :
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true}
{:db/ident :
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true}
{:db/ident :
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true}
{:db/ident :
:db/valueType :db.type/string
:db/cardinality :db.cardinality/one
:db/fulltext true}
{:db/ident :
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one}
{:db/ident :
:db/valueType :db.type/instant
:db/cardinality :db.cardinality/one}
{:db/ident :patient/name
:db/valueType :db.type/tuple
:db/cardinality :db.cardinality/one
:db/fulltext true
:db/tupleAttrs [:
:
:
:
:
:
:
: