Professional Documents
Culture Documents
Bpath Lesson 05 Sub String Get Functions Plus Global Variables
Bpath Lesson 05 Sub String Get Functions Plus Global Variables
Hello everybody,
After the excursion into various topics last time (BPath. Lesson 04. Enhanceable structures, aggregations,
derefencation and transposition. And object retrieval.) we are looking forward to presenting some functions. Or
at least kind of, since subs are not filed under functions from a syntactical point of view:
Sub functions
sub-functions are triggering a new BPath statement starting on the actual entity:
~*/SearchResFlightRel{!DIST=SUB(FlightConnectionRel/@DISTANCE);!VALUE=0}$/FlightBookRel{!VALUE=!
VALUE+1}/*
In the early days a new parser was instantiated for any sub query. This was changed and integrated into the
syntax as some future features were too difficult with several installations of BPath.
Note that on this way only one field can be returned with every sub function.
~*/SearchResFlightRel{!DS=SUB(FlightConnectionRel/*);!VALUE=0}$/FlightBookRel{!VALUE=!VALUE+1}/*
Code Example 24, SUB (.../*), sub functions returning structures or tables
Looks quite similar to the code above, but instead of attribute we sub-request a structure. This structure is
consequently included into the target structure as reference. You may do the same with subs returning tables,
which are also included as references.
Goal of this feature is the possibility to assemble hierarchical structured return data of course.
You may nest sub-functions even further and run a sub-query within a sub query.
One may transfer data from the embedding query to the sub query using the global variable table (see below).
But it is not possible to access the data of the embedding structure from within the sub query.
String functions
To ease working with strings, some string functions were introduced as the following:
SearchResFlightRel/FlightBookRel[strcomp(@PASSNAME,"CA","äöüÄÖÜ")]/*$
Code Example 25, STRCOMP(), string comparisons
The STRCOMP function offers the string comparison functionalities as known in ABAP. These are: "CA", "CS",
"CP", "CO", "CN", "NA", "NS" and "NP". The parameters are all of type string in the same order as in ABAP;
this means that the comparator has to be specified (as string) as second parameter.
The function returns a Boolean Value.
For the often used CP (covers pattern) comparison a different syntax is also available:
SearchResFlightRel/FlightBookRel[@PASSNAME~="*sch*"]/*$
The basic string functions LEN( string ), LEFT( string, len ) , RIGHT ( string, len ) and MID( string, offset, len )
were also included. In contrast to ABAP they are not dumping if len is exceeding the string length (then the
complete string is returned) or if the offset exceeds the length (then an empty string is returned).
~LISTZEILE/SearchResFlightRel/FlightBookRel[LEN(@PASSNAME)>0]
[LEFT(@PASSNAME,1)=UPPER(RIGHT(@PASSNAME,1))]{!
ZEILE=MID(@PASSNAME,1,LEN(@PASSNAME)-2)}$
Code Example 27, LEFT, RIGHT and CENTER ... oops sorry MID. And LEN.
Code example takes all non-empty Passenger Names where the first and the (uppercased) last character are
equal and returns the content in between.
The original version followed a simple approach when it came to assignments. The return content was simply
never cleared and overwritten by the next row (at least for the standard case). This is fine in most of the cases
but when we used dereferenced targets for transposing data it turned out that the data was never cleared and
hence incorrect. Unfortunately there is also no way to clear the data manually.
Hence we switched the concept completely and introduced the concept of locality and globality. Since the
targets are not declared, the difference has to be expressed with the assignment itself.
A local target will be cleared after a row is written to the return table. The content of a global target
will 'survive' the writing to the return table. Locality is expressed with the = operator, for a global
assignment the := operator has to be used.
Technically the return structure will be cleared completely after adding a row to the return table. The content
of the global targets is then filled again as done with the last assignment which is stored in the global variable
table (see next chapter).
1. For normal fields written by each and every row local assignments with = should be used.
2. (De-referenced) Entries only touched by selected rows and valid only for these rows local assignments must
be used.
In case you do not like to use mixed types of assignments you can avoid the := operator by simply using global
variables in all cases were the globality is needed.
~*/SearchResFlightRel{!1=0;!!SUM=0}$/FlightBookRel{!1=!1+1;!!SUM=!!SUM+(1000*@LUGGWEIGHT);!
AVG=!!SUM%!1}
This is basically the same example as number 21, solved with global variables, so the intermediate variables
do not appear in the return structure.
As learnt before, you may assign data to targets which are part of the returned data. Sometimes the user might
want to calculate and store data which is not used immediately within the target structure. To do so the Global
Variable Table was introduced which stores all variables. Technically it's a hash table with the variable name
as key.
In general the following rules apply:
• All identifiers beginning with a number are treated as variable, e.g. !1 refers to a variable and also !
0Var. !Var refers to a target (means an attribute in the returned structure). Anyway attribute names
beginning with numbers are not allowed in data structures.
• In case you want to use speaking names without numbers the following syntax may be used !!
Variable. The second ! indicates that it refers to a variable.
• A variable is always global, hence it lives "forever". At least till the complete statement is executed.
• All sub-queries share the same variable table.
• A variable is initialized with the first usage on the left side of an assignment.
In some cases the initialization of aggregation data is very hard or even impossible. In case a query starts
from a collection the syntax does not allow any assignment on the highest level (which is the collection). This
means, if the collection contains 3 items, any assignment is executed at least 3 times.
For dereferenced summations it might be also impossible to know whether the target is available or must be
instantiated. So we introduced a GET(x,y) method, which has the following semantics:
If x can be evaluated x is returned. If the evaluation of x leads to the access of a not initialized variable / target
or attribute (last one makes hardly sense, since it is known at design time) y is used to initialize the return.
So a typical usage is !s=GET(!s,0)+1. Please note that the failed access is propagated, this means a statement
like !s=GET(!s+1,1) would lead to the same result as the statement before. GET methods can be nested also.
The following example illustrates a non-trivial usage of the GET function, in a scenario where it can not be
avoided since the field is added dynamically.
~*/SearchResFlightRel$/FlightBookRel{!1=LEFT(@PASSNAME,1);!1=IFF(!1="","EMPTY",!1);!1^!:=GET(!
1^!,0)+1}
The code extracts the first character of the passenger name. The iff statement is required to avoid creating
fields with empty names (leading to a short dump). Then we try to access the column with the corresponding
name ( e.g. column A ). If it is available we increase its value by one, otherwise we create it newly.
Before closing this lesson I have to make a small confession. Above example and also example 22 is only
working after applying note 1586957 which solves several smaller problems I identified over the past days.
The note also introduces the possibility to 'keep the global variable table alive'. This means you might use
information stored by one BPath query at the execution of another BPath query.
I will add a small chapter on the available notes in one of the coming lessons, currently 4 notes are
recommended.
That's it for today. Lesson 6 will bring sorting and unstructured returns. And some performance investigations
in case I manage to do so.
1458 Views