|
@@ -42,10 +42,9 @@ export class queryService {
|
|
}
|
|
}
|
|
|
|
|
|
// Logic 1: Success. But argument must specifies header.messageID.... to search
|
|
// Logic 1: Success. But argument must specifies header.messageID.... to search
|
|
- private hasMatchingProps(data, ...conditions): boolean {
|
|
|
|
|
|
+ private hasMatchingProps(data, condition): boolean {
|
|
// Merge all condtions into searchObj
|
|
// Merge all condtions into searchObj
|
|
- let searchObj = Object.assign({}, ...conditions)
|
|
|
|
- let result = _.every(searchObj, (val, key) => {
|
|
|
|
|
|
+ let result = _.every(condition, (val, key) => {
|
|
const propKeys = key.split('.');
|
|
const propKeys = key.split('.');
|
|
let nestedObj = data;
|
|
let nestedObj = data;
|
|
_.forEach(propKeys, propKey => {
|
|
_.forEach(propKeys, propKey => {
|
|
@@ -62,73 +61,107 @@ export class queryService {
|
|
// Logic 2: Success: More superior version than Logic 1 since it can perform flat searches like {messageID : 1234}
|
|
// Logic 2: Success: More superior version than Logic 1 since it can perform flat searches like {messageID : 1234}
|
|
// without specifying its parent property's name. eg: {header.messageID: 1234}
|
|
// without specifying its parent property's name. eg: {header.messageID: 1234}
|
|
private filterByKeyValue(data, ...conditions): boolean {
|
|
private filterByKeyValue(data, ...conditions): boolean {
|
|
- // Merge all conditions into searchObj
|
|
|
|
- let searchObj = Object.assign({}, ...conditions)
|
|
|
|
- let dateCheck = true
|
|
|
|
- let regexCheck = true
|
|
|
|
- if (searchObj.hasOwnProperty("dateRange")) {
|
|
|
|
- dateCheck = this.filterByDateRange(data, searchObj.dateRange)
|
|
|
|
- // Must delete, otherwise the function below will attempt to match date range with the date property and it will inevitably returns false
|
|
|
|
- delete searchObj.dateRange
|
|
|
|
- }
|
|
|
|
- if (searchObj.hasOwnProperty("regex")) {
|
|
|
|
- dateCheck = this.filterViaRegex(data, searchObj.regex)
|
|
|
|
- // Must delete, otherwise the function below will attempt to match date range with the date property and it will inevitably returns false
|
|
|
|
- delete searchObj.regex
|
|
|
|
- }
|
|
|
|
- if (typeof data !== 'object' || typeof searchObj !== 'object') {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ try {
|
|
|
|
+ // Merge all conditions into searchObj
|
|
|
|
+ let searchObj = Object.assign({}, ...conditions)
|
|
|
|
+ let recordFound = true
|
|
|
|
+
|
|
|
|
+ // Check for data type. Can actually remove this code if dont want. Not that important anyways
|
|
|
|
+ if (typeof data !== 'object' || typeof searchObj !== 'object') {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
|
|
- if (dateCheck == true && regexCheck == true) {
|
|
|
|
- let matchKeys = Object.keys(searchObj);
|
|
|
|
- let isMatchingObject = (object) => {
|
|
|
|
- return matchKeys.every((key) => {
|
|
|
|
- let lodashPath = key.replace(/\[(\w+)\]/g, '.$1').replace(/^\./, '');
|
|
|
|
- let objectValue = _.get(object, lodashPath);
|
|
|
|
- let searchValue = searchObj[key];
|
|
|
|
-
|
|
|
|
- if (Array.isArray(searchValue) && key === 'msgTag') {
|
|
|
|
- // Check if any of the search values are included in the object value
|
|
|
|
- return searchValue.some((value) => {
|
|
|
|
- return Array.isArray(objectValue) ? objectValue.includes(value) : objectValue === value;
|
|
|
|
- });
|
|
|
|
- } else if (typeof searchValue === 'object' && typeof objectValue === 'object') {
|
|
|
|
- return isMatchingObject(objectValue);
|
|
|
|
- } else {
|
|
|
|
- return objectValue === searchValue;
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
- };
|
|
|
|
|
|
+ // Check data to see if it is within the date range.
|
|
|
|
+ if (recordFound == true) {
|
|
|
|
+ if (searchObj.hasOwnProperty("$dateRange")) {
|
|
|
|
+ recordFound = this.filterByDateRange(data, searchObj.$dateRange)
|
|
|
|
+ delete searchObj.$dateRange
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Check data if there is any $msgTag property and match it
|
|
|
|
+ if (recordFound == true) {
|
|
|
|
+ if (searchObj.hasOwnProperty('$msgTag')) {
|
|
|
|
+ let keyToExtract = '$msgTag';
|
|
|
|
+ let [, extractedValue] = Object.entries(searchObj).find(([key]) => key === keyToExtract)
|
|
|
|
+ let newObj = { [keyToExtract]: extractedValue };
|
|
|
|
+ let oldKey = '$msgTag';
|
|
|
|
+ let newKey = 'msgTag';
|
|
|
|
+ let newPair = { [newKey]: newObj[oldKey] };
|
|
|
|
+ delete newObj[oldKey];
|
|
|
|
+ // console.log(newPair)
|
|
|
|
+ recordFound = this.matchValues(data, newPair)
|
|
|
|
+ delete searchObj.$msgTag
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
|
|
- let isObjectMatching = (object) => {
|
|
|
|
- if (typeof object !== 'object') {
|
|
|
|
- return false;
|
|
|
|
|
|
+ // Check if the regular expression value matches any of the data string
|
|
|
|
+ if (recordFound == true) {
|
|
|
|
+ if (searchObj.hasOwnProperty("$regex")) {
|
|
|
|
+ recordFound = this.filterViaRegex(data, searchObj.$regex)
|
|
|
|
+ delete searchObj.$regex
|
|
}
|
|
}
|
|
- return isMatchingObject(object) || Object.values(object).some(isObjectMatching);
|
|
|
|
- };
|
|
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Check if the key has parent key notation and then perform matching sequences. Eg : "header.appdata. etc etc"
|
|
|
|
+ if (recordFound == true) {
|
|
|
|
+ // check if key is header.is like 'propertyName1.propertyName2'
|
|
|
|
+ let searchkey = Object.keys(searchObj)
|
|
|
|
+ searchkey.every((key) => {
|
|
|
|
+ if (key.includes('.')) {
|
|
|
|
+ let condition = {
|
|
|
|
+ key: searchObj[key]
|
|
|
|
+ }
|
|
|
|
+ this.hasMatchingProps(data, condition)
|
|
|
|
+ delete searchObj[key]
|
|
|
|
+ }
|
|
|
|
+ })
|
|
|
|
+ }
|
|
|
|
|
|
- return isObjectMatching(data);
|
|
|
|
- } else {
|
|
|
|
- return false
|
|
|
|
|
|
+ // Check the rest of the key value pairs to see if the conditions are fulfilled(entries must matched)
|
|
|
|
+ if (recordFound == true) {
|
|
|
|
+ recordFound = this.matchValues(data, searchObj)
|
|
|
|
+ }
|
|
|
|
+ return recordFound
|
|
}
|
|
}
|
|
|
|
+ catch (e) {
|
|
|
|
+ console.error(e.message)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Match the key values pair between conditions and the given data
|
|
|
|
+ private matchValues(data, searchObj): boolean {
|
|
|
|
+ let matchKeys = Object.keys(searchObj);
|
|
|
|
+ let isMatchingObject = (object) => {
|
|
|
|
+ return matchKeys.every((key) => {
|
|
|
|
+ let lodashPath = key.replace(/\[(\w+)\]/g, '.$1').replace(/^\./, '');
|
|
|
|
+ let objectValue = _.get(object, lodashPath);
|
|
|
|
+ let searchValue = searchObj[key];
|
|
|
|
+
|
|
|
|
+ if (Array.isArray(searchValue)) {
|
|
|
|
+ // Check if any of the search values are included in the object value
|
|
|
|
+ return searchValue.some((value) => {
|
|
|
|
+ return Array.isArray(objectValue) ? objectValue.includes(value) : objectValue === value;
|
|
|
|
+ });
|
|
|
|
+ } else if (typeof searchValue === 'object' && typeof objectValue === 'object') {
|
|
|
|
+ return isMatchingObject(objectValue);
|
|
|
|
+ } else {
|
|
|
|
+ return objectValue === searchValue;
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let isObjectMatching = (object) => {
|
|
|
|
+ if (typeof object !== 'object') {
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ return isMatchingObject(object) || Object.values(object).some(isObjectMatching);
|
|
|
|
+ };
|
|
|
|
|
|
- /* This function first merges all the ...conditions objects into a single searchObj object using the Object.assign() method.
|
|
|
|
- It then checks whether both data and searchObj are of type object. If either one is not an object, the function returns false.
|
|
|
|
- Next, the function defines two helper functions: isMatchingObject and isObjectMatching. isMatchingObject takes an object and
|
|
|
|
- returns true if all the key-value pairs in searchObj are present in the object. isObjectMatching takes an object and returns
|
|
|
|
- true if the object itself or any of its nested objects satisfy the conditions specified in searchObj.
|
|
|
|
- The isMatchingObject function uses the every() method to iterate through each key in searchObj and check if the corresponding
|
|
|
|
- value in the object matches the value in searchObj. The function also uses the _.get() method from the Lodash library to get
|
|
|
|
- the value of nested object properties using a string path. If the value in searchObj or the object is an object itself,
|
|
|
|
- isMatchingObject calls itself recursively to check for nested properties.
|
|
|
|
- The isObjectMatching function first checks if the input object is of type object. If not, it returns false. If the object is an object,
|
|
|
|
- it checks whether the object or any of its nested objects satisfy the conditions in searchObj by calling isMatchingObject and isObjectMatching recursively.
|
|
|
|
- Finally, the hasKeyAndValue function returns the result of isObjectMatching(data), which is a boolean indicating whether data satisfies
|
|
|
|
- the conditions specified in searchObj.
|
|
|
|
- PS: this function is not my code. */
|
|
|
|
|
|
+ return isObjectMatching(data);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Matching the regex args to see if it matches the data that is now converted to string. As long as partial match, it will return true
|
|
private filterViaRegex(element: any, inquiry: any): boolean {
|
|
private filterViaRegex(element: any, inquiry: any): boolean {
|
|
// create a new regular expression to use regex.test
|
|
// create a new regular expression to use regex.test
|
|
const regex = new RegExp(inquiry);
|
|
const regex = new RegExp(inquiry);
|
|
@@ -136,14 +169,14 @@ export class queryService {
|
|
return hasMatchingSubstring;
|
|
return hasMatchingSubstring;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ // Check if the data's date is within the date range provided and also the column in which the data is to be compared with
|
|
private filterByDateRange(data: any, dateRange: DateRange): boolean {
|
|
private filterByDateRange(data: any, dateRange: DateRange): boolean {
|
|
// Lodash implemetation to get the specific property of data
|
|
// Lodash implemetation to get the specific property of data
|
|
- let msgDate : string = get(data, 'data.data.appData.msgDateTime')
|
|
|
|
- // console.log(msgDate)
|
|
|
|
|
|
+ let msgDate: string = get(data, dateRange.column)
|
|
|
|
+ let date = new Date(msgDate)
|
|
const start = new Date(dateRange.startDate);
|
|
const start = new Date(dateRange.startDate);
|
|
const end = new Date(dateRange.endDate);
|
|
const end = new Date(dateRange.endDate);
|
|
- const target = new Date(data.header.dateCreated);
|
|
|
|
- return target >= start && target <= end;
|
|
|
|
|
|
+ return date >= start && date <= end;
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
}
|
|
@@ -152,20 +185,15 @@ export class queryService {
|
|
|
|
|
|
// Entries that client will use. Subject to be improved later on
|
|
// Entries that client will use. Subject to be improved later on
|
|
export interface Conditions {
|
|
export interface Conditions {
|
|
- _id?: string,
|
|
|
|
- appLogLocId?: string,
|
|
|
|
- msgId?: string,
|
|
|
|
- msgLogDateTime?: Date | string,
|
|
|
|
- msgDateTime?: Date | string,
|
|
|
|
- msgTag?: string[],
|
|
|
|
- msgPayload?: string,
|
|
|
|
- messageID?: string,
|
|
|
|
- regex?: string,
|
|
|
|
- dateRange?: DateRange
|
|
|
|
|
|
+ $regex?: string,
|
|
|
|
+ $dateRange?: DateRange,
|
|
|
|
+ $msgTag?: string[],
|
|
|
|
+ [key: string]: string | Date | DateRange | string[]
|
|
}
|
|
}
|
|
export interface DateRange {
|
|
export interface DateRange {
|
|
- startDate?: string | Date,
|
|
|
|
- endDate?: string | Date
|
|
|
|
|
|
+ startDate: string | Date,
|
|
|
|
+ endDate: string | Date,
|
|
|
|
+ column: string
|
|
}
|
|
}
|
|
export interface Storage {
|
|
export interface Storage {
|
|
type: string,
|
|
type: string,
|