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>
NME 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 = 1002
andorigin = crm#customer#party
. -
Traverse over the
addresses
relationship.-
Filter records: Allow only those for which there is a related instance address record that has
sco_address
equal 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
eq
andexists
subelements. -
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
sourceId
andorigin
. -
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_gender
is 'M'. -
Get all related address records and check there is at least one with
cmo_city
equal to 'Anmore'. -
Get all related contact records and filter only those of type 'email'.
-
Then get all related instance records and check whether
sco_value
equals 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_value
equal 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?