Jelajahi Sumber

update on search filter functions

Enzo 1 tahun lalu
induk
melakukan
9b3833275b
2 mengubah file dengan 177 tambahan dan 98 penghapusan
  1. 106 78
      services/query.service.ts
  2. 71 20
      test/test1.ts

+ 106 - 78
services/query.service.ts

@@ -42,10 +42,9 @@ export class queryService {
     }
 
     //  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
-        let searchObj = Object.assign({}, ...conditions)
-        let result = _.every(searchObj, (val, key) => {
+        let result = _.every(condition, (val, key) => {
             const propKeys = key.split('.');
             let nestedObj = data;
             _.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}
     //  without specifying its parent property's name. eg: {header.messageID: 1234}
     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 {
         // create a new regular expression to use regex.test
         const regex = new RegExp(inquiry);
@@ -136,14 +169,14 @@ export class queryService {
         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 {
         // 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 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
 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 {
-    startDate?: string | Date,
-    endDate?: string | Date
+    startDate: string | Date,
+    endDate: string | Date,
+    column: string
 }
 export interface Storage {
     type: string,

+ 71 - 20
test/test1.ts

@@ -17,7 +17,7 @@ let storageAddress: Storage = {
 
 // Array inquiry: should return mutiple data
 let conditions1: Conditions[] = [
-    { 'msgTag': ['free'] }
+    { 'msgTag': ['basic'] }
 ]
 
 // Basic inquiry, but with multi search: should return one data
@@ -28,28 +28,30 @@ let conditions2: Conditions[] = [
 
 // Value only argument! : should return one data
 let conditions3: Conditions[] = [
-    { "regex": "cum incidunt maxime voluptatibus" }
+    { "$regex": "cum incidunt maxime voluptatibus" }
 ]
 
-// Date Range inquiry: Should return multiple data
+// Date Range inquiry: Should return 1 data
 let conditions4: Conditions[] = [
     {
-        "dateRange": {
-            "startDate": "2023-04-09T21:00:00.000Z",
-            'endDate': "2023-04-10T00:00:00.000Z"
+        "$dateRange": {
+            "startDate": "2022-04-29T00:00:00.000Z",
+            'endDate': "2022-04-30T00:00:00.000Z",
+            'column': "data.data.appData.msgDateTime"
         }
-    }
+    },
 ]
 
 // Multi conditions except for regex search: Should return at least 1 data
 let conditions5: Conditions[] = [
     {
-        "dateRange": {
-            "startDate": "2023-04-09T21:00:00.000Z",
-            'endDate': "2023-04-10T00:00:00.000Z"
+        "$dateRange": {
+            "startDate": "2022-04-29T00:00:00.000Z",
+            'endDate': "2022-04-30T00:00:00.000Z",
+            'column': "data.data.appData.msgDateTime"
         }
     },
-    { 'msgTag': ['basic'] },
+    { '$msgTag': ['basic'] },
     { "msgId": "4f710c4b-a258-4c7e-a4b6-6095bb7028e9" },
     { "msgLogDateTime": "2023-01-14T21:50:19.917Z" }
 ]
@@ -57,17 +59,66 @@ let conditions5: Conditions[] = [
 // Ultimate search. With all conditions piling at once: Should at least returns 1 data
 let conditions6: Conditions[] = [
     {
-        "dateRange": {
-            "startDate": "2023-04-09T21:00:00.000Z",
-            'endDate': "2023-04-10T00:00:00.000Z"
+        "$dateRange": {
+            "startDate": "2022-04-29T00:00:00.000Z",
+            'endDate': "2022-04-30T00:00:00.000Z",
+            'column': "data.data.appData.msgDateTime"
         }
     },
-    { "regex": "maxime voluptatibus ad quasi eveniet" },
-    { 'msgTag': ['basic'] },
+    { "$regex": "maxime voluptatibus ad quasi eveniet" },
+    { '$msgTag': ['basic'] },
     { "msgId": "4f710c4b-a258-4c7e-a4b6-6095bb7028e9" },
-    { "msgLogDateTime": "2023-01-14T21:50:19.917Z" }
 ]
-query.query(storageAddress, ...conditions4).subscribe((element) => { console.log(`${element.header.messageName} is matched`) })
-query.query(storageAddress, ...conditions6).subscribe((element) => { console.log(`${element.header.messageName} is matched`) })
-
 
+// should return 1 data
+let conditions7: Conditions[] = [
+    {
+        "$dateRange": {
+            "startDate": "2022-04-29T00:00:00.000Z",
+            'endDate': "2022-04-30T00:00:00.000Z",
+            'column': "data.data.appData.msgDateTime"
+        }
+    },
+    { "$regex": "maxime voluptatibus ad quasi eveniet" },
+    // { 'data.data.appData.$msgTag': ['basic'] },
+    { "data.data.appData.msgId": "4f710c4b-a258-4c7e-a4b6-6095bb7028e9" },
+    { "msgLogDateTime": "2023-01-14T21:50:19.917Z" }
+]
+// should not return anything
+let conditions8: Conditions[] = [
+    {
+        "$dateRange": {
+            "startDate": "2022-04-29T00:00:00.000Z",
+            'endDate': "2022-04-30T00:00:00.000Z",
+            'column': "data.data.appData.msgDateTime"
+        }
+    },
+    { "$regex": "maxime voluptatibus ad quasi eveniet" },
+    { '$msgTag': ['basic'] },
+    { "data.data.appDatamsgId": "4f710c4b-a258-4c7e-a4b6-6095bb7028e9" },
+    { "header.msgLogDateTime": "2023-01-14T21:50:19.917Z" }
+]
+query.query(storageAddress, ...conditions1).subscribe((element) => {
+    console.log(`OBS1 : ${element.header.messageName} is matched`)
+})
+query.query(storageAddress, ...conditions2).subscribe((element) => {
+    console.log(`OBS2 :${element.header.messageName} is matched`)
+})
+query.query(storageAddress, ...conditions3).subscribe((element) => {
+    console.log(`OBS3 :${element.header.messageName} is matched`)
+})
+query.query(storageAddress, ...conditions4).subscribe((element) => {
+    console.log(`OBS4 :${element.header.messageName} is matched`)
+})
+query.query(storageAddress, ...conditions5).subscribe((element) => {
+    console.log(`OBS5 :${element.header.messageName} is matched`)
+})
+query.query(storageAddress, ...conditions6).subscribe((element) => {
+    console.log(`OBS6 :${element.header.messageName} is matched`)
+})
+query.query(storageAddress, ...conditions7).subscribe((element) => {
+    console.log(`OBS7 :${element.header.messageName} is matched`)
+})
+query.query(storageAddress, ...conditions8).subscribe((element) => {
+    console.log(`OBS8 :${element.header.messageName} is matched`)
+})