SAP Gateway: $expand

A powerful parameter to expand a response with related entities

Introduction

In OData, $expand is a very powerful query parameter. It allows providing multiple entities and/or entity sets as a response to a single service call. Doing this can vastly improve performance and significantly simplify the code on the service consumer side compared to doing multiple calls subsequently. Although the SAP Gateway framework can handle expands out of the box, implementing custom logic is recommended for performance reasons.

OData URI syntax

OData.org defines the syntax of an $expand query option as a comma-separated list of Navigation Properties. Additionally each Navigation Property can be followed by a forward slash and another Navigation Property to enable identifying a multi-level relationship.

Examples

The OData.org reference services contain a few clear examples of expand URI syntax [1].

…/Categories?$expand=Products

Identifies the collection of Categories as well as each of the Products associated with each Category.

…/Categories?$expand=Products/Suppliers

Identifies the collection of Categories as well as each of the Products associated with each Category. In addition, the URI also indentifies the Suppliers associated with each Product.

…/Products?$expand=Category,Suppliers

Identifies the set of Products as well as the Category and Suppliers associated with each product.

Default $expand functionality

Generic expands

$expand statements are resolved by the SAP Gateway framework in a generic way out of the box (a so-called “generic expand”). The framework calls the respective Get_Entity and/or Get_EntitySet methods of the relevant Data Provider Class and puts the results together in a nested structure or table. Naturally a prerequisite for this functionality is that the appropriate navigation properties have been modeled in the Gateway Service Builder (transaction SEGW).

How does this work?
Since an $expand follows the navigation properties defined in the OData model and the framework knows the associations between each entity as defined in the data model, it knows which methods it needs to call and how to put the results together.

Performance considerations

In the standard implementation, several single roundtrips are executed by the gateway runtime when resolving an $expand call. This means that for n leading entities, n database selections are executed for each navigation step specified in the $expand [2]. The framework does not know the business context of the related entities and corresponding data and therefore cannot optimize the database selections or BAPI calls to get all data in a single call. Therefore it is best practice (and recommended by SAP) to always implement the $expand (therefore using the data provider expand) rather than relying on the framework $expand due to performance reasons.

Custom implementation

Redefining get_expanded_entity(set)

Custom implementation of expands on entities and entity sets can be achieved by redefining the DPC class methods (/get_expanded_entity or get_expanded_entityset respectively, both part of the interface /iwbep/if_mgw_appl_srv_runtime. We can fetch all required data ourselves and return the result in a nested structure or table using these implementations. This approach allows for full flexibility on which expands the framework should handle versus those better suited with a custom implementation. It is possible to handle an $expand partially and let the framework handle certain navigation steps.

Major logical steps

Let’s take a closer look at the major logical steps of a custom $expand implementation in get_expanded_entity using a fictitious sample call. Assume this call is used to fetch an airline entity together with the relevant flights: /sap/opu/odata/sap/ZEXAMPLE_SRV/AirlineSet('AA')/ToFlights.

  1. Define a nested structure (or table) for storing the result data

    "Nested result type
    DATA: BEGIN OF ls_airline_flights.
      INCLUDE TYPE zcl_zexample_mpc=>ts_airline.
      DATA: toflights TYPE STANDARD TABLE OF zcl_zexample_mpc=>ts_flight
        WITH DEFAULT KEY,
    END OF ls_airline_flights.
    
  2. Fill a variable of the above type with the fetched data

    "Select airline data into corresponding fields of ls_airline_fields
    "Select flights for airline into table ls_airline_flights-toflights
    
    "Copy the nested structure with the expanded airline data to the
    "data reference that get_expanded_entity returns
    copy_data_to_ref(
      EXPORTING
        is_data = ls_airline_flights
      CHANGING
        cr_data = er_entity ).
    
  3. Let the framework know which navigation propertie you’ve handled:
    add a record to exporting parameter et_expanded_tech_clauses.

    APPEND 'TOFLIGHTS' TO et_expanded_tech_clauses.
    

et_expanded_tech_clauses

As the last step of a custom implementation, the framework needs informing that we have taken care of one or more $expand properties. We do this by adding a line to et_expanded_tech_clauses specifying the handled navigation property. The parameter - a string table - is filled with a list of handled expand clauses, based on technical navigation property names.

Helper method

The code snippet below contains a utility method to dynamically fill et_expanded_tech_clauses with the navigation property names for the current expand call. The method can be called in each implementation of /iwbep/if_mgw_appl_srv_runtime~get_expanded_entity instead of manually filling the parameter.

Method definition
CLASS-METHODS get_expanded_tech_clauses
    IMPORTING
        !io_tech_request_context        TYPE REF TO /iwbep/if_mgw_req_entity
    RETURNING
        VALUE(rt_expanded_tech_clauses) TYPE string_table .
Method implementation
METHOD get_expanded_tech_clauses.
    CONSTANTS comma TYPE c VALUE ','.

    DATA(lo_tech_request) = CAST /iwbep/cl_mgw_request( 
      io_tech_request_context ).
    DATA(lv_expand) = to_upper( 
      lo_tech_request->/iwbep/if_mgw_req_entityset~get_expand( ) ).
    SPLIT lv_expand AT comma INTO TABLE rt_expanded_tech_clauses.
ENDMETHOD.
Example call

METHOD /iwbep/if_mgw_appl_srv_runtime~get_expanded_entity.
    "...
    et_expanded_tech_clauses = zcl_utility_class=>get_expanded_tech_clauses( 
      io_tech_request_context ).
ENDMETHOD.

Bibliography

Laurens Deprost
Laurens Deprost
SAP Consultant

Related