Part 7: Key Mapping with SAP Graph

This is the seventh part in the SAP Graph multi-part tutorial series. In part 5 – Use SAP Graph with your own data – and in part 6 – Construct an SAP Graph Business Data Graph – you learned how you, as a key user, can set up a Business Data Graph (BDG) that enables developers to access all data in your landscape without having to deal with different connection protocols, query languages, and API styles. In parts 5 and 6, you had the choice to connect to sandbox systems provided by SAP or connect to your own landscape. For testing this part of the tutorial, you must have access to your own landscape that includes an SAP Sales Cloud and an SAP S/4HANA instance, which are integrated with SAP Cloud Integration.

This part of the tutorial addresses an interesting challenge: navigating between copies of the same business object, present in different business systems with different keys. This is commonly the result of master data replication. For instance, some of the main attributes of a new business partner in an SAP S/4HANA system are copied by an integration process to another system, where the new copy is assigned a key from a different key range.

While each system serves different purposes and maintains different data details, client applications occasionally must access both copies in both systems. As an example, consider a list of products in which you want to show the status of each product, stored in SAP Sales Cloud, alongside its country of origin, stored in SAP S/4HANA.

In such cases, without SAP Graph you must educate your client developers about all the different keys of all entities that exist in multiple systems and where to learn which keys identify the same objects. Your developers will then have to code the key lookups and further processing explicitly into their applications. This challenging task obviously has no relationship to the business problem they actually need to solve.

SAP Graph hides the complexity of such cross-system navigations from developers via the configuration of key mapping. But before exploring how SAP Graph solves the problem, we shall investigate the challenge and how developers must solve it when SAP Graph is not used.

The problem: educating your developers to be data integration experts

Almost every enterprise landscape contains duplicate data objects. These are often objects like business partners (customers, partners, suppliers), products, and cost centers. For instance, the same bicycle may be described in SAP S/4HANA with details about its manufacturer, and in SAP Sales Cloud, with information about how it is sold. At the same time, it is normal for different data sources to use different key ranges, and thus the same bicycle is identified with a different key in each system. Consequently, the developer task of getting a 360° view on the product details is highly complex because the developer must juggle with the different keys of the same object in different systems and manually dereference links between objects by joining data based on these cross-system references.

For our example, we shall assume a landscape with two destinations

  • s4h-product: an SAP S/4HANA Cloud tenant serving A_Product
  • c4c-odata: an SAP Sales Cloud tenant serving ProductCollection

In the landscape, we have set up SAP Cloud Integration to replicate products from SAP S/4HANA to SAP Sales Cloud. Consequently, both destinations provide information about products. The products in SAP Sales Cloud don’t contain all the information that can be found in the SAP S/4HANA tenant. For instance, the attribute CountryOfOrigin may exist only in SAP S/4HANA. On the other hand, the copies in SAP Sales Cloud contain additional built-in attributes such as a StatusText field and may be further augmented with custom fields. Consider a particular product, the racing bike RB100. This product is identified in SAP Sales Cloud by two unique attribute values: ObjectID ‘999’ and ProductID789′. That same product is exposed by the SAP S/4HANA Product API with key Product ‘xyz’.


Figure 1: Representations of the same product in one landscape

The developer is now tasked to build an application that displays all the information about selected products available in the landscape. To implement such a seemingly simple task, the developer must join the information about the product with key ‘999’ in SAP Sales Cloud with the information with key ‘xyz’ in SAP S/4HANA.

However, how can they know that ‘xyz’ in SAP S/4HANA describes the same product as ‘999’ in SAP Sales Cloud? There are many ways in which such relationships are established in practice. One common pattern, in particular when using SAP Sales Cloud, is the use of a dedicated attribute to store a reference from objects in SAP Sales Cloud to objects outside of SAP Sales Cloud. In our case, the ProductCollection instance with ObjectID ‘999’ has another attribute, ExternalID, with the value ‘xyz’, establishing the (equality) relationship to the corresponding A_Product instance ‘xyz’ in SAP S/4HANA. Maintaining the values of this attribute for all products is the responsibility of the integration which was set up with SAP Cloud Integration.

Consequently, to get a 360° view of our bicycle RB100 given its ID in SAP S/4HANA, the developer must execute two queries:

…/c4codata/ProductCollection?$filter=ExternalID eq 'xyz'

When the application user navigates another reference to the same object, but starting with an ID from SAP Sales Cloud, then first, the following request must be constructed and executed against SAP Sales Cloud:


