mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-05 16:10:43 +00:00
### **PR Type**
Bug fix, Enhancement
___
### **Description**
- Replace regex-based operator spacing with util
- Add state-based `addSpacesToOperators` function
- Update composables and component imports
- Prevent catastrophic regex backtracking
___
### Diagram Walkthrough
```mermaid
flowchart LR
A["Raw whereClause"] -- "addSpacesToOperators" --> B["Spaced whereClause"]
B -- "used in" --> C["IndexList.vue / useLogs / useQuery"]
```
<details> <summary><h3> File Walkthrough</h3></summary>
<table><thead><tr><th></th><th align="left">Relevant
files</th></tr></thead><tbody><tr><td><strong>Enhancement</strong></td><td><table>
<tr>
<td>
<details>
<summary><strong>IndexList.vue</strong><dd><code>Replace regex spacing
with util call</code>
</dd></summary>
<hr>
web/src/plugins/logs/IndexList.vue
<ul><li>Imported <code>addSpacesToOperators</code> util<br> <li> Removed
inline regex-based spacing<br> <li> Replaced with
<code>addSpacesToOperators(whereClause)</code></ul>
</details>
</td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7653/files#diff-08f058f883b659e713e606991a7ea37ae1ff016b7cccef970489ab5840dc3d09">+2/-10</a>
</td>
</tr>
<tr>
<td>
<details>
<summary><strong>useLogs.ts</strong><dd><code>Use util for query
operator spacing</code>
</dd></summary>
<hr>
web/src/composables/useLogs.ts
<ul><li>Added <code>addSpacesToOperators</code> import<br> <li> Removed
chained <code>.replace</code> regex calls<br> <li> Applied
<code>addSpacesToOperators</code> for whereClause</ul>
</details>
</td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7653/files#diff-1040f61354f92711e0e77ed4e9bfa15586d45a94c7e2ab06cc37372068030e7b">+4/-11</a>
</td>
</tr>
<tr>
<td>
<details>
<summary><strong>useQuery.ts</strong><dd><code>Replace regex in useQuery
with util</code>
</dd></summary>
<hr>
web/src/composables/useQuery.ts
<ul><li>Imported <code>addSpacesToOperators</code><br> <li> Removed
manual regex replacements<br> <li> Called
<code>addSpacesToOperators(whereClause)</code></ul>
</details>
</td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7653/files#diff-fe0bf52b216893c6a31f2923aab979e0d817c742b0cfd75e524e60db75b1d3e8">+4/-11</a>
</td>
</tr>
<tr>
<td>
<details>
<summary><strong>zincutils.ts</strong><dd><code>Implement
addSpacesToOperators utility</code>
</dd></summary>
<hr>
web/src/utils/zincutils.ts
<ul><li>Added <code>addSpacesToOperators</code> function<br> <li>
Implements state-based spacing logic<br> <li> Handles quotes,
parentheses, two-char operators<br> <li> Avoids exponential regex
backtracking</ul>
</details>
</td>
<td><a
href="https://github.com/openobserve/openobserve/pull/7653/files#diff-d3d59688cccdcc6c84ebe88b698c6910030abecc3125f4c0ef81b0df023699ba">+98/-0</a>
</td>
</tr>
</table></td></tr></tr></tbody></table>
</details>
___
204 lines
5.9 KiB
TypeScript
204 lines
5.9 KiB
TypeScript
import { useStore } from "vuex";
|
|
import useNotifications from "@/composables/useNotifications";
|
|
import { b64EncodeUnicode, addSpacesToOperators } from "@/utils/zincutils";
|
|
import { onBeforeMount, onBeforeUnmount } from "vue";
|
|
|
|
interface BuildQueryPayload {
|
|
from?: number;
|
|
size?: number;
|
|
timestamp_column?: string;
|
|
timestamps?: {
|
|
startTime: number | "Invalid Date";
|
|
endTime: number | "Invalid Date";
|
|
};
|
|
timeInterval?: string;
|
|
sqlMode: boolean;
|
|
currentPage?: number;
|
|
selectedStream?: string;
|
|
parsedQuery?: {
|
|
queryFunctions: string;
|
|
whereClause: string;
|
|
limit: number;
|
|
query: string;
|
|
offset: number;
|
|
};
|
|
streamName: string;
|
|
}
|
|
|
|
const getTimeInterval = (start_time: number, end_time: number) => {
|
|
const resPayload = {
|
|
interval: "10 second",
|
|
keyFormat: "HH:mm:ss",
|
|
};
|
|
|
|
if (end_time - start_time >= 1000000 * 60 * 30) {
|
|
resPayload.interval = "15 second";
|
|
resPayload.keyFormat = "HH:mm:ss";
|
|
}
|
|
if (end_time - start_time >= 1000000 * 60 * 60) {
|
|
resPayload.interval = "30 second";
|
|
resPayload.keyFormat = "HH:mm:ss";
|
|
}
|
|
if (end_time - start_time >= 1000000 * 3600 * 2) {
|
|
resPayload.interval = "1 minute";
|
|
resPayload.keyFormat = "MM-DD HH:mm";
|
|
}
|
|
if (end_time - start_time >= 1000000 * 3600 * 6) {
|
|
resPayload.interval = "5 minute";
|
|
resPayload.keyFormat = "MM-DD HH:mm";
|
|
}
|
|
if (end_time - start_time >= 1000000 * 3600 * 24) {
|
|
resPayload.interval = "30 minute";
|
|
resPayload.keyFormat = "MM-DD HH:mm";
|
|
}
|
|
if (end_time - start_time >= 1000000 * 86400 * 7) {
|
|
resPayload.interval = "1 hour";
|
|
resPayload.keyFormat = "MM-DD HH:mm";
|
|
}
|
|
if (end_time - start_time >= 1000000 * 86400 * 30) {
|
|
resPayload.interval = "1 day";
|
|
resPayload.keyFormat = "YYYY-MM-DD";
|
|
}
|
|
return resPayload;
|
|
};
|
|
|
|
const useQuery = () => {
|
|
let parser: any;
|
|
const store = useStore();
|
|
const { showErrorNotification } = useNotifications();
|
|
|
|
onBeforeMount(async () => {
|
|
await importSqlParser();
|
|
});
|
|
|
|
onBeforeUnmount(() => {
|
|
parser = null;
|
|
});
|
|
|
|
const importSqlParser = async () => {
|
|
const useSqlParser: any = await import("@/composables/useParser");
|
|
const { sqlParser }: any = useSqlParser.default();
|
|
parser = await sqlParser();
|
|
};
|
|
|
|
const parseQuery = (query: string, sqlMode = false) => {
|
|
const parsedParams = {
|
|
queryFunctions: "",
|
|
whereClause: "",
|
|
limit: null,
|
|
query: query,
|
|
offset: null,
|
|
};
|
|
if (sqlMode == true) {
|
|
const parsedSQL: any = parser.astify(query);
|
|
if (parsedSQL.limit != null) {
|
|
parsedParams.limit = parsedSQL.limit.value[0].value;
|
|
|
|
if (parsedSQL.limit.separator == "offset") {
|
|
parsedParams.offset = parsedSQL.limit.value[1].value || 0;
|
|
}
|
|
|
|
parsedSQL.limit = null;
|
|
|
|
parsedParams.query = parser.sqlify(parsedSQL);
|
|
|
|
//replace backticks with \" for sql_mode
|
|
parsedParams.query = parsedParams.query.replace(/`/g, '"');
|
|
}
|
|
} else {
|
|
const parseQuery = parsedParams.query.split("|");
|
|
let queryFunctions = "";
|
|
let whereClause = "";
|
|
if (parseQuery.length > 1) {
|
|
queryFunctions = "," + parseQuery[0].trim();
|
|
whereClause = parseQuery[1].trim();
|
|
} else {
|
|
whereClause = parseQuery[0].trim();
|
|
}
|
|
|
|
if (whereClause.trim() != "") {
|
|
// More efficient approach: replace operators only when not inside quotes or parentheses
|
|
// This avoids catastrophic backtracking by using a simpler state-based approach
|
|
whereClause = addSpacesToOperators(whereClause);
|
|
|
|
// const parsedSQL = whereClause.split(" ");
|
|
// searchObj.data.stream.selectedStreamFields.forEach((field: any) => {
|
|
// parsedSQL.forEach((node: any, index: any) => {
|
|
// if (node == field.name) {
|
|
// node = node.replaceAll('"', "");
|
|
// parsedSQL[index] = '"' + node + '"';
|
|
// }
|
|
// });
|
|
// });
|
|
|
|
// whereClause = parsedSQL.join(" ");
|
|
|
|
parsedParams.whereClause = whereClause;
|
|
}
|
|
|
|
parsedParams.queryFunctions = queryFunctions;
|
|
}
|
|
};
|
|
|
|
const buildQueryPayload = (data: BuildQueryPayload) => {
|
|
try {
|
|
const req: any = {
|
|
query: {
|
|
sql: 'select *[QUERY_FUNCTIONS] from "[INDEX_NAME]" [WHERE_CLAUSE]',
|
|
start_time: (new Date().getTime() - 900000) * 1000,
|
|
end_time: new Date().getTime() * 1000,
|
|
from: data.from || 0,
|
|
size: data.size || 100,
|
|
},
|
|
aggs: {
|
|
histogram:
|
|
"select histogram(" +
|
|
(data?.timestamp_column ||
|
|
store.state.zoConfig.timestamp_column ||
|
|
"_timestamp") +
|
|
", '[INTERVAL]') AS zo_sql_key, count(*) AS zo_sql_num from query GROUP BY zo_sql_key ORDER BY zo_sql_key",
|
|
},
|
|
};
|
|
|
|
req.aggs.histogram = req.aggs.histogram.replaceAll(
|
|
"[INTERVAL]",
|
|
data.timeInterval,
|
|
);
|
|
|
|
req.query.sql = req.query.sql.replaceAll(
|
|
"[QUERY_FUNCTIONS]",
|
|
data.parsedQuery?.queryFunctions || "",
|
|
);
|
|
|
|
req.query.sql = req.query.sql.replaceAll("[INDEX_NAME]", data.streamName);
|
|
|
|
req.query.sql = req.query.sql.replaceAll(
|
|
"[WHERE_CLAUSE]",
|
|
data.parsedQuery?.whereClause || "",
|
|
);
|
|
|
|
if (data?.timestamps?.startTime)
|
|
req.query.start_time = data.timestamps.startTime;
|
|
|
|
if (data?.timestamps?.endTime)
|
|
req.query.end_time = data.timestamps.endTime;
|
|
|
|
if (store.state.zoConfig.sql_base64_enabled) {
|
|
req["encoding"] = "base64";
|
|
req.query.sql = b64EncodeUnicode(req.query.sql);
|
|
if (!data.sqlMode && data.currentPage == 0) {
|
|
req.aggs.histogram = b64EncodeUnicode(req.aggs.histogram);
|
|
}
|
|
}
|
|
|
|
return req;
|
|
} catch (e: any) {
|
|
showErrorNotification("Invalid SQL Syntax");
|
|
}
|
|
};
|
|
|
|
return { getTimeInterval, parseQuery, buildQueryPayload };
|
|
};
|
|
|
|
export default useQuery;
|