Fork me on GitHub

@mikerod I can't seem to create a minimal repro of this, which leaves me more confused.


{:?fact #roll_altc.LaunchConfigurationFact{:e #roll_altc.LaunchConfiguration{:id "app", :qualifier nil},
                                            :a :roll/created-for,
                                            :v #roll_altc.ASG{:id "app", :qualifier nil}},
  :?by #roll_altc.LaunchConfiguration{:id "app", :qualifier nil},
  :?for #roll_altc.ASG{:id "app", :qualifier nil}}
I definitely have this in my results though, which I just can't grasp.


I added a prn to do some debugging, and if I refer to ?for I get: > Using variable that is not previously bound. This can happen when an expression uses a previously unbound variable, or if a variable is referenced in a nested part of a parent expression, such as (or (= ?my-expression my-field) ...). I'm guessing this is suggesting that ?for is unbound, which is weird, but does explain what I am seeing.


I get that in the minimal case where it works too, so I guess that's not it.


> A parameter definition, which allow callers to control the scope of the query when it is called. Makes me think that params should be bound when queries start, but instead it seems like they're unbound, and filtered at the end. Is that true?


(defquery created-for-e
  [RollCreatedFor (= e ?e) (= ?v v)])
Is there a reason that :?e is optional, when I execute this query without an :?e I get no results, I thought it might be like in datalog rules where it would be treated as an unbound variable in that case.


Maybe bound to nil?


@dominicm are you using the with meta eav pattern? Also curious if you’re using def session or mk session and how you’re defining rules...I forget whether there’s any potential pitfalls with reloading them in clj


@alex-dixon I'm onto "namespace d" now, where I'm trying (defrecord AWSDBType [e v]) and (defrecord AWSDB [id qualifier])


I have experimented with 4 approaches now 🙂


What’s not working? Sorry kind of got lost


Oh, in the original question you mean? I was getting the ASG in the results, when I was specifying :?for as (->DBBox "main" nil)


Oh ok. Is there anything you’re stuck on still?


I'm still confused as to why I'm having issues with the query but I'm unable to create a minimal reproducible.


But otherwise, I'm doing okay. I think I've got a grip on this generally though.


(defrule DatabaseBox-Engine
  [?dbi <- AWSDBInstance]
  [?dbox <- DatabaseBox]
  [RollCreatedFor (= e ?dbi)]
  [DatabaseBoxEngine (= e ?dbox) (= ?v v)]
    (->AWSDBInstanceEngine ?dbi ?v)))
This inserts 2 AWSDBInstanceEngine rules, I think I understand why, it's due to how the dataset is being queried. Do rule engines / Does clara provide anything around stopping the insertion of facts twice here? I'm quite happy to "defend" on the LHS, rather than attempt to deduplicate on the RHS. I'm somewhat trying to avoid having to figure out conflict resolution in my final state.


@dominicm I haven’t been able to look at anything today. I can quickly answe your last question though: Clara doesn’t try to prevent duplicates. That’s up to your logical structuring. You can often do something like an accumulate all sort of rule to “de dupe “ or aggregate your duplicate facts and choose how to merge it. So you’d let some “intermediate type” be duplicated but have some downstream aggregate/merged type result from them and then be used in subsequent rules


It’d be interesting to have a mode for Clara to prevent duplicates. However there are practical use cases for actually wanting duplicates too. So the default I feel is sane. Think something about logic that cares about cardinality of matches etc. Also removing duplicates tends to be performance overhead.


@mikerod yeah, I actually want the many cardinality in some cases. I was hoping there might be a way to structure the rule such that there was no duplicates in the query.


I could give good example if not on phone. Hah


(defrule DatabaseBox-Engine
  [?dbi <- AWSDBInstance]
  [?dbox <- DatabaseBox]
  [RollCreatedFor (= e ?dbi)]
  [DatabaseBoxEngine (= e ?dbox) (= ?v v)]
[:not AWSDBInstanceEngine ?dbi ?v]
Maybe something like this? Sry on phone also


I tend to just let the rules insert duplicates. But make it a intermediate fact type. Then have a single rule that aggregates it.


There are negation patterns too. Like @alex-dixon just said. You just have to watch out for making a logical infinite loop

👍 4

Defrule example

Lhs Logic


Insert IntermediateThing data from rule

Defrule aggregate-dups 

?all <- acc/all :from IntermediateThing (= ?id is)


Insert RealThing (merge-somehow ?all)

👍 4

Pseudo code


Appreciate the help here. I'll take a shot at this tomorrow


Nb I think my example above would loop with insert logical, not if you were to use insert unconditional


I'm worried about unconditional insert, because I don't want ordering to become a problem. The intermediate facts also feel "icky", I think because they expose a part of the LHS which isn't able to match the user's view of the world. It feels more incidental, and less domain.


I like to be able to avoid unconditional inserts. But there are reasons where it can’t be easily avoided currently. Those are mostly around expressing removing old facts etc. I typically don’t use it though.


I don’t see intermediate facts as a bad thing. I think it is really common.


Often have to express intermediate concepts to connect rules for more complex logical conditions. At least in my experience with the rules I’ve needed to write.