A RAP query is generally managed because the data can be collected using the modeled CDS entities and the corresponding associations automatically from the underlying tables. There are cases however where the query is too complex, due too many set of dataset exclusions by complex criteria in many steps, or even the record key is delivered by an ABAP API. The break-out in this scenario within RAP context is the Unmanaged Query.

You can implement interface if_rap_query_provider in your ABAP class and refer it in Your custom CDS Entity. Drawback that You need to implement the required filtering, sorting and counting capabilities manually. Neither ADT Data Preview is available, nor can You select from custom entities in ABAP, simply because a custom entity has no database artefact behind. In the background again the trick/intermediation is done by SADL, precisely by its unmanaged query engine. But we’ll show You options to test it out without having a ready front-end .

Custom Entity

Custom Entities can be created using the ADT Wizards. The sample below shows You a custom entity including properties of RAP Behavior Definition entities and their aliases. Yes, this is an entity to list entities of a RAP BDEF, but You can create custom entities for anything like Material Long Texts etc. You link the ABAP class using the @ObjectModel.query.implementedBy annotation.

@EndUserText.label: 'Entities of RAP Behavior'
@ObjectModel.query.implementedBy: 'ABAP:ZCL_RAP_ENTITY_VH_QUERY'
@UI.headerInfo : { typeName: 'Entity', typeNamePlural: 'Entities'  }
@ObjectModel.resultSet.sizeCategory: #XS
define custom entity ZC_RAP_Entities_VH_Custom

{

      @UI.hidden: true
  key rootName  : abp_root_entity_name;

      @ObjectModel.filter.enabled: false
      @Consumption.filter.hidden: true
      @ObjectModel.text.element: [ 'aliasName' ]
  key name      : abp_entity_name;
      @ObjectModel.filter.enabled: false
      @Consumption.filter.hidden: true
      @EndUserText.label: 'Alias'
      aliasName : abap.char(30);

}

Query Implementation

The difficult step comes next. If Your are familiar with manual SEGW service implementations from the good old times, You know that You need to properly build the response according to the requested features by the client. Fortunately unamanged queries are mainly used for very specific purposes, and You usually don’t need to implement all feature for all property due the consumption is limited in the given context. Fortuntely we show You some helper functions to do so when needed.

Notes

  • The consumers of the query can send You a request demanding unspported cababilities. To respond them back, inherit Your exception class from the abstract cx_rap_query_prov_not_impl and raise it
  • Snippets to apply request filtering, sorting etc. on the result internal table is provided, but this is not the only way to do it. In case You select from DB, those can be translated to a dynamic SQL query.
"! <p class="shorttext synchronized">RAP Behavior Entities Value Help</p>
"! <p><strong>Purpose</strong><br/>
"! Return entities of a given RAP Behavior. Providing the Root entity name is mandatory !
"! </p>
CLASS zcl_rap_entity_vh_query DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    INTERFACES if_rap_query_provider .
  PROTECTED SECTION.
  PRIVATE SECTION.
    "! <p class="shorttext synchronized">Reponse table for BO entity list</p>
    TYPES ty_response_tab TYPE STANDARD TABLE OF ZC_RAP_Entities_VH_Custom WITH EMPTY KEY.

    "! <p class="shorttext synchronized">Read entities of BDEF</p>
    "!
    "! @parameter i_root_name | <p class="shorttext synchronized">Root entity</p>
    "! @parameter r_entities  | <p class="shorttext synchronized">Entity List with Alias</p>
    METHODS _read_bdef_entities
      IMPORTING i_root_name       TYPE abp_root_entity_name
      RETURNING VALUE(r_entities) TYPE ty_response_tab.
ENDCLASS.

CLASS zcl_rap_entity_vh_query IMPLEMENTATION.
  METHOD if_rap_query_provider~select.
    DATA response TYPE ty_response_tab.

    IF NOT io_request->is_data_requested( ).
      RETURN.
    ENDIF.

    " GET SELECT-OPTIONS
    TRY.
        DATA(filters) = io_request->get_filter( )->get_as_ranges( ).
      CATCH cx_rap_query_filter_no_range INTO DATA(ex_ranges).
        RAISE EXCEPTION NEW zcx_rap_query_prov_not_im( previous = ex_ranges ).
    ENDTRY.

    DATA(selopt_rootname) = VALUE #( filters[ name = 'ROOTNAME' ]-range OPTIONAL ).

    " VALIDATE SELECT-OPTIONS
    "- We may not process all behavior in the system
    IF selopt_rootname IS INITIAL.
      RAISE EXCEPTION NEW zcx_rap_query_prov_not_im( ).
    ENDIF.

    " - Only EQUALS is implemented
    LOOP AT selopt_rootname TRANSPORTING NO FIELDS
         WHERE sign <> 'I' OR option <> 'EQ' OR low IS INITIAL.
    ENDLOOP.
    IF sy-subrc = 0.
      RAISE EXCEPTION NEW zcx_rap_query_prov_not_im( ).
    ENDIF.

    LOOP AT selopt_rootname INTO DATA(root_entity_name_range).
      APPEND LINES OF _read_bdef_entities( i_root_name = CONV #( root_entity_name_range-low ) ) TO response.
    ENDLOOP.

    DATA(selopt_name) = VALUE #( filters[ name = 'NAME' ]-range OPTIONAL ).

    IF selopt_name IS INITIAL.
      DELETE response WHERE name NOT IN selopt_name.
    ENDIF.

    " SORTING
    DATA(sort_order) = VALUE abap_sortorder_tab(
                                 FOR sort_element IN io_request->get_sort_elements( )
                                 ( name = sort_element-element_name descending = sort_element-descending ) ).
    IF sort_order IS NOT INITIAL.
      SORT response BY (sort_order).
    ENDIF.

    " PAGING
    DATA(top) = io_request->get_paging( )->get_page_size( ).
    IF top < 0.
      top = 50.
    ENDIF.

    DATA(skip) = io_request->get_paging( )->get_offset( ).

    IF top IS NOT INITIAL OR skip IS NOT INITIAL.
      /iwbep/cl_mgw_data_util=>paging( EXPORTING is_paging = VALUE #( top  = top
                                                                      skip = skip )
                                       CHANGING  ct_data   = response ).
    ENDIF.

    IF io_request->is_total_numb_of_rec_requested( ).
      io_response->set_total_number_of_records( lines( response ) ).
    ENDIF.

    io_response->set_data( response ).
  ENDMETHOD.

  METHOD _read_bdef_entities.
    cl_abap_behv_load=>get_load( EXPORTING entity   = i_root_name
                                           all      = abap_true
                                 IMPORTING entities = DATA(entities) ).

    LOOP AT entities INTO DATA(entity).
      APPEND VALUE #( rootName  = i_root_name
                      name      = entity-name
                      aliasname = entity-alias )
             TO r_entities.
    ENDLOOP.
  ENDMETHOD.

ENDCLASS.

Testing

You can diplay the entityset on a UI and test. Without having your own front-end, You can expose the entity in a Service Definition and run the Fiori Elements Preview for the entity from the Service Binding.
If You want to go wild, Create a new ABAP test class

image RAP Unmanaged Query through Custom Entity

Share this content: