Semantic seeds
The connector development walkthrough describes how to create a service that accepts seed records to drive the searches that it performs. In that example, the records that the service accepts are constrained by their item types.
Starting from version 4.4.1 of i2 Analyze, you can specify semantic constraints instead of item type constraints, so that:
- A service can accept seed records of any item type, provided that they have properties with particular semantic types (or descendants of those types).
- Where a seed record has more than one property with the same (or a related) semantic type, you can base a search on the values of all such properties.
This topic describes how to specify semantic constraints. For more information about the uses to which you can put seeded searches, see Supporting seeds.
Setting up an example connector
This example is based on the auth-connector
.
We'll extend that example to add a service that uses a semantic seed constraint.
The service uses the SPI to examine the seed that it receives in the request, and returns a response containing entities and links whose property values match the values in that seed.
Ensure that you have a working deployment of the auth-connector
before you continue. You can find instructions for deploying it here.
Add a new service
The new service will query an external data source (just a text file in this example) for person records that have the same last name as the seed in the request.
By using a semantic constraint, the service will accept a seed of any type that has a property with the "Person Last Name" semantic type. In addition, since the "Maiden Name" semantic type is a child of "Person Last Name", the service will also accept a seed with a property of that type.
In the config.json
file that defines the connector, you will need to add the new service to the services
array. You will also need to add seedConstraints
to define a seeded search, and within it semanticPropertyTypes
to define the semantic types of properties that records must have in order to be used as seeds.
The following code specifies a seed constraint that uses the "Person Last Name" semantic type. (The GUID guid62B01C18-2E64-46D5-B2FF-3C69B4F76FEB
appears as the identifier of that semantic type in the i2 Semantic Type Library.)
{
"services": [
{
"id": "find-same-last-name",
"name": "Auth Connector: Find Same Last Name",
"description": "A service that finds records with the same last name as the seed.",
"clientConfigType": "NONE",
"acquireUrl": "/find-same-last-name",
"seedConstraints": {
"min": 1,
"max": 1,
"seedTypes": {
"semanticPropertyTypes": {
"Name constraints": [
"guid62B01C18-2E64-46D5-B2FF-3C69B4F76FEB"
]
}
}
}
}
]
}
Note: When you write your own services with semantic type constraints, you'll need to find the GUIDs of the semantic property types that you want to use. See Semantic type identifiers for more information.
Implement the service endpoint
The service will work by examining the property values of a selected record according to the defined constraints, and searching for data in the external source that has matching values in its properties.
In code, you can access the semantic properties of the seed record in the request through the request.payload.seeds.semanticData
object. The service that you added to config.json
specifies an endpoint named /find-same-last-name
that you can implement like this, in ConnectorController.java
:
@RequestMapping(method = RequestMethod.POST, value = "/find-same-last-name",
consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
public ResponseEntity<?> findSameLastName(@Valid @RequestBody DaodRequest request) {
return connectorDataService.findSameName(request.payload.seeds);
}
Note: This code does not compile because the findSameName()
method is undefined.
You can ignore this for now, and we will cover the implementation of that method later in this guide.
Access the seeds
The findSameName()
method needs to examine the seeds passed into the request according to the SPI, and return a response that contains entity and link records.
The DaodRequest
object that the service receives for a search that uses semantic seeds might look like the following example.
{
"payload": {
"seeds": {
"entities": [
{
"accessDimensionValues": [],
"extensions": [],
"label": "",
"properties": {
"PER1": "Sally",
"PER2": "Armstrong",
"PER6": "Hendricks"
},
"seedId": "123",
"sourceIds": [],
"typeId": "Person"
}
],
"links": [],
"semanticData": {
"Name constraints": [
{
"properties": {
"guid62B01C18-2E64-46D5-B2FF-3C69B4F76FEB": [
{
"logicalType": "SINGLE_LINE_STRING",
"value": "Armstrong"
},
{
"logicalType": "SINGLE_LINE_STRING",
"value": "Hendricks",
"sourceSemanticTypeId": "guid699AD2D5-B30B-4508-9349-0E1AC1FF876C"
}
],
"seedId": "123",
"isLink": false
}
}
]
}
}
}
}
In this example, a single seed with the identifier 123
was selected on the chart. That seed had values for the Last Name
property (PER2) and the Maiden Name
property (PER6).
In the semantic data, the entry with the value Hendricks
has a sourceSemanticTypeId
value, which means that the containing property does not have the "Person Last Name" semantic type, but rather a descendant of that type - in this case, "Maiden Name".
Find data based on the seed
As with any service, how you use the seed data depends on what you want to achieve.
To perform a search using this seed data, we need to filter the list of records in people.json
according to whether they have values of "Hendricks" or "Armstrong" for any properties whose meaning is the same as "last name".
In this case, the records in people.json
have a property called surname
, which we can compare with the contents of the seed data. Add this implementation of the findSameName()
method to the ExternalConnectorDataService.java
file:
public ResponseEntity<?> findSameName(DaodSeeds seed) {
final Set<Object> nameValues = new HashSet<>();
final List<SemanticSeed> nameConstraintSemanticProperties =
seed.semanticData.get("Name constraints");
for (SemanticSeed semanticData : nameConstraintSemanticProperties) {
for (Set<SemanticProperty> constraintSemanticProperties :
semanticData.properties.values()) {
nameValues.addAll(constraintSemanticProperties.stream().map(constraint -> constraint.value).collect(Collectors.toList()));
}
}
final List<Person> people = retrievePeopleData();
// Remove any people that do not match the name constraint values
people.removeIf(p -> !nameValues.contains(p.surname));
final I2ConnectData acquireResponse = marshalItemsFromResponse(people);
return ResponseEntity.status(200).body(acquireResponse);
}
Only one of the people in people.json
has the surname
"Armstrong" or "Hendricks", and so for this example the response should just contain the following data:
{
"entities": [
{
"typeId": "Person",
"id": "21bbe625-c8da-4461-a8a0-f7c89aa85889",
"version": 1,
"properties": {
"PER2" : "Armstrong",
"PER1" : "Bryce",
"PER4" : "00453474",
"PER3" : "1971-04-29",
"PER5" : "1976-10-23T13:30:00.409"
}
}
],
"links": []
}
Update the i2 Connect gateway
In order for i2 Analyze to recognize these changes, you must reload the i2 Connect gateway.
Open a web browser and navigate to
<i2-Analyze-URL>/admin#/connectors
, where<i2-Analyze-URL>
is the URL used to access your i2 Analyze deployment. For example,http://localhost:9082/opaldaod/admin#/connectors
.Note: For more information about the Admin Console, refer to i2 Analyze Server Admin Console.
If you are prompted to log in, enter the credentials for your default user. If you added an example user, the username and the password will be
Jenny
.Click Reload gateway to enact your changes.
Run the query
Now, you can use the new query:
- Open the Analyst's Notebook desktop client.
- Select a record on the chart that has a value for the "Family Name" or "Maiden Name" property (or for both).
- Click "External Search".
- Select the new "Find Same Last Name" query, and run it.
The list of records that you see in the results should include all the "people" from the people.json
file whose surname matches either the family name or the maiden name of the record that you originally selected.