.\" Automatically generated by Pod::Man 4.10 (Pod::Simple 3.35) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "UR::BoolExpr 3pm" .TH UR::BoolExpr 3pm "2019-01-02" "perl v5.28.1" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" UR::BoolExpr \- a "where clause" for objects .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 8 \& my $o = Acme::Employee\->create( \& ssn => \*(Aq123\-45\-6789\*(Aq, \& name => \*(AqPat Jones\*(Aq, \& status => \*(Aqactive\*(Aq, \& start_date => UR::Context\->current\->now, \& payroll_category => \*(Aqhourly\*(Aq, \& boss => $other_employee, \& ); \& \& my $bx = Acme::Employee\->define_boolexpr( \& \*(Aqpayroll_category\*(Aq => \*(Aqhourly\*(Aq, \& \*(Aqstatus\*(Aq => [\*(Aqactive\*(Aq,\*(Aqterminated\*(Aq], \& \*(Aqname like\*(Aq => \*(Aq%Jones\*(Aq, \& \*(Aqssn matches\*(Aq => \*(Aq\ed{3}\-\ed{2}\-\ed{4}\*(Aq, \& \*(Aqstart_date between\*(Aq => [\*(Aq2009\-01\-01\*(Aq,\*(Aq2009\-02\-01\*(Aq], \& \*(Aqboss.name in\*(Aq => [\*(AqCletus Titus\*(Aq, \*(AqMitzy Mayhem\*(Aq], \& ); \& \& $bx\->evaluate($o); # true \& \& $bx\->specifies_value_for(\*(Aqpayroll_category\*(Aq) # true \& \& $bx\->value_for(\*(Aqpayroll_cagtegory\*(Aq) # \*(Aqhourly\*(Aq \& \& $o\->payroll_category(\*(Aqsalary\*(Aq); \& $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(\*(Aqboss\*(Aq); \& #\*(Aqemployees.payroll_category\*(Aq => \*(Aqhourly\*(Aq, \& #\*(Aqemployees.status\*(Aq => [\*(Aqactive\*(Aq,\*(Aqterminated\*(Aq], \& #\*(Aqemployees.name like\*(Aq => \*(Aq%Jones\*(Aq, \& #\*(Aqemployees.ssn matches\*(Aq => \*(Aq\ed{3}\-\ed{2}\-\ed{4}\*(Aq, \& #\*(Aqemployees.start_date between\*(Aq => [\*(Aq2009\-01\-01\*(Aq,\*(Aq2009\-02\-01\*(Aq], \& #\*(Aqname in\*(Aq => [\*(AqCletus Titus\*(Aq, \*(AqMitzy Mayhem\*(Aq], \& \& my $bx3 = $bx\->flatten(); \& # any indirection in the params takes the form a.b.c at the lowest level \& # also \*(Aqpayroll_category\*(Aq might become \*(Aqpay_history.category\*(Aq, and \*(Aqpay_history.is_current\*(Aq => 1 is added to the list \& # if this parameter has that as a custom filter .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" A UR::BoolExpr object captures a set of match criteria for some class of object. .PP Calls to \fBget()\fR, \fBcreate()\fR, and \fBdefine_set()\fR 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. .PP They have a 1:1 correspondence within the \s-1WHERE\s0 clause in an \s-1SQL\s0 statement where \&\s-1RDBMS\s0 persistence is used. They also imply the \s-1FROM\s0 clause in these cases, since the query properties control which joins must be included to return the matching object set. .SH "REFLECTION" .IX Header "REFLECTION" The data used to create the boolean expression can be re-extracted: .PP .Vb 2 \& 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 .Ve .SH "TEMPLATE SUBCLASSES" .IX Header "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. .PP 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. .SH "CONSTRUCTOR" .IX Header "CONSTRUCTOR" .Vb 7 \& my $bx = UR::BoolExpr\->resolve(\*(AqSome::Class\*(Aq, property_1 => \*(Aqvalue_1\*(Aq, ... property_n => \*(Aqvalue_n\*(Aq); \& my $bx1 = Some::Class\->define_boolexpr(property_1 => value_1, ... property_n => \*(Aqvalue_n\*(Aq); \& my $bx2 = Some::Class\->define_boolexpr(\*(Aqproperty_1 >\*(Aq => 12345); \& my $bx3 = UR::BoolExpr\->resolve_for_string( \& \*(AqSome::Class\*(Aq, \& \*(Aqproperty_1 = value_1 and ( property_2 < value_2 or property_3 = value_3 )\*(Aq, \& ); .Ve .Sp .RS 4 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. .RE .PP \&\f(CW\*(C`resolve_for_string()\*(C'\fR 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. .PP The parser understands all the same operators the underlying \f(CW\*(C`resolve()\*(C'\fR method understands: =, <, >, <=, >=, \*(L"like\*(R", \*(L"between\*(R" and \*(L"in\*(R". Operators may be prefixed by a bang (!) or the word \*(L"not\*(R" to negate the operator. The \*(L"like\*(R" operator understands the \s-1SQL\s0 wildcards % and _. Values for the \*(L"between\*(R" operator should be separated by a minus (\-). Values for the \*(L"in\*(R" 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] .PP Simple expressions may be joined together with the words \*(L"and\*(R" and \*(L"or\*(R" to form a more complicated expression. \*(L"and\*(R" has higher precedence than \*(L"or\*(R", 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] .PP In general, whitespace is insignificant. The strings \*(L"prop1 = 1\*(R" is parsed the same as \&\*(L"prop1=1\*(R". 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. .PP Specific ordering may be requested by putting an \*(L"order by\*(R" clause at the end, and is the same as using a \-order argument to \fBresolve()\fR: score > 10 order by name,score. .PP Likewise, grouping and Set construction is indicated with a \*(L"group by\*(R" clause: score > 10 group by color .SH "METHODS" .IX Header "METHODS" .IP "evaluate" 4 .IX Item "evaluate" .Vb 1 \& $bx\->evaluate($object) .Ve .Sp Returns true if the given object satisfies the BoolExpr .IP "template_and_values" 4 .IX Item "template_and_values" .Vb 1 \& ($template, @values) = $bx\->template_and_values(); .Ve .Sp Returns the UR::BoolExpr::Template and list of the values for the given BoolExpr .IP "is_subset_of" 4 .IX Item "is_subset_of" .Vb 1 \& $bx\->is_subset_of($other_bx) .Ve .Sp Returns true if the set of objects that matches this BoolExpr is a subset of the set of objects that matches \f(CW$other_bx\fR. In practice this means: .Sp .Vb 3 \& * 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\*(Aqs properties match $other_bx .Ve .IP "values" 4 .IX Item "values" .Vb 1 \& @values = $bx\->values .Ve .Sp Return a list of the values from \f(CW$bx\fR. The values will be in the same order the BoolExpr was created from .IP "value_for_id" 4 .IX Item "value_for_id" .Vb 1 \& $id = $bx\->value_for_id .Ve .Sp If \f(CW$bx\fR's properties include all the \s-1ID\s0 properties of its subject class, \&\f(CW\*(C`value_for_id\*(C'\fR returns that value. Otherwise, it returns the empty list. If the subject class has more than one \s-1ID\s0 property, this returns the value of the composite \s-1ID.\s0 .IP "specifies_value_for" 4 .IX Item "specifies_value_for" .Vb 1 \& $bx\->specifies_value_for(\*(Aqproperty_name\*(Aq); .Ve .Sp Returns true if the filter list of \f(CW$bx\fR includes the given property name .IP "value_for" 4 .IX Item "value_for" .Vb 1 \& my $value = $bx\->value_for(\*(Aqproperty_name\*(Aq); .Ve .Sp Return the value for the given property .IP "operator_for" 4 .IX Item "operator_for" .Vb 1 \& my $operator = $bx\->operator_for(\*(Aqproperty_name\*(Aq); .Ve .Sp Return a string for the operator of the given property. A value of '' (the empty string) means equality (\*(L"=\*(R"). Other possible values include '<', '>', \&'<=', '>=', 'between', 'true', 'false', 'in', 'not <', 'not >', etc. .IP "normalize" 4 .IX Item "normalize" .Vb 1 \& $bx2 = $bx\->normalize; .Ve .Sp 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. .IP "flatten" 4 .IX Item "flatten" .Vb 1 \& $bx2 = $bx\->flatten(); .Ve .Sp Transforms a boolean expression into a functional equivalent where indirect properties are turned into property chains. .Sp For instance, in a class with .Sp .Vb 3 \& a => { is => "A", id_by => "a_id" }, \& b => { via => "a", to => "bb" }, \& c => { via => "b", to => "cc" }, .Ve .Sp An expression of: .Sp .Vb 1 \& c => 1234 .Ve .Sp Becomes: .Sp .Vb 1 \& a.bb.cc => 1234 .Ve .Sp In cases where one of the indirect properties includes a \*(L"where\*(R" clause, the flattened expression would have an additional value for each element: .Sp .Vb 3 \& a => { is => "A", id_by => "a_id" }, \& b => { via => "a", to => "bb" }, \& c => { via => "b", where ["xx" => 5678], to => "cc" }, .Ve .Sp An expression of: .Sp .Vb 1 \& c => 1234 .Ve .Sp Becomes: .Sp .Vb 2 \& a.bb.cc => 1234 \& a.bb.xx => 5678 .Ve .IP "reframe" 4 .IX Item "reframe" .Vb 2 \& $bx = Acme::Order\->define_boolexpr(status => \*(Aqactive\*(Aq); \& $bx2 = $bx\->reframe(\*(Aqcustomer\*(Aq); .Ve .Sp 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 \&\*(L"customer\*(R" with a defined relationship to another class. .SH "INTERNAL STRUCTURE" .IX Header "INTERNAL STRUCTURE" A boolean expression (or \*(L"rule\*(R") has an \*(L"id\*(R", which completely describes the rule in stringified form, and a method called evaluate($o) which tests the rule on a given object. .PP The id is composed of two parts: \&\- A template_id. \&\- A value_id. .PP Nearly all real work delegates to the template to avoid duplication of cached details. .PP 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 \*(L"or\*(R") \&\- the body of the rule either key-op-val, or a list of other rules .PP 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: \*(L"chromosome>,name=\*(R" \&\- the value_id embeds the x,y values in a special format .SH "EXAMPLES" .IX Header "EXAMPLES" my \f(CW$bool\fR = \f(CW$x\fR\->evaluate($obj); .PP my \f(CW$t\fR = GSC::Clone\->template_for_params( \*(L"status =\*(R", \*(L"chromosome []\*(R", \*(L"clone_name like\*(R", \*(L"clone_size between\*(R" ); .PP my \f(CW@results\fR = \f(CW$t\fR\->get_matching_objects( \*(L"active\*(R", [2,4,7], \*(L"Foo%\*(R", [100000,200000] ); .PP my \f(CW$r\fR = \f(CW$t\fR\->get_rule($v1,$v2,$v3); .PP my \f(CW$t\fR = \f(CW$r\fR\->template; .PP my \f(CW@results\fR = \f(CW$t\fR\->get_matching_objects($v1,$v2,$v3); my \f(CW@results\fR = \f(CW$r\fR\->\fBget_matching_objects()\fR; .PP \&\f(CW@r\fR = \f(CW$r\fR\->\fBunderlying_rules()\fR; for (@r) { print \f(CW$r\fR\->evaluate($c1); } .PP my \f(CW$rt\fR = \f(CW$r\fR\->\fBtemplate()\fR; my \f(CW@rt\fR = \f(CW$rt\fR\->\fBget_underlying_rule_templates()\fR; .PP \&\f(CW$r\fR = \f(CW$rt\fR\->get_rule_for_values(@v); .PP \&\f(CW$r\fR = UR::BoolExpr\->resolve_for_string( 'My::Class', 'name=Bob and (score=10 or score < 5)', ); .SH "SEE ALSO" .IX Header "SEE ALSO" \&\s-1\fBUR\s0\fR\|(3), \fBUR::Object\fR\|(3), \fBUR::Object::Set\fR\|(3), \fBUR::BoolExpr::Template\fR\|(3)