first version of query parser
This commit is contained in:
parent
7fc76d2781
commit
269a844a68
177
lib/ttcQuery/TTCQueryParser.ts
Normal file
177
lib/ttcQuery/TTCQueryParser.ts
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
type QueryableObject = Record<string, any>;
|
||||||
|
|
||||||
|
class TtcQueryParser {
|
||||||
|
private data: QueryableObject;
|
||||||
|
private relativeMap: Map<QueryableObject, QueryableObject>;
|
||||||
|
|
||||||
|
constructor(data: QueryableObject) {
|
||||||
|
this.data = data;
|
||||||
|
this.relativeMap = new Map();
|
||||||
|
this.buildRelativeMap(this.data, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public search(
|
||||||
|
query: string,
|
||||||
|
currentObject: QueryableObject = this.data
|
||||||
|
): any[] {
|
||||||
|
// Normalize the query string by trimming whitespace
|
||||||
|
query = query.trim();
|
||||||
|
|
||||||
|
// Determine the base structure to search
|
||||||
|
let result: any[] = [];
|
||||||
|
|
||||||
|
// Perform initial parsing based on the query syntax
|
||||||
|
if (query.startsWith("^")) {
|
||||||
|
result = this.queryCurrentObject(query, currentObject);
|
||||||
|
} else if (query.startsWith("$")) {
|
||||||
|
result = this.queryRelativeObject(query, currentObject);
|
||||||
|
} else {
|
||||||
|
result = this.queryPublication(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private queryPublication(query: string): any[] {
|
||||||
|
// Example implementation for searching publication
|
||||||
|
const publicationMatch = query.match(/^(\w+)(\[(\w+)\])?(\..+)?/);
|
||||||
|
if (publicationMatch) {
|
||||||
|
const subQuery = publicationMatch[4];
|
||||||
|
|
||||||
|
// Search within the publication data
|
||||||
|
return this.searchInObject(this.data, subQuery);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
private queryCurrentObject(
|
||||||
|
query: string,
|
||||||
|
currentObject: QueryableObject
|
||||||
|
): any[] {
|
||||||
|
// Example implementation for querying the current object
|
||||||
|
const subQuery = query.substring(1); // Remove '^'
|
||||||
|
return this.searchInObject(currentObject, subQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
private queryRelativeObject(
|
||||||
|
query: string,
|
||||||
|
currentObject: QueryableObject
|
||||||
|
): any[] {
|
||||||
|
const relativeObject = this.relativeMap.get(currentObject);
|
||||||
|
|
||||||
|
if (!relativeObject) {
|
||||||
|
throw new Error("Relative object not found.");
|
||||||
|
}
|
||||||
|
const subQuery = query.substring(1); // Remove '$'
|
||||||
|
return this.searchInObject(relativeObject, subQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildRelativeMap(
|
||||||
|
obj: QueryableObject,
|
||||||
|
relative: QueryableObject | null
|
||||||
|
): void {
|
||||||
|
if (obj.relative) {
|
||||||
|
relative = obj;
|
||||||
|
}
|
||||||
|
this.relativeMap.set(obj, relative || obj);
|
||||||
|
|
||||||
|
for (const key in obj) {
|
||||||
|
if (typeof obj[key] === "object" && obj[key] !== null) {
|
||||||
|
this.buildRelativeMap(obj[key], relative);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private searchInObject(obj: any, subQuery?: string): any[] {
|
||||||
|
// Handle subqueries and search in the provided object
|
||||||
|
if (!subQuery) {
|
||||||
|
return [obj]; // Return the entire object if no subquery is present
|
||||||
|
}
|
||||||
|
|
||||||
|
// Split the subquery based on dot (.) to navigate through the object
|
||||||
|
const keys = subQuery.split(".");
|
||||||
|
let current: any = obj;
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
if (current && typeof current === "object") {
|
||||||
|
if (key.includes("[") && key.includes("]")) {
|
||||||
|
const [prop, selector] = key.split("[");
|
||||||
|
const index = selector.slice(0, -1);
|
||||||
|
|
||||||
|
if (Array.isArray(current[prop])) {
|
||||||
|
if (!isNaN(Number(index))) {
|
||||||
|
current = current[prop][Number(index)];
|
||||||
|
} else {
|
||||||
|
const [k, comparator, value] = this.parseSelector(index);
|
||||||
|
current = this.applySelector(current[prop], k, comparator, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current = current.map((e: any) => e[key]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.isArray(current) ? current : [current];
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseSelector(selector: string): [string, string, string] {
|
||||||
|
const match = selector.match(/(.+)(=|==|>|<|>=|<=|!!|!|!=)(.+)/);
|
||||||
|
if (match) {
|
||||||
|
return [match[1], match[2], match[3]];
|
||||||
|
}
|
||||||
|
return ["", "=", selector];
|
||||||
|
}
|
||||||
|
|
||||||
|
private applySelector(
|
||||||
|
array: any[],
|
||||||
|
key: string,
|
||||||
|
comparator: string,
|
||||||
|
value: string
|
||||||
|
): any[] {
|
||||||
|
switch (comparator) {
|
||||||
|
case "=":
|
||||||
|
return array.filter((item) => (key ? item[key] : item).includes(value));
|
||||||
|
case "==":
|
||||||
|
return array.filter((item) => (key ? item[key] : item) === value);
|
||||||
|
case ">":
|
||||||
|
return array.filter((item) => (key ? item[key] : item) > value);
|
||||||
|
case "<":
|
||||||
|
return array.filter((item) => (key ? item[key] : item) < value);
|
||||||
|
case ">=":
|
||||||
|
return array.filter((item) => (key ? item[key] : item) >= value);
|
||||||
|
case "<=":
|
||||||
|
return array.filter((item) => (key ? item[key] : item) <= value);
|
||||||
|
case "!!":
|
||||||
|
return array.filter((item) => (key ? item[key] : item));
|
||||||
|
case "!=":
|
||||||
|
return array.filter(
|
||||||
|
(item) => !(key ? item[key] : item).includes(value)
|
||||||
|
);
|
||||||
|
case "!":
|
||||||
|
return array.filter((item) => !(key ? item[key] : item));
|
||||||
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example usage:
|
||||||
|
const data = {
|
||||||
|
relative: true,
|
||||||
|
weapon_abilities: [
|
||||||
|
{ name: "Rapid Fire", body: "Shoot again" },
|
||||||
|
{ name: "Sustained Hits", body: "More damage", relative: true },
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const parser = new TtcQueryParser(data);
|
||||||
|
|
||||||
|
// Example queries
|
||||||
|
console.log(parser.search("weapon_abilities[name=Rapid Fire].body")); // Example output
|
||||||
|
// console.log(parser.search("^weapon_abilities[name=Rapid Fire]", data)); // Example output
|
||||||
|
console.log(parser.search("$weapon_abilities[name=Rapid Fire].body", data)); // Example output
|
Loading…
x
Reference in New Issue
Block a user