2024-11-17 - ComplexFilter traversal filterElement
Overview
Add filter element in Complex Filter service that allows for deep path searching in graph relationships.
Examples
Find all parent locationNodes for locationNode/s
Idea
New filter element names 'traversal' that follows a similar format to a deepPathSearch request but in reverse order.
Reverse order because in the complex filter request the top level complex filter is the result of the deep path search, so the traversal request object will start with the final deep path step.
Each traversal filter element object can set an array of objTypes and relTypes+direction (links?), and how many hops, perhaps limit result count as well.
Example 1
- Find all SellOfferPlan identifiers that link to a product identifiers
Traversal filter element object:
- ComplexFilter request objType sellOfferPlan, it has a traversal filterElement that has 2 steps
- First step objType SellOffer, relType hasSellOfferPlan, direction sellOfferPlan>SellOffer, hops 1
- Second step objType Product, relType hasSellOffer, direction SellOffer>Product, hops 1, obj property search is product identifier
DeepPathSearch request object:
- Initial step objType Product, node property filter is product identifier
- First step objType SellOffer, relType hasSellOffer, relType Product>SellOffer, hops 1
- Second step objType sellOfferPlan, relType hasSellOfferPlan, direction SellOffer>sellOfferPlan, hops 1
Example 2
- Find all parent locationNodes for a locationNode, needs to traverse many hops across 2 relationships (child)locationNode > locationLink > (parent)locationNode
- Wants to return every parent locationNode id, not just leaf results
Traversal filter element object:
- ComplexFilter request objType (parent)locationNode, it has a traversal filterElement that has 1 step
- objTypes [locationNode, locationLink], relTypes [has_locationLink, is_locationNode], direction (child)locationNode > locationLink > (parent)locationNode, hops (traversal limit), obj property search is locationNode identifier
DeepPathSearch request object:
- Initial step objType locationNode, node property filter is locationNode identifier
- First step objTypes [locationNode, locationLink], relTypes [has_locationLink, is_locationNode], direction (child)locationNode > locationLink > (parent)locationNode, hops (traversal limit)
Logic flow
We could have an endpoint in external service to handle each traversal element but I think not necessary because the conversion to deepPathSearch request is standard, can put the flow and awaitingStep into ComplexFilter service.
Might want to wrap DeepPathSearch logic in a way that allows for scale, this would mean paginating results of deepPathSearch and either saving them into a storedCache DynamoDB table in GraphHandler, or GraphHandler saving to an external service's DynamoDB table.
Future idea
- Add filter to each objType in objTypes, eg only pass locationLinks that are of an allowable 'weight'
- Order by 'weight' and add record count limit to returned results. This might be handled better as sortResult, pass all locationLinks to SortResult service, sort by weight and perform comparison.
traversalFilter Syntax
singleLinkSegment
Represents a single traversal segment used in traversalFilter.
Structure
singleLinkSegment = {
pathLinkType: { // REQUIRED
objType: ObjTypeDefinition,
relType: {
relationshipTag: string,
serviceTag: string
},
direction: "to" | "from"
},
hopsStart?: number, // OPTIONAL (minimum: 1)
hopsEnd?: number, // OPTIONAL (minimum: 1)
fields?: Object, // OPTIONAL
relationshipProperties?: Object // OPTIONAL
}
Example: singleLinkSegment
{
pathLinkType: {
objType: {
serviceTag: "Cart",
objectType: "cart"
},
relType: {
relationshipTag: "cartShipToAddress",
serviceTag: "Cart"
},
direction: "from"
},
hopsStart: 1, // OPTIONAL
hopsEnd: 4, // OPTIONAL
fields: { // OPTIONAL
addressId: "address4123"
},
relationshipProperties: { // OPTIONAL
timestamp: "566"
}
}
multiLinkSegment
Structure
multiLinkSegment = {
links: LinkDefinition[], // REQUIRED
fields?: Object, // OPTIONAL
relationshipProperties?: Object, // OPTIONAL
// Number of relationship hops between two nodes
hopsStart?: number, // OPTIONAL (minimum: 1)
hopsEnd?: number, // OPTIONAL (minimum: 1)
hopsPassObjType?: ObjTypeDefinition, // REQUIRED (if single)
hopsPassObjTypes?: ObjTypeDefinition[], // REQUIRED (if multiple)
objType?: ObjTypeDefinition // REQUIRED
}
Example: multiLinkSegment
{
links: [
{
relType: {
serviceTag: "Cart",
relationshipTag: "cartSellOfferLinkIsSellOffer"
},
direction: "to"
},
{
relType: {
serviceTag: "Cart",
relationshipTag: "hasCartSellOfferLink"
},
direction: "to"
}
],
fields: {
cartId: "cartuuid01"
},
relationshipProperties: {
aaa: "aaa"
},
hopsStart: 1,
hopsEnd: 2,
hopsPassObjType: {
serviceTag: "Cart",
objectType: "cartSellOfferLink"
},
objType: {
serviceTag: "SellOfferManager",
objectType: "sellOffer"
}
}
Property Details
pathLinkType (Required)
- Defines the relationship and the source object type.
links (Required)
- Defines the sequence of relationships used in traversal.
- At least one link must be defined.
hopsPassObjType OR hopsPassObjTypes (Required)
- Defines which objectType(s) are allowed while traversing intermediate hops.
- Used to resolve intermediate traversal objectTypes in the graph service.
Single
hopsPassObjType: {
serviceTag: string,
objectType: string
}
Multiple
hopsPassObjTypes: [
{
serviceTag: string,
objectType: string
},
{
serviceTag: string,
objectType: string
}
]
- Supports multiple objTypes.
objType (Required)
- Defines the source object type of this segment.
fields (Optional)
- Filters the node (objectType) defined in the upper segment of the traversal.
Important Notes
- Applies only to node properties, not relationship properties.
- To filter relationship properties, use relationshipProperties.
Structure Syntax: fields and relationshipProperties
Basic Equality
fields: {
name: {
anyValue: [
"alice",
"john"
]
},
fieldNameA: "aa"
}
Comparison Syntax
- comparison is required when using comparison mode.
- Supported comparison operators:
- greaterThan
- lessThan
- lessThanOrEqual
- greaterThanOrEqual
- between
Standard Comparison
fields: {
fieldName: {
comparison: "greaterThan",
value: 100
}
}
Rules
- Supports: string, integer
- value must match the field type
Between Comparison
Used for range filtering.
fields: {
fieldName: {
comparison: "between",
valueBetweenStart: 100,
valueBetweenEnd: 200
}
}
Rules
- Only valid when comparison = "between"
- Both valueBetweenStart and valueBetweenEnd are required
- Range is inclusive
Full Example
fields: {
status: {
anyValue: ["ACTIVE", "PENDING"]
},
price: {
comparison: "greaterThan",
value: 100
},
quantity: {
comparison: "between",
valueBetweenStart: 10,
valueBetweenEnd: 50
},
category: "electronics"
}
relationshipProperties (Optional)
- Filters relationship properties for the defined links.
hopsStart (Optional)
- Minimum hop distance.
- If set, the value must be greater than or equal to 1.
hopsEnd (Optional)
- Maximum hop distance.
- If not defined, the value defaults to hopsStart.
ComplexFilter Traversal Examples
- Find "sellOfferPlan" objects starting from a "product" objectidentified by { productId: "xy" }.
Exam 1
let complexFilterMessageExam_1 = {
objType: {
serviceTag: 'SellOfferPlan',
objectType: 'sellOfferPlan'
},
filterMainId: '0d1b6115f474275b8144c7cc5384be502d7d03de',
filterElements: {
'0d1b6115f474275b8144c7cc5384be502d7d03de': {
objType: {
serviceTag: 'SellOfferPlan',
objectType: 'sellOfferPlan'
},
filterElement: {
filterType: 'traversal',
traversals: [
{
pathLinkType: {
objType: {
serviceTag: 'SellOfferManager',
objectType: 'sellOffer'
},
relType: {
serviceTag: 'SellOfferManager',
relationshipTag: 'usesSellOfferPlan'
},
direction: 'from'
}
},
{
pathLinkType: {
objType: {
serviceTag: 'ProductManager',
objectType: 'product'
},
relType: {
serviceTag: 'SellOfferManager',
relationshipTag: 'hasSellOffer'
},
direction: 'from'
}
},
{ fields: { productId: 'xy' } }
]
}
}
}
}
Exam 2
- Find connected "locationNode" objects starting from
{ locationNodeId: "Nan" } with hopsStart = 1 and hopsEnd = 20.
let complexFilterMessageExam_2 = {
objType: { serviceTag: 'Locations', objectType: 'locationNode' },
filterMainId: '688aac6e53f05e5634e6b71b0990b9bf1650f0cc',
filterElements: {
'688aac6e53f05e5634e6b71b0990b9bf1650f0cc': {
objType: { serviceTag: 'Locations', objectType: 'locationNode' },
filterElement: {
filterType: 'traversal',
traversals: [
{
links: [
{
relType: {
serviceTag: 'Locations',
relationshipTag: 'hasLocationLink'
},
direction: 'to'
},
{
relType: {
serviceTag: 'Locations',
relationshipTag: 'isLocationNode'
},
direction: 'to'
}
],
hopsEnd: 20,
hopsPassObjType: {
serviceTag: 'Locations',
objectType: 'locationLink'
},
objType: {
serviceTag: 'Locations',
objectType: 'locationNode'
}
},
{ fields: { locationNodeId: 'Nan' } }
]
}
}
}
}
Exam 3
- Example of a traversal FilterElement combining multiLinkSegment and singleLinkSegment.
let complexFilterMessageExam_3 = {
objType: { serviceTag: 'Locations', objectType: 'locationNode' },
filterMainId: '700958b71e1576d249715c085991a300f704fe99',
filterElements: {
'700958b71e1576d249715c085991a300f704fe99': {
objType: { serviceTag: 'Locations', objectType: 'locationNode' },
filterElement: {
filterType: 'traversal',
traversals: [
{
pathLinkType: {
objType: {
serviceTag: 'Addresses',
objectType: 'address'
},
relType: {
relationshipTag: 'addressHasLocationNode',
serviceTag: 'Addresses'
},
direction: 'from'
}
},
{
pathLinkType: {
objType: { serviceTag: 'Cart', objectType: 'cart' },
relType: {
relationshipTag: 'cartShipToAddress',
serviceTag: 'Cart'
},
direction: 'from'
}
},
{
links: [
{
relType: {
serviceTag: 'Cart',
relationshipTag: 'cartOrderHasCartSellOfferLink'
},
direction: 'to'
},
{
relType: {
serviceTag: 'Cart',
relationshipTag: 'hasCartOrder'
},
direction: 'to'
}
],
hopsEnd: 2,
hopsPassObjType: { serviceTag: 'Cart', objectType: 'cartOrder' },
objType: {
serviceTag: 'Cart',
objectType: 'cartSellOfferLink'
}
},
{
pathLinkType: {
objType: {
serviceTag: 'SellOfferManager',
objectType: 'sellOffer'
},
relType: {
serviceTag: 'Cart',
relationshipTag: 'cartSellOfferLinkIsSellOffer'
},
direction: 'to'
}
},
{
fields: {
sellOfferId: {
anyValue: [
'sellOfferIduuid1',
'sellOfferIduuid2',
'sellOfferIduuid3'
]
}
}
}
]
}
}
}
}