Then, the developer must extract the value of the ExternalID attribute, namely ‘xyz’, from the result of this query. Finally, the developer must construct another request for the A_Product instance with that key:


When the developer wants to do a filter query to select the data for a whole page of products in the app, they must concatenate strings to produce a long $filter-condition about ExternalID values. Then, when receiving the result, they must do a join inside the extension application to marry the different records from SAP S/4HANA to their counterparts from SAP Sales Cloud. This is a tedious, repetitive, and error-prone task. Further, implementing a relational join in an extension application obviously has nothing to do with the business problem to be solved, which is to show all data about products.

However, the biggest problem with this approach is that to be able to construct all these queries, the developer must in the first place know of this reference being modeled via the ExternalID attribute. To make things even worse, such external key attributes are just a pattern for integrating enterprise systems, but the external key attributes have different names in different entities and systems. This presents a major obstacle for the task of implementing a supposedly simple 360° view on distributed objects.

SAP Graph to the rescue

Wouldn’t it be nice if you, as a key user of SAP Graph, could declare such cross-service references once and for all and then developers could just remain ignorant about these complications and navigate transparently along associations between the different representations of the products, for all applications?

We shall now explore how exactly this is achieved with SAP Graph. The first step will be to configure a BDG that pulls its data from the two given data sources.

As a key user you configure these two data sources:

  • mySC: an SAP Sales Cloud tenant serving ProductCollection
  • myS4: an SAP S/4HANA tenant serving A_Product

We can use the graphctl tool to generate a configuration for our landscape. We shall rename the BDG and the data sources in the generated configuration. The following listing shows our generated BDG configuration with a few manual adaptations marked in blue.

{ "businessDataGraphIdentifier": "my-bdg", "graphModelVersion": "1.0.0", "dataSources": [ { "name": "mySC", "services": [{ "destinationName": "c4c-odata" }] }, { "name": "myS4", "services": [{ "destinationName": "s4h-product" }] } ], "locatingPolicy": { "rules": [ { "name": "sap.s4.*", "leading": "myS4" }, { "name": "sap.cxsales.*", "leading": "mySC" }, { "name": "sap.graph.*", "leading": "myS4", "local": ["mySC"] }

Listing 1: Initial BDG configuration

Please refer to part 6 of the tutorial to see how to generate and activate the above BDG configuration.

SAP Graph exposes data from the underlying data sources in different system-specific namespaces of our Business Data Graph. Entities from SAP S/4HANA instances are exposed in the sap.s4 namespace, whereas entities from SAP Sales Cloud instances are exposed in the sap.cxsales namespace. Furthermore, there is another namespace, called sap.graph, which exposes select entity types and their attributes in a unified and simplified form. We shall use this namespace as the entry point to the graph. In our example, there is an A_Product object ‘xyz’ in myS4 (SAP S/4HANA) and a ProductCollection object ‘999’ in mySC (SAP Sales Cloud) Lastly, there is an sap.graph/Product with displayId ‘xyz’, which is a virtual view on the A_Product in SAP S/4HANA. The following figure depicts these objects as nodes in a graph with associations as edges.


Figure 2: Objects and associations in my-bdg

As you can see, the sap.graph/Product view has associations to the two system-specific representations of the same product. The sap.graph/Product‘s association _s4 points to the corresponding A_Product, because myS4 is configured as the leading system for all sap.graph entities in my-bdg. Thus, the system-specific representation in SAP S/4HANA can be reached by navigating from the sap.graph/Product along this dedicated association or by expanding the association.
Likewise, the sap.graph/Product has the association _cxsales because mySC is defined as having local copies of sap.graph entities. However, it looks like the _cxsales association is a “dangling pointer” into the void. We shall try out our BDG with some example queries to see what’s going on. First, we execute the following query to retrieve a (short) list of sap.graph products along with their S/4-representations:


For the sake of simplicity, we assume that our example product is the first result in the response. Consequently, this request will result in the racing bike RB100 as a sap.graph/Product with displayID ‘xyz’, and populated with data from myS4, which is the leading system as configured by our locating policy. Nested into it, we find the sap.s4/A_Product instance that represents the very same object with the additional property CountryOfOrigin.

{ "@odata.context": "$metadata#Product(_s4(Product,CountryOfOrigin))", "value": [ { "id": "s4~xyz", "displayId": "xyz", "name": "RB100", "_s4": { "Product": "xyz", “CountryOfOrigin”: “DE” }

Listing 2: Result of a list query against the sap.graph/Product

Of course, the developer would like to get the information about the same product that is stored only in mySC in the same way. That is the ProductCollection instance with ObjectID ‘999’. The developer can expect to get this data by expanding the _cxsales association as in the following query

…/my-bdg/sap.graph/Product?$top=1& $expand=_s4($select=Product,CountryOfOrigin),_cxsales($select=StatusText)

However, as we saw in figure 2, there is no connection from the sap.graph/Product to the sap.cxsales/ProductCollection. Therefore, this query will result in a HTTP 404 error. The reason is that without any further configuration, the dereferencing of the _cxsales association will result in the lookup of the sap.cxsales/ProductCollection with ObjectID ‘xyz’, which does not exist.

The solution is to define key mappings for the system-specific entity types. To do so, we modify our BDG configuration as follows (changes in blue):

{ "businessDataGraphIdentifier": "my-bdg", "graphModelVersion": "1.0.0", "dataSources": [ { "name": "mySC", "services": [{ "destinationName": "c4c-odata" }] }, { "name": "myS4", "services": [{ "destinationName": "s4h-product" }] } ], "locatingPolicy": { "rules": [ { "name": "sap.s4.*", "leading": "myS4" }, { "name": "sap.cxsales.*", "leading": "mySC" }, { "name": "sap.graph.*", "leading": "myS4", "local": [ "mySC" ] }, { "name": "sap.cxsales.ProductCollection", "leading": "mySC", "mapped": [ { "referenceQualifier": "myS4", "strategy": "externalIdInReferencedSystem", "params": [{ "name": "externalKey", "value": "ExternalID" }] }]}, { "name": "sap.s4.A_Product", "leading": "myS4", "mapped": [ { "referenceQualifier": "mySC", "strategy": "externalIdInReferencingSystem", "params": [{ "name": "externalKey", "value": "ExternalID"}] }]}

Listing 3: BDG configuration with key mappings

This new BDG configuration is almost the same as the previous one. It only adds two rules specifically for mapping the source-specific representations of products.

The first rule declares that when accessing an sap.cxsales/ProductCollection instance with a reference from myS4 (the referenceQualifier is myS4) then SAP Graph will find that same known myS4-ID in the ExternalID attribute of the referenced ProductCollection instance. Therefore, we must use the strategy externalIdInReferencedSystem.

The second rule specifies the reverse direction; the processing of a request that targets an sap.s4.A_Product with a reference originating from mySC (the referenceQualifier is mySC). This requires again looking for the myS4-ID in the ExternalID attribute of the ProductCollection instance, this time in the referencing system. Consequently, we must use the strategy externalIdInReferencingSystem.

Now, this configuration enables SAP Graph to transparently respond to the SAP Graph developer’s query, repeated here:

/my-bdg/sap.graph/Product?$top=1& $expand=_s4($select=Product,CountryOfOrigin),_cxsales($select=StatusText)

The result of this query is the sap.graph representation of the product RB100 with both its system-specific representations nested into it.

{ "@odata.context": "$metadata#Product(_s4(Product,CountryOfOrigin),_cxsales(StatusText,ObjectID))", "value": [    { "id": "myS4~xyz", "displayId": "xyz", "name": "RB100", "_cxsales": { "ObjectID": "999", "StatusText": "Active", }, "_s4": { "Product": "xyz", "CountryOfOrigin": "DE" }

Listing 4: 360° view on Product ‘xyz’

The SAP Graph developer can now execute a single query to get a true 360° view on the product including all the available information. It is not necessary to think about joins between multiple result sets in the extension application. What’s best, the developer does not even have to know or care how the equality of the different objects is determined; they just navigate along the edges of the graph. The complexity has been moved from the extension applications into SAP Graph. This reduces the complexity of applications that cross system borders. Consequently, the developer can now fully focus on the business requirements.

Effectively, the entities and associations now look like this.


Figure 3: objects and associations in my-bdg with cross-service edges enabled by key mapping

The sap.graph/Product now has an edge to each of its representations regardless of the source system.


Key mapping removes the burden of merging different representations of the same data from the API clients, that is the extension applications. The knowledge about key mapping is moved into the configuration of the Business Data Graph where it is declared once for all clients. SAP Graph takes care of the developers by executing the complex key translation logic of their requests against the BDG. Consequently, extension application developers can focus on the business requirements and don’t have to be data integration experts at the same time.

Key mapping in SAP Graph thus creates new edges on the data level of the BDG and, in doing so, it enables a 360° view on the very same real-world objects despite different identifiers being used for the same objects in disparate data sources.

David Kensche, Engineering Lead (Runtime) – SAP Graph

Learn more about SAP Graph, or contact us at