Skip to Content
Technical Articles

SAP Business Client Search Provider

In this blog post you will learn what Search Providers in the Business Client are and how to create your own in the version 7.0.

A Search Provider in the Business Client is a service that can handle search queries from the search bar in the top of your Client. It can also provide you with search results, shown below the search bar.

Each Client can have multiple Search Providers and you can add your own ones.

To see which search providers you currently have, you can navigate your options to “Connections -> Search Providers”.

Here you can not only view which search providers you currently have, but you can also add your own ones here. The only thing you need is a Search Provider URL. If you don’t know what that is, you can read Bernd Dittrich’s Blog about how to activate the TM Quicksearch, where he also explains what a Search Provider URL is and how to create a service for the URL. You will need to change the Service Name and Handler Class to your own ones.

If you don’t already have a handler class, you can create it in the transaction se80.

I have called mine ZMS_SEARCH_PROVIDER in the system ER9/001.

To use this class as a handler class you need to use the interface IF_HTTP_EXTENSION in your public section and implement a method IF_HTTP_EXTENSION~HANDLE_REQUEST in your class implementation.

public section.

  interfaces IF_HTTP_EXTENSION .
* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZMS_SEARCH_PROVIDER->IF_HTTP_EXTENSION~HANDLE_REQUEST
* +-------------------------------------------------------------------------------------------------+
* | [--->] SERVER                         TYPE REF TO IF_HTTP_SERVER
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD if_http_extension~handle_request.

    DATA(lv_query_string) = to_upper( server->request->get_form_field( name = cs_form_field-query ) ).
    DATA(lv_request_query) = escape( val = lv_query_string format = cl_abap_format=>e_xss_url ).
    DATA(lv_type_string) = server->request->get_form_field( name = cs_form_field-type ).
    DATA(lv_request_type) = escape( val = lv_type_string format = cl_abap_format=>e_xss_url ).
    DATA(lv_max_results) = server->request->get_form_field( name = cs_form_field-results ).
    IF lv_max_results <> ''.
      mv_max_results = lv_max_results.
    ENDIF.

    CASE lv_request_type.
      WHEN cs_req_types-osd.
        server->response->set_cdata(
          EXPORTING
            data   = me->open_search_xml( )
        ).
      WHEN cs_req_types-suggestionsxml OR
           cs_req_types-suggestionsjson OR
           cs_req_types-model OR
           cs_req_types-page.
        get_data( iv_request_query = lv_request_query ).
        DATA(lv_response_xml) = me->open_search_response(
                     iv_request_query = lv_request_query
                     it_result_tab    = get_result_list( )
                   ).
        server->response->set_cdata( data = lv_response_xml ).
      WHEN OTHERS.
        server->response->set_cdata(
          EXPORTING
            data   = |no/wrong type defined: { lv_request_type }|
        ).
    ENDCASE.

  ENDMETHOD.

In this method you can also implement Service URL parameters. I am using the parameter max_results, simply by requesting it from the server and writing the value inside mv_max_results. The server then writes the value of the parameter from the Service URL into the variable. All parameter names from this class are stored in the structure cs_form_field.

protected section.

  class-data MV_MAX_RESULTS(2) type C value 25 ##NO_TEXT.
private section.

  constants:
    BEGIN OF cs_form_field,
        type     TYPE string VALUE 'type' ##no_text,
        location TYPE string VALUE 'location' ##no_text,
        query    TYPE string VALUE 'q' ##no_text,
        results  TYPE string VALUE 'max_results' ##no_text,
      END OF cs_form_field .
  constants:
    BEGIN OF cs_req_types,
        osd             TYPE string VALUE 'OpenSearchDescription' ##no_text,
        page            TYPE string VALUE 'result' ##no_text,
        model           TYPE string VALUE 'resultModel' ##no_text,
        view            TYPE string VALUE 'resultView' ##no_text,
        suggestionsxml  TYPE string VALUE 'suggestionsXML' ##no_text,
        suggestionsjson TYPE string VALUE 'suggestionsJSON' ##no_text,
      END OF cs_req_types .

The CASE in the handler method differentiates between a wrong request type, a search relevant request type and the Open Search Description type. The Open Search Description type is used to initiate the Search Provider and returns an xml that provides multiple URL with query parameters. A search relevant type is used to return result data for the search bar using a query.

For the Open Search Description, the method open_search_xml is called. This method creates a host URL for the service and returns an xml. If you want to use a parameter, you’ll want to add it to the variable lv_param because if you don’t, the parameter you have added before will only be used during the initialization of the search provider.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZMS_SEARCH_PROVIDER=>OPEN_SEARCH_XML
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RV_RESPONSE                    TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD open_search_xml.

    cl_wd_utilities=>construct_wd_url(
      EXPORTING
        application_name  = ''
        in_protocol       = 'HTTPS'
      IMPORTING
        out_host          = DATA(lv_host)
        out_port          = DATA(lv_port) ).

    lv_host = escape( val = lv_host format = cl_abap_format=>e_xss_ml ).
    lv_port = escape( val = lv_port format = cl_abap_format=>e_xss_ml ).
    mv_host_url_http = |https://{ lv_host }:{ lv_port }|.

    DATA(lv_path)  = |{ to_lower( mv_host_url_http ) }{ c_service_path }?{ cs_form_field-type }=|.
    DATA(lv_param) = |{ cs_form_field-results }={ mv_max_results }&{ cs_form_field-query }=\{searchTerms\}|.

    DATA(lv_result_url)  = |{ lv_path }{ cs_req_types-page }&{ lv_param }|.
    DATA(lv_sugges_xml)  = |{ lv_path }{ cs_req_types-suggestionsxml }&{ lv_param }|.
    DATA(lv_sugges_json) = |{ lv_path }{ cs_req_types-suggestionsjson }&{ lv_param }|.

    lv_result_url  = escape( val = lv_result_url  format = cl_abap_format=>e_xml_attr_sq ).
    lv_sugges_xml  = escape( val = lv_sugges_xml  format = cl_abap_format=>e_xml_attr_sq ).
    lv_sugges_json = escape( val = lv_sugges_json format = cl_abap_format=>e_xml_attr_sq ).
    DATA(lv_sys)   = escape( val = |{ sy-sysid } { sy-mandt }| format = cl_abap_format=>e_xss_ml ).

    rv_response = |<?xml version="1.0" encoding="UTF-8"?>|
      && |<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/" xmlns:moz="http://www.mozilla.org/2006/browser/search/">|
      && |<ShortName>Search Provider</ShortName>|
      && |<InputEncoding>UTF-8</InputEncoding>|
      && |<Description>Search Provider Search { lv_sys }</Description>|
      && |<Url type="text/html" method="get" template="{ lv_result_url }"/>|
      && |<Url type="application/x-suggestions+xml" method="get" template="{ lv_sugges_xml }" /&gt;|
      && |<Url type="application/x-suggestions+json" method="get" template="{ lv_sugges_json }" />|
      && |</OpenSearchDescription>| ##no_text.

  ENDMETHOD.

For this class you also need to add a constant c_service_path with the service path of your service in the private section.

  constants C_SERVICE_PATH type STRING value '/sap/bc/zms_search_prov' ##NO_TEXT.
  class-data MV_HOST_URL_HTTP type STRING .

For all search relevant request types, a method get_data is called. It is recommended that you use this method to use the request query and get the data, you want to display in the search result. For demonstration purposes my search result will only show the request query so my get_data method is very short, but you can receive your data from a database or maybe from a http call.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZMS_SEARCH_PROVIDER->GET_DATA
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_REQUEST_QUERY               TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD get_data.
    mv_search_query = iv_request_query.
  ENDMETHOD.

After we have the data we need, we can start creating the result xml. For that two methods are called. The first one is get_result_list, which returns a table with three columns(text, description, url) and the second open_search_response is called to turn the table into an xml.

  types:
    BEGIN OF xml_row,
        text(255)        TYPE c,
        description(255) TYPE c,
        url(255)         TYPE c,
      END OF xml_row .
  types:
    xml_rows TYPE STANDARD TABLE OF xml_row WITH NON-UNIQUE KEY text .

The column text of the table defines what text is shown in the search result. Each row of the table is a row in the search result and if you click on that row, the Business Client will navigate to the URL provided.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Instance Public Method ZMS_SEARCH_PROVIDER->GET_RESULT_LIST
* +-------------------------------------------------------------------------------------------------+
* | [<-()] RT_RESULT_TAB                  TYPE        XML_ROWS
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD get_result_list.

    INSERT VALUE #( text = mv_search_query
                    description = |Search Query|
                    url = '#'
    ) INTO TABLE rt_result_tab.

*    INSERT VALUE #( text =
*                    description =
*                    url = '#'
*    ) INTO TABLE rt_result_tab.
  ENDMETHOD.

If you want to do post processing to your data, it is recommended to do it inside the get_result_list method.

The method open_search_response simply loops through the result table and adds items to the xml. The variable lv_count is used, so the result is sorted correctly and doesn’t sort itself.

* <SIGNATURE>---------------------------------------------------------------------------------------+
* | Static Public Method ZMS_SEARCH_PROVIDER=>OPEN_SEARCH_RESPONSE
* +-------------------------------------------------------------------------------------------------+
* | [--->] IV_REQUEST_QUERY               TYPE        STRING
* | [--->] IT_RESULT_TAB                  TYPE        XML_ROWS
* | [<-()] RV_RESPONSE                    TYPE        STRING
* +--------------------------------------------------------------------------------------</SIGNATURE>
  METHOD open_search_response.
    DATA(lv_xml) =
           |<?xml version="1.0" encoding="UTF-8"?>|
        && |<SearchSuggestion xmlns="http://opensearch.org/searchsuggest2" version="2.0">|
        && |<Query>{ iv_request_query }</Query>|
        && |<Section>|.
    DATA(lv_xml_end) =
           |</Section>|
        && |</SearchSuggestion>|.
    LOOP AT it_result_tab ASSIGNING FIELD-SYMBOL(<fs_row>).
      DATA lv_count(2) TYPE n.
      MOVE sy-tabix TO lv_count.
      DATA(lv_xml_row) =
          |<Item>|
       && |<Text>{ lv_count }: { <fs_row>-text }</Text>|
       && |<Description>{ <fs_row>-description }</Description>|
       && |<Url>{ <fs_row>-url }</Url>|
       && |</Item>|.
      CONCATENATE lv_xml lv_xml_row INTO lv_xml.
      IF sy-tabix >= mv_max_results.
        EXIT.
      ENDIF.
    ENDLOOP.
    CONCATENATE lv_xml lv_xml_end INTO lv_xml.
    rv_response = lv_xml.
  ENDMETHOD.

 

Search Providers can easily customize your experience with the Business Client. You can also add custom features for your or other’s needs. When you don’t need a Search Provider you can simply disable them in the options mentioned above.

1 Comment
You must be Logged on to comment or reply to a post.