zeroSteiner / rule-engine

A lightweight, optionally typed expression language with a custom grammar for matching arbitrary Python objects.
https://zerosteiner.github.io/rule-engine/
BSD 3-Clause "New" or "Revised" License
433 stars 54 forks source link

Cannot iterate over list of objects. #74

Closed KiranNadig62 closed 10 months ago

KiranNadig62 commented 10 months ago

I have initialised rules with the resolve_attribute context. I am passing in an object to the match method which has an array or list of objects.

The rule engine cannot identify this as an array and attribute resolution like .length fails. Iterators fail as well.

can not map python type 'MyDataType' to a compatible data type

$all([ smt.something - $today == t'P4D' for smt in list_of_objects ])

Also, it is not really clear how to define and use map and filter methods. Can we add in a couple of examples in the documentation?

zeroSteiner commented 10 months ago

You can iterate over objects.

rule > $all([ d - $today <= t'P4D' for d in [$today + t'P1D', $today]])  
result: 
True

I don't know where MyDataType is coming from. That's not defined in my code, so you'll need to provide more details in order for me to reproduce this.

Also, it is not really clear how to define and use map and filter methods. Can we add in a couple of examples in the documentation?

Map and filter work the same way as they do in Python. The first argument is a function, the second argument is an iterable and the result is an iterable of that function applied to each member of the second argument.

Rule Engine:

rule > $map($abs, [-1, 0, 1])
result: 
(Decimal('1'), Decimal('0'), Decimal('1'))

Python:

In [1]: tuple(map(abs, [-1, 0, 1]))
Out[1]: (1, 0, 1)

I'll see about adding something to the getting started page later.

KiranNadig62 commented 10 months ago

Here MyDataType is my Python class which I want to iterate over. It a model which I have defined - which is a list.

Regarding the filter and map methods - I understand how to use inbuild methods of the rule engine - my question was how do I run some custom logic on map and filter methods - a method which I can define.

Appreciate the quick response.

zeroSteiner commented 10 months ago

Here MyDataType is my Python class which I want to iterate over. It a model which I have defined - which is a list.

You're still going to have to be more specific. Can you please provide a script I can run that demonstrates the issue so I can reproduce it? The problem is likely related to your class or the datatypes of it's members.

Regarding the filter and map methods - I understand how to use inbuild methods of the rule engine - my question was how do I run some custom logic on map and filter methods - a method which I can define.

You'd just define your own function in Python, pass it in as a variable and use it as the function to apply. In the previous example you'd replace $abs with whatever function it was you wrote and exposed.

KiranNadig62 commented 10 months ago
    class MyDataType(BaseModel):

       user_details: UserDetails

       user_preferences: List[UserPreferences]

I want to iterate over UserPreferences [ //some logic// for preference in user_preference]

This is when rule engine fails can not map python type 'UserPreferences' to a compatible data type

I'm not sure if I'm doing something wrong - please let me know.

You'd just define your own function in Python, pass it in as a variable and use it as the function to apply. In the previous example you'd replace $abs with whatever function it was you wrote and exposed.

You mean passing in the method in the class / model I'm providing to the rule engine

   class MyDataType(BaseModel):
     user_details: UserDetails
     user_preferences: List[UserPreferences] 

     def my_method(self): 
        ##some logic. 

If the above is the case - then what are the arguments I can expect from the map or filter function of the rule engine I can consume in my method?

zeroSteiner commented 10 months ago

Yeah UserPreferences needs to be the builtin Python data types. A list of strings or whatever. You'll need to do that conversion yourself since Rule Engine doesn't know how to translate UserPreferences into whatever type you're expecting it to be. If I were you, I'd go into MyDataType and add a method like to_dict() that returns a dictionary with all of the columns you want mapped to their Python primitive data types. Otherwise if you wanted to retain the lazy lookups (I'm not even sure it really will be lazy) you'd need to define your own attribute resolver and ensure that it is mapping the input type to a Python primitive based on whatever the column is. That'd be more advanced.

If the above is the case - then what are the arguments I can expect from the map or filter function of the rule engine I can consume in my method?

Map and filter functions take a single required argument, just like $abs.