Using Generic Traversal Master Service
genericTraversalMasterService is the most powerful native RO service.
It allows retrieving multiple entities connected by relations and traversing the data in the master model.
For every master view Mv, there is one service genericMvTraversal.
The basic use case of the service is retrieving one record from a master entity identified by id or a combination of origin and sourceId.
There are two advanced features:
-
Traversing: Allows changing the starting point for the entities preloaded in the model, that is, which record to return as the root element of the response.
-
Preloading: Allows preloading records connected to the current record by a relationship (simulates joins).
Data model
The following examples are done using the MDM Example Project, working with its data and model:
|
When traversing or preloading, use parent and child roles for relationship names instead of technical names of relationships (the Name field in the figure below), for example, If you have no roles defined in relationships, you have to use the value in the Name attribute (
|
Basic usage
The simplest usage is similar to getMasterByIdService - specify the entity name and record ID in the request (internal MDM primary key):
<request>
<startWith entity="party" id="38" />
</request>
The response contains one party record:
<list>
<party>
<metadata>
<id>38</id>
<active>true</active>
<creationTid>1002</creationTid>
<lastUpdateTid>1002</lastUpdateTid>
<creationDate>2014-09-02T12:47:59+02:00</creationDate>
<lastUpdateDate>2014-09-02T12:47:59+02:00</lastUpdateDate>
</metadata>
<attributes>
<cmo_type>P</cmo_type>
<cmo_first_name>Smith</cmo_first_name>
<cmo_last_name>John</cmo_last_name>
<cmo_gender>M</cmo_gender>
<cmo_birth_date>1978-12-16T00:00:00+01:00</cmo_birth_date>
<cmo_sin>095242434</cmo_sin>
</attributes>
<relationships/>
</party>
</list>
The master record to retrieve is identified either by ID (as in the previous example) or by a combination of the instance record sourceId and origin:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
</request>
The MDM engine automatically finds the instance record and its master record. The response is the same.
Simple traversing
genericTraversalMasterService allows traversing from the starting point to other entities over relationships defined in the master model, for example, you want to retrieve addresses related to a given party:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<traversal>
<traverse relationshipName="addresses" />
</traversal>
</request>
The response is a list of addresses:
<list>
<address>
<metadata>
...
</metadata>
<attributes>
<party_master_id>38</party_master_id>
<cmo_type>R</cmo_type>
<cmo_street>13-3295 Sunnyside</cmo_street>
<cmo_state>BC</cmo_state>
<cmo_zip>V3H4Z4</cmo_zip>
</attributes>
<relationships/>
</address>
<address>
<metadata>
...
</metadata>
<attributes>
<party_master_id>38</party_master_id>
<cmo_type>M</cmo_type>
<cmo_street>80 Hemlock Dr</cmo_street>
<cmo_city>Anmore</cmo_city>
<cmo_state>BC</cmo_state>
<cmo_zip>V3H4W9</cmo_zip>
</attributes>
<relationships/>
</address>
</list>
Traversing with a simple filter
Traversing over relationships can be enhanced by a filter.
For example, if you want to see only addresses of type M, add a filter element with the eq subelement:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<traversal>
<traverse relationshipName="addresses">
<filter>
<eq attribute="cmo_type" value="M" />
</filter>
</traverse>
</traversal>
</request>
The response is only one address:
<list>
<address>
<metadata>
...
</metadata>
<attributes>
<party_master_id>38</party_master_id>
<cmo_type>M</cmo_type>
<cmo_street>80 Hemlock Dr</cmo_street>
<cmo_city>Anmore</cmo_city>
<cmo_state>BC</cmo_state>
<cmo_zip>V3H4W9</cmo_zip>
</attributes>
<relationships/>
</address>
</list>
Traversing with an advanced filter
Traversing filter can have more advanced conditions. For example, if you want only addresses that have good quality address instances:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<traversal>
<traverse relationshipName="addresses">
<filter>
<exists>
<traverse relationshipName="instances"> <!-- virtual traverse to address_instance records -->
<filter>
<eq attribute="sco_address" value="0" /> <!-- good quality means score is 0 -->
</filter>
</traverse>
</exists>
</filter>
</traverse>
</traversal>
</request>
This request can be translated as:
-
Get the party record with
sourceId = 1002andorigin = crm#customer#party. -
Traverse over the
addressesrelationship.-
Filter records: Allow only those for which there is a related instance address record that has
sco_addressequal to zero.
-
This example shows that a filter defined by an exists condition can be recursive: depth is not limited.
However, every step of recursion decreases the performance of the service.
The filter element can contain multiple eq and exists subelements.
Those are evaluated by default as AND, that is, all conditions must be met to return a record.
You can change this to OR by setting up an operator attribute on the filter element:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<traversal>
<traverse relationshipName="addresses">
<filter operator="OR">
<eq attribute="cmo_type" value="M" />
<eq attribute="cmo_type" value="R" />
</filter>
</traverse>
</traversal>
</request>
Chained traverse
Traversing over relationships can be chained, that is, multiple traverse elements can define a long path from the starting point to the destination entity. For example, if you want to get all parties related to input party records:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<traversal>
<traverse relationshipName="relparent" /> <!-- traverse to associative entity rel_party_party -->
<traverse relationshipName="child" /> <!-- traverse back to party -->
</traversal>
</request>
Each traverse element can have its own complex filter as described previously.
Preloading of related records
The second advanced feature of genericTraversalMasterService is the ability to return not only records of one entity but related records as well.
For example, if you want a party record with all addresses:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<preloadedRelationships>
<rel name="addresses" />
</preloadedRelationships>
</request>
The response is a complex hierarchical XML:
<list>
<party>
<metadata>
...
</metadata>
<attributes> <!-- party record attributes -->
<cmo_type>P</cmo_type>
<cmo_first_name>Smith</cmo_first_name>
<cmo_last_name>John</cmo_last_name>
<cmo_gender>M</cmo_gender>
<cmo_birth_date>1978-12-16T00:00:00+01:00</cmo_birth_date>
<cmo_sin>095242434</cmo_sin>
</attributes>
<relationships>
<addresses> <!-- address records -->
<address>
<metadata>
...
</metadata>
<attributes>
<party_master_id>38</party_master_id>
<cmo_type>R</cmo_type>
<cmo_street>13-3295 Sunnyside</cmo_street>
<cmo_state>BC</cmo_state>
<cmo_zip>V3H4Z4</cmo_zip>
</attributes>
<relationships/>
</address>
<address>
<metadata>
...
</metadata>
<attributes>
<party_master_id>38</party_master_id>
<cmo_type>M</cmo_type>
<cmo_street>80 Hemlock Dr</cmo_street>
<cmo_city>Anmore</cmo_city>
<cmo_state>BC</cmo_state>
<cmo_zip>V3H4W9</cmo_zip>
</attributes>
<relationships/>
</address>
</addresses>
</relationships>
</party>
</list>
Preloading of related records with a filter
When preloading related records, you can apply a filter so that only some records are returned. For example, if you want a party record with all addresses of type M:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<preloadedRelationships>
<rel name="addresses">
<filter>
<eq attribute="cmo_type" value="M" />
</filter>
</rel>
</preloadedRelationships>
</request>
The response contains a party record with one address:
<list>
<party>
<metadata>
...
</metadata>
<attributes>
<cmo_type>P</cmo_type>
<cmo_first_name>Smith</cmo_first_name>
<cmo_last_name>John</cmo_last_name>
<cmo_gender>M</cmo_gender>
<cmo_birth_date>1978-12-16T00:00:00+01:00</cmo_birth_date>
<cmo_sin>095242434</cmo_sin>
</attributes>
<relationships>
<addresses>
<address>
<metadata>
...
</metadata>
<attributes>
<party_master_id>38</party_master_id>
<cmo_type>M</cmo_type>
<cmo_street>80 Hemlock Dr</cmo_street>
<cmo_city>Anmore</cmo_city>
<cmo_state>BC</cmo_state>
<cmo_zip>V3H4W9</cmo_zip>
</attributes>
<relationships/>
</address>
</addresses>
</relationships>
</party>
</list>
Advanced preloading of related records
There are several more possibilities:
-
Specifying a more complex filter: Several
eqandexistssubelements. -
Preloading multiple relationships: For example, a party record with all related addresses and contacts.
-
Preloading records related to related records: For example, a party record with related addresses and address instances or a party record with all related parties (jump over the associative entity
rel_party_party). This scenario is described in the following section.
Preloading records related over several relationships
Let’s assume you want to retrieve a party record with all related parties:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1002" />
<preloadedRelationships>
<rel name="relparent/child" />
</preloadedRelationships>
</request>
Note the definition of preloadedRelationships: relparent/child.
If you want to preload records related over several relationships, you have to concatenate relationship names with the slash '/'.
This forces the service to jump over two relationships to get to records.
|
If you specify a filter for this preloaded relationship, it is applied to records of the last entity, that is, party in this example.
If you want to specify a filter to be applied to the first entity (that is, associative entity rel_party_party in this example), you have to add that entity to preloadedRelationships:
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1018" />
<preloadedRelationships>
<rel name="relparent">
<filter> <!-- filter on associative entity rel_party_party -->
<eq attribute="cmo_p2p_rel_type" value="FAMILY" />
</filter>
</rel>
<rel name="relparent/child">
<filter> <!-- filter on related party entity -->
<eq attribute="cmo_gender" value="M" />
</filter>
</rel>
</preloadedRelationships>
</request>
The response is an even more complex hierarchical XML:
<list>
<party>
<metadata>
...
</metadata>
<attributes> <!-- party attributes -->
<cmo_type>P</cmo_type>
<cmo_first_name>Sandy</cmo_first_name>
<cmo_last_name>Hettinger</cmo_last_name>
<cmo_gender>F</cmo_gender>
<cmo_birth_date>1965-09-21T00:00:00+01:00</cmo_birth_date>
<cmo_sin>856527270</cmo_sin>
</attributes>
<relationships>
<relparent>
<rel_party_party>
<metadata>
...
</metadata>
<attributes> <!-- party2party relation attributes -->
<parent_id>29</parent_id>
<child_id>31</child_id>
<cmo_p2p_rel_type>FAMILY</cmo_p2p_rel_type>
</attributes>
<relationships>
<child>
<party>
<metadata>
...
</metadata>
<attributes> <!-- attributes of related party -->
<cmo_type>P</cmo_type>
<cmo_first_name>Tom</cmo_first_name>
<cmo_last_name>Donathan</cmo_last_name>
<cmo_gender>M</cmo_gender>
<cmo_birth_date>1966-02-04T00:00:00+01:00</cmo_birth_date>
<cmo_sin>961085248</cmo_sin>
</attributes>
<relationships/>
</party>
</child>
</relationships>
</rel_party_party>
</relparent>
</relationships>
</party>
</list>
Complex example
Imagine you have party sourceId as an input, and you want to get:
-
All 'family' related party records representing males
-
But only those with good data quality email addresses
-
And only those living in the city of Anmore
-
-
Also return this party’s email addresses that have good data quality and their addresses.
In other words: "We want to email every man in Ohio this party is related to as family."
<request>
<startWith entity="party" origin="crm#customer#party" sourceId="1018" />
<traversal>
<traverse relationshipName="relparent">
<filter>
<eq attribute="cmo_p2p_rel_type" value="FAMILY" />
</filter>
</traverse>
<traverse relationshipName="child">
<filter>
<eq attribute="cmo_gender" value="M" />
<exists>
<traverse relationshipName="contacts">
<filter>
<eq attribute="cmo_type" value="email" />
<exists>
<traverse relationshipName="instances">
<filter>
<eq attribute="sco_value" value="0" />
</filter>
</traverse>
</exists>
</filter>
</traverse>
</exists>
<exists>
<traverse relationshipName="addresses">
<filter>
<eq attrbitue="cmo_city" value="Anmore" />
</filter>
</traverse>
</exists>
</filter>
</traverse>
</traversal>
<preloadedRelationships>
<rel name="contacts">
<filter>
<eq attribute="cmo_type" value="email" />
<exists>
<traverse relationshipName="instances">
<filter>
<eq attribute="sco_value" value="0" />
</filter>
</traverse>
</exists>
</filter>
</rel>
<rel name="contacts/instances" />
<rel name="addresses" />
</preloadedRelationships>
</request>
The service does a lot of work:
-
Get the party instance record by its
sourceIdandorigin. -
Get the master record for this instance.
-
Get all relations of this record of type
FAMILY. -
Get all party records from those relations.
-
Filter those party records:
-
cmo_genderis 'M'. -
Get all related address records and check there is at least one with
cmo_cityequal to 'Anmore'. -
Get all related contact records and filter only those of type 'email'.
-
Then get all related instance records and check whether
sco_valueequals to zero.
-
-
Check that at least one contact record satisfied the previous conditions.
-
-
For every party record that was not filtered out, preload related records:
-
Get all related contact records of type 'email'.
-
Get all related contact instance records that have
sco_valueequal to zero.
-
-
Get all related address records.
-
As we can see, genericTraverseMasterService is very powerful and can create quite complex queries on master data.
However, keep in mind that complex queries require corresponding HW resources for both the MDM engine and underlying database storage.
This particular example will execute about 10 SQL queries just for one call.
Was this page useful?