Service - Search Results: Difference between revisions

From Izara Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
Line 1: Line 1:
= Overview =
= Overview =


Service that handles search result requests, feeding work to [[Service - Search Result (handlers)]].
Service that handles search result requests, feeding work to [[Service - Search Result (handlers)]]. Takes results from Complex Filter service which are identifier ids only and adds additional fields of information ready for display to end consumer, either directly if no sorting required, or via Sort Result service.
 
Functions as a cache of Search Result data, so if subsequent matching requests come in they do not need to pass on to Complex Filter or find required data.


= Repository =
= Repository =
Line 15: Line 17:
<syntaxhighlight lang="JavaScript">
<syntaxhighlight lang="JavaScript">
{
{
"configKey": "searchType",
configKey: "SearchType",
"configTag": {eg: sellOffer/Product/etc..}
configTag: "xx" // {eg: sellOffer/Product/VariantProduct etc..}
"configValue": {
configValue: {
"complexFilterServiceName": {service name of complex filter that handles this type}
complexFilterServiceName: "xx" // {service name of complex filter that handles this type}
"searchResultHandlerServiceName": {service name used to create function/queue names etc.}
dataHandlerServiceName: "xx" {service name used to create function/queue names for finding data results, often a Manager service for the object}
"requireProperties": [
requireRequestProperties: {
.. {properties that must be sent with the request}
{requiredData fieldName}: [ // dependant on what requiredData requested
"xx",
// .. {properties that must be received in the request for this searchType to process correctly}
]
},
parentDataIdentifierFields: [
"xx", // these are always found with any request for this SearchType to add into ParentDataId table for each found data
],
],
childSearchResult: {
childSearchResults: {
child_search_type: {searchType of child searchResult},
{childSearchType}: { // searchType of child searchResult
setRequiredData: [
requiredData: { // requiredData we request from the child searchType depending on what requiredData fields are set in the parent's request
.. {hardcodes the requiredData to be sent to the searchResult request}
{parent requiredData fieldName}: [
],
"xx", // list of child requiredData fields to add
passOnParentToChild: [
// ..
.. {properties to pass on to the child searchResult}
],
]
// ..
}
},
passOnRequestProperties: [ // parent requestProperties to pass on to the child searchResult request, if not received in parent request will error
"xx",
// ..
]
},
// ..
}
}
}
},
},
</syntaxhighlight>
</syntaxhighlight>
== SearchResultMain ==
<syntaxhighlight lang="JavaScript">
{
searchResultId: "xx" // main element for one set of search results, comes from {searchType}_{filterMainId}
searchDetailId: "xx" // hashes of other values that affect result data: {requestPropertiesHash}_{requiredDataHash}
    processStatus: "processing",
    createTime: currentTime.getTime(),
    expiryTime: expiryTime.getTime(),
}
</syntaxhighlight>
* partition key: searchResultId
* sort key: searchDetailId
* at the moment a parent Main can only have 1 child SearchResultMain (in future could probably change to a list of child Main’s pretty easily). One child SearchResultMain can have many parent SearchResultMain’s pointing to it
== SearchResultParents ==
<syntaxhighlight lang="JavaScript">
{
childSearchId: "xx", // {searchResultId}_{searchDetailId}
parentSearchId: "xx" // {searchResultId}_{searchDetailId}
}
</syntaxhighlight>
* partition key: childSearchId
* sort key: parentSearchId
* when a SearchResultMain is finished it checks this table to see if any parents need to be processed
== SearchResultData ==
<syntaxhighlight lang="JavaScript">
{
searchDataId: "xx", // {searchResultId}_{searchDetailId}
dataId: "xx", // id of element (sellOfferId/productId/variantProduct(type and id))
// reconsider this field, does not scale: childData: {}, // only used when childId set in SearchResultMain, saves requiredData results for all matching child records
    SearchResultDataStatus: "processing",
}
</syntaxhighlight>
* partition key: searchDataId
* sort key: dataId
== RequiredData ==
<syntaxhighlight lang="JavaScript">
{
requiredDataId: "xx", // {searchType}_{dataId}_{requestPropertiesHash} OR maybe hash of object of these values?
fieldName: "xx",
value: "xx", // value found for this reqdata field
requiredDataStatus: "xx", // waitingExternalData | complete | invalid
receivedExternalData: .. // externalData fields received data from handler services
waitingExternalData: .. // externalData fields waiting for data from handler services
    createTime: currentTime.getTime(),
    expiryTime: expiryTime.getTime(),
}
</syntaxhighlight>
* partition key: requiredDataId
* one record is one result we can calculate from external services, this can be shared by multiple SearchResultData records
* requiredDataId includes identifiers that can affect the data's value
* if keys too long for Dynamo we could hash an object containing all required fields and can add the values of each into the item separately, if needed
* primary key can be used when result comes back from external service, so that message must include all values needed to make requiredDataId
* can probably add fields during processing to store extra data for processing this SearchResultData record
* if we want to find all records for one SearchResultData we could search for just the partition key, but it might return additonal fields used by other searches with different requiredData
== RequiredDataSearchResultData ==
<syntaxhighlight lang="JavaScript">
{
requiredDataId_fieldName: "xx", // {requiredDataId}_{fieldName}
searchDataId: "xx"
}
</syntaxhighlight>
* partition key: requiredDataId_fieldName
* sort key: searchDataId
* can extract dataId from requiredDataId_fieldName to find primary key for SearchResultData
* used to create links between RequiredData results and all SearchResultData that need it
== ParentDataId ==
<syntaxhighlight lang="JavaScript">
{
parentId: "xx", // {parent searchType}_{parent dataId}_{child searchType}_{child filterMainId}
childDataId: "xx" // {child dataId}
}
</syntaxhighlight>
* partition key: parentId
* sort key: childDataId
* child filterMainId is required because we will have different ranges of childIds stored depending on what filter was used to find them
= requestProperties =
These are properties that are added to a Search Result request that affect the data found, they may be required or optional. Because these properties can affect the value of data they must be added to SearchResultMain identifiers to differentiate batches of results.
= requireRequestProperties =
Some requiredData fields will need additional properties to be received in the initial request.
* example: variant/product/sellOffer pricing fields will require locationNodeId/s and browseQuantity
* example: all translations require language/s
= Moving child SearchResult data to parent =
* eg: product searchType has sellOffer child searchType data
* eg: variantProduct searchType has product child searchType data
* parent must wait for child DigData to complete before can process parents DigData
* child SearchResult might already have completed before the parent request was received (ie by a different request), need to account for this
* (old design - can still use if have to, but would be best to separate results so can scale, eg if want to show details about child products, that could pass as a separate Search/Sort set of results including pagination) aggregate multiple child data into the parents data values, but also keep a record of all children in the parents data record
= Multiple Search Results services =
* design to allow for different implementations, eg catalogs can choose which Search Results service it uses
* could achieve this be having a setting in Catalog handlers or Catalog Manager that records which Search Result service the Catalog uses
= browseQuantity for calculating prices =
* when browsing, a user can define browseQuantity they want to buy (default 1), this would be injected into price calculation to obtain available price for each sellOffer
* different to filters, eg availableQuantity, which determines which sellOffers are returned. (we might have a method/hook that validates or updates the complex filter so these settings don't conflict, but not needed)
* added to requestProperties in request
* I think if we request prices for a SellOffer in a quantity it cannot handle, eg browseQuantity is 100 but selloffer only has 20 remaining, the pricing function should return an error, could then remove (or mark as not enough quantity available) such sellOffers, so they might still exist in search results and maybe sort results but show quantity not/available.
= Notes =
* Consider requests dig all possible needed data, even if the current request does not use it, that way subsequent requests can feed off the same processed data, ie standardize requiredData at request level to use cache more often


= Working documents =
= Working documents =

Revision as of 05:02, 17 October 2021

Overview

Service that handles search result requests, feeding work to Service - Search Result (handlers). Takes results from Complex Filter service which are identifier ids only and adds additional fields of information ready for display to end consumer, either directly if no sorting required, or via Sort Result service.

Functions as a cache of Search Result data, so if subsequent matching requests come in they do not need to pass on to Complex Filter or find required data.

Repository

https://bitbucket.org/izara-core-search/izara-core-search-search-results/src/master/

DynamoDB tables

Standard Config Table Per Service

Configuration tags

{
	configKey: "SearchType",
	configTag: "xx" // {eg: sellOffer/Product/VariantProduct etc..}
	configValue: {
		complexFilterServiceName: "xx" // {service name of complex filter that handles this type}
		dataHandlerServiceName: "xx" {service name used to create function/queue names for finding data results, often a Manager service for the object}
		requireRequestProperties: {
			{requiredData fieldName}: [ // dependant on what requiredData requested
				"xx",
				// .. {properties that must be received in the request for this searchType to process correctly}
			]
		},
		parentDataIdentifierFields: [
			"xx", // these are always found with any request for this SearchType to add into ParentDataId table for each found data
		],
		childSearchResults: {
			{childSearchType}: { // searchType of child searchResult
				requiredData: { // requiredData we request from the child searchType depending on what requiredData fields are set in the parent's request
					{parent requiredData fieldName}: [
						"xx", // list of child requiredData fields to add
						// ..
					],
					// ..
				},
				passOnRequestProperties: [ // parent requestProperties to pass on to the child searchResult request, if not received in parent request will error
					"xx", 
					// .. 
				]
			},
			// ..
		}
	}
},

SearchResultMain

{
	searchResultId: "xx" // main element for one set of search results, comes from {searchType}_{filterMainId}
	searchDetailId: "xx" // hashes of other values that affect result data: {requestPropertiesHash}_{requiredDataHash}
    processStatus: "processing",
    createTime: currentTime.getTime(),
    expiryTime: expiryTime.getTime(),
}
  • partition key: searchResultId
  • sort key: searchDetailId
  • at the moment a parent Main can only have 1 child SearchResultMain (in future could probably change to a list of child Main’s pretty easily). One child SearchResultMain can have many parent SearchResultMain’s pointing to it

SearchResultParents

{
	childSearchId: "xx", // {searchResultId}_{searchDetailId}
	parentSearchId: "xx" // {searchResultId}_{searchDetailId}
}
  • partition key: childSearchId
  • sort key: parentSearchId
  • when a SearchResultMain is finished it checks this table to see if any parents need to be processed

SearchResultData

{
	searchDataId: "xx", // {searchResultId}_{searchDetailId}
	dataId: "xx", // id of element (sellOfferId/productId/variantProduct(type and id))
	// reconsider this field, does not scale: childData: {}, // only used when childId set in SearchResultMain, saves requiredData results for all matching child records
    SearchResultDataStatus: "processing",
}
  • partition key: searchDataId
  • sort key: dataId

RequiredData

{
	requiredDataId: "xx", // {searchType}_{dataId}_{requestPropertiesHash} OR maybe hash of object of these values?
	fieldName: "xx",
	value: "xx", // value found for this reqdata field
	requiredDataStatus: "xx", // waitingExternalData | complete | invalid
	receivedExternalData: .. // externalData fields received data from handler services
	waitingExternalData: .. // externalData fields waiting for data from handler services
    createTime: currentTime.getTime(),
    expiryTime: expiryTime.getTime(),
}
  • partition key: requiredDataId
  • one record is one result we can calculate from external services, this can be shared by multiple SearchResultData records
  • requiredDataId includes identifiers that can affect the data's value
  • if keys too long for Dynamo we could hash an object containing all required fields and can add the values of each into the item separately, if needed
  • primary key can be used when result comes back from external service, so that message must include all values needed to make requiredDataId
  • can probably add fields during processing to store extra data for processing this SearchResultData record
  • if we want to find all records for one SearchResultData we could search for just the partition key, but it might return additonal fields used by other searches with different requiredData

RequiredDataSearchResultData

{
	requiredDataId_fieldName: "xx", // {requiredDataId}_{fieldName}
	searchDataId: "xx"
}
  • partition key: requiredDataId_fieldName
  • sort key: searchDataId
  • can extract dataId from requiredDataId_fieldName to find primary key for SearchResultData
  • used to create links between RequiredData results and all SearchResultData that need it

ParentDataId

{
	parentId: "xx", // {parent searchType}_{parent dataId}_{child searchType}_{child filterMainId}
	childDataId: "xx" // {child dataId}
}
  • partition key: parentId
  • sort key: childDataId
  • child filterMainId is required because we will have different ranges of childIds stored depending on what filter was used to find them

requestProperties

These are properties that are added to a Search Result request that affect the data found, they may be required or optional. Because these properties can affect the value of data they must be added to SearchResultMain identifiers to differentiate batches of results.

requireRequestProperties

Some requiredData fields will need additional properties to be received in the initial request.

  • example: variant/product/sellOffer pricing fields will require locationNodeId/s and browseQuantity
  • example: all translations require language/s

Moving child SearchResult data to parent

  • eg: product searchType has sellOffer child searchType data
  • eg: variantProduct searchType has product child searchType data
  • parent must wait for child DigData to complete before can process parents DigData
  • child SearchResult might already have completed before the parent request was received (ie by a different request), need to account for this
  • (old design - can still use if have to, but would be best to separate results so can scale, eg if want to show details about child products, that could pass as a separate Search/Sort set of results including pagination) aggregate multiple child data into the parents data values, but also keep a record of all children in the parents data record

Multiple Search Results services

  • design to allow for different implementations, eg catalogs can choose which Search Results service it uses
  • could achieve this be having a setting in Catalog handlers or Catalog Manager that records which Search Result service the Catalog uses

browseQuantity for calculating prices

  • when browsing, a user can define browseQuantity they want to buy (default 1), this would be injected into price calculation to obtain available price for each sellOffer
  • different to filters, eg availableQuantity, which determines which sellOffers are returned. (we might have a method/hook that validates or updates the complex filter so these settings don't conflict, but not needed)
  • added to requestProperties in request
  • I think if we request prices for a SellOffer in a quantity it cannot handle, eg browseQuantity is 100 but selloffer only has 20 remaining, the pricing function should return an error, could then remove (or mark as not enough quantity available) such sellOffers, so they might still exist in search results and maybe sort results but show quantity not/available.

Notes

  • Consider requests dig all possible needed data, even if the current request does not use it, that way subsequent requests can feed off the same processed data, ie standardize requiredData at request level to use cache more often

Working documents

Working_documents - Search Results