potassco / clorm

🗃️ A Python ORM-like interface for the Clingo Answer Set Programming (ASP) reasoner
https://clorm.readthedocs.io
MIT License
52 stars 5 forks source link

Query end-points to make it easier to replace facts in a factbase #116

Closed daveraja closed 11 months ago

daveraja commented 1 year ago

It can be useful to want to modify the facts in a factbase the way you might modify entries in a traditional database. However, clorm facts are immutable (see [1]). But we can come close to a modify mechanism by providing a query feature that can be used with the existing clone() method to make it easy to replace some existing fact in a fact base.

For example if you have a predicate P with a field a and you want to modify all a values of 1 with value of 2, currently you would have to do something like the following:

 query =  fb.query(P).where(P.a == 1)
 to_add = {p.clone(a=2) for p in query.all()}
 query.delete()
 fb.add(to_add)

This is a bit cumbersome and prone to mistakes. For example adding a fact to a factbase from inside a query of that factbase could lead to unpredictable interactions between the addition function and the query. Also it would make more sense to first delete the old items and then add the new items rather than the other way around in case these two sets have some intersection.

The new query replace feature would instead allow this to be done in a single declarative line:

 fb.query(P).where(P.a == 1).replace(lambda p: p.clone(a=2))

This query replace() feature should also handle queries with joins where we want to modify a set of related facts. We could also provide a function extend() that doesn't delete the original facts and simply allows new facts to be added.

[1] making clorm facts mutable isn't really an option since we want to be able to use it in a factbase and/or set (which means that it needs an immutable hash value).

daveraja commented 1 year ago

I have a first cut with of a replace(fn) and a modify(fn) end point. With replace(fn) fn returns a collection (list/set) of replace facts, while the query selected elements are deleted. In contrast modify(fn) is more general and fn must return a pair of collections where the first is the facts to delete and the second are the facts to add.

This change is in the clorm dev version on test.pypi.org and conda dev.

Some documentation:

https://clorm.readthedocs.io/en/latest/clorm/factbase.html#queries-that-modify-the-factbase

While these endpoints can be useful, the required parameter function signatures might make it too complicated and prone to error.

@MaxOstrowski I remember you were talking about wanting to modify facts, do you think this feature would be useful and not to complicated to use?

MaxOstrowski commented 1 year ago

I actually quite like the new functions. I'm a bit unsure how the modify function is supposed to work. Does it still get the query selection criteria as input like replace ? If yes, this sounds pretty useful.

daveraja commented 1 year ago

@MaxOstrowski Yes, the modify function still gets the selected facts as input. So it's just a more general version of replace where you can explicitly decide which facts to delete. But you're right that it is potentially confusing since you could also delete arbitrary facts that where not selected by the query.

I'm also tempted to have one more version of replace (either overloading replace or give it a new name) which would be a special case where you pass the replace function the named parameters of the fields to modify. So with the replace query:

   fb.query(P).where(P.a == 1).replace(lambda p: p.clone(a=2))

you could also write it as:

   fb.query(P).where(P.a == 1).replace(a=2)

I think it's pretty clear what it means if there is only one predicate selected, but it does create a bit of ambiguity if there are multiple predicates. For example if you had:

  class P(Predicate):
       a: int
       b: int

 class Q(Predicate):
      a: int
      b: int
      c: int

 fb.query(P,Q).join(P.a == Q.a).replace(b=4)
 fb.query(P,Q).join(P.a == Q.a).replace(c=5)

In these two queries it is not completely clear what a natural intuition would be for which facts should be deleted and which added.

MaxOstrowski commented 1 year ago

I think the first version is completely sufficient and there is no need to make things "easier" but ambiguous.