Provided by: libur-perl_0.470+ds-2_all bug

NAME

       UR::BoolExpr - a "where clause" for objects

SYNOPSIS

           my $o = Acme::Employee->create(
               ssn => '123-45-6789',
               name => 'Pat Jones',
               status => 'active',
               start_date => UR::Context->current->now,
               payroll_category => 'hourly',
               boss => $other_employee,
           );

           my $bx = Acme::Employee->define_boolexpr(
               'payroll_category'                  => 'hourly',
               'status'                            => ['active','terminated'],
               'name like'                         => '%Jones',
               'ssn matches'                       => '\d{3}-\d{2}-\d{4}',
               'start_date between'                => ['2009-01-01','2009-02-01'],
               'boss.name in'                      => ['Cletus Titus', 'Mitzy Mayhem'],
           );

           $bx->evaluate($o); # true

           $bx->specifies_value_for('payroll_category') # true

           $bx->value_for('payroll_cagtegory') # 'hourly'

           $o->payroll_category('salary');
           $bx->evaluate($o); # false

           # these could take either a boolean expression, or a list of params
           # from which it will generate one on-the-fly
           my $set     = Acme::Employee->define_set($bx);  # same as listing all of the params
           my @matches = Acme::Employee->get($bx);         # same as above, but returns the members

           my $bx2 = $bx->reframe('boss');
           #'employees.payroll_category'            => 'hourly',
           #'employees.status'                      => ['active','terminated'],
           #'employees.name like'                   => '%Jones',
           #'employees.ssn matches'                 => '\d{3}-\d{2}-\d{4}',
           #'employees.start_date between'          => ['2009-01-01','2009-02-01'],
           #'name in'                               => ['Cletus Titus', 'Mitzy Mayhem'],

           my $bx3 = $bx->flatten();
           # any indirection in the params takes the form a.b.c at the lowest level
           # also 'payroll_category' might become 'pay_history.category', and 'pay_history.is_current' => 1 is added to the list
           # if this parameter has that as a custom filter

DESCRIPTION

       A UR::BoolExpr object captures a set of match criteria for some class of object.

       Calls to get(), create(), and define_set() all use this internally to objectify their
       parameters.  If given a boolean expression object directly they will use it.  Otherwise
       they will construct one from the parameters given.

       They have a 1:1 correspondence within the WHERE clause in an SQL statement where RDBMS
       persistence is used.  They also imply the FROM clause in these cases, since the query
       properties control which joins must be included to return the matching object set.

REFLECTION

       The data used to create the boolean expression can be re-extracted:

           my $c = $r->subject_class_name;
           # $c eq "GSC::Clone"

           my @p = $r->params_list;
           # @p = four items

           my %p = $r->params_list;
           # %p = two key value pairs

TEMPLATE SUBCLASSES

       The template behind the expression can be of type ::Or, ::And or ::PropertyComparison.
       These classes handle all of the operating logic for the expressions.

       Each of those classes incapsulates 0..n of the next type in the list.  All templates
       simplify to this level.  See UR::BoolExpr::Template for details.

CONSTRUCTOR

         my $bx = UR::BoolExpr->resolve('Some::Class', property_1 => 'value_1', ... property_n => 'value_n');
         my $bx1 = Some::Class->define_boolexpr(property_1 => value_1, ... property_n => 'value_n');
         my $bx2 = Some::Class->define_boolexpr('property_1 >' => 12345);
         my $bx3 = UR::BoolExpr->resolve_for_string(
                       'Some::Class',
                       'property_1 = value_1 and ( property_2 < value_2 or property_3 = value_3 )',
                   );

           Returns a UR::BoolExpr object that can be used to perform tests on the given class and
           properties.  The default comparison for each property is equality.  The third example
           shows using greater-than operator for property_1.  The last example shows constructing
           a UR::BoolExpr from a string containing properties, operators and values joined with
           'and' and 'or', with parentheses indicating precedence.

       "resolve_for_string()" can parse simple and complicated expressions.  A simple expression
       is a property name followed by an operator followed by a value.  The property name can be
       a series of properties joined by dots (.) to indicate traversal of multiple layers of
       indirect properties.  Values that include spaces, characters that look like operators,
       commas, or other special characters should be enclosed in quotes.

       The parser understands all the same operators the underlying "resolve()" method
       understands: =, <, >, <=, >=, "like", "between" and "in".  Operators may be prefixed by a
       bang (!) or the word "not" to negate the operator.  The "like" operator understands the
       SQL wildcards % and _.  Values for the "between" operator should be separated by a minus
       (-).  Values for the "in" operator should begin with a left bracket, end with a right
       bracket, and have commas between them.  For example:
           name_property in [Bob,Fred,Joe]

       Simple expressions may be joined together with the words "and" and "or" to form a more
       complicated expression.  "and" has higher precedence than "or", and parentheses can
       surround sub-expressions to indicate the requested precedence.  For example:
           ((prop1 = foo or prop2 = 1) and (prop2 > 10 or prop3 like 'Yo%')) or prop4 in [1,2,3]

       In general, whitespace is insignificant.  The strings "prop1 = 1" is parsed the same as
       "prop1=1".  Spaces inside quoted value strings are preserved.  For backward compatibility
       with the deprecated string parser, bare words that appear after the operators =,<,>,<= and
       >= which are separated by one or more spaces is treated as if it had quotes around the
       list of words starting with the first character of the first word and ending with the last
       character of the last word, meaning that spaces at the start and end of the list are
       trimmed.

       Specific ordering may be requested by putting an "order by" clause at the end, and is the
       same as using a -order argument to resolve():
           score > 10 order by name,score.

       Likewise, grouping and Set construction is indicated with a "group by" clause:
           score > 10 group by color

METHODS

       evaluate
               $bx->evaluate($object)

           Returns true if the given object satisfies the BoolExpr

       template_and_values
             ($template, @values) = $bx->template_and_values();

           Returns the UR::BoolExpr::Template and list of the values for the given BoolExpr

       is_subset_of
             $bx->is_subset_of($other_bx)

           Returns true if the set of objects that matches this BoolExpr is a subset of the set
           of objects that matches $other_bx.  In practice this means:

             * The subject class of $bx isa the subject class of $other_bx
             * all the properties from $bx also appear in $other_bx
             * the operators and values for $bx's properties match $other_bx

       values
             @values = $bx->values

           Return a list of the values from $bx.  The values will be in the same order the
           BoolExpr was created from

       value_for_id
             $id = $bx->value_for_id

           If $bx's properties include all the ID properties of its subject class, "value_for_id"
           returns that value.  Otherwise, it returns the empty list.  If the subject class has
           more than one ID property, this returns the value of the composite ID.

       specifies_value_for
             $bx->specifies_value_for('property_name');

           Returns true if the filter list of $bx includes the given property name

       value_for
             my $value = $bx->value_for('property_name');

           Return the value for the given property

       operator_for
             my $operator = $bx->operator_for('property_name');

           Return a string for the operator of the given property.  A value of '' (the empty
           string) means equality ("=").  Other possible values include '<', '>', '<=', '>=',
           'between', 'true', 'false', 'in', 'not <', 'not >', etc.

       normalize
               $bx2 = $bx->normalize;

           A boolean expression can be changed in incidental ways and still be equivalent.  This
           method converts the expression into a normalized form so that it can be compared to
           other normalized expressions without incidental differences affecting the comparison.

       flatten
               $bx2 = $bx->flatten();

           Transforms a boolean expression into a functional equivalent where indirect properties
           are turned into property chains.

           For instance, in a class with

               a => { is => "A", id_by => "a_id" },
               b => { via => "a", to => "bb" },
               c => { via => "b", to => "cc" },

           An expression of:

               c => 1234

           Becomes:

               a.bb.cc => 1234

           In cases where one of the indirect properties includes a "where" clause, the flattened
           expression would have an additional value for each element:

               a => { is => "A", id_by => "a_id" },
               b => { via => "a", to => "bb" },
               c => { via => "b", where ["xx" => 5678], to => "cc" },

           An expression of:

               c => 1234

           Becomes:

               a.bb.cc => 1234
               a.bb.xx => 5678

       reframe
               $bx  = Acme::Order->define_boolexpr(status => 'active');
               $bx2 = $bx->reframe('customer');

           The above will turn a query for orders which are active into a query for customers
           with active orders, presuming an Acme::Order has a property called "customer" with a
           defined relationship to another class.

INTERNAL STRUCTURE

       A boolean expression (or "rule") has an "id", which completely describes the rule in
       stringified form, and a method called evaluate($o) which tests the rule on a given object.

       The id is composed of two parts: - A template_id.  - A value_id.

       Nearly all real work delegates to the template to avoid duplication of cached details.

       The template_id embeds several other properties, for which the rule delegates to it: -
       subject_class_name, objects of which the rule can be applied-to - subclass_name, the
       subclass of rule (property comparison, and, or "or") - the body of the rule either key-op-
       val, or a list of other rules

       For example, the rule GSC::Clone name=x,chromosome>y: - the template_id embeds:
           subject_class_name = GSC::Clone
           subclass_name = UR::BoolExpr::And
           and the key-op pairs in sorted order: "chromosome>,name=" - the value_id embeds the
       x,y values in a special format

EXAMPLES

       my $bool = $x->evaluate($obj);

       my $t = GSC::Clone->template_for_params(
           "status =",
           "chromosome []",
           "clone_name like",
           "clone_size between" );

       my @results = $t->get_matching_objects(
           "active",
           [2,4,7],
           "Foo%",
           [100000,200000] );

       my $r = $t->get_rule($v1,$v2,$v3);

       my $t = $r->template;

       my @results = $t->get_matching_objects($v1,$v2,$v3); my @results =
       $r->get_matching_objects();

       @r = $r->underlying_rules(); for (@r) {
           print $r->evaluate($c1); }

       my $rt = $r->template(); my @rt = $rt->get_underlying_rule_templates();

       $r = $rt->get_rule_for_values(@v);

       $r = UR::BoolExpr->resolve_for_string(
              'My::Class',
              'name=Bob and (score=10 or score < 5)',
            );

SEE ALSO

       UR(3), UR::Object(3), UR::Object::Set(3), UR::BoolExpr::Template(3)