first version of query parser

This commit is contained in:
Emmaline Autumn 2024-08-05 01:26:29 -06:00
parent 7fc76d2781
commit 269a844a68

View 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