Service - Complex Filter
Jump to navigation
Jump to search
Overview
Allows for filters that span nested levels of related entities
Repository
https://bitbucket.org/izara-core-search/izara-core-search-complex-filters
DynamoDB tables
Standard Config Table Per Service
Configuration tags
{
configKey: "FilterType"
configTag: "xx" // eg: variant / product / sellOffer
configValue: {
childComplexFilterTypes: {
{child filterType}: {
"translateIdsServiceName": "xx" // eg: variantManager / sellOfferManager, service than converts child id to parent id
},
},
logicalServiceNames : ["xx", "yy"] // eg: "ProductStandard", "ProductCustom"
}
}
- logicalServiceNames should not duplicate over mulitiple FilterType's, because we use this setting to match a Logical Service response to a FilterMain record, need it to uniquely identify one filterMain record
{
configKey: "ComplexFilterServiceName"
configTag: "xx" // eg: product / sellOffer
configValue: "yy" // eg: ComplexFilterProducts
}
{
configKey: "ExpiryInterval"
configTag: "ExpiryInterval"
configValue: xx // minutes(?) complexfilter ttl
}
{
configKey: "MaxProcessInvocationCount"
configTag: "MaxProcessInvocationCount"
configValue: 10 // maximum number of invocations allowed for large operation complex filter processing
}
complexFilter request
{
filterType: "xx" // eg: "mediaLink",
type: "group",
elements:
[
{
type: "complexFilter",
complexFilter: {
filterType: "xx", //eg: mediaLinkProperty
type: "group",
elements:
[
{
type: "logical",
logicalTag: "propertyLabelId", // is used by the logical service/s to decide how to find data
// the below properties are up to the logicalTag what is needed, but these are common:
comparison: "equals",
value: "lksdflkfldfgldfkgjldfg"
},
]
}
},
{
type: "logicalOperator",
operator: "and"
},
{
type: "complexFilter",
complexFilter: {
filterType: "xx", //eg: mediaLinkPropertyTranslation
type: "group",
elements:
[
{
type: "logical",
logicalTag: "textTag_languageId_text",
//example for matching to specific translation text:
textTag: "mediaLinkPropertyValue",
languageId: "en",
text: "Blue",
subjectIdentifierPropertyName: "propertyId",
caseSensitive: true
},
]
}
},
...
]
}
How to request Complex Filter
- invoke shared library function createFilterMainId which iterates through elements and builds filterMainId by applying operators. There are no database requests in this function so it can be run from any service and can trust the filterMainId will be standardized
- No cleaning of the filter object is performed, so additional unused properties would result in a different filterMainId, even if the underlying filter is the same
- calling function saves filterMainId in pending table waiting for filter complete, this needs to be done before calling ProcessComplexFilter to protect against race condition of complex filter completing before pending record saved (probably use AwaitingStep process)
- All requests to ProcessComplexFilter will send a message out, even if cache exists
- CallingFlow param allows Complex Filter service to pass a message back to only the calling flow when a request hits cached results
How is filterMainId generated
- cannot hash entire request elements without iterating through the elements because a final filterMainId of multiple elements is actually the final operation filterMainId (hash of the last element joined with the previous culmination of operation elements)
- this does not match a hash of the original request object
- needs to be done this way so we can back track through parent operations in a standardized way when receiving results for each element
Normal Structure ComplexFilter Request
{ //* type: "logical" structure
objType: {
serviceTag: "XX",
objectType: "xx"
},
filterMainId: "hash_operation",
filterElements: { //* this is top filterElements that keep all filterElement of request
hash_ref_filterElement_A: {
objType: {
serviceTag: "XX",
objectType: "xx"
},
filterElement: {
filterType: "logical",
partitionKeyValues: {
xxId: "xx",
yyId: "yy"
},
sortKeyValues: {
zzId: "zz",
},
comparison: "equals",
conditionalFieldValues: [
{
fieldname: "language",
includeValues: ["th"],
// excludeValues: ["en","jp"]
}
]
}
},
hash_childComplexFilter: {
objType: {},
filterElement: {
filterType: "childComplexFilter",
pathLinkType: {
objType: {
serviceTag: "YY",
objectType: "yy"
},
relType: {
serviceTag: "YY",
relationshipTag: "hasYY"
},
direction: "from"
},
childFilterElementId: "hash_ref_filterElement_A",
requestProperties: {
tag_xxxxx: "xxxxx"
}
}
},
hash_ref_filterElement_B: {
//* normalize structure
},
hash_operation: {
objType: {},
filterElement: {
filterType: "operation",
filterElementLeft: "hash_childComplexFilter",
filterElementRight: "hash_ref_filterElement_B",
operator: "AND" | "OR"
}
}
}
}
example pattern each filterType structure in filterElement
[
{
filterType: "logical",
partitionKeyValues: {
xxId: "xx",
yyId: "yy"
},
sortKeyValues: {
zzId: "zz",
},
comparison: "equals",
conditionalFieldValues: [
{
fieldname: "language",
includeValues: ["th"],
// excludeValues: ["en","jp"]
}
]
},
{
filterType: "childComplexFilter",
filterElements: { //* use when hash own filterMainId, but when keep in top filterElmenets must delete
ref_filterElement_A: {
//* normalize structure
}
},
pathLinkType: {
objType: {
serviceTag: "YY",
objectType: "yy"
},
relType: {
serviceTag: "YY",
relationshipTag: "hasYY"
},
direction: "from"
},
childFilterElementId: "ref_filterElement_A",
requestProperties: {
tag_xxxxx: "xxxxx"
}
},
{
filterType: "operation",
filterElements: { //* use when hash own filterMainId, but when keep in top filterElmenets must delete
ref_filterElement_A: {
//* normalize structure
},
ref_filterElement_B: {
//* normalize structure
}
},
filterElementLeft: "ref_filterElement_A",
filterElementRight: "ref_filterElement_B",
operator: "AND"
},
{
filterType: "translateIds ",
childIdentifiers: [
{
xxId: "111"
},
{
xxId: "222"
}
],
pathLinkType: {
objType: {
serviceTag: "yy",
objectType: "yy"
},
relType: {
serviceTag: "yy",
objectType: "hasYY"
},
direction: ""
},
requesetProperties: {
tag_quantity: 20
}
},
{ //* traversal has hopsPassObjType
filterType: "traversal",
traversals: [
{
links: [
{
relType: {},
direction: ""
},
{
relType: {},
direction: ""
}
],
//* other settings
fields: { //* optional
categoryId: 'sellOfferId_value',
productId: 'sellOfferId_value'
},
relationshipProperties: { timestamp: 'sellOfferId_value' }, //* optional
hopsStart: 1, //* optional default = 1
hopsEnd: 10, //* optional
hopsPassObjType: {
serviceTag: "",
objectType: ""
},
objType: {
serviceTag: "",
objectType: ""
}
},
{
fields: {
name: { //fieldName
anyValue: [
'alice',
'john',
//...
]
},
xxId_fieldname: "xxxx"
}
//example other fields pattern: //* front end not yet.
// fields: {
// fieldName: {
// comparison: "greaterThan", // greaterThan | lessThan | lessThanOrEqual | greaterThanOrEqual
// value: 100
// }
// }
// fields: {
// fieldName: {
// comparison: "between",
// valueBetweenStart: 100,
// valueBetweenEnd: 200
// }
// }
}
]
},
{ //* traversal no hopsPassObjType
filterType: "traversal",
traversals: [
{
links: [
{
relType: {},
direction: ""
},
{
relType: {},
direction: ""
}
],
//* other settings
hopsPassObjType: {
serviceTag: "",
objectType: ""
},
objType: {
serviceTag: "",
objectType: ""
}
},
{
fields: {
xxId: "xxxx"
}
}
]
}
]
example hash filterElement of each filterType
let hash_ref_filterElement_A = hash({
objType: {
serviceTag: "",
objectType: ""
},
filterElement: {
filterType: "logical" | "childComplexFilter" | "operation" | "translateIds" | "traversal",
filterElements: { //* use in filterType: "childComplexFilter" | "operation"
//* all ref filterElement that this filterElement ref
},
//* other parameters depend on the filterType.
//..
//..
}
};