Files
openobserve/web/src/composables/useLogs.ts
T

6389 lines
215 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright 2023 OpenObserve Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { date, useQuasar } from "quasar";
import { useI18n } from "vue-i18n";
import {
reactive,
ref,
type Ref,
toRaw,
nextTick,
onBeforeMount,
watch,
computed,
} from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import { cloneDeep, startCase } from "lodash-es";
import {
SearchRequestPayload,
WebSocketSearchResponse,
WebSocketSearchPayload,
WebSocketErrorResponse,
} from "@/ts/interfaces/query";
import {
useLocalLogFilterField,
b64EncodeUnicode,
b64DecodeUnicode,
formatSizeFromMB,
timestampToTimezoneDate,
histogramDateTimezone,
useLocalWrapContent,
useLocalTimezone,
useLocalInterestingFields,
useLocalSavedView,
convertToCamelCase,
getFunctionErrorMessage,
getUUID,
getWebSocketUrl,
generateTraceContext,
arraysMatch,
isWebSocketEnabled,
isStreamingEnabled
} from "@/utils/zincutils";
import {
convertDateToTimestamp,
getConsumableRelativeTime,
} from "@/utils/date";
import { byString } from "@/utils/json";
import { logsErrorMessage } from "@/utils/common";
import useSqlSuggestions from "@/composables/useSuggestions";
// import {
// b64EncodeUnicode,
// useLocalLogFilterField,
// b64DecodeUnicode,
// } from "@/utils/zincutils";
import useFunctions from "@/composables/useFunctions";
import useNotifications from "@/composables/useNotifications";
import useStreams from "@/composables/useStreams";
import useWebSocket from "@/composables/useWebSocket";
import searchService from "@/services/search";
import savedviewsService from "@/services/saved_views";
import config from "@/aws-exports";
import useSearchWebSocket from "./useSearchWebSocket";
import useActions from "./useActions";
import useStreamingSearch from "./useStreamingSearch";
const defaultObject = {
organizationIdentifier: "",
runQuery: false,
loading: false,
loadingHistogram: false,
loadingCounter: false,
loadingStream: false,
loadingSavedView: false,
shouldIgnoreWatcher: false,
communicationMethod: "http",
config: {
splitterModel: 20,
lastSplitterPosition: 0,
splitterLimit: [0, 40],
fnSplitterModel: 60,
fnLastSplitterPosition: 0,
fnSplitterLimit: [40, 100],
refreshTimes: [
[
{ label: "5 sec", value: 5 },
{ label: "1 min", value: 60 },
{ label: "1 hr", value: 3600 },
],
[
{ label: "10 sec", value: 10 },
{ label: "5 min", value: 300 },
{ label: "2 hr", value: 7200 },
],
[
{ label: "15 sec", value: 15 },
{ label: "15 min", value: 900 },
{ label: "1 day", value: 86400 },
],
[
{ label: "30 sec", value: 30 },
{ label: "30 min", value: 1800 },
],
],
},
meta: {
logsVisualizeToggle: "logs",
refreshInterval: <number>0,
refreshIntervalLabel: "Off",
refreshHistogram: false,
showFields: true,
showQuery: true,
showHistogram: true,
showDetailTab: false,
showTransformEditor: true,
searchApplied: false,
toggleSourceWrap: useLocalWrapContent()
? JSON.parse(useLocalWrapContent())
: false,
histogramDirtyFlag: false,
sqlMode: false,
sqlModeManualTrigger: false,
quickMode: false,
queryEditorPlaceholderFlag: true,
functionEditorPlaceholderFlag: true,
resultGrid: {
rowsPerPage: 50,
wrapCells: false,
manualRemoveFields: false,
chartInterval: "1 second",
chartKeyFormat: "HH:mm:ss",
navigation: {
currentRowIndex: 0,
},
showPagination: true,
},
jobId: "",
jobRecords: "100",
scrollInfo: {},
pageType: "logs", // 'logs' or 'stream
regions: [],
clusters: [],
useUserDefinedSchemas: "user_defined_schema",
hasUserDefinedSchemas: false,
selectedTraceStream: "",
showSearchScheduler: false,
toggleFunction: false, // DEPRECATED use showTransformEditor instead
isActionsEnabled: false,
resetPlotChart: false,
},
data: {
query: <any>"",
histogramQuery: <any>"",
parsedQuery: {},
countErrorMsg: "",
errorMsg: "",
errorDetail: "",
errorCode: 0,
filterErrMsg: "",
missingStreamMessage: "",
additionalErrorMsg: "",
savedViewFilterFields: "",
hasSearchDataTimestampField: false,
originalDataCache: new Map(),
stream: {
loading: false,
streamLists: <object[]>[],
selectedStream: <any>[],
selectedStreamFields: <any>[],
selectedFields: <string[]>[],
filterField: "",
addToFilter: "",
functions: <any>[],
streamType: "logs",
interestingFieldList: <string[]>[],
userDefinedSchema: <any>[],
expandGroupRows: <any>{},
expandGroupRowsFieldCount: <any>{},
filteredField: <any>[],
missingStreamMultiStreamFilter: <any>[],
pipelineQueryStream: <any>[],
},
resultGrid: {
currentDateTime: new Date(),
currentPage: 1,
columns: <any>[],
colOrder: <any>{},
colSizes: <any>{},
},
histogramInterval: <any>0,
transforms: <any>[],
transformType: "function",
actions: <any>[],
selectedTransform: <any>null,
queryResults: <any>[],
sortedQueryResults: <any>[],
streamResults: <any>[],
histogram: <any>{
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorMsg: "",
errorCode: 0,
errorDetail: "",
},
editorValue: <any>"",
datetime: <any>{
startTime: (new Date().getTime() - 900000) * 1000,
endTime: new Date().getTime(),
relativeTimePeriod: "15m",
type: "relative",
selectedDate: <any>{},
selectedTime: <any>{},
queryRangeRestrictionMsg: "",
queryRangeRestrictionInHour: 100000,
},
searchAround: {
indexTimestamp: 0,
size: <number>10,
histogramHide: false,
},
tempFunctionName: "",
tempFunctionContent: "",
tempFunctionLoading: false,
savedViews: <any>[],
customDownloadQueryObj: <any>{},
functionError: "",
searchRequestTraceIds: <string[]>[],
searchWebSocketTraceIds: <string[]>[],
isOperationCancelled: false,
searchRetriesCount: <{ [key: string]: number }>{},
actionId: null,
},
};
const maxSearchRetries = 2;
const searchReconnectDelay = 1000; // 1 second
// TODO OK:
// useStreamManagement for stream-related functions
// useQueryProcessing for query-related functions
// useDataVisualization for histogram and data display functions
const searchObj = reactive(Object.assign({}, defaultObject));
const searchObjDebug = reactive({
queryDataStartTime: 0,
queryDataEndTime: 0,
buildSearchStartTime: 0,
buildSearchEndTime: 0,
partitionStartTime: 0,
partitionEndTime: 0,
paginatedDatawithAPIStartTime: 0,
paginatedDatawithAPIEndTime: 0,
pagecountStartTime: 0,
pagecountEndTime: 0,
paginatedDataReceivedStartTime: 0,
paginatedDataReceivedEndTime: 0,
histogramStartTime: 0,
histogramEndTime: 0,
histogramProcessingStartTime: 0,
histogramProcessingEndTime: 0,
extractFieldsStartTime: 0,
extractFieldsEndTime: 0,
extractFieldsWithAPI: "",
});
const searchAggData = reactive({
total: 0,
hasAggregation: false,
});
const initialQueryPayload: Ref<SearchRequestPayload | null> = ref(null);
let histogramResults: any = [];
let histogramMappedData: any = [];
const intervalMap: any = {
"10 second": 10 * 1000 * 1000,
"15 second": 15 * 1000 * 1000,
"30 second": 30 * 1000 * 1000,
"1 minute": 60 * 1000 * 1000,
"5 minute": 5 * 60 * 1000 * 1000,
"30 minute": 30 * 60 * 1000 * 1000,
"1 hour": 60 * 60 * 1000 * 1000,
"1 day": 24 * 60 * 60 * 1000 * 1000,
};
const {
fetchQueryDataWithWebSocket,
sendSearchMessageBasedOnRequestId,
cancelSearchQueryBasedOnRequestId,
closeSocketBasedOnRequestId,
} = useSearchWebSocket();
const {
fetchQueryDataWithHttpStream,
} = useStreamingSearch();
const searchPartitionMap = reactive<{ [key: string]: number }>({});
const useLogs = () => {
const store = useStore();
const { t } = useI18n();
const $q = useQuasar();
const { getAllFunctions } = useFunctions();
const { getAllActions } = useActions();
const { showErrorNotification } = useNotifications();
const { getStreams, getStream, getMultiStreams } = useStreams();
const router = useRouter();
let parser: any;
const fieldValues = ref();
const notificationMsg = ref("");
const { updateFieldKeywords } = useSqlSuggestions();
onBeforeMount(async () => {
if (router.currentRoute.value.query?.quick_mode == "true") {
searchObj.meta.quickMode = true;
}
await importSqlParser();
extractValueQuery();
});
const importSqlParser = async () => {
const useSqlParser: any = await import("@/composables/useParser");
const { sqlParser }: any = useSqlParser.default();
parser = await sqlParser();
};
searchObj.organizationIdentifier =
store.state.selectedOrganization.identifier;
const resetSearchObj = () => {
// searchObj = reactive(Object.assign({}, defaultObject));
searchObj.data.errorMsg = "No stream found in selected organization!";
searchObj.data.stream.streamLists = [];
searchObj.data.stream.selectedStream = [];
searchObj.data.stream.selectedStreamFields = [];
searchObj.data.queryResults = {};
searchObj.data.sortedQueryResults = [];
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
searchObj.data.tempFunctionContent = "";
searchObj.data.query = "";
searchObj.data.editorValue = "";
searchObj.meta.sqlMode = false;
searchObj.runQuery = false;
searchObj.data.savedViews = [];
};
const updatedLocalLogFilterField = (): void => {
const identifier: string = searchObj.organizationIdentifier || "default";
const selectedFields: any =
useLocalLogFilterField()?.value != null
? useLocalLogFilterField()?.value
: {};
const stream = searchObj.data.stream.selectedStream.sort().join("_");
selectedFields[`${identifier}_${stream}`] =
searchObj.data.stream.selectedFields;
useLocalLogFilterField(selectedFields);
};
function resetFunctions() {
store.dispatch("setFunctions", []);
searchObj.data.transforms = [];
searchObj.data.stream.functions = [];
return;
}
const getFunctions = async () => {
try {
if (store.state.organizationData.functions.length == 0) {
await getAllFunctions();
}
store.state.organizationData.functions.map((data: any) => {
const args: any = [];
for (let i = 0; i < parseInt(data.num_args); i++) {
args.push("'${1:value}'");
}
const itemObj: {
name: any;
args: string;
} = {
name: data.name,
args: "(" + args.join(",") + ")",
};
searchObj.data.transforms.push({
name: data.name,
function: data.function,
});
if (!data.stream_name) {
searchObj.data.stream.functions.push(itemObj);
}
});
return;
} catch (e) {
showErrorNotification("Error while fetching functions");
}
};
const getActions = async () => {
try {
searchObj.data.actions = [];
if (store.state.organizationData.actions.length == 0) {
await getAllActions();
}
store.state.organizationData.actions.forEach((data: any) => {
if (data.execution_details_type === "service") {
searchObj.data.actions.push({
name: data.name,
id: data.id,
});
}
});
return;
} catch (e) {
showErrorNotification("Error while fetching actions");
}
};
function resetStreamData() {
store.dispatch("resetStreams", {});
searchObj.data.stream.selectedStream = [];
searchObj.data.stream.selectedStreamFields = [];
searchObj.data.stream.selectedFields = [];
searchObj.data.stream.filterField = "";
searchObj.data.stream.addToFilter = "";
searchObj.data.stream.functions = [];
searchObj.data.stream.streamType =
(router.currentRoute.value.query.stream_type as string) || "logs";
searchObj.data.stream.streamLists = [];
resetQueryData();
resetSearchAroundData();
}
function resetQueryData() {
// searchObj.data.queryResults = {};
searchObj.data.sortedQueryResults = [];
// searchObj.data.histogram = {
// xData: [],
// yData: [],
// chartParams: {},
// };
// searchObj.data.resultGrid.columns = [];
searchObj.data.resultGrid.currentPage = 1;
searchObj.runQuery = false;
searchObj.data.errorMsg = "";
searchObj.data.errorDetail = "";
searchObj.data.countErrorMsg = "";
}
function resetSearchAroundData() {
searchObj.data.searchAround.indexTimestamp = -1;
searchObj.data.searchAround.size = 0;
}
async function loadStreamLists() {
try {
if (searchObj.data.streamResults.list.length > 0) {
let lastUpdatedStreamTime = 0;
let selectedStream: any[] = [];
searchObj.data.stream.streamLists = [];
let itemObj: {
label: string;
value: string;
};
// searchObj.data.streamResults.list.forEach((item: any) => {
for (const item of searchObj.data.streamResults.list) {
itemObj = {
label: item.name,
value: item.name,
};
searchObj.data.stream.streamLists.push(itemObj);
// If isFirstLoad is true, then select the stream from query params
if (router.currentRoute.value?.query?.stream == item.name) {
selectedStream.push(itemObj.value);
}
if (
!router.currentRoute.value?.query?.stream &&
item.stats.doc_time_max >= lastUpdatedStreamTime
) {
selectedStream = [];
lastUpdatedStreamTime = item.stats.doc_time_max;
selectedStream.push(itemObj.value);
}
}
if (
store.state.zoConfig.query_on_stream_selection == false ||
router.currentRoute.value.query?.type == "stream_explorer"
) {
searchObj.data.stream.selectedStream = selectedStream;
}
} else {
searchObj.data.errorMsg = "No stream found in selected organization!";
}
return;
} catch (e: any) {
console.log("Error while loading stream list");
}
}
async function loadStreamFields(streamName: string) {
try {
if (streamName != "") {
searchObj.loadingStream = true;
return await getStream(
streamName,
searchObj.data.stream.streamType || "logs",
true,
).then((res) => {
searchObj.loadingStream = false;
return res;
});
} else {
searchObj.data.errorMsg = "No stream found in selected organization!";
}
return;
} catch (e: any) {
searchObj.loadingStream = false;
console.log("Error while loading stream fields");
}
}
const getStreamList = async () => {
try {
// commented below function as we are doing resetStreamData from all the places where getStreamList is called
// resetStreamData();
const streamType = searchObj.data.stream.streamType || "logs";
const streamData: any = await getStreams(streamType, false);
searchObj.data.streamResults["list"] = streamData.list;
await nextTick();
await loadStreamLists();
return;
} catch (e: any) {
console.error("Error while getting stream list", e);
}
};
const generateURLQuery = (isShareLink: boolean = false) => {
const date = searchObj.data.datetime;
const query: any = {};
if (searchObj.data.stream.streamType) {
query["stream_type"] = searchObj.data.stream.streamType;
}
if (
searchObj.data.stream.selectedStream.length > 0 &&
typeof searchObj.data.stream.selectedStream != "object"
) {
query["stream"] = searchObj.data.stream.selectedStream.join(",");
} else if (
typeof searchObj.data.stream.selectedStream == "object" &&
searchObj.data.stream.selectedStream.hasOwnProperty("value")
) {
query["stream"] = searchObj.data.stream.selectedStream.value;
} else {
query["stream"] = searchObj.data.stream.selectedStream.join(",");
}
if (date.type == "relative") {
if (isShareLink) {
query["from"] = date.startTime;
query["to"] = date.endTime;
} else {
query["period"] = date.relativeTimePeriod;
}
} else if (date.type == "absolute") {
query["from"] = date.startTime;
query["to"] = date.endTime;
}
query["refresh"] = searchObj.meta.refreshInterval;
if (searchObj.data.query) {
query["sql_mode"] = searchObj.meta.sqlMode;
query["query"] = b64EncodeUnicode(searchObj.data.query.trim());
}
if (
searchObj.data.transformType === "function" &&
searchObj.data.tempFunctionContent != ""
) {
query["functionContent"] = b64EncodeUnicode(
searchObj.data.tempFunctionContent.trim(),
);
}
// TODO : Add type in query params for all types
if (searchObj.meta.pageType !== "logs") {
query["type"] = searchObj.meta.pageType;
}
query["defined_schemas"] = searchObj.meta.useUserDefinedSchemas;
query["org_identifier"] = store.state.selectedOrganization.identifier;
query["quick_mode"] = searchObj.meta.quickMode;
query["show_histogram"] = searchObj.meta.showHistogram;
// query["timezone"] = store.state.timezone;
return query;
};
const updateUrlQueryParams = () => {
const query = generateURLQuery(false);
if (
(Object.hasOwn(query, "type") &&
query.type == "search_history_re_apply") ||
query.type == "search_scheduler"
) {
delete query.type;
}
router.push({ query });
};
function extractFilterColumns(expression: any) {
const columns: any[] = [];
function traverse(node: {
type: string;
column: any;
left: any;
right: any;
args: { type: string; value: any[] };
}) {
if (node.type === "column_ref") {
columns.push(node.column);
} else if (node.type === "binary_expr") {
traverse(node.left);
traverse(node.right);
} else if (node.type === "function") {
// Function expressions might contain columns as arguments
if (node.args && node.args.type === "expr_list") {
node.args.value.forEach((arg: any) => traverse(arg));
}
}
}
traverse(expression);
return columns;
}
const validateFilterForMultiStream = () => {
const filterCondition = searchObj.data.query;
const parsedSQL: any = parser.astify(
"select * from stream where " + filterCondition,
);
searchObj.data.stream.filteredField = extractFilterColumns(
parsedSQL?.where,
);
searchObj.data.filterErrMsg = "";
searchObj.data.missingStreamMessage = "";
searchObj.data.stream.missingStreamMultiStreamFilter = [];
for (const fieldObj of searchObj.data.stream.filteredField) {
const fieldName = fieldObj.expr.value;
const filteredFields: any =
searchObj.data.stream.selectedStreamFields.filter(
(field: any) => field.name === fieldName,
);
if (filteredFields.length > 0) {
const streamsCount = filteredFields[0].streams.length;
const allStreamsEqual = filteredFields.every(
(field: any) => field.streams.length === streamsCount,
);
if (!allStreamsEqual) {
searchObj.data.filterErrMsg += `Field '${fieldName}' exists in different number of streams.\n`;
}
} else {
searchObj.data.filterErrMsg += `Field '${fieldName}' does not exist in the one or more stream.\n`;
}
const fieldStreams: any = searchObj.data.stream.selectedStreamFields
.filter((field: any) => field.name === fieldName)
.map((field: any) => field.streams)
.flat();
searchObj.data.stream.missingStreamMultiStreamFilter =
searchObj.data.stream.selectedStream.filter(
(stream: any) => !fieldStreams.includes(stream),
);
if (searchObj.data.stream.missingStreamMultiStreamFilter.length > 0) {
searchObj.data.missingStreamMessage = `One or more filter fields do not exist in "${searchObj.data.stream.missingStreamMultiStreamFilter.join(
", ",
)}", hence no search is performed in the mentioned stream.\n`;
}
}
return searchObj.data.filterErrMsg === "" ? true : false;
};
function buildSearch() {
try {
let query = searchObj.data.query.trim();
searchObj.data.filterErrMsg = "";
searchObj.data.missingStreamMessage = "";
searchObj.data.stream.missingStreamMultiStreamFilter = [];
const req: any = {
query: {
sql: searchObj.meta.sqlMode
? query
: 'select [FIELD_LIST][QUERY_FUNCTIONS] from "[INDEX_NAME]" [WHERE_CLAUSE]',
start_time: (new Date().getTime() - 900000) * 1000,
end_time: new Date().getTime() * 1000,
from:
searchObj.meta.resultGrid.rowsPerPage *
(searchObj.data.resultGrid.currentPage - 1) || 0,
size: searchObj.meta.resultGrid.rowsPerPage,
quick_mode: searchObj.meta.quickMode,
},
aggs: {
histogram:
"select histogram(" +
store.state.zoConfig.timestamp_column +
", '[INTERVAL]') AS zo_sql_key, count(*) AS zo_sql_num from \"[INDEX_NAME]\" [WHERE_CLAUSE] GROUP BY zo_sql_key ORDER BY zo_sql_key",
},
};
if (
config.isEnterprise == "true" &&
store.state.zoConfig.super_cluster_enabled
) {
req["regions"] = searchObj.meta.regions;
req["clusters"] = searchObj.meta.clusters;
}
// if (searchObj.data.stream.selectedStreamFields.length == 0) {
// const streamData: any = getStreams(
// searchObj.data.stream.streamType,
// true,
// true,
// );
// searchObj.data.stream.selectedStreamFields = streamData.schema;
// if (
// !searchObj.data.stream.selectedStreamFields ||
// searchObj.data.stream.selectedStreamFields.length == 0
// ) {
// searchObj.data.stream.selectedStreamFields = [];
// searchObj.loading = false;
// return false;
// }
// }
const streamFieldNames: any =
searchObj.data.stream.selectedStreamFields.map(
(item: any) => item.name,
);
for (
let i = searchObj.data.stream.interestingFieldList.length - 1;
i >= 0;
i--
) {
const fieldName = searchObj.data.stream.interestingFieldList[i];
if (!streamFieldNames.includes(fieldName)) {
searchObj.data.stream.interestingFieldList.splice(i, 1);
}
}
if (
searchObj.data.stream.interestingFieldList.length > 0 &&
searchObj.meta.quickMode
) {
if (searchObj.data.stream.selectedStream.length == 1) {
req.query.sql = req.query.sql.replace(
"[FIELD_LIST]",
searchObj.data.stream.interestingFieldList.join(","),
);
}
} else {
req.query.sql = req.query.sql.replace("[FIELD_LIST]", "*");
}
const timestamps: any =
searchObj.data.datetime.type === "relative"
? getConsumableRelativeTime(
searchObj.data.datetime.relativeTimePeriod,
)
: cloneDeep(searchObj.data.datetime);
if (searchObj.data.datetime.type === "relative") {
searchObj.data.datetime.startTime = timestamps.startTime;
searchObj.data.datetime.endTime = timestamps.endTime;
}
if (
timestamps.startTime != "Invalid Date" &&
timestamps.endTime != "Invalid Date"
) {
if (timestamps.startTime > timestamps.endTime) {
notificationMsg.value = "Start time cannot be greater than end time";
// showErrorNotification("Start time cannot be greater than end time");
return false;
}
searchObj.meta.resultGrid.chartKeyFormat = "HH:mm:ss";
req.query.start_time = timestamps.startTime;
req.query.end_time = timestamps.endTime;
searchObj.meta.resultGrid.chartInterval = "10 second";
if (req.query.end_time - req.query.start_time >= 1000000 * 60 * 30) {
searchObj.meta.resultGrid.chartInterval = "15 second";
searchObj.meta.resultGrid.chartKeyFormat = "HH:mm:ss";
}
if (req.query.end_time - req.query.start_time >= 1000000 * 60 * 60) {
searchObj.meta.resultGrid.chartInterval = "30 second";
searchObj.meta.resultGrid.chartKeyFormat = "HH:mm:ss";
}
if (req.query.end_time - req.query.start_time >= 1000000 * 3600 * 2) {
searchObj.meta.resultGrid.chartInterval = "1 minute";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (req.query.end_time - req.query.start_time >= 1000000 * 3600 * 6) {
searchObj.meta.resultGrid.chartInterval = "5 minute";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (req.query.end_time - req.query.start_time >= 1000000 * 3600 * 24) {
searchObj.meta.resultGrid.chartInterval = "30 minute";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (req.query.end_time - req.query.start_time >= 1000000 * 86400 * 7) {
searchObj.meta.resultGrid.chartInterval = "1 hour";
searchObj.meta.resultGrid.chartKeyFormat = "MM-DD HH:mm";
}
if (req.query.end_time - req.query.start_time >= 1000000 * 86400 * 30) {
searchObj.meta.resultGrid.chartInterval = "1 day";
searchObj.meta.resultGrid.chartKeyFormat = "YYYY-MM-DD";
}
req.aggs.histogram = req.aggs.histogram.replaceAll(
"[INTERVAL]",
searchObj.meta.resultGrid.chartInterval,
);
} else {
notificationMsg.value = "Invalid date format";
return false;
}
if (searchObj.meta.sqlMode == true) {
req.aggs.histogram = req.aggs.histogram.replace(
"[INDEX_NAME]",
searchObj.data.stream.selectedStream[0],
);
req.aggs.histogram = req.aggs.histogram.replace("[WHERE_CLAUSE]", "");
searchObj.data.query = query;
const parsedSQL: any = fnParsedSQL();
if (parsedSQL != undefined) {
const histogramParsedSQL: any = fnHistogramParsedSQL(
req.aggs.histogram,
);
histogramParsedSQL.where = parsedSQL.where;
let histogramQuery = fnUnparsedSQL(histogramParsedSQL);
histogramQuery = histogramQuery.replace(/`/g, '"');
req.aggs.histogram = histogramQuery;
//check if query is valid or not , if the query is invalid --> empty query
if(Array.isArray(parsedSQL) && parsedSQL.length == 0){
notificationMsg.value = "SQL query is missing or invalid. Please submit a valid SQL statement.";
return false;
}
if (!parsedSQL?.columns?.length && !searchObj.meta.sqlMode) {
notificationMsg.value = "No column found in selected stream.";
return false;
}
if (parsedSQL.limit != null && parsedSQL.limit.value.length != 0) {
req.query.size = parsedSQL.limit.value[0].value;
if (parsedSQL.limit.separator == "offset") {
req.query.from = parsedSQL.limit.value[1].value || 0;
}
query = fnUnparsedSQL(parsedSQL);
//replace backticks with \" for sql_mode
query = query.replace(/`/g, '"');
searchObj.data.queryResults.hits = [];
}
}
req.query.sql = query;
req.query["sql_mode"] = "full";
// delete req.aggs;
} else {
const parseQuery = [query];
let queryFunctions = "";
let whereClause = "";
if (parseQuery.length > 1) {
queryFunctions = "," + parseQuery[0].trim();
whereClause = parseQuery[1].trim();
} else {
whereClause = parseQuery[0].trim();
}
whereClause = whereClause
.split("\n")
.filter((line: string) => !line.trim().startsWith("--"))
.join("\n");
if (whereClause.trim() != "") {
whereClause = whereClause
.replace(/=(?=(?:[^"']*"[^"']*"')*[^"']*$)/g, " =")
.replace(/>(?=(?:[^"']*"[^"']*"')*[^"']*$)/g, " >")
.replace(/<(?=(?:[^"']*"[^"']*"')*[^"']*$)/g, " <");
whereClause = whereClause
.replace(/!=(?=(?:[^"']*"[^"']*"')*[^"']*$)/g, " !=")
.replace(/! =(?=(?:[^"']*"[^"']*"')*[^"']*$)/g, " !=")
.replace(/< =(?=(?:[^"']*"[^"']*"')*[^"']*$)/g, " <=")
.replace(/> =(?=(?:[^"']*"[^"']*"')*[^"']*$)/g, " >=");
//remove everything after -- in where clause
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 + '"';
// }
// });
// });
let field: any;
let node: any;
let index: any;
for (field of searchObj.data.stream.selectedStreamFields) {
for ([node, index] of parsedSQL) {
if (node === field.name) {
parsedSQL[index] = '"' + node.replaceAll('"', "") + '"';
}
}
}
whereClause = parsedSQL.join(" ");
req.query.sql = req.query.sql.replace(
"[WHERE_CLAUSE]",
" WHERE " + whereClause,
);
req.aggs.histogram = req.aggs.histogram.replace(
"[WHERE_CLAUSE]",
" WHERE " + whereClause,
);
} else {
req.query.sql = req.query.sql.replace("[WHERE_CLAUSE]", "");
req.aggs.histogram = req.aggs.histogram.replace("[WHERE_CLAUSE]", "");
}
req.query.sql = req.query.sql.replace(
"[QUERY_FUNCTIONS]",
queryFunctions.trim(),
);
// in the case of multi stream, we need to pass query for each selected stream in the form of array
// additional checks added for filter condition,
// 1. all fields in filter condition should be present in same streams.
// if one or more fields belongs to different stream then error will be shown
// 2. if multiple streams are selected but filter condition contains fields from only one stream
// then we need to send the search request for only matched stream
if (searchObj.data.stream.selectedStream.length > 1) {
let streams: any = searchObj.data.stream.selectedStream;
if (whereClause.trim() != "") {
const validationFlag = validateFilterForMultiStream();
if (!validationFlag) {
return false;
}
if (
searchObj.data.stream.missingStreamMultiStreamFilter.length > 0
) {
streams = searchObj.data.stream.selectedStream.filter(
(streams: any) =>
!searchObj.data.stream.missingStreamMultiStreamFilter.includes(
streams,
),
);
}
}
const preSQLQuery = req.query.sql;
const preHistogramSQLQuery = req.aggs.histogram;
req.query.sql = [];
streams
.join(",")
.split(",")
.forEach((item: any) => {
let finalQuery: string = preSQLQuery.replace(
"[INDEX_NAME]",
item,
);
// const finalHistogramQuery: string = preHistogramSQLQuery.replace(
// "[INDEX_NAME]",
// item
// );
const listOfFields: any = [];
let streamField: any = {};
for (const field of searchObj.data.stream.interestingFieldList) {
for (streamField of searchObj.data.stream
.selectedStreamFields) {
if (
streamField?.name == field &&
streamField?.streams.indexOf(item) > -1 &&
listOfFields.indexOf(field) == -1
) {
listOfFields.push(field);
}
}
}
let queryFieldList: string = "";
if (listOfFields.length > 0) {
queryFieldList = "," + listOfFields.join(",");
}
finalQuery = finalQuery.replace(
"[FIELD_LIST]",
`'${item}' as _stream_name` + queryFieldList,
);
// finalHistogramQuery = finalHistogramQuery.replace(
// "[FIELD_LIST]",
// `'${item}' as _stream_name,` + listOfFields.join(",")
// );
req.query.sql.push(finalQuery);
// req.aggs.histogram.push(finalHistogramQuery);
});
} else {
req.query.sql = req.query.sql.replace(
"[INDEX_NAME]",
searchObj.data.stream.selectedStream[0],
);
req.aggs.histogram = req.aggs.histogram.replace(
"[INDEX_NAME]",
searchObj.data.stream.selectedStream[0],
);
}
}
if (
searchObj.data.resultGrid.currentPage > 1 ||
searchObj.meta.showHistogram === false
) {
// delete req.aggs;
if (searchObj.meta.showHistogram === false) {
// delete searchObj.data.histogram;
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
searchObj.meta.histogramDirtyFlag = true;
} else {
searchObj.meta.histogramDirtyFlag = false;
}
}
if (store.state.zoConfig.sql_base64_enabled) {
req["encoding"] = "base64";
req.query.sql = b64EncodeUnicode(req.query.sql);
//encode the histogram only if the current page is 1
if (
searchObj.data.resultGrid.currentPage == 1
) {
req.aggs.histogram = b64EncodeUnicode(req.aggs.histogram);
}
}
updateUrlQueryParams();
return req;
} catch (e: any) {
notificationMsg.value =
"An error occurred while constructing the search query.";
return "";
}
}
const isLimitQuery = (parsedSQL: any = null) => {
return parsedSQL?.limit && parsedSQL?.limit.value?.length > 0;
};
const isDistinctQuery = (parsedSQL: any = null) => {
return parsedSQL?.distinct?.type === "DISTINCT";
};
const isWithQuery = (parsedSQL: any = null) => {
return parsedSQL?.with && parsedSQL?.with?.length > 0;
};
const getQueryPartitions = async (queryReq: any) => {
try {
// const queryReq = buildSearch();
searchObj.data.queryResults.hits = [];
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
const parsedSQL: any = fnParsedSQL();
// if (searchObj.meta.sqlMode && parsedSQL == undefined) {
// searchObj.data.queryResults.error =
// "Error while search partition. Search query is invalid.";
// return;
// }
// In Limit we don't need to get partitions, as we directly hit search request with query limit
if (
!searchObj.meta.sqlMode ||
(searchObj.meta.sqlMode && !isLimitQuery(parsedSQL))
) {
const partitionQueryReq: any = {
sql: queryReq.query.sql,
start_time: queryReq.query.start_time,
end_time: queryReq.query.end_time,
sql_mode: searchObj.meta.sqlMode ? "full" : "context",
};
if (store.state.zoConfig.sql_base64_enabled) {
partitionQueryReq["encoding"] = "base64";
}
if (
config.isEnterprise == "true" &&
store.state.zoConfig.super_cluster_enabled
) {
if (queryReq.query.hasOwnProperty("regions")) {
partitionQueryReq["regions"] = queryReq.query.regions;
}
if (queryReq.query.hasOwnProperty("clusters")) {
partitionQueryReq["clusters"] = queryReq.query.clusters;
}
}
// if (parsedSQL != undefined) {
// searchObj.data.queryResults.partitionDetail = {
// partitions: [],
// partitionTotal: [],
// paginations: [],
// };
// let pageObject: any = [];
// const partitions: any = [
// [partitionQueryReq.start_time, partitionQueryReq.end_time],
// ];
// searchObj.data.queryResults.partitionDetail.partitions = partitions;
// for (const [index, item] of partitions.entries()) {
// pageObject = [
// {
// startTime: item[0],
// endTime: item[1],
// from: 0,
// size: searchObj.meta.resultGrid.rowsPerPage,
// },
// ];
// searchObj.data.queryResults.partitionDetail.paginations.push(
// pageObject,
// );
// searchObj.data.queryResults.partitionDetail.partitionTotal.push(-1);
// }
// } else {
const { traceparent, traceId } = generateTraceContext();
addTraceId(traceId);
partitionQueryReq["streaming_output"] = true;
await searchService
.partition({
org_identifier: searchObj.organizationIdentifier,
query: partitionQueryReq,
page_type: searchObj.data.stream.streamType,
traceparent,
})
.then(async (res: any) => {
searchObj.data.queryResults.partitionDetail = {
partitions: [],
partitionTotal: [],
paginations: [],
};
if (typeof partitionQueryReq.sql != "string") {
const partitionSize = 0;
let partitions = [];
let pageObject = [];
// Object.values(res).forEach((partItem: any) => {
const partItem = res.data;
searchObj.data.queryResults.total += partItem.records;
if (partItem.partitions.length > partitionSize) {
partitions = partItem.partitions;
searchObj.data.queryResults.partitionDetail.partitions =
partitions;
for (const [index, item] of partitions.entries()) {
pageObject = [
{
startTime: item[0],
endTime: item[1],
from: 0,
size: searchObj.meta.resultGrid.rowsPerPage,
streaming_output: res.data?.streaming_aggs || false,
streaming_id: res.data?.streaming_id || null,
},
];
searchObj.data.queryResults.partitionDetail.paginations.push(
pageObject,
);
searchObj.data.queryResults.partitionDetail.partitionTotal.push(
-1,
);
}
}
// });
} else {
searchObj.data.queryResults.total = 0;
// delete searchObj.data.histogram.chartParams.title;
// generateHistogramData();
const partitions = res.data.partitions;
let pageObject = [];
searchObj.data.queryResults.partitionDetail.partitions =
partitions;
for (const [index, item] of partitions.entries()) {
pageObject = [
{
startTime: item[0],
endTime: item[1],
from: 0,
size: searchObj.meta.resultGrid.rowsPerPage,
streaming_output: res.data?.streaming_aggs || false,
streaming_id: res.data?.streaming_id || null,
},
];
searchObj.data.queryResults.partitionDetail.paginations.push(
pageObject,
);
searchObj.data.queryResults.partitionDetail.partitionTotal.push(
-1,
);
}
}
})
.catch((err: any) => {
searchObj.loading = false;
// Reset cancel query on search error
searchObj.data.isOperationCancelled = false;
let trace_id = "";
searchObj.data.errorMsg =
"Error while processing partition request.";
if (err.response != undefined) {
searchObj.data.errorMsg =
err.response?.data?.error ||
err.response?.data?.message ||
"";
if (err.response.data.hasOwnProperty("error_detail")) {
searchObj.data.errorDetail = err.response.data.error_detail;
}
if (err.response.data.hasOwnProperty("trace_id")) {
trace_id = err.response.data?.trace_id;
}
} else {
searchObj.data.errorMsg = err.message;
if (err.hasOwnProperty("trace_id")) {
trace_id = err?.trace_id;
}
}
notificationMsg.value = searchObj.data.errorMsg;
if (err?.request?.status >= 429 || err?.request?.status == 400) {
notificationMsg.value = err?.response?.data?.message;
searchObj.data.errorMsg = err?.response?.data?.message || "";
searchObj.data.errorDetail = err?.response?.data?.error_detail;
}
if (trace_id) {
searchObj.data.errorMsg +=
" <br><span class='text-subtitle1'>TraceID:" +
trace_id +
"</span>";
notificationMsg.value += " TraceID:" + trace_id;
trace_id = "";
}
})
.finally(() => {
removeTraceId(traceId);
});
// }
} else {
searchObj.data.queryResults.partitionDetail = {
partitions: [],
partitionTotal: [],
paginations: [],
};
searchObj.data.queryResults.partitionDetail.partitions = [
[queryReq.query.start_time, queryReq.query.end_time],
];
let pageObject: any = [];
for (const [
index,
item,
] of searchObj.data.queryResults.partitionDetail.partitions.entries()) {
pageObject = [
{
startTime: item[0],
endTime: item[1],
from: 0,
size: searchObj.meta.resultGrid.rowsPerPage,
},
];
searchObj.data.queryResults.partitionDetail.paginations.push(
pageObject,
);
searchObj.data.queryResults.partitionDetail.partitionTotal.push(-1);
}
}
} catch (e: any) {
console.log("error", e);
notificationMsg.value = "Error while getting search partitions.";
searchObj.data.queryResults.error = e.message;
throw e;
}
};
const refreshPartitionPagination = (regenrateFlag: boolean = false, isStreamingOutput: boolean = false) => {
if (searchObj.meta.jobId != "") {
return;
}
try {
const { rowsPerPage } = searchObj.meta.resultGrid;
const { currentPage } = searchObj.data.resultGrid;
const partitionDetail = searchObj.data.queryResults.partitionDetail;
let remainingRecords = rowsPerPage;
let lastPartitionSize = 0;
if (
partitionDetail.paginations.length <= currentPage + 3 ||
regenrateFlag
) {
partitionDetail.paginations = [];
let pageNumber = 0;
let partitionFrom = 0;
let total = 0;
let totalPages = 0;
let recordSize = 0;
let from = 0;
let lastPage = 0;
const parsedSQL: any = fnParsedSQL();
if (
(searchObj.data.queryResults.aggs !== undefined &&
searchObj.data.resultGrid.currentPage == 1 &&
searchObj.meta.showHistogram == true &&
searchObj.data.stream.selectedStream.length <= 1 &&
(!searchObj.meta.sqlMode ||
(searchObj.meta.sqlMode && !isLimitQuery(parsedSQL) && !isDistinctQuery(parsedSQL) && !isWithQuery(parsedSQL)))) ||
(searchObj.loadingHistogram == false &&
searchObj.meta.showHistogram == true &&
searchObj.data.stream.selectedStream.length <= 1 &&
searchObj.meta.sqlMode == false &&
searchObj.data.resultGrid.currentPage == 1)
) {
if (
searchObj.data.queryResults.hasOwnProperty("aggs") &&
searchObj.data.queryResults.aggs != null
) {
}
} else {
// if streaming output is enabled, then we need to update the total as the last partition total, as the last partition total is the total of all the records in case of streaming output
if(isStreamingOutput) {
if(partitionDetail.partitionTotal[partitionDetail.partitionTotal?.length - 1] > -1)
searchObj.data.queryResults.total = partitionDetail.partitionTotal[partitionDetail.partitionTotal.length - 1];
} else {
// if streaming output is disabled, then we need to update the total as the sum of all partition totals
searchObj.data.queryResults.total =
partitionDetail.partitionTotal.reduce(
(accumulator: number, currentValue: number) =>
accumulator + Math.max(currentValue, 0),
0,
);
}
}
// partitionDetail.partitions.forEach((item: any, index: number) => {
for (const [index, item] of partitionDetail.partitions.entries()) {
total = partitionDetail.partitionTotal[index];
totalPages = Math.ceil(total / rowsPerPage);
if (!partitionDetail.paginations[pageNumber]) {
partitionDetail.paginations[pageNumber] = [];
}
if (totalPages > 0) {
partitionFrom = 0;
for (let i = 0; i < totalPages; i++) {
remainingRecords = rowsPerPage;
recordSize =
i === totalPages - 1
? total - partitionFrom || rowsPerPage
: rowsPerPage;
from = partitionFrom;
// if (i === 0 && partitionDetail.paginations.length > 0) {
lastPartitionSize = 0;
if (pageNumber > 0) {
lastPage = partitionDetail.paginations.length - 1;
// partitionDetail.paginations[lastPage].forEach((item: any) => {
for (const item of partitionDetail.paginations[lastPage]) {
lastPartitionSize += item.size;
}
if (lastPartitionSize != rowsPerPage) {
recordSize = rowsPerPage - lastPartitionSize;
}
}
if (!partitionDetail.paginations[pageNumber]) {
partitionDetail.paginations[pageNumber] = [];
}
partitionDetail.paginations[pageNumber].push({
startTime: item[0],
endTime: item[1],
from,
size: Math.abs(Math.min(recordSize, rowsPerPage)),
});
partitionFrom += recordSize;
if (
recordSize == rowsPerPage ||
lastPartitionSize + recordSize == rowsPerPage
) {
pageNumber++;
}
if (
partitionDetail.paginations.length >
searchObj.data.resultGrid.currentPage + 10
) {
return true;
}
}
} else {
lastPartitionSize = 0;
recordSize = rowsPerPage;
lastPage = partitionDetail.paginations.length - 1;
// partitionDetail.paginations[lastPage].forEach((item: any) => {
for (const item of partitionDetail.paginations[lastPage]) {
lastPartitionSize += item.size;
}
if (lastPartitionSize != rowsPerPage) {
recordSize = rowsPerPage - lastPartitionSize;
}
from = 0;
if (total == 0) {
recordSize = 0;
}
partitionDetail.paginations[pageNumber].push({
startTime: item[0],
endTime: item[1],
from,
size: Math.abs(recordSize),
});
if (partitionDetail.paginations[pageNumber].size > 0) {
pageNumber++;
remainingRecords =
rowsPerPage - partitionDetail.paginations[pageNumber].size;
} else {
remainingRecords = rowsPerPage;
}
}
if (
partitionDetail.paginations.length >
searchObj.data.resultGrid.currentPage + 10
) {
return true;
}
}
searchObj.data.queryResults.partitionDetail = partitionDetail;
}
} catch (e: any) {
console.log("Error while refreshing partition pagination", e);
notificationMsg.value = "Error while refreshing partition pagination.";
return false;
}
};
const getQueryData = async (isPagination = false) => {
try {
//remove any data that has been cached
if(searchObj.data.originalDataCache.size > 0){
searchObj.data.originalDataCache.clear();
}
// Reset cancel query on new search request initation
searchObj.data.isOperationCancelled = false;
searchObj.data.searchRequestTraceIds = [];
searchObj.data.searchWebSocketTraceIds = [];
// get websocket enable config from store
// window will have more priority
// if window has use_web_socket property then use that
// else use organization settings
searchObj.meta.jobId = "";
const shouldUseWebSocket = isWebSocketEnabled(store.state);
const shouldUseStreaming = isStreamingEnabled(store.state);
const isMultiStreamSearch =
searchObj.data.stream.selectedStream.length > 1 &&
!searchObj.meta.sqlMode;
// Determine communication method based on available options and constraints
if (shouldUseStreaming) {
searchObj.communicationMethod = "streaming";
} else if (shouldUseWebSocket && !isMultiStreamSearch) {
searchObj.communicationMethod = "ws";
} else {
searchObj.communicationMethod = "http";
}
// searchObj.data.histogram.chartParams.title = "";
searchObjDebug["queryDataStartTime"] = performance.now();
searchObj.meta.showDetailTab = false;
searchObj.meta.searchApplied = true;
searchObj.data.functionError = "";
if (
!searchObj.data.stream.streamLists?.length ||
searchObj.data.stream.selectedStream.length == 0
) {
searchObj.loading = false;
return;
}
if (
isNaN(searchObj.data.datetime.endTime) ||
isNaN(searchObj.data.datetime.startTime)
) {
setDateTime(
(router.currentRoute.value?.query?.period as string) || "15m",
);
}
// Use the appropriate method to fetch data
if (searchObj.communicationMethod === "ws" || searchObj.communicationMethod === "streaming") {
getDataThroughStream(isPagination);
return;
}
searchObjDebug["buildSearchStartTime"] = performance.now();
const queryReq: any = buildSearch();
searchObjDebug["buildSearchEndTime"] = performance.now();
if (queryReq == false) {
throw new Error(notificationMsg.value || "Something went wrong.");
}
// reset query data and get partition detail for given query.
if (!isPagination) {
resetQueryData();
searchObjDebug["partitionStartTime"] = performance.now();
await getQueryPartitions(queryReq);
searchObjDebug["partitionEndTime"] = performance.now();
}
//reset the plot chart when the query is run
//this is to avoid the issue of chart not updating when histogram is disabled and enabled and clicking the run query button
//only reset the plot chart when the query is not run for pagination
// if(!isPagination && searchObj.meta.refreshInterval == 0) searchObj.meta.resetPlotChart = true;
if (queryReq != null) {
// in case of live refresh, reset from to 0
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs"
) {
queryReq.query.from = 0;
searchObj.meta.refreshHistogram = true;
}
// update query with function or action
addTransformToQuery(queryReq);
// in case of relative time, set start_time and end_time to query
// it will be used in pagination request
if (searchObj.data.datetime.type === "relative") {
if (!isPagination) initialQueryPayload.value = cloneDeep(queryReq);
else {
if (
searchObj.meta.refreshInterval == 0 &&
router.currentRoute.value.name == "logs" &&
searchObj.data.queryResults.hasOwnProperty("hits")
) {
const start_time: number =
initialQueryPayload.value?.query?.start_time || 0;
const end_time: number =
initialQueryPayload.value?.query?.end_time || 0;
queryReq.query.start_time = start_time;
queryReq.query.end_time = end_time;
}
}
}
// reset errorCode
searchObj.data.errorCode = 0;
// copy query request for histogram query and same for customDownload
searchObj.data.histogramQuery = JSON.parse(JSON.stringify(queryReq));
searchObj.data.histogramQuery.query.sql =
searchObj.data.histogramQuery.aggs.histogram;
searchObj.data.histogramQuery.query.sql_mode = "full";
// searchObj.data.histogramQuery.query.start_time =
// queryReq.query.start_time;
// searchObj.data.histogramQuery.query.end_time =
// queryReq.query.end_time;
delete searchObj.data.histogramQuery.query.quick_mode;
delete searchObj.data.histogramQuery.query.from;
if(searchObj.data.histogramQuery.query.action_id) delete searchObj.data.histogramQuery.query.action_id;
// Removing sql_mode from histogram query, as it is not required
//delete searchObj.data.histogramQuery.query.sql_mode;
delete searchObj.data.histogramQuery.aggs;
delete queryReq.aggs;
searchObj.data.customDownloadQueryObj = JSON.parse(
JSON.stringify(queryReq),
);
// get the current page detail and set it into query request
queryReq.query.start_time =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].startTime;
queryReq.query.end_time =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].endTime;
queryReq.query.from =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].from;
queryReq.query.size =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].size;
queryReq.query.streaming_output = searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].streaming_output;
queryReq.query.streaming_id = searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].streaming_id;
// setting subpage for pagination to handle below scenario
// for one particular page, if we have to fetch data from multiple partitions in that case we need to set subpage
// in below example we have 2 partitions and we need to fetch data from both partitions for page 2 to match recordsPerPage
/*
[
{
"startTime": 1704869331795000,
"endTime": 1705474131795000,
"from": 500,
"size": 34
},
{
"startTime": 1705474131795000,
"endTime": 1706078931795000,
"from": 0,
"size": 216
}
],
[
{
"startTime": 1706078931795000,
"endTime": 1706683731795000,
"from": 0,
"size": 250
}
]
*/
searchObj.data.queryResults.subpage = 1;
// based on pagination request, get the data
searchObjDebug["paginatedDatawithAPIStartTime"] = performance.now();
await getPaginatedData(queryReq);
if(!isPagination && searchObj.meta.refreshInterval == 0 && searchObj.data.queryResults.hits.length > 0) searchObj.meta.resetPlotChart = true;
searchObjDebug["paginatedDatawithAPIEndTime"] = performance.now();
const parsedSQL: any = fnParsedSQL();
if (
(searchObj.data.queryResults.aggs == undefined &&
searchObj.meta.refreshHistogram == true &&
searchObj.loadingHistogram == false &&
searchObj.meta.showHistogram == true &&
searchObj.data.stream.selectedStream.length <= 1 &&
(!searchObj.meta.sqlMode ||
isNonAggregatedSQLMode(searchObj, parsedSQL))) ||
(searchObj.loadingHistogram == false &&
searchObj.meta.showHistogram == true &&
searchObj.meta.sqlMode == false &&
searchObj.data.stream.selectedStream.length <= 1 &&
searchObj.meta.refreshHistogram == true)
) {
searchObj.meta.refreshHistogram = false;
if (searchObj.data.queryResults.hits.length > 0) {
if (searchObj.data.stream.selectedStream.length > 1) {
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: getHistogramTitle(),
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "Histogram is not available for multi stream search.",
errorDetail: "",
};
} else {
searchObjDebug["histogramStartTime"] = performance.now();
searchObj.data.histogram.errorMsg = "";
searchObj.data.histogram.errorCode = 0;
searchObj.data.histogram.errorDetail = "";
searchObj.loadingHistogram = true;
const parsedSQL: any = fnParsedSQL();
searchObj.data.queryResults.aggs = [];
const partitions = JSON.parse(
JSON.stringify(
searchObj.data.queryResults.partitionDetail.partitions,
),
);
// is _timestamp orderby ASC then reverse the partition array
if (isTimestampASC(parsedSQL?.orderby) && partitions.length > 1) {
partitions.reverse();
}
await generateHistogramSkeleton();
for (const partition of partitions) {
searchObj.data.histogramQuery.query.start_time = partition[0];
searchObj.data.histogramQuery.query.end_time = partition[1];
//to improve the cancel query UI experience we add additional check here and further we need to remove it
if (searchObj.data.isOperationCancelled) {
searchObj.loadingHistogram = false;
searchObj.data.isOperationCancelled = false;
if (!searchObj.data.histogram?.xData?.length) {
notificationMsg.value = "Search query was cancelled";
searchObj.data.histogram.errorMsg =
"Search query was cancelled";
searchObj.data.histogram.errorDetail =
"Search query was cancelled";
}
showCancelSearchNotification();
break;
}
await getHistogramQueryData(searchObj.data.histogramQuery);
if (partitions.length > 1) {
setTimeout(async () => {
await generateHistogramData();
refreshPartitionPagination(true);
}, 100);
}
}
searchObj.loadingHistogram = false;
}
}
await generateHistogramData();
refreshPartitionPagination(true);
} else if (searchObj.meta.sqlMode && isLimitQuery(parsedSQL)) {
resetHistogramWithError(
"Histogram unavailable for CTEs, DISTINCT and LIMIT queries.",
-1
);
searchObj.meta.histogramDirtyFlag = false;
} else if (searchObj.meta.sqlMode && (isDistinctQuery(parsedSQL) || isWithQuery(parsedSQL))) {
let aggFlag = false;
if (parsedSQL) {
aggFlag = hasAggregation(parsedSQL?.columns);
}
if (
queryReq.query.from == 0 &&
searchObj.data.queryResults.hits.length > 0 &&
!aggFlag
) {
setTimeout(async () => {
searchObjDebug["pagecountStartTime"] = performance.now();
// TODO : check the page count request
getPageCount(queryReq);
searchObjDebug["pagecountEndTime"] = performance.now();
}, 0);
}
if(isWithQuery(parsedSQL)){
resetHistogramWithError(
"Histogram unavailable for CTEs, DISTINCT and LIMIT queries.",
-1
);
}
else{
resetHistogramWithError(
"Histogram unavailable for CTEs, DISTINCT and LIMIT queries",
-1
);
}
searchObj.meta.histogramDirtyFlag = false;
}
else {
let aggFlag = false;
if (parsedSQL) {
aggFlag = hasAggregation(parsedSQL?.columns);
}
if (
queryReq.query.from == 0 &&
searchObj.data.queryResults.hits.length > 0 &&
!aggFlag
) {
setTimeout(async () => {
searchObjDebug["pagecountStartTime"] = performance.now();
await getPageCount(queryReq);
searchObjDebug["pagecountEndTime"] = performance.now();
}, 0);
}
if (searchObj.data.stream.selectedStream.length > 1) {
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: getHistogramTitle(),
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "Histogram is not available for multi stream search.",
errorDetail: "",
};
}
}
} else {
searchObj.loading = false;
if (!notificationMsg.value) {
notificationMsg.value = "Search query is empty or invalid.";
}
}
searchObjDebug["queryDataEndTime"] = performance.now();
} catch (e: any) {
searchObj.loading = false;
showErrorNotification(
notificationMsg.value || "Error occurred during the search operation.",
);
notificationMsg.value = "";
}
};
const getJobData = async (isPagination = false) => {
try {
// get websocket enable config from store
// window will have more priority
// if window has use_web_socket property then use that
// else use organization settings
const queryReq: any = buildSearch();
if (queryReq == false) {
throw new Error(notificationMsg.value || "Something went wrong.");
}
if (searchObj.meta.jobId == "") {
queryReq.query.size = parseInt(searchObj.meta.jobRecords);
}
// reset query data and get partition detail for given query.
if (queryReq != null) {
// in case of live refresh, reset from to 0
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs"
) {
queryReq.query.from = 0;
searchObj.meta.refreshHistogram = true;
}
// get function definition
addTransformToQuery(queryReq);
// in case of relative time, set start_time and end_time to query
// it will be used in pagination request
if (searchObj.data.datetime.type === "relative") {
if (!isPagination) initialQueryPayload.value = cloneDeep(queryReq);
else {
if (
searchObj.meta.refreshInterval == 0 &&
router.currentRoute.value.name == "logs" &&
searchObj.data.queryResults.hasOwnProperty("hits")
) {
const start_time: number =
initialQueryPayload.value?.query?.start_time || 0;
const end_time: number =
initialQueryPayload.value?.query?.end_time || 0;
queryReq.query.start_time = start_time;
queryReq.query.end_time = end_time;
}
}
}
delete queryReq.aggs;
}
searchObj.data.queryResults.subpage = 1;
if (searchObj.meta.jobId == "" ) {
searchService
.schedule_search(
{
org_identifier: searchObj.organizationIdentifier,
query: queryReq,
page_type: searchObj.data.stream.streamType,
},
"ui",
).then((res: any) => {
$q.notify({
type: "positive",
message: "Job Added Succesfully",
timeout: 2000,
actions: [
{
label: "Go To Job Scheduler",
color: "white",
handler: () => routeToSearchSchedule(),
},
],
});
})
}
else {
await getPaginatedData(queryReq);
}
if (searchObj.meta.jobId == ""){
searchObj.data.histogram.chartParams.title = getHistogramTitle();
}
} catch (e: any) {
searchObj.loading = false;
showErrorNotification(
notificationMsg.value || "Error occurred during the search operation.",
);
throw e;
// notificationMsg.value = "";
}
};
function shouldAddFunctionToSearch() {
if (!isActionsEnabled.value) return searchObj.data.tempFunctionContent != "" && searchObj.meta.showTransformEditor;
return searchObj.data.transformType === "function" && searchObj.data.tempFunctionContent != "";
}
function addTransformToQuery(queryReq: any) {
if (shouldAddFunctionToSearch()) {
queryReq.query["query_fn"] =
b64EncodeUnicode(searchObj.data.tempFunctionContent) || "";
}
// Add action ID if it exists
if (searchObj.data.transformType === "action" && searchObj.data.selectedTransform?.id) {
queryReq.query["action_id"] = searchObj.data.selectedTransform.id;
}
}
function resetHistogramWithError(errorMsg: string, errorCode: number = 0) {
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: getHistogramTitle(),
unparsed_x_data: [],
timezone: "",
},
errorCode,
errorMsg,
errorDetail: "",
};
}
function isTimestampASC(orderby: any) {
if (orderby) {
for (const order of orderby) {
if (
order.expr &&
order.expr.column === store.state.zoConfig.timestamp_column
) {
if (order.type && order.type === "ASC") {
return true;
}
}
}
}
return false;
}
function generateHistogramSkeleton() {
if (
searchObj.data.queryResults.hasOwnProperty("aggs") &&
searchObj.data.queryResults.aggs
) {
histogramResults = [];
histogramMappedData = [];
const intervalMs: any =
intervalMap[searchObj.meta.resultGrid.chartInterval];
if (!intervalMs) {
throw new Error("Invalid interval");
}
searchObj.data.histogramInterval = intervalMs;
const date = new Date();
const startTimeDate = new Date(
searchObj.data.customDownloadQueryObj.query.start_time / 1000,
); // Convert microseconds to milliseconds
if (searchObj.meta.resultGrid.chartInterval.includes("second")) {
startTimeDate.setSeconds(startTimeDate.getSeconds() > 30 ? 30 : 0, 0); // Round to the nearest whole minute
} else if (searchObj.meta.resultGrid.chartInterval.includes("1 minute")) {
startTimeDate.setSeconds(0, 0); // Round to the nearest whole minute
// startTimeDate.setMinutes(0, 0); // Round to the nearest whole minute
} else if (searchObj.meta.resultGrid.chartInterval.includes("minute")) {
// startTimeDate.setSeconds(0, 0); // Round to the nearest whole minute
startTimeDate.setMinutes(
parseInt(
searchObj.meta.resultGrid.chartInterval.replace(" minute", ""),
),
0,
); // Round to the nearest whole minute
} else if (searchObj.meta.resultGrid.chartInterval.includes("hour")) {
startTimeDate.setHours(startTimeDate.getHours() + 1);
startTimeDate.setUTCMinutes(0, 0); // Round to the nearest whole minute
} else {
startTimeDate.setMinutes(0, 0); // Round to the nearest whole minute
startTimeDate.setUTCHours(0, 0, 0); // Round to the nearest whole minute
startTimeDate.setDate(startTimeDate.getDate() + 1);
}
const startTime = startTimeDate.getTime() * 1000;
}
}
function hasAggregation(columns: any) {
if (columns) {
for (const column of columns) {
if (column.expr && column.expr.type === "aggr_func") {
return true; // Found aggregation function or non-null groupby property
}
}
}
return false; // No aggregation function or non-null groupby property found
}
const fnParsedSQL = () => {
try {
const filteredQuery = searchObj.data.query
.split("\n")
.filter((line: string) => !line.trim().startsWith("--"))
.join("\n");
return parser.astify(filteredQuery);
// return convertPostgreToMySql(parser.astify(filteredQuery));
} catch (e: any) {
return {
columns: [],
orderby: null,
limit: null,
groupby: null,
where: null,
};
}
};
// TODO OK : Replace backticks with double quotes, as stream name from sqlify is coming with backticks
const fnUnparsedSQL = (parsedObj: any) => {
try {
return parser.sqlify(parsedObj);
} catch (e: any) {
throw new Error(`Error while unparsing SQL : ${e.message}`);
}
};
const fnHistogramParsedSQL = (query: string) => {
try {
const filteredQuery = query
.split("\n")
.filter((line: string) => !line.trim().startsWith("--"))
.join("\n");
return parser.astify(filteredQuery);
// return convertPostgreToMySql(parser.astify(filteredQuery));
} catch (e: any) {
return {
columns: [],
orderby: null,
limit: null,
groupby: null,
where: null,
};
}
};
const getPageCount = async (queryReq: any) => {
return new Promise((resolve, reject) => {
try {
const isStreamingOutput = !!queryReq.query.streaming_output;
searchObj.loadingCounter = true;
searchObj.data.countErrorMsg = "";
queryReq.query.size = 0;
delete queryReq.query.from;
delete queryReq.query.quick_mode;
if(queryReq.query.action_id) delete queryReq.query.action_id;
if(queryReq.query.hasOwnProperty("streaming_output")) delete queryReq.query.streaming_output;
if(queryReq.query.hasOwnProperty("streaming_id")) delete queryReq.query.streaming_id;
queryReq.query["sql_mode"] = "full";
queryReq.query.track_total_hits = true;
const { traceparent, traceId } = generateTraceContext();
addTraceId(traceId);
searchService
.search(
{
org_identifier: searchObj.organizationIdentifier,
query: queryReq,
page_type: searchObj.data.stream.streamType,
traceparent,
},
"ui",
)
.then(async (res) => {
// check for total records update for the partition and update pagination accordingly
// searchObj.data.queryResults.partitionDetail.partitions.forEach(
// (item: any, index: number) => {
searchObj.data.queryResults.scan_size += res.data.scan_size;
searchObj.data.queryResults.took += res.data.took;
// Update total for the last partition
if (searchObj.meta.jobId == "") {
for (const [
index,
item,
] of searchObj.data.queryResults.partitionDetail.partitions.entries()) {
if (
(searchObj.data.queryResults.partitionDetail.partitionTotal[
index
] == -1 ||
searchObj.data.queryResults.partitionDetail.partitionTotal[
index
] < res.data.total) &&
queryReq.query.start_time == item[0]
) {
searchObj.data.queryResults.partitionDetail.partitionTotal[
index
] = res.data.total;
}
}
}
if (isStreamingOutput) {
searchObj.data.queryResults.total = res.data.total;
}
let regeratePaginationFlag = false;
if (res.data.hits.length != searchObj.meta.resultGrid.rowsPerPage) {
regeratePaginationFlag = true;
}
// if total records in partition is greater than recordsPerPage then we need to update pagination
// setting up forceFlag to true to update pagination as we have check for pagination already created more than currentPage + 3 pages.
refreshPartitionPagination(regeratePaginationFlag, isStreamingOutput);
searchObj.data.histogram.chartParams.title = getHistogramTitle();
searchObj.loadingCounter = false;
resolve(true);
})
.catch((err) => {
searchObj.loading = false;
searchObj.loadingCounter = false;
// Reset cancel query on search error
searchObj.data.isOperationCancelled = false;
let trace_id = "";
searchObj.data.countErrorMsg =
"Error while retrieving total events: ";
if (err.response != undefined) {
if (err.response.data.hasOwnProperty("trace_id")) {
trace_id = err.response.data?.trace_id;
}
} else {
if (err.hasOwnProperty("trace_id")) {
trace_id = err?.trace_id;
}
}
const customMessage = logsErrorMessage(err?.response?.data.code);
searchObj.data.errorCode = err?.response?.data.code;
notificationMsg.value = searchObj.data.countErrorMsg;
if (err?.request?.status >= 429 || err?.request?.status == 400) {
notificationMsg.value = err?.response?.data?.message;
searchObj.data.countErrorMsg += err?.response?.data?.message;
}
if (trace_id) {
searchObj.data.countErrorMsg += " TraceID:" + trace_id;
notificationMsg.value += " TraceID:" + trace_id;
trace_id = "";
}
reject(false);
})
.finally(() => {
removeTraceId(traceId);
});
} catch (e) {
searchObj.loadingCounter = false;
reject(false);
}
});
};
const processPostPaginationData = () => {
updateFieldValues();
//extract fields from query response
extractFields();
//update grid columns
updateGridColumns();
filterHitsColumns();
searchObj.data.histogram.chartParams.title = getHistogramTitle();
};
const getPaginatedData = async (
queryReq: any,
appendResult: boolean = false,
) => {
return new Promise((resolve, reject) => {
// // set track_total_hits true for first request of partition to get total records in partition
// // it will be used to send pagination request
// if (queryReq.query.from == 0) {
// queryReq.query.track_total_hits = true;
// } else if (
// searchObj.data.queryResults.partitionDetail.partitionTotal[
// searchObj.data.resultGrid.currentPage - 1
// ] > -1 &&
// queryReq.query.hasOwnProperty("track_total_hits")
// ) {
// delete queryReq.query.track_total_hits;
// }
if (searchObj.data.isOperationCancelled) {
notificationMsg.value = "Search operation is cancelled.";
searchObj.loading = false;
searchObj.data.isOperationCancelled = false;
showCancelSearchNotification();
return;
}
if (searchObj.meta.jobId != "") {
searchObj.meta.resultGrid.rowsPerPage = queryReq.query.size;
}
const parsedSQL: any = fnParsedSQL();
searchObj.meta.resultGrid.showPagination = true;
if (searchObj.meta.sqlMode == true && parsedSQL != undefined) {
// if query has aggregation or groupby then we need to set size to -1 to get all records
// issue #5432
if (hasAggregation(parsedSQL?.columns) || parsedSQL.groupby != null) {
queryReq.query.size = -1;
}
if (isLimitQuery(parsedSQL)) {
queryReq.query.size = parsedSQL.limit.value[0].value;
searchObj.meta.resultGrid.showPagination = false;
//searchObj.meta.resultGrid.rowsPerPage = queryReq.query.size;
if (parsedSQL.limit.separator == "offset") {
queryReq.query.from = parsedSQL.limit.value[1].value || 0;
}
delete queryReq.query.track_total_hits;
}
if (isDistinctQuery(parsedSQL) || isWithQuery(parsedSQL)) {
delete queryReq.query.track_total_hits;
}
}
const { traceparent, traceId } = generateTraceContext();
addTraceId(traceId);
const decideSearch = searchObj.meta.jobId
? "get_scheduled_search_result"
: "search";
searchService[decideSearch](
{
org_identifier: searchObj.organizationIdentifier,
query: queryReq,
jobId: searchObj.meta.jobId ? searchObj.meta.jobId : "",
page_type: searchObj.data.stream.streamType,
traceparent,
},
"ui",
)
.then(async (res) => {
if (
res.data.hasOwnProperty("function_error") &&
res.data.function_error != ""
) {
searchObj.data.functionError = res.data.function_error;
}
if (
res.data.hasOwnProperty("function_error") &&
res.data.function_error != "" &&
res.data.hasOwnProperty("new_start_time") &&
res.data.hasOwnProperty("new_end_time")
) {
res.data.function_error = getFunctionErrorMessage(
res.data.function_error,
res.data.new_start_time,
res.data.new_end_time,
store.state.timezone,
);
searchObj.data.datetime.startTime = res.data.new_start_time;
searchObj.data.datetime.endTime = res.data.new_end_time;
searchObj.data.datetime.type = "absolute";
queryReq.query.start_time = res.data.new_start_time;
queryReq.query.end_time = res.data.new_end_time;
searchObj.data.histogramQuery.query.start_time =
res.data.new_start_time;
searchObj.data.histogramQuery.query.end_time =
res.data.new_end_time;
if (
searchObj.data.queryResults.partitionDetail.partitions.length == 1
) {
searchObj.data.queryResults.partitionDetail.partitions[0].start_time =
res.data.new_start_time;
}
updateUrlQueryParams();
}
searchObjDebug["paginatedDataReceivedStartTime"] = performance.now();
// check for total records update for the partition and update pagination accordingly
// searchObj.data.queryResults.partitionDetail.partitions.forEach(
// (item: any, index: number) => {
if (searchObj.meta.jobId == "") {
for (const [
index,
item,
] of searchObj.data.queryResults.partitionDetail.partitions.entries()) {
if (
searchObj.data.queryResults.partitionDetail.partitionTotal[
index
] == -1 &&
queryReq.query.start_time == item[0]
) {
searchObj.data.queryResults.partitionDetail.partitionTotal[
index
] = res.data.total;
}
}
}
searchAggData.total = 0;
searchAggData.hasAggregation = false;
if (searchObj.meta.sqlMode == true && parsedSQL != undefined) {
if (
hasAggregation(parsedSQL?.columns) ||
parsedSQL.groupby != null
) {
searchAggData.total = res.data.total;
searchAggData.hasAggregation = true;
searchObj.meta.resultGrid.showPagination = false;
}
}
let regeratePaginationFlag = false;
if (res.data.hits.length != searchObj.meta.resultGrid.rowsPerPage) {
regeratePaginationFlag = true;
}
// if total records in partition is greater than recordsPerPage then we need to update pagination
// setting up forceFlag to true to update pagination as we have check for pagination already created more than currentPage + 3 pages.
if (searchObj.meta.jobId == "") {
refreshPartitionPagination(regeratePaginationFlag);
}
// Scan-size and took time in histogram title
// For the initial request, we get histogram and logs data. So, we need to sum the scan_size and took time of both the requests.
// For the pagination request, we only get logs data. So, we need to consider scan_size and took time of only logs request.
if (res.data.from > 0 || searchObj.data.queryResults.subpage > 1) {
if (appendResult && !queryReq.query?.streaming_output) {
searchObj.data.queryResults.from += res.data.from;
searchObj.data.queryResults.scan_size += res.data.scan_size;
searchObj.data.queryResults.took += res.data.took;
await chunkedAppend(searchObj.data.queryResults.hits, res.data.hits);
} else {
if(queryReq.query?.streaming_output) {
searchObj.data.queryResults.total = searchObj.data.queryResults.hits.length;
searchObj.data.queryResults.from = res.data.from;
searchObj.data.queryResults.scan_size += res.data.scan_size;
searchObj.data.queryResults.took += res.data.took;
searchObj.data.queryResults.hits = res.data.hits;
} else {
searchObj.data.queryResults.from = res.data.from;
searchObj.data.queryResults.scan_size = res.data.scan_size;
searchObj.data.queryResults.took = res.data.took;
searchObj.data.queryResults.hits = res.data.hits;
}
}
} else {
resetFieldValues();
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs" &&
searchObj.data.queryResults.hasOwnProperty("hits") &&
searchObj.data.queryResults.hits.length > 0
) {
searchObj.data.queryResults.from = res.data.from;
searchObj.data.queryResults.scan_size = res.data.scan_size;
searchObj.data.queryResults.took = res.data.took;
searchObj.data.queryResults.aggs = res.data.aggs;
const lastRecordTimeStamp = parseInt(
searchObj.data.queryResults.hits[0][
store.state.zoConfig.timestamp_column
],
);
searchObj.data.queryResults.hits = res.data.hits;
} else {
if (searchObj.meta.jobId != "") {
searchObj.data.queryResults.total = res.data.total;
}
if (!queryReq.query.hasOwnProperty("track_total_hits")) {
delete res.data.total;
}
searchObj.data.queryResults = {
...searchObj.data.queryResults,
...res.data,
};
}
}
// check for pagination request for the partition and check for subpage if we have to pull data from multiple partitions
// it will check for subpage and if subpage is present then it will send pagination request for next partition
if (
searchObj.meta.jobId == "" &&
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
].length > searchObj.data.queryResults.subpage &&
searchObj.data.queryResults.hits.length <
searchObj.meta.resultGrid.rowsPerPage *
searchObj.data.stream.selectedStream.length
) {
queryReq.query.start_time =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][searchObj.data.queryResults.subpage].startTime;
queryReq.query.end_time =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][searchObj.data.queryResults.subpage].endTime;
queryReq.query.from =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][searchObj.data.queryResults.subpage].from;
queryReq.query.size =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][searchObj.data.queryResults.subpage].size;
searchObj.data.queryResults.subpage++;
setTimeout(async () => {
processPostPaginationData();
// searchObj.data.functionError = "";
// if (
// res.data.hasOwnProperty("function_error") &&
// res.data.function_error
// ) {
// searchObj.data.functionError = res.data.function_error;
// }
}, 0);
await getPaginatedData(queryReq, true);
}
if (searchObj.meta.jobId != "") {
searchObj.meta.resultGrid.rowsPerPage = queryReq.query.size;
searchObj.data.queryResults.pagination = [];
refreshJobPagination(true);
}
await processPostPaginationData();
// searchObj.data.functionError = "";
// if (
// res.data.hasOwnProperty("function_error") &&
// res.data.function_error
// ) {
// searchObj.data.functionError = res.data.function_error;
// }
searchObj.loading = false;
searchObjDebug["paginatedDataReceivedEndTime"] = performance.now();
resolve(true);
})
.catch((err) => {
// TODO OK : create handleError function, which will handle error and return error message and detail
searchObj.loading = false;
// Reset cancel query on search error
searchObj.data.isOperationCancelled = false;
let trace_id = "";
searchObj.data.errorMsg =
typeof err == "string" && err
? err
: "Error while processing histogram request.";
if (err.response != undefined) {
searchObj.data.errorMsg =
err.response?.data?.error || err.response?.data?.message || "";
if (err.response.data.hasOwnProperty("error_detail")) {
searchObj.data.errorDetail =
err.response?.data?.error_detail || "";
}
if (err.response.data.hasOwnProperty("trace_id")) {
trace_id = err.response.data?.trace_id;
}
} else {
searchObj.data.errorMsg = err?.message || "";
if (err.hasOwnProperty("trace_id")) {
trace_id = err?.trace_id;
}
}
const customMessage = logsErrorMessage(
err?.response?.data?.code || "",
);
searchObj.data.errorCode = err?.response?.data?.code || "";
if (customMessage != "") {
searchObj.data.errorMsg = t(customMessage);
}
notificationMsg.value = searchObj.data.errorMsg;
if (err?.request?.status >= 429 || err?.request?.status == 400) {
notificationMsg.value = err?.response?.data?.message || "";
searchObj.data.errorMsg = err?.response?.data?.message || "";
searchObj.data.errorDetail =
err?.response?.data?.error_detail || "";
}
if (trace_id) {
searchObj.data.errorMsg +=
" <br><span class='text-subtitle1'>TraceID:" +
trace_id +
"</span>";
notificationMsg.value += " TraceID:" + trace_id;
trace_id = "";
}
reject(false);
})
.finally(() => {
removeTraceId(traceId);
});
});
};
const filterHitsColumns = () => {
searchObj.data.queryResults.filteredHit = [];
let itemHits: any = {};
if (searchObj.data.stream.selectedFields.length > 0) {
searchObj.data.queryResults.hits.map((hit: any) => {
itemHits = {};
// searchObj.data.stream.selectedFields.forEach((field) => {
for (const field of searchObj.data.stream.selectedFields) {
if (hit.hasOwnProperty(field)) {
itemHits[field] = hit[field];
}
}
itemHits[store.state.zoConfig.timestamp_column] =
hit[store.state.zoConfig.timestamp_column];
searchObj.data.queryResults.filteredHit.push(itemHits);
});
} else {
searchObj.data.queryResults.filteredHit =
searchObj.data.queryResults.hits;
}
};
const routeToSearchSchedule = () => {
router.push({
query: {
action: "search_scheduler",
org_identifier: store.state.selectedOrganization.identifier,
type: "search_scheduler_list",
},
});
};
const getHistogramQueryData = (queryReq: any) => {
return new Promise((resolve, reject) => {
if (searchObj.data.isOperationCancelled) {
searchObj.loadingHistogram = false;
searchObj.data.isOperationCancelled = false;
if (!searchObj.data.histogram?.xData?.length) {
notificationMsg.value = "Search query was cancelled";
searchObj.data.histogram.errorMsg = "Search query was cancelled";
searchObj.data.histogram.errorDetail = "Search query was cancelled";
}
showCancelSearchNotification();
return;
}
const dismiss = () => {};
try {
const { traceparent, traceId } = generateTraceContext();
addTraceId(traceId);
queryReq.query.size = -1;
searchService
.search(
{
org_identifier: searchObj.organizationIdentifier,
query: queryReq,
page_type: searchObj.data.stream.streamType,
traceparent,
},
"ui",
)
.then(async (res: any) => {
removeTraceId(traceId);
searchObjDebug["histogramProcessingStartTime"] = performance.now();
searchObj.loading = false;
if (searchObj.data.queryResults.aggs == null) {
searchObj.data.queryResults.aggs = [];
}
const parsedSQL: any = fnParsedSQL();
const partitions = JSON.parse(
JSON.stringify(
searchObj.data.queryResults.partitionDetail.partitions,
),
);
// is _timestamp orderby ASC then reverse the partition array
if (isTimestampASC(parsedSQL?.orderby) && partitions.length > 1) {
partitions.reverse();
}
if (
searchObj.data.queryResults.aggs.length == 0 &&
res.data.hits.length > 0
) {
for (const partition of partitions) {
if (
partition[0] == queryReq.query.start_time &&
partition[1] == queryReq.query.end_time
) {
histogramResults = [];
let date = new Date();
const startDateTime =
searchObj.data.customDownloadQueryObj.query.start_time /
1000;
const endDateTime =
searchObj.data.customDownloadQueryObj.query.end_time / 1000;
const nowString = res.data.hits[0].zo_sql_key;
const now = new Date(nowString);
const day = String(now.getDate()).padStart(2, "0");
const month = String(now.getMonth() + 1).padStart(2, "0");
const year = now.getFullYear();
const dateToBePassed = `${day}-${month}-${year}`;
const hours = String(now.getHours()).padStart(2, "0");
let minutes = String(now.getMinutes()).padStart(2, "0");
if (searchObj.data.histogramInterval / 1000 <= 9999) {
minutes = String(now.getMinutes() + 1).padStart(2, "0");
}
const time = `${hours}:${minutes}`;
const currentTimeToBePassed = convertDateToTimestamp(
dateToBePassed,
time,
"UTC",
);
for (
let currentTime: any =
currentTimeToBePassed.timestamp / 1000;
currentTime < endDateTime;
currentTime += searchObj.data.histogramInterval / 1000
) {
date = new Date(currentTime);
histogramResults.push({
zo_sql_key: date.toISOString().slice(0, 19),
zo_sql_num: 0,
});
}
for (
let currentTime: any =
currentTimeToBePassed.timestamp / 1000;
currentTime > startDateTime;
currentTime -= searchObj.data.histogramInterval / 1000
) {
date = new Date(currentTime);
histogramResults.push({
zo_sql_key: date.toISOString().slice(0, 19),
zo_sql_num: 0,
});
}
}
}
}
searchObj.data.queryResults.aggs.push(...res.data.hits);
searchObj.data.queryResults.scan_size += res.data.scan_size;
searchObj.data.queryResults.took += res.data.took;
searchObj.data.queryResults.result_cache_ratio +=
res.data.result_cache_ratio;
const currentStartTime = queryReq.query.start_time;
const currentEndTime = queryReq.query.end_time;
let totalHits = 0;
searchObj.data.queryResults.partitionDetail.partitions.map(
(item: any, index: any) => {
if (item[0] == currentStartTime && item[1] == currentEndTime) {
totalHits = res.data.hits.reduce(
(accumulator: number, currentValue: any) =>
accumulator +
Math.max(parseInt(currentValue.zo_sql_num, 10), 0),
0,
);
searchObj.data.queryResults.partitionDetail.partitionTotal[
index
] = totalHits;
return;
}
},
);
queryReq.query.start_time =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].startTime;
queryReq.query.end_time =
searchObj.data.queryResults.partitionDetail.paginations[
searchObj.data.resultGrid.currentPage - 1
][0].endTime;
// if (hasAggregationFlag) {
// searchObj.data.queryResults.total = res.data.total;
// }
// searchObj.data.histogram.chartParams.title = getHistogramTitle();
searchObjDebug["histogramProcessingEndTime"] = performance.now();
searchObjDebug["histogramEndTime"] = performance.now();
dismiss();
resolve(true);
})
.catch((err) => {
searchObj.loadingHistogram = false;
// Reset cancel query on search error
searchObj.data.isOperationCancelled = false;
let trace_id = "";
if (err?.request?.status != 429) {
searchObj.data.histogram.errorMsg =
typeof err == "string" && err
? err
: "Error while processing histogram request.";
if (err.response != undefined) {
searchObj.data.histogram.errorMsg = err.response.data.error;
if (err.response.data.hasOwnProperty("trace_id")) {
trace_id = err.response.data?.trace_id;
}
} else {
searchObj.data.histogram.errorMsg = err.message;
if (err.hasOwnProperty("trace_id")) {
trace_id = err?.trace_id;
}
}
const customMessage = logsErrorMessage(err?.response?.data.code);
searchObj.data.histogram.errorCode = err?.response?.data.code;
searchObj.data.histogram.errorDetail =
err?.response?.data?.error_detail;
if (customMessage != "") {
searchObj.data.histogram.errorMsg = t(customMessage);
}
notificationMsg.value = searchObj.data.histogram.errorMsg;
if (trace_id) {
searchObj.data.histogram.errorMsg +=
" <br><span class='text-subtitle1'>TraceID:" +
trace_id +
"</span>";
notificationMsg.value += " TraceID:" + trace_id;
trace_id = "";
}
}
reject(false);
})
.finally(() => {
removeTraceId(traceId);
});
} catch (e: any) {
dismiss();
// searchObj.data.histogram.errorMsg = e.message;
// searchObj.data.histogram.errorCode = e.code;
searchObj.loadingHistogram = false;
notificationMsg.value = searchObj.data.histogram.errorMsg;
showErrorNotification("Error while fetching histogram data");
reject(false);
}
});
};
const updateFieldValues = () => {
try {
const excludedFields = [
store.state.zoConfig.timestamp_column,
"log",
"msg",
];
// searchObj.data.queryResults.hits.forEach((item: { [x: string]: any }) => {
for (const item of searchObj.data.queryResults.hits) {
// Create set for each field values and add values to corresponding set
// Object.keys(item).forEach((key) => {
for (const key of Object.keys(item)) {
if (excludedFields.includes(key)) {
return;
}
if (fieldValues.value[key] == undefined) {
fieldValues.value[key] = new Set();
}
if (!fieldValues.value[key].has(item[key])) {
fieldValues.value[key].add(item[key]);
}
}
}
} catch (e: any) {
console.log("Error while updating field values", e);
}
};
const resetFieldValues = () => {
fieldValues.value = {};
};
async function extractFields() {
try {
searchObjDebug["extractFieldsStartTime"] = performance.now();
searchObjDebug["extractFieldsWithAPI"] = "";
searchObj.data.errorMsg = "";
searchObj.data.errorDetail = "";
searchObj.data.countErrorMsg = "";
searchObj.data.stream.selectedStreamFields = [];
searchObj.data.stream.interestingFieldList = [];
const schemaFields: any = [];
const commonSchemaFields: any = [];
if (searchObj.data.streamResults.list.length > 0) {
const timestampField = store.state.zoConfig.timestamp_column;
const allField = store.state.zoConfig?.all_fields_name;
const schemaInterestingFields: string[] = [];
let userDefineSchemaSettings: any = [];
const schemaMaps: any = [];
const commonSchemaMaps: any = [];
let schemaFieldsIndex: number = -1;
let commonSchemaFieldsIndex: number = -1;
let fieldObj: any = {};
const localInterestingFields: any = useLocalInterestingFields();
const streamInterestingFields: any = [];
let streamInterestingFieldsLocal: any = [];
const selectedStreamValues = searchObj.data.stream.selectedStream
.join(",")
.split(",");
searchObj.data.stream.expandGroupRows = {
common: true,
...Object.fromEntries(
selectedStreamValues
.sort()
.map((stream: any) => [
stream,
searchObj.data.stream.expandGroupRows[stream] &&
selectedStreamValues.length > 1
? searchObj.data.stream.expandGroupRows[stream]
: selectedStreamValues.length > 1
? false
: true,
]),
),
};
searchObj.data.stream.expandGroupRowsFieldCount = {
common: 0,
...Object.fromEntries(
selectedStreamValues.sort().map((stream: any) => [stream, 0]),
),
};
searchObj.data.datetime.queryRangeRestrictionMsg = "";
searchObj.data.datetime.queryRangeRestrictionInHour = -1;
for (const stream of searchObj.data.streamResults.list) {
if (searchObj.data.stream.selectedStream.includes(stream.name)) {
if (searchObj.data.stream.selectedStream.length > 1) {
schemaMaps.push({
name: convertToCamelCase(stream.name),
label: true,
ftsKey: false,
isSchemaField: false,
showValues: false,
group: stream.name,
isExpanded: false,
streams: [stream.name],
isInterestingField: false,
});
schemaFields.push("dummylabel");
// searchObj.data.stream.expandGroupRowsFieldCount[stream.name] = searchObj.data.stream.expandGroupRowsFieldCount[stream.name] + 1;
}
userDefineSchemaSettings =
stream.settings?.defined_schema_fields?.slice() || [];
// check for schema exist in the object or not
// if not pull the schema from server.
if (!stream.hasOwnProperty("schema")) {
searchObjDebug["extractFieldsWithAPI"] = " with API ";
const streamData: any = await loadStreamFields(stream.name);
const streamSchema: any = streamData.schema;
if (streamSchema == undefined) {
searchObj.loadingStream = false;
searchObj.data.errorMsg = t("search.noFieldFound");
throw new Error(searchObj.data.errorMsg);
return;
}
stream.settings = streamData.settings;
stream.schema = streamSchema;
}
if (
(stream.settings.max_query_range > 0 || store.state.zoConfig.max_query_range > 0) &&
(searchObj.data.datetime.queryRangeRestrictionInHour >
stream.settings.max_query_range ||
stream.settings.max_query_range == 0 ||
searchObj.data.datetime.queryRangeRestrictionInHour == -1) &&
searchObj.data.datetime.queryRangeRestrictionInHour != 0
) {
//if stream has max_query_range, then use that, otherwise use the default max_query_range from the config
searchObj.data.datetime.queryRangeRestrictionInHour = stream.settings.max_query_range > 0 ? stream.settings.max_query_range : store.state.zoConfig.max_query_range;
searchObj.data.datetime.queryRangeRestrictionMsg = t(
"search.queryRangeRestrictionMsg",
{
range:
searchObj.data.datetime.queryRangeRestrictionInHour > 1
? searchObj.data.datetime.queryRangeRestrictionInHour +
" hours"
: searchObj.data.datetime.queryRangeRestrictionInHour +
" hour",
},
);
}
let environmentInterestingFields = [];
if (
store.state.zoConfig.hasOwnProperty("default_quick_mode_fields")
) {
environmentInterestingFields =
store.state?.zoConfig?.default_quick_mode_fields;
}
if (
stream.settings.hasOwnProperty("defined_schema_fields") &&
userDefineSchemaSettings.length > 0
) {
searchObj.meta.hasUserDefinedSchemas = true;
if (store.state.zoConfig.hasOwnProperty("timestamp_column")) {
userDefineSchemaSettings.push(
store.state.zoConfig?.timestamp_column,
);
}
if (store.state.zoConfig.hasOwnProperty("all_fields_name")) {
userDefineSchemaSettings.push(
store.state.zoConfig?.all_fields_name,
);
}
} else {
searchObj.meta.hasUserDefinedSchemas =
searchObj.meta.hasUserDefinedSchemas &&
searchObj.data.stream.selectedStream.length > 1;
}
streamInterestingFieldsLocal =
localInterestingFields.value != null &&
localInterestingFields.value[
searchObj.organizationIdentifier + "_" + stream.name
] !== undefined &&
localInterestingFields.value[
searchObj.organizationIdentifier + "_" + stream.name
].length > 0
? localInterestingFields.value[
searchObj.organizationIdentifier + "_" + stream.name
]
: environmentInterestingFields.length > 0
? [...environmentInterestingFields]
: [...schemaInterestingFields];
searchObj.data.stream.interestingFieldList.push(
...streamInterestingFieldsLocal,
);
const intField = new Set(
searchObj.data.stream.interestingFieldList,
);
searchObj.data.stream.interestingFieldList = [...intField];
// create a schema field mapping based on field name to avoid iteration over object.
// in case of user defined schema consideration, loop will be break once all defined fields are mapped.
let UDSFieldCount = 0;
const fields: [string] =
stream.settings?.defined_schema_fields &&
searchObj.meta.useUserDefinedSchemas != "all_fields"
? [
store.state.zoConfig?.timestamp_column,
...stream.settings?.defined_schema_fields,
store.state.zoConfig?.all_fields_name,
]
: stream.schema.map((obj: any) => obj.name);
for (const field of fields) {
fieldObj = {
name: field,
ftsKey:
stream.settings.full_text_search_keys.indexOf > -1
? true
: false,
isSchemaField: true,
group: stream.name,
streams: [stream.name],
showValues: field !== timestampField && field !== allField,
isInterestingField:
searchObj.data.stream.interestingFieldList.includes(field)
? true
: false,
};
if (
store.state.zoConfig.user_defined_schemas_enabled &&
searchObj.meta.useUserDefinedSchemas == "user_defined_schema" &&
stream.settings.hasOwnProperty("defined_schema_fields") &&
userDefineSchemaSettings.length > 0
) {
if (userDefineSchemaSettings.includes(field)) {
schemaFieldsIndex = schemaFields.indexOf(field);
commonSchemaFieldsIndex = commonSchemaFields.indexOf(field);
if (schemaFieldsIndex > -1) {
fieldObj.group = "common";
if (
schemaMaps[schemaFieldsIndex].hasOwnProperty("streams") &&
schemaMaps[schemaFieldsIndex].streams.length > 0
) {
fieldObj.streams.push(
...schemaMaps[schemaFieldsIndex].streams,
);
searchObj.data.stream.expandGroupRowsFieldCount[
schemaMaps[schemaFieldsIndex].streams[0]
] =
searchObj.data.stream.expandGroupRowsFieldCount[
schemaMaps[schemaFieldsIndex].streams[0]
] - 1;
}
commonSchemaMaps.push(fieldObj);
commonSchemaFields.push(field);
searchObj.data.stream.expandGroupRowsFieldCount["common"] =
searchObj.data.stream.expandGroupRowsFieldCount[
"common"
] + 1;
//remove the element from the index
schemaFields.splice(schemaFieldsIndex, 1);
schemaMaps.splice(schemaFieldsIndex, 1);
} else if (commonSchemaFieldsIndex > -1) {
commonSchemaMaps[commonSchemaFieldsIndex].streams.push(
stream.name,
);
// searchObj.data.stream.expandGroupRowsFieldCount["common"] =
// searchObj.data.stream.expandGroupRowsFieldCount[
// "common"
// ] + 1;
} else {
schemaMaps.push(fieldObj);
schemaFields.push(field);
searchObj.data.stream.expandGroupRowsFieldCount[
stream.name
] =
searchObj.data.stream.expandGroupRowsFieldCount[
stream.name
] + 1;
}
if (UDSFieldCount < userDefineSchemaSettings.length) {
UDSFieldCount++;
} else {
break;
}
}
// if (schemaMaps.length == userDefineSchemaSettings.length) {
// break;
// }
} else {
schemaFieldsIndex = schemaFields.indexOf(field);
commonSchemaFieldsIndex = commonSchemaFields.indexOf(field);
if (schemaFieldsIndex > -1) {
fieldObj.group = "common";
if (
schemaMaps[schemaFieldsIndex].hasOwnProperty("streams") &&
schemaMaps[schemaFieldsIndex].streams.length > 0
) {
fieldObj.streams.push(
...schemaMaps[schemaFieldsIndex].streams,
);
searchObj.data.stream.expandGroupRowsFieldCount[
schemaMaps[schemaFieldsIndex].streams[0]
] =
searchObj.data.stream.expandGroupRowsFieldCount[
schemaMaps[schemaFieldsIndex].streams[0]
] - 1;
}
commonSchemaMaps.push(fieldObj);
commonSchemaFields.push(field);
searchObj.data.stream.expandGroupRowsFieldCount["common"] =
searchObj.data.stream.expandGroupRowsFieldCount["common"] +
1;
//remove the element from the index
schemaFields.splice(schemaFieldsIndex, 1);
schemaMaps.splice(schemaFieldsIndex, 1);
} else if (commonSchemaFieldsIndex > -1) {
commonSchemaMaps[commonSchemaFieldsIndex].streams.push(
stream.name,
);
// searchObj.data.stream.expandGroupRowsFieldCount["common"] =
// searchObj.data.stream.expandGroupRowsFieldCount["common"] +
// 1;
} else {
schemaMaps.push(fieldObj);
schemaFields.push(field);
searchObj.data.stream.expandGroupRowsFieldCount[stream.name] =
searchObj.data.stream.expandGroupRowsFieldCount[
stream.name
] + 1;
}
}
}
if (
searchObj.data.stream.selectedStream.length > 1 &&
commonSchemaFields.length == 0
) {
commonSchemaMaps.unshift({
name: "Common Group Fields",
label: true,
ftsKey: false,
isSchemaField: false,
showValues: false,
group: "common",
isExpanded: false,
streams: [stream.name],
isInterestingField: false,
});
commonSchemaFields.unshift("dummylabel");
// searchObj.data.stream.expandGroupRowsFieldCount["common"] = searchObj.data.stream.expandGroupRowsFieldCount["common"] + 1;
}
//here we check whether timestamp field is present or not
//as we append timestamp dynamically for userDefined schema we need to check this
if (
userDefineSchemaSettings.includes(
store.state.zoConfig?.timestamp_column,
)
) {
searchObj.data.hasSearchDataTimestampField = true;
} else {
searchObj.data.hasSearchDataTimestampField = false;
}
// check for user defined schema is false then only consider checking new fields from result set
if (
searchObj.data.queryResults.hasOwnProperty("hits") &&
searchObj.data.queryResults?.hits.length > 0 &&
searchObj.data.stream.selectedStream.length == 1 &&
(!store.state.zoConfig.user_defined_schemas_enabled ||
!searchObj.meta.hasUserDefinedSchemas)
) {
// Find the index of the record with max attributes
const maxAttributesIndex =
searchObj.data.queryResults.hits.reduce(
(
maxIndex: string | number,
obj: {},
currentIndex: any,
array: { [x: string]: {} },
) => {
const numAttributes = Object.keys(obj).length;
const maxNumAttributes = Object.keys(
array[maxIndex],
).length;
return numAttributes > maxNumAttributes
? currentIndex
: maxIndex;
},
0,
);
const recordwithMaxAttribute =
searchObj.data.queryResults.hits[maxAttributesIndex];
// Object.keys(recordwithMaxAttribute).forEach((key) => {
for (const key of Object.keys(recordwithMaxAttribute)) {
if (key == "_o2_id" || key == "_original" || key == "_all_values") {
continue;
}
if (key == store.state.zoConfig.timestamp_column) {
searchObj.data.hasSearchDataTimestampField = true;
}
if (
!schemaFields.includes(key) &&
!commonSchemaFields.includes(key) &&
key != "_stream_name"
) {
fieldObj = {
name: key,
type: "Utf8",
ftsKey: false,
group: stream.name,
isSchemaField: false,
showValues: false,
isInterestingField:
searchObj.data.stream.interestingFieldList.includes(key)
? true
: false,
streams: [],
};
schemaMaps.push(fieldObj);
schemaFields.push(key);
}
}
}
searchObj.data.stream.userDefinedSchema =
userDefineSchemaSettings || [];
}
}
// searchObj.data.stream.selectedStreamFields = schemaMaps;
searchObj.data.stream.selectedStreamFields = [
...commonSchemaMaps,
...schemaMaps,
];
if (
searchObj.data.stream.selectedStreamFields != undefined &&
searchObj.data.stream.selectedStreamFields.length
)
updateFieldKeywords(searchObj.data.stream.selectedStreamFields);
}
searchObjDebug["extractFieldsEndTime"] = performance.now();
} catch (e: any) {
searchObj.loadingStream = false;
console.log("Error while extracting fields.", e);
notificationMsg.value = "Error while extracting stream fields.";
}
}
const updateGridColumns = () => {
try {
searchObj.data.resultGrid.columns = [];
const logFilterField: any =
useLocalLogFilterField()?.value != null
? useLocalLogFilterField()?.value
: {};
const logFieldSelectedValue: any = [];
const stream = searchObj.data.stream.selectedStream.sort().join("_");
if (
logFilterField.length > 0 &&
logFilterField[
`${store.state.selectedOrganization.identifier}_${stream}`
] != undefined
) {
logFieldSelectedValue.push(
...logFilterField[
`${store.state.selectedOrganization.identifier}_${stream}`
],
);
}
let selectedFields = (logFilterField && logFieldSelectedValue) || [];
if (
searchObj.data.stream.selectedFields.length == 0 &&
selectedFields.length > 0
) {
return (searchObj.data.stream.selectedFields = selectedFields);
}
// As in saved view, we observed field getting duplicated in selectedFields
// So, we are removing duplicates before applying saved view
if (searchObj.data.stream.selectedFields?.length) {
selectedFields = [...new Set(searchObj.data.stream.selectedFields)];
}
const parsedSQL: any = fnParsedSQL();
// By default when no fields are selected. Timestamp and Source will be visible. If user selects field, then only selected fields will be visible in table
// In SQL and Quick mode.
// If user adds timestamp manually then only we get it in response.
// If we don’t add timestamp and add timestamp to table it should show invalid date.
if (
selectedFields.length == 0 ||
!searchObj.data.queryResults?.hits?.length
) {
searchObj.meta.resultGrid.manualRemoveFields = false;
if (
(searchObj.meta.sqlMode == true &&
parsedSQL.hasOwnProperty("columns") &&
searchObj.data.queryResults?.hits[0].hasOwnProperty(
store.state.zoConfig.timestamp_column,
)) ||
searchObj.meta.sqlMode == false ||
selectedFields.includes(store.state.zoConfig.timestamp_column)
) {
searchObj.data.resultGrid.columns.push({
name: store.state.zoConfig.timestamp_column,
id: store.state.zoConfig.timestamp_column,
accessorFn: (row: any) =>
timestampToTimezoneDate(
row[store.state.zoConfig.timestamp_column] / 1000,
store.state.timezone,
"yyyy-MM-dd HH:mm:ss.SSS",
),
prop: (row: any) =>
timestampToTimezoneDate(
row[store.state.zoConfig.timestamp_column] / 1000,
store.state.timezone,
"yyyy-MM-dd HH:mm:ss.SSS",
),
label: t("search.timestamp") + ` (${store.state.timezone})`,
header: t("search.timestamp") + ` (${store.state.timezone})`,
align: "left",
sortable: true,
enableResizing: false,
meta: {
closable: false,
showWrap: false,
wrapContent: false,
},
size: 260,
});
}
if (selectedFields.length == 0) {
searchObj.data.resultGrid.columns.push({
name: "source",
id: "source",
accessorFn: (row: any) => JSON.stringify(row),
cell: (info: any) => info.getValue(),
header: "source",
sortable: true,
enableResizing: false,
meta: {
closable: false,
showWrap: false,
wrapContent: false,
},
});
}
} else {
if (
searchObj.data.hasSearchDataTimestampField ||
selectedFields.includes(store.state.zoConfig.timestamp_column)
) {
searchObj.data.resultGrid.columns.unshift({
name: store.state.zoConfig.timestamp_column,
id: store.state.zoConfig.timestamp_column,
accessorFn: (row: any) =>
timestampToTimezoneDate(
row[store.state.zoConfig.timestamp_column] / 1000,
store.state.timezone,
"yyyy-MM-dd HH:mm:ss.SSS",
),
prop: (row: any) =>
timestampToTimezoneDate(
row[store.state.zoConfig.timestamp_column] / 1000,
store.state.timezone,
"yyyy-MM-dd HH:mm:ss.SSS",
),
label: t("search.timestamp") + ` (${store.state.timezone})`,
header: t("search.timestamp") + ` (${store.state.timezone})`,
align: "left",
sortable: true,
enableResizing: false,
meta: {
closable: false,
showWrap: false,
wrapContent: false,
},
size: 260,
});
}
let sizes: any;
if (
searchObj.data.resultGrid.colSizes &&
searchObj.data.resultGrid.colSizes.hasOwnProperty(
searchObj.data.stream.selectedStream,
)
) {
sizes =
searchObj.data.resultGrid.colSizes[
searchObj.data.stream.selectedStream
];
}
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
for (const field of selectedFields) {
if (field != store.state.zoConfig.timestamp_column) {
let foundKey, foundValue;
if (sizes?.length > 0) {
Object.keys(sizes[0]).forEach((key) => {
const trimmedKey = key
.replace(/^--(header|col)-/, "")
.replace(/-size$/, "");
if (trimmedKey === field) {
foundKey = key;
foundValue = sizes[0][key];
}
});
}
searchObj.data.resultGrid.columns.push({
name: field,
id: field,
accessorFn: (row: { [x: string]: any; source: any }) => {
return byString(row, field);
},
header: field,
sortable: true,
enableResizing: true,
meta: {
closable: true,
showWrap: true,
wrapContent: false,
},
size: foundValue ? foundValue : getColumnWidth(context, field),
maxSize: window.innerWidth,
});
}
}
}
extractFTSFields();
} catch (e: any) {
searchObj.loadingStream = false;
notificationMsg.value = "Error while updating table columns.";
}
};
/**
* Helper function to calculate width of the column based on its content(from first 5 rows)
* @param context - Canvas Context to calculate width of column using its content
* @param field - Field name for which width needs to be calculated
* @returns - Width of the column
*/
const getColumnWidth = (context: any, field: string) => {
// Font of table header
context.font = "bold 14px sans-serif";
let max = context.measureText(field).width + 16;
// Font of the table content
context.font = "12px monospace";
let width = 0;
try {
for (let i = 0; i < 5; i++) {
if (searchObj.data.queryResults.hits?.[i]?.[field]) {
width = context.measureText(
searchObj.data.queryResults.hits[i][field],
).width;
if (width > max) max = width;
}
}
} catch (err) {
console.log("Error while calculation column width");
}
max += 24; // 24px padding
if (max > 800) return 800;
if (max < 150) return 150;
return max;
};
function getHistogramTitle() {
try {
const currentPage = searchObj.data.resultGrid.currentPage - 1 || 0;
const startCount =
currentPage * searchObj.meta.resultGrid.rowsPerPage + 1;
let endCount;
let totalCount = Math.max(
searchObj.data.queryResults.hits?.length,
searchObj.data.queryResults.total,
);
if (!searchObj.meta.resultGrid.showPagination) {
endCount = searchObj.data.queryResults.hits?.length;
totalCount = searchObj.data.queryResults.hits?.length;
} else {
endCount = searchObj.meta.resultGrid.rowsPerPage * (currentPage + 1);
if (
currentPage >=
(searchObj.communicationMethod === "ws" || searchObj.communicationMethod === "streaming" || searchObj.meta.jobId != ""
? searchObj.data.queryResults?.pagination?.length
: searchObj.data.queryResults.partitionDetail?.paginations
?.length || 0) -
1
) {
endCount = Math.min(
startCount + searchObj.meta.resultGrid.rowsPerPage - 1,
totalCount,
);
} else {
endCount = searchObj.meta.resultGrid.rowsPerPage * (currentPage + 1);
}
}
if (searchObj.meta.sqlMode && searchAggData.hasAggregation) {
totalCount = searchAggData.total;
}
if (isNaN(totalCount)) {
totalCount = 0;
}
if (isNaN(endCount)) {
endCount = 0;
}
let plusSign: string = "";
if (
searchObj.data.queryResults?.partitionDetail?.partitions?.length > 1 &&
searchObj.meta.showHistogram == false
) {
plusSign = "+";
}
if (
(searchObj.communicationMethod === "ws" || searchObj.communicationMethod === "streaming") &&
endCount < totalCount &&
!searchObj.meta.showHistogram
) {
plusSign = "+";
}
const scanSizeLabel =
searchObj.data.queryResults.result_cache_ratio !== undefined &&
searchObj.data.queryResults.result_cache_ratio > 0
? "Delta Scan Size"
: "Scan Size";
const title =
"Showing " +
startCount +
" to " +
endCount +
" out of " +
totalCount.toLocaleString() +
plusSign +
" events in " +
searchObj.data.queryResults.took +
" ms. (" +
scanSizeLabel +
": " +
formatSizeFromMB(searchObj.data.queryResults.scan_size) +
plusSign +
")";
return title;
} catch (e: any) {
console.log("Error while generating histogram title", e);
notificationMsg.value = "Error while generating histogram title.";
return "";
}
}
function generateHistogramData() {
try {
// searchObj.data.histogram.chartParams.title = ""
let num_records: number = 0;
const unparsed_x_data: any[] = [];
const xData: number[] = [];
const yData: number[] = [];
let hasAggregationFlag = false;
const parsedSQL: any = fnParsedSQL();
if (searchObj.meta.sqlMode && parsedSQL.hasOwnProperty("columns")) {
hasAggregationFlag = hasAggregation(parsedSQL.columns);
}
if (
searchObj.data.queryResults.hasOwnProperty("aggs") &&
searchObj.data.queryResults.aggs
) {
histogramMappedData = new Map(
histogramResults.map((item: any) => [
item.zo_sql_key,
JSON.parse(JSON.stringify(item)),
]),
);
searchObj.data.queryResults.aggs.forEach((item: any) => {
if (histogramMappedData.has(item.zo_sql_key)) {
histogramMappedData.get(item.zo_sql_key).zo_sql_num +=
item.zo_sql_num;
} else {
histogramMappedData.set(item.zo_sql_key, item);
}
});
const mergedData: any = Array.from(histogramMappedData.values());
mergedData.map(
(bucket: {
zo_sql_key: string | number | Date;
zo_sql_num: string;
}) => {
num_records = num_records + parseInt(bucket.zo_sql_num, 10);
unparsed_x_data.push(bucket.zo_sql_key);
// const histDate = new Date(bucket.zo_sql_key);
xData.push(
histogramDateTimezone(bucket.zo_sql_key, store.state.timezone),
);
// xData.push(Math.floor(histDate.getTime()))
yData.push(parseInt(bucket.zo_sql_num, 10));
},
);
searchObj.data.queryResults.total = num_records;
}
const chartParams = {
title: getHistogramTitle(),
unparsed_x_data: unparsed_x_data,
timezone: store.state.timezone,
};
searchObj.data.histogram = {
xData,
yData,
chartParams,
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
} catch (e: any) {
console.log("Error while generating histogram data", e);
notificationMsg.value = "Error while generating histogram data.";
}
}
const searchAroundData = (obj: any) => {
try {
searchObj.loading = true;
searchObj.data.errorCode = 0;
searchObj.data.functionError = "";
const sqlContext: any = [];
let query_context: any = "";
const query = searchObj.data.query;
if (searchObj.meta.sqlMode == true) {
const parsedSQL: any = parser.astify(query);
parsedSQL.where = null;
sqlContext.push(
b64EncodeUnicode(parser.sqlify(parsedSQL).replace(/`/g, '"')),
);
} else {
const parseQuery = [query];
let queryFunctions = "";
let whereClause = "";
if (parseQuery.length > 1) {
queryFunctions = "," + parseQuery[0].trim();
whereClause = "";
} else {
whereClause = "";
}
query_context = `SELECT [FIELD_LIST]${queryFunctions} FROM "[INDEX_NAME]" `;
if (!searchObj.meta.quickMode) {
query_context = query_context.replace("[FIELD_LIST]", "*");
}
// const preSQLQuery = req.query.sql;
const streamsData: any = searchObj.data.stream.selectedStream.filter(
(streams: any) =>
!searchObj.data.stream.missingStreamMultiStreamFilter.includes(
streams,
),
);
let finalQuery: string = "";
streamsData.forEach((item: any) => {
finalQuery = query_context.replace("[INDEX_NAME]", item);
const listOfFields: any = [];
let streamField: any = {};
for (const field of searchObj.data.stream.interestingFieldList) {
for (streamField of searchObj.data.stream.selectedStreamFields) {
if (
streamField?.name == field &&
streamField?.streams.indexOf(item) > -1
) {
listOfFields.push(field);
}
}
}
let queryFieldList: string = "";
if (listOfFields.length > 0) {
queryFieldList = "," + listOfFields.join(",");
}
finalQuery = finalQuery.replace(
"[FIELD_LIST]",
`'${item}' as _stream_name` + queryFieldList,
);
sqlContext.push(b64EncodeUnicode(finalQuery));
});
}
let query_fn: any = "";
if (shouldAddFunctionToSearch()) {
query_fn = b64EncodeUnicode(searchObj.data.tempFunctionContent);
}
let action_id: any = "";
if (searchObj.data.transformType === "action" && searchObj.data.selectedTransform?.id) {
action_id = searchObj.data.selectedTransform.id;
}
let streamName: string = "";
if (searchObj.data.stream.selectedStream.length > 1) {
streamName =
b64EncodeUnicode(searchObj.data.stream.selectedStream.join(",")) ||
"";
} else {
streamName = searchObj.data.stream.selectedStream[0];
}
const { traceparent, traceId } = generateTraceContext();
addTraceId(traceId);
searchService
.search_around({
org_identifier: searchObj.organizationIdentifier,
index: streamName,
key: obj.key,
size: obj.size,
body: obj.body,
query_context: sqlContext,
query_fn: query_fn,
stream_type: searchObj.data.stream.streamType,
regions: searchObj.meta.hasOwnProperty("regions")
? searchObj.meta.regions.join(",")
: "",
clusters: searchObj.meta.hasOwnProperty("clusters")
? searchObj.meta.clusters.join(",")
: "",
action_id,
is_multistream:
searchObj.data.stream.selectedStream.length > 1 ? true : false,
traceparent,
})
.then((res) => {
searchObj.loading = false;
searchObj.data.histogram.chartParams.title = "";
if (res.data.from > 0) {
searchObj.data.queryResults.from = res.data.from;
searchObj.data.queryResults.scan_size += res.data.scan_size;
searchObj.data.queryResults.took += res.data.took;
searchObj.data.queryResults.hits.push(...res.data.hits);
} else {
searchObj.data.queryResults = res.data;
}
//extract fields from query response
extractFields();
generateHistogramSkeleton();
generateHistogramData();
//update grid columns
updateGridColumns();
filterHitsColumns();
if (searchObj.meta.showHistogram) {
searchObj.meta.showHistogram = false;
searchObj.data.searchAround.histogramHide = true;
}
searchObj.data.histogram.chartParams.title = "";
// segment.track("Button Click", {
// button: "Search Around Data",
// user_org: store.state.selectedOrganization.identifier,
// user_id: store.state.userInfo.email,
// stream_name: searchObj.data.stream.selectedStream.value,
// show_timestamp: obj.key,
// show_size: obj.size,
// show_histogram: searchObj.meta.showHistogram,
// sqlMode: searchObj.meta.sqlMode,
// showFields: searchObj.meta.showFields,
// page: "Search Logs - Search around data",
// });
// const visibleIndex =
// obj.size > 30 ? obj.size / 2 - 12 : obj.size / 2;
// setTimeout(() => {
// searchResultRef.value.searchTableRef.scrollTo(
// visibleIndex,
// "start-force"
// );
// }, 500);
})
.catch((err) => {
let trace_id = "";
searchObj.data.errorMsg = "Error while processing search request.";
if (err.response != undefined) {
searchObj.data.errorMsg = err.response.data.error;
searchObj.data.errorDetail = err.response.data.error_detail;
if (err.response.data.hasOwnProperty("trace_id")) {
trace_id = err.response.data?.trace_id;
}
} else {
searchObj.data.errorMsg = err.message;
if (err.hasOwnProperty("trace_id")) {
trace_id = err?.trace_id;
}
}
const customMessage = logsErrorMessage(err.response.data.code);
searchObj.data.errorCode = err.response.data.code;
if (customMessage != "") {
searchObj.data.errorMsg = customMessage;
}
if (err?.request?.status >= 429 || err?.request?.status == 400) {
notificationMsg.value = err?.response?.data?.message;
searchObj.data.errorMsg = err?.response?.data?.message;
}
if (trace_id) {
searchObj.data.errorMsg +=
" <br><span class='text-subtitle1'>TraceID:" +
trace_id +
"</span>";
trace_id = "";
}
})
.finally(() => {
removeTraceId(traceId);
searchObj.loading = false;
});
} catch (e: any) {
searchObj.loading = false;
showErrorNotification("Error while fetching data");
}
};
const refreshData = () => {
try {
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs" &&
enableRefreshInterval(searchObj.meta.refreshInterval)
) {
clearInterval(store.state.refreshIntervalID);
const refreshIntervalID = setInterval(async () => {
if (
searchObj.loading == false &&
searchObj.loadingHistogram == false &&
searchObj.meta.logsVisualizeToggle == "logs"
) {
searchObj.loading = true;
await getQueryData(false);
}
}, searchObj.meta.refreshInterval * 1000);
store.dispatch("setRefreshIntervalID", refreshIntervalID);
// only notify if user is in logs page
if (searchObj.meta.logsVisualizeToggle == "logs") {
$q.notify({
message: `Live mode is enabled. Only top ${searchObj.meta.resultGrid.rowsPerPage} results are shown.`,
color: "positive",
position: "top",
timeout: 1000,
});
}
} else {
clearInterval(store.state.refreshIntervalID);
}
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs" &&
!enableRefreshInterval(searchObj.meta.refreshInterval)
) {
searchObj.meta.refreshInterval = 0;
clearInterval(store.state.refreshIntervalID);
store.dispatch("setRefreshIntervalID", 0);
}
} catch (e: any) {
console.log("Error while refreshing data", e);
}
};
const loadLogsData = async () => {
try {
resetFunctions();
await getStreamList();
// await getSavedViews();
await getFunctions();
if (isActionsEnabled.value) await getActions();
await extractFields();
if (searchObj.meta.jobId == "") {
await getQueryData();
} else {
await getJobData();
}
refreshData();
} catch (e: any) {
searchObj.loading = false;
}
};
const loadJobData = async () => {
try {
resetFunctions();
await getStreamList();
await getFunctions();
await extractFields();
await getJobData();
refreshData();
} catch (e: any) {
searchObj.loading = false;
console.log("Error while loading logs data");
}
};
const handleQueryData = async () => {
try {
searchObj.data.tempFunctionLoading = false;
searchObj.data.tempFunctionName = "";
searchObj.data.tempFunctionContent = "";
searchObj.loading = true;
await getQueryData();
} catch (e: any) {
console.log("Error while loading logs data");
}
};
const saveColumnSizes = () => {};
const handleRunQuery = async () => {
try {
searchObj.loading = true;
searchObj.meta.refreshHistogram = true;
initialQueryPayload.value = null;
searchObj.data.queryResults.aggs = null;
if (
Object.hasOwn(router.currentRoute.value.query, "type") &&
router.currentRoute.value.query.type == "search_history_re_apply"
) {
delete router.currentRoute.value.query.type;
}
const queryTimeout = setTimeout(() => {
if (searchObj.loading) {
searchObj.meta.showSearchScheduler = true;
}
}, 120000);
await getQueryData();
clearTimeout(queryTimeout);
} catch (e: any) {
console.log("Error while loading logs data");
}
};
function extractTimestamps(period: string) {
const currentTime = new Date();
let fromTimestamp, toTimestamp;
switch (period.slice(-1)) {
case "s":
fromTimestamp = currentTime.getTime() - parseInt(period) * 1000; // 1 second = 1000 milliseconds
toTimestamp = currentTime.getTime();
break;
case "m":
fromTimestamp = currentTime.getTime() - parseInt(period) * 60000; // 1 minute = 60000 milliseconds
toTimestamp = currentTime.getTime();
break;
case "h":
fromTimestamp = currentTime.getTime() - parseInt(period) * 3600000; // 1 hour = 3600000 milliseconds
toTimestamp = currentTime.getTime();
break;
case "d":
fromTimestamp = currentTime.getTime() - parseInt(period) * 86400000; // 1 day = 86400000 milliseconds
toTimestamp = currentTime.getTime();
break;
case "w":
fromTimestamp = currentTime.getTime() - parseInt(period) * 604800000; // 1 week = 604800000 milliseconds
toTimestamp = currentTime.getTime();
break;
case "M":
const currentMonth = currentTime.getMonth();
const currentYear = currentTime.getFullYear();
const prevMonth = currentMonth === 0 ? 11 : currentMonth - 1;
const prevYear = currentMonth === 0 ? currentYear - 1 : currentYear;
const fromDate = new Date(prevYear, prevMonth, 1);
fromTimestamp = fromDate.getTime();
toTimestamp = currentTime.getTime();
break;
default:
console.error("Invalid period format!");
return;
}
return { from: fromTimestamp, to: toTimestamp };
}
const restoreUrlQueryParams = async () => {
searchObj.shouldIgnoreWatcher = true;
const queryParams: any = router.currentRoute.value.query;
if (!queryParams.stream) {
searchObj.shouldIgnoreWatcher = false;
return;
}
const date = {
startTime: Number(queryParams.from),
endTime: Number(queryParams.to),
relativeTimePeriod: queryParams.period || null,
type: queryParams.period ? "relative" : "absolute",
};
if (date.type === "relative") {
queryParams.period = date.relativeTimePeriod;
} else {
queryParams.from = date.startTime;
queryParams.to = date.endTime;
}
if (date) {
searchObj.data.datetime = date;
}
if (queryParams.query) {
searchObj.meta.sqlMode = queryParams.sql_mode == "true" ? true : false;
searchObj.data.editorValue = b64DecodeUnicode(queryParams.query);
searchObj.data.query = b64DecodeUnicode(queryParams.query);
}
if (
queryParams.hasOwnProperty("defined_schemas") &&
queryParams.defined_schemas != ""
) {
searchObj.meta.useUserDefinedSchemas = queryParams.defined_schemas;
}
if (
queryParams.refresh &&
enableRefreshInterval(parseInt(queryParams.refresh))
) {
searchObj.meta.refreshInterval = parseInt(queryParams.refresh);
}
if (
queryParams.refresh &&
!enableRefreshInterval(parseInt(queryParams.refresh))
) {
delete queryParams.refresh;
}
useLocalTimezone(queryParams.timezone);
if (queryParams.functionContent) {
searchObj.data.tempFunctionContent =
b64DecodeUnicode(queryParams.functionContent) || "";
searchObj.meta.functionEditorPlaceholderFlag = false;
searchObj.data.transformType = "function";
}
if (queryParams.stream_type) {
searchObj.data.stream.streamType = queryParams.stream_type;
} else {
searchObj.data.stream.streamType = "logs";
}
if (queryParams.type) {
searchObj.meta.pageType = queryParams.type;
}
searchObj.meta.quickMode = queryParams.quick_mode == "false" ? false : true;
if (queryParams.stream) {
searchObj.data.stream.selectedStream.push(
...queryParams.stream.split(","),
);
}
if (queryParams.show_histogram) {
searchObj.meta.showHistogram =
queryParams.show_histogram == "true" ? true : false;
}
searchObj.shouldIgnoreWatcher = false;
if (
Object.hasOwn(queryParams, "type") &&
queryParams.type == "search_history_re_apply"
) {
delete queryParams.type;
}
// TODO OK : Replace push with replace and test all scenarios
router.push({
query: {
...queryParams,
sql_mode: searchObj.meta.sqlMode,
defined_schemas: searchObj.meta.useUserDefinedSchemas,
},
});
};
const enableRefreshInterval = (value: number) => {
return (
value >= (Number(store.state?.zoConfig?.min_auto_refresh_interval) || 0)
);
};
const showNotification = () => {
return $q.notify({
type: "positive",
message: "Waiting for response...",
timeout: 10000,
actions: [
{
icon: "cancel",
color: "white",
handler: () => {
/* ... */
},
},
],
});
};
const updateStreams = async () => {
if (searchObj.data.streamResults?.list?.length) {
const streamType = searchObj.data.stream.streamType || "logs";
const streams: any = await getStreams(streamType, false);
searchObj.data.streamResults["list"] = streams.list;
searchObj.data.stream.streamLists = [];
streams.list.map((item: any) => {
const itemObj = {
label: item.name,
value: item.name,
};
searchObj.data.stream.streamLists.push(itemObj);
});
} else {
searchObj.loading = true;
loadLogsData();
}
};
const ftsFields: any = ref([]);
const extractFTSFields = () => {
if (
searchObj.data.stream.selectedStreamFields != undefined &&
searchObj.data.stream.selectedStreamFields.length > 0
) {
ftsFields.value = searchObj.data.stream.selectedStreamFields
.filter((item: any) => item.ftsKey === true)
.map((item: any) => item.name);
}
// if there is no FTS fields set by user then use default FTS fields
if (ftsFields.value.length == 0) {
ftsFields.value = store.state.zoConfig.default_fts_keys;
}
};
const getSavedViews = async () => {
try {
searchObj.loadingSavedView = true;
const favoriteViews: any = [];
savedviewsService
.get(store.state.selectedOrganization.identifier)
.then((res) => {
searchObj.loadingSavedView = false;
searchObj.data.savedViews = res.data.views;
})
.catch((err) => {
searchObj.loadingSavedView = false;
console.log(err);
});
} catch (e: any) {
searchObj.loadingSavedView = false;
console.log("Error while getting saved views", e);
}
};
const onStreamChange = async (queryStr: string) => {
try {
searchObj.loadingStream = true;
// Reset query results
searchObj.data.queryResults = { hits: [] };
// Build UNION query once
const streams = searchObj.data.stream.selectedStream;
const unionquery = streams
.map((stream: string) => `SELECT [FIELD_LIST] FROM "${stream}"`)
.join(" UNION ");
const query = searchObj.meta.sqlMode ? queryStr || unionquery : "";
// Fetch all stream data in parallel
const streamDataPromises = streams.map((stream: string) =>
getStream(stream, searchObj.data.stream.streamType || "logs", true),
);
const streamDataResults = await Promise.all(streamDataPromises);
// Collect all schema fields
const allStreamFields = streamDataResults
.filter((data) => data?.schema)
.flatMap((data) => data.schema);
// Update selectedStreamFields once
searchObj.data.stream.selectedStreamFields = allStreamFields;
//check if allStreamFields is empty or not
//if empty then we are displaying no events found... message on the UI instead of throwing in an error format
if (allStreamFields.length === 0) {
// searchObj.data.errorMsg = t("search.noFieldFound");
return;
}
// Update selected fields if needed
const streamFieldNames = new Set(
allStreamFields.map((item) => item.name),
);
if (searchObj.data.stream.selectedFields.length > 0) {
searchObj.data.stream.selectedFields =
searchObj.data.stream.selectedFields.filter((fieldName) =>
streamFieldNames.has(fieldName),
);
}
// Update interesting fields list
searchObj.data.stream.interestingFieldList =
searchObj.data.stream.interestingFieldList.filter((fieldName) =>
streamFieldNames.has(fieldName),
);
// Replace field list in query
const fieldList =
searchObj.meta.quickMode &&
searchObj.data.stream.interestingFieldList.length > 0
? searchObj.data.stream.interestingFieldList.join(",")
: "*";
const finalQuery = query.replace(/\[FIELD_LIST\]/g, fieldList);
// Update query related states
searchObj.data.editorValue = finalQuery;
searchObj.data.query = finalQuery;
searchObj.data.tempFunctionContent = "";
searchObj.meta.searchApplied = false;
// Update histogram visibility
if (streams.length > 1) {
searchObj.meta.showHistogram = false;
}
if (!store.state.zoConfig.query_on_stream_selection) {
await handleQueryData();
} else {
// Reset states when query on selection is disabled
searchObj.data.sortedQueryResults = [];
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
extractFields();
}
} catch (e: any) {
console.error("Error while getting stream data:", e);
} finally {
searchObj.loadingStream = false;
}
};
function quoteTableNameDirectly(sql: string, streamName: string) {
// This regular expression looks for the FROM keyword followed by
// an optional schema name, a table name, and handles optional spaces.
// It captures the table name to be replaced with double quotes.
const regex = new RegExp(`FROM\\s+${streamName}`, "gi");
// Replace the captured table name with the same name enclosed in double quotes
const modifiedSql = sql.replace(regex, `FROM "${streamName}"`);
return modifiedSql;
}
const getRegionInfo = () => {
searchService.get_regions().then((res) => {
const clusterData = [];
let regionObj: any = {};
const apiData = res.data;
for (const region in apiData) {
regionObj = {
label: region,
children: [],
};
for (const cluster of apiData[region]) {
regionObj.children.push({ label: cluster });
}
clusterData.push(regionObj);
}
store.dispatch("setRegionInfo", clusterData);
});
};
const addTraceId = (traceId: string) => {
if (searchObj.communicationMethod !== "ws") {
if (searchObj.data.searchRequestTraceIds.includes(traceId)) {
return;
}
searchObj.data.searchRequestTraceIds.push(traceId);
} else {
if (searchObj.data.searchWebSocketTraceIds.includes(traceId)) {
return;
}
searchObj.data.searchWebSocketTraceIds.push(traceId);
}
};
const removeTraceId = (traceId: string) => {
if (searchObj.communicationMethod !== "ws") {
searchObj.data.searchRequestTraceIds =
searchObj.data.searchRequestTraceIds.filter(
(id: string) => id !== traceId,
);
} else {
searchObj.data.searchWebSocketTraceIds =
searchObj.data.searchWebSocketTraceIds.filter(
(_traceId) => _traceId !== traceId,
);
}
};
const cancelQuery = () => {
if (searchObj.communicationMethod === "ws") {
sendCancelSearchMessage([
...searchObj.data.searchWebSocketTraceIds,
]);
return;
}
const tracesIds = [...searchObj.data.searchRequestTraceIds];
if (!searchObj.data.searchRequestTraceIds.length) {
searchObj.data.isOperationCancelled = false;
return;
}
searchObj.data.isOperationCancelled = true;
searchService
.delete_running_queries(
store.state.selectedOrganization.identifier,
searchObj.data.searchRequestTraceIds,
)
.then((res) => {
const isCancelled = res.data.some((item: any) => item.is_success);
if (isCancelled) {
searchObj.data.isOperationCancelled = false;
$q.notify({
message: "Running query cancelled successfully",
color: "positive",
position: "bottom",
timeout: 4000,
});
}
})
.catch((error: any) => {
$q.notify({
message:
error.response?.data?.message || "Failed to cancel running query",
color: "negative",
position: "bottom",
timeout: 1500,
});
})
.finally(() => {
searchObj.data.searchRequestTraceIds =
searchObj.data.searchRequestTraceIds.filter(
(id: string) => !tracesIds.includes(id),
);
});
};
const reorderArrayByReference = (arr1: string[], arr2: string[]) => {
arr1.sort((a, b) => {
const indexA = arr2.indexOf(a);
const indexB = arr2.indexOf(b);
// If an element is not found in arr1, keep it at the end
if (indexA === -1) return 1;
if (indexB === -1) return -1;
return indexA - indexB;
});
};
const reorderSelectedFields = () => {
const selectedFields = [...searchObj.data.stream.selectedFields];
let colOrder =
searchObj.data.resultGrid.colOrder[searchObj.data.stream.selectedStream];
if (!selectedFields.includes(store.state.zoConfig.timestamp_column)) {
colOrder = colOrder.filter(
(v: any) => v !== store.state.zoConfig.timestamp_column,
);
}
if (JSON.stringify(selectedFields) !== JSON.stringify(colOrder)) {
reorderArrayByReference(selectedFields, colOrder);
}
return selectedFields;
};
function getFieldsWithStreamNames() {
const fieldMap: any = {};
searchObj.data.streamResults.list
.filter((stream: any) =>
searchObj.data.stream.selectedStream.includes(stream.name),
)
.forEach((stream: any) => {
stream.schema.forEach((field: any) => {
const fieldKey = field.name;
const fieldValue = `${stream.name}`;
// Add the fieldValue to the corresponding fieldKey in the map
if (!fieldMap[fieldKey]) {
fieldMap[fieldKey] = [];
}
fieldMap[fieldKey].push(fieldValue);
});
});
return fieldMap;
}
const getFilterExpressionByFieldType = (
field: string | number,
field_value: string | number | boolean,
action: string,
) => {
let operator = action == "include" ? "=" : "!=";
try {
let fieldType: string = "utf8";
const getStreamFieldTypes = (stream: any) => {
if (!stream.schema) return {};
return Object.fromEntries(
stream.schema.map((schema: any) => [schema.name, schema.type]),
);
};
const fieldTypeList = searchObj.data.streamResults.list
.filter((stream: any) =>
searchObj.data.stream.selectedStream.includes(stream.name),
)
.reduce(
(acc: any, stream: any) => ({
...acc,
...getStreamFieldTypes(stream),
}),
{},
);
if (Object.hasOwn(fieldTypeList, field)) {
fieldType = fieldTypeList[field];
}
if (
field_value === "null" ||
field_value === "" ||
field_value === null
) {
operator = action == "include" ? "is" : "is not";
field_value = "null";
}
let expression =
field_value == "null"
? `${field} ${operator} ${field_value}`
: `${field} ${operator} '${field_value}'`;
const isNumericType = (type: string) =>
["int64", "float64"].includes(type.toLowerCase());
const isBooleanType = (type: string) => type.toLowerCase() === "boolean";
if (isNumericType(fieldType)) {
expression = `${field} ${operator} ${field_value}`;
} else if (isBooleanType(fieldType)) {
operator = action == "include" ? "is" : "is not";
expression = `${field} ${operator} ${field_value}`;
}
return expression;
} catch (e: any) {
console.log("Error while getting filter expression by field type", e);
return `${field} ${operator} '${field_value}'`;
}
};
const setSelectedStreams = (value: string) => {
try {
const parsedSQL = fnParsedSQL();
if (!Object.hasOwn(parsedSQL, "from") || parsedSQL?.from.length === 0) {
console.error("Failed to parse SQL query:", value);
throw new Error("Invalid SQL syntax");
}
const newSelectedStreams: string[] = [];
// handled WITH query
if (parsedSQL?.with) {
let withObj = parsedSQL.with;
withObj.forEach((obj: any) => {
// Map through each "from" array in the _next object, as it can contain multiple tables
if (obj?.stmt?.from) {
obj?.stmt?.from.forEach((stream: { table: string }) => {
newSelectedStreams.push(stream.table);
}
);
}
});
}
// additionally, if union is there then it will have _next object which will have the table name it should check recursuvely as user can write multiple union
else if (parsedSQL?._next) {
//get the first table if it is next
//this is to handle the union queries when user selects the multi stream selection the first table will be there in from array
newSelectedStreams.push(...parsedSQL.from.map((stream: any) => {
return stream.table}));
let nextTable = parsedSQL._next;
//this will handle the union queries
while (nextTable) {
// Map through each "from" array in the _next object, as it can contain multiple tables
if (nextTable.from) {
nextTable.from.forEach((stream: { table: string }) =>
newSelectedStreams.push(stream.table),
);
}
nextTable = nextTable._next;
}
}
//for simple query get the table name from the parsedSQL object
// this will handle joins as well
else if (parsedSQL?.from) {
parsedSQL.from.map((stream: any) => {
// Check if 'expr' and 'ast' exist, then access 'from' to get the table
if (stream.expr?.ast?.from) {
stream.expr.ast.from.forEach((subStream: any) => {
if (subStream.table != undefined) {
newSelectedStreams.push(subStream.table);
}
});
}
// Otherwise, return the table name directly
if (stream.table != undefined) {
newSelectedStreams.push(stream.table);
}
});
}
if (
!arraysMatch(searchObj.data.stream.selectedStream, newSelectedStreams)
) {
searchObj.data.stream.selectedStream = newSelectedStreams;
onStreamChange(value);
}
} catch (error) {
console.error("Error in setSelectedStreams:", {
error,
query: value,
currentStreams: searchObj.data.stream.selectedStream,
});
throw error;
}
};
/**
* Extract value query
* - Extract the query from the query string
* - Replace the INDEX_NAME with the stream name
* - Return the query
*
* JOIN Queries
* - Parse the query and make where null
* - Replace the INDEX_NAME with the stream name
* - Return the query
*
* UNION Queries
* - Parse the query and add where clause to each query
* - Replace the INDEX_NAME with the stream name
* - Return the query
*
* @returns
*/
const extractValueQuery = () => {
try {
if (searchObj.meta.sqlMode == false || searchObj.data.query == "") {
return {};
}
const orgQuery: string = searchObj.data.query
.split("\n")
.filter((line: string) => !line.trim().startsWith("--"))
.join("\n");
const outputQueries: any = {};
const parsedSQL = parser.astify(orgQuery);
let query = `select * from INDEX_NAME`;
const newParsedSQL = parser.astify(query);
if (
Object.hasOwn(parsedSQL, "from") &&
parsedSQL.from.length <= 1 &&
parsedSQL._next == null
) {
newParsedSQL.where = parsedSQL.where;
query = parser.sqlify(newParsedSQL).replace(/`/g, '"');
outputQueries[parsedSQL.from[0].table] = query.replace(
"INDEX_NAME",
"[INDEX_NAME]",
);
} else {
// parse join queries & union queries
if (Object.hasOwn(parsedSQL, "from") && parsedSQL.from.length > 1) {
parsedSQL.where = cleanBinaryExpression(parsedSQL.where);
// parse join queries and make where null
searchObj.data.stream.selectedStream.forEach((stream: string) => {
newParsedSQL.where = null;
query = parser.sqlify(newParsedSQL).replace(/`/g, '"');
outputQueries[stream] = query.replace("INDEX_NAME", "[INDEX_NAME]");
});
} else if (parsedSQL._next != null) {
//parse union queries
if (Object.hasOwn(parsedSQL, "from") && parsedSQL.from) {
let query = `select * from INDEX_NAME`;
const newParsedSQL = parser.astify(query);
newParsedSQL.where = parsedSQL.where;
query = parser.sqlify(newParsedSQL).replace(/`/g, '"');
outputQueries[parsedSQL.from[0].table] = query.replace(
"INDEX_NAME",
"[INDEX_NAME]",
);
}
let nextTable = parsedSQL._next;
let depth = 0;
const MAX_DEPTH = 100;
while (nextTable && depth++ < MAX_DEPTH) {
// Map through each "from" array in the _next object, as it can contain multiple tables
if (nextTable.from) {
let query = "select * from INDEX_NAME";
const newParsedSQL = parser.astify(query);
newParsedSQL.where = nextTable.where;
query = parser.sqlify(newParsedSQL).replace(/`/g, '"');
outputQueries[nextTable.from[0].table] = query.replace(
"INDEX_NAME",
"[INDEX_NAME]",
);
}
nextTable = nextTable._next;
}
if (depth >= MAX_DEPTH) {
throw new Error("Maximum query depth exceeded");
}
}
}
return outputQueries;
} catch (error) {
console.error("Error in extractValueQuery:", error);
throw error;
}
};
const cleanBinaryExpression = (node: any): any => {
if (!node) return null;
switch (node.type) {
case "binary_expr": {
const left: any = cleanBinaryExpression(node.left);
const right: any = cleanBinaryExpression(node.right);
// Remove the operator and keep only the non-null side if the other side is a field-only reference
if (left && isFieldOnly(left) && isFieldOnly(right)) {
return null; // Ignore this condition entirely if both sides are fields
} else if (!left) {
return right; // Only the right side remains, so return it
} else if (!right) {
return left; // Only the left side remains, so return it
}
// Return the expression if both sides are valid
return {
type: "binary_expr",
operator: node.operator,
left: left,
right: right,
};
}
case "column_ref":
return {
type: "column_ref",
table: node.table || null,
column:
node.column && node.column.expr
? node.column.expr.value
: node.column,
};
case "single_quote_string":
case "number":
return {
type: node.type,
value: node.value,
};
default:
return node;
}
};
// Helper function to check if a node is a field-only reference (column_ref without literal)
function isFieldOnly(node: any): boolean {
return node && node.type === "column_ref";
}
const getQueryReq = (isPagination: boolean) => {
if (!isPagination) {
resetQueryData();
searchObj.data.queryResults = {};
}
// reset searchAggData
searchAggData.total = 0;
searchAggData.hasAggregation = false;
searchObj.meta.showDetailTab = false;
searchObj.meta.searchApplied = true;
searchObj.data.functionError = "";
if (
!searchObj.data.stream.streamLists?.length ||
searchObj.data.stream.selectedStream.length == 0
) {
searchObj.loading = false;
return;
}
if (
Number.isNaN(searchObj.data.datetime.endTime) ||
Number.isNaN(searchObj.data.datetime.startTime)
) {
setDateTime(
(router.currentRoute.value?.query?.period as string) || "15m",
);
}
const queryReq: SearchRequestPayload = buildSearch();
if (queryReq === null) {
searchObj.loading = false;
if (!notificationMsg.value) {
notificationMsg.value = "Search query is empty or invalid.";
}
return;
}
if (!queryReq) {
searchObj.loading = false;
throw new Error(
notificationMsg.value ||
"Something went wrong while creating Search Request.",
);
}
// get function definition
addTransformToQuery(queryReq);
// Add action ID if it exists
if (searchObj.data.actionId && searchObj.data.transformType === "action") {
queryReq.query["action_id"] = searchObj.data.actionId;
}
if (searchObj.data.datetime.type === "relative") {
if (!isPagination) initialQueryPayload.value = cloneDeep(queryReq);
else {
if (
searchObj.meta.refreshInterval == 0 &&
router.currentRoute.value.name == "logs" &&
searchObj.data.queryResults.hasOwnProperty("hits")
) {
const start_time: number =
initialQueryPayload.value?.query?.start_time || 0;
const end_time: number =
initialQueryPayload.value?.query?.end_time || 0;
queryReq.query.start_time = start_time;
queryReq.query.end_time = end_time;
}
}
}
// copy query request for histogram query and same for customDownload
searchObj.data.histogramQuery = JSON.parse(JSON.stringify(queryReq));
// reset errorCode
searchObj.data.errorCode = 0;
searchObj.data.histogramQuery.query.sql =
searchObj.data.histogramQuery.aggs.histogram;
searchObj.data.histogramQuery.query.sql_mode = "full";
searchObj.data.histogramQuery.query.size = -1;
delete searchObj.data.histogramQuery.query.quick_mode;
delete searchObj.data.histogramQuery.query.from;
delete searchObj.data.histogramQuery.aggs;
delete queryReq.aggs;
if(searchObj.data.histogramQuery.query.action_id) delete searchObj.data.histogramQuery.query.action_id;
searchObj.data.customDownloadQueryObj = JSON.parse(
JSON.stringify(queryReq),
);
queryReq.query.from =
(searchObj.data.resultGrid.currentPage - 1) *
searchObj.meta.resultGrid.rowsPerPage;
queryReq.query.size = searchObj.meta.resultGrid.rowsPerPage;
const parsedSQL: any = fnParsedSQL();
searchObj.meta.resultGrid.showPagination = true;
if (searchObj.meta.sqlMode == true) {
// if query has aggregation or groupby then we need to set size to -1 to get all records
// issue #5432
if (hasAggregation(parsedSQL?.columns) || parsedSQL.groupby != null) {
queryReq.query.size = -1;
}
if (isLimitQuery(parsedSQL)) {
queryReq.query.size = parsedSQL.limit.value[0].value;
searchObj.meta.resultGrid.showPagination = false;
//searchObj.meta.resultGrid.rowsPerPage = queryReq.query.size;
if (parsedSQL.limit.separator == "offset") {
queryReq.query.from = parsedSQL.limit.value[1].value || 0;
}
delete queryReq.query.track_total_hits;
}
if (isDistinctQuery(parsedSQL) || isWithQuery(parsedSQL)) {
delete queryReq.query.track_total_hits;
}
}
return queryReq;
};
const getDataThroughStream = (isPagination: boolean) => {
try {
const queryReq = getQueryReq(isPagination) as SearchRequestPayload;
if(!queryReq) return;
if(!isPagination) {
resetQueryData();
histogramResults = [];
searchObj.data.queryResults.hits = [];
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
if(searchObj.meta.refreshInterval == 0) searchObj.meta.resetPlotChart = true;
}
const payload = buildWebSocketPayload(queryReq, isPagination, "search");
if(shouldGetPageCount(queryReq, fnParsedSQL())) {
queryReq.query.size = queryReq.query.size + 1;
}
const requestId = initializeSearchConnection(payload);
if (!requestId) {
throw new Error(`Failed to initialize ${searchObj.communicationMethod} connection`);
}
addTraceId(payload.traceId);
} catch (e: any) {
console.error(`Error while getting data through ${searchObj.communicationMethod}`, e);
searchObj.loading = false;
showErrorNotification(
notificationMsg.value || "Error occurred during the search operation.",
);
notificationMsg.value = "";
}
};
const buildWebSocketPayload = (
queryReq: SearchRequestPayload,
isPagination: boolean,
type: "search" | "histogram" | "pageCount" | "values",
meta?: any,
) => {
const { traceId } = generateTraceContext();
addTraceId(traceId);
const payload: {
queryReq: SearchRequestPayload;
type: "search" | "histogram" | "pageCount" | "values";
isPagination: boolean;
traceId: string;
org_id: string;
meta?: any;
} = {
queryReq,
type,
isPagination,
traceId,
org_id: searchObj.organizationIdentifier,
meta,
};
return payload;
};
const initializeSearchConnection = (payload: any): string | Promise<void> | null=> {
// Use the appropriate method to fetch data
if (searchObj.communicationMethod === "ws") {
return fetchQueryDataWithWebSocket(payload, {
open: sendSearchMessage,
close: handleSearchClose,
error: handleSearchError,
message: handleSearchResponse,
reset: handleSearchReset,
}) as string;
} else if (searchObj.communicationMethod === "streaming") {
payload.searchType = "ui";
payload.pageType = searchObj.data.stream.streamType;
return fetchQueryDataWithHttpStream(payload, {
data: handleSearchResponse,
error: handleSearchError,
complete: handleSearchClose,
reset: handleSearchReset,
}) as Promise<void>;
}
return null;
};
const initializeStreamingConnection = (payload: any): Promise<void> => {
return fetchQueryDataWithHttpStream(payload, {
data: handleSearchResponse,
error: handleSearchError,
complete: handleSearchClose,
reset: handleSearchReset,
}) as Promise<void>;
};
const handleSearchReset = async (data: any, traceId?: string) => {
// reset query data
try {
if(data.type === "search") {
if(!data.isPagination) {
resetQueryData();
searchObj.data.queryResults = {};
}
// reset searchAggData
searchAggData.total = 0;
searchAggData.hasAggregation = false;
searchObj.meta.showDetailTab = false;
searchObj.meta.searchApplied = true;
searchObj.data.functionError = "";
searchObj.data.errorCode = 0;
if(!data.isPagination) {
// Histogram reset
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
resetHistogramError();
}
const payload = buildWebSocketPayload(data.queryReq, data.isPagination, "search");
initializeSearchConnection(payload);
addTraceId(payload.traceId);
}
if(data.type === "histogram" || data.type === "pageCount") {
searchObj.data.queryResults.aggs = [];
searchObj.data.histogram = {
xData: [],
yData: [],
chartParams: {
title: "",
unparsed_x_data: [],
timezone: "",
},
errorCode: 0,
errorMsg: "",
errorDetail: "",
};
resetHistogramError();
searchObj.loadingHistogram = true;
if(data.type === "histogram") await generateHistogramSkeleton();
if(data.type === "histogram") histogramResults = [];
const payload = buildWebSocketPayload(
data.queryReq,
false,
data.type,
);
initializeSearchConnection(payload);
addTraceId(payload.traceId);
}
} catch(e: any){
console.error("Error while resetting search", e);
}
};
const sendSearchMessage = (queryReq: any) => {
try {
if (searchObj.data.isOperationCancelled) {
closeSocketBasedOnRequestId(queryReq.traceId);
return;
}
const payload = {
type: "search",
content: {
trace_id: queryReq.traceId,
payload: {
query: queryReq.queryReq.query,
// pass encodig if enabled,
// make sure that `encoding: null` is not being passed, that's why used object extraction logic
...(store.state.zoConfig.sql_base64_enabled
? { encoding: "base64" }
: {}),
} as SearchRequestPayload,
stream_type: searchObj.data.stream.streamType,
search_type: "ui",
use_cache: (window as any).use_cache ?? true,
org_id: searchObj.organizationIdentifier,
},
};
if (
Object.hasOwn(queryReq.queryReq, "regions") &&
Object.hasOwn(queryReq.queryReq, "clusters")
) {
payload.content.payload["regions"] = queryReq.queryReq.regions;
payload.content.payload["clusters"] = queryReq.queryReq.clusters;
}
sendSearchMessageBasedOnRequestId(payload);
} catch (e: any) {
searchObj.loading = false;
showErrorNotification(
notificationMsg.value || "Error occurred while sending socket message.",
);
notificationMsg.value = "";
}
};
const setDateTime = (period: string = "15m") => {
const extractedDate: any = extractTimestamps(period);
searchObj.data.datetime.startTime = extractedDate.from;
searchObj.data.datetime.endTime = extractedDate.to;
};
// Get metadata
// Update metadata
// Update results
const handleStreamingHits = (payload: WebSocketSearchPayload, response: WebSocketSearchResponse, isPagination: boolean, appendResult: boolean = false) => {
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs"
) {
searchObj.data.queryResults.hits = response.content.results.hits;
}
if (!searchObj.meta.refreshInterval) {
// Scan-size and took time in histogram title
// For the initial request, we get histogram and logs data. So, we need to sum the scan_size and took time of both the requests.
// For the pagination request, we only get logs data. So, we need to consider scan_size and took time of only logs request.
if (appendResult) {
searchObj.data.queryResults.hits.push(
...response.content.results.hits,
);
} else {
searchObj.data.queryResults.hits = response.content.results.hits;
}
if(shouldGetPageCount(payload.queryReq, fnParsedSQL()) && (searchObj.data.queryResults.hits.length === payload.queryReq.query.size)) {
searchObj.data.queryResults.hits = searchObj.data.queryResults.hits.slice(0, payload.queryReq.query.size - 1);
}
}
processPostPaginationData();
}
const handleStreamingMetadata = (payload: WebSocketSearchPayload, response: WebSocketSearchResponse, isPagination: boolean, appendResult: boolean = false) => {
////// Handle function error ///////
handleFunctionError(payload.queryReq, response);
////// Handle aggregation ///////
handleAggregation(payload.queryReq, response);
////// Handle reset field values ///////
resetFieldValues();
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs"
) {
searchObj.data.queryResults.from = response.content.results.from;
searchObj.data.queryResults.scan_size =
response.content.results.scan_size;
searchObj.data.queryResults.took = response.content.results.took;
searchObj.data.queryResults.aggs = response.content.results.aggs;
}
if (!searchObj.meta.refreshInterval) {
// In page count we set track_total_hits
if (!payload.queryReq.query.hasOwnProperty("track_total_hits")) {
delete response.content.total;
}
// Scan-size and took time in histogram title
// For the initial request, we get histogram and logs data. So, we need to sum the scan_size and took time of both the requests.
// For the pagination request, we only get logs data. So, we need to consider scan_size and took time of only logs request.
if (appendResult) {
searchObj.data.queryResults.total += response.content.results.total;
searchObj.data.queryResults.took += response.content.results.took;
searchObj.data.queryResults.scan_size +=
response.content.results.scan_size;
} else {
if (isPagination && response.content?.streaming_aggs) {
searchObj.data.queryResults.from = response.content.results.from;
searchObj.data.queryResults.scan_size =
response.content.results.scan_size;
searchObj.data.queryResults.took = response.content.results.took;
} else if (response.content?.streaming_aggs) {
searchObj.data.queryResults = {
...response.content.results,
took: (searchObj.data?.queryResults?.took || 0) + response.content.results.took,
scan_size: (searchObj.data?.queryResults?.scan_size || 0) + response.content.results.scan_size,
hits: searchObj.data?.queryResults?.hits || [],
streaming_aggs: response.content?.streaming_aggs,
}
} else if (isPagination) {
searchObj.data.queryResults.from = response.content.results.from;
searchObj.data.queryResults.scan_size =
response.content.results.scan_size;
searchObj.data.queryResults.took = response.content.results.took;
searchObj.data.queryResults.total = response.content.results.total;
} else {
searchObj.data.queryResults = response.content.results;
}
}
if(shouldGetPageCount(payload.queryReq, fnParsedSQL()) && (response.content.results.total === payload.queryReq.query.size)) {
searchObj.data.queryResults.pageCountTotal = payload.queryReq.query.size * searchObj.data.resultGrid.currentPage;
}
}
// We are storing time_offset for the context of pagecount, to get the partial pagecount
if (searchObj.data.queryResults) {
searchObj.data.queryResults.time_offset = response.content?.time_offset;
}
// If its a pagination request, then append
if (!isPagination) {
searchObj.data.queryResults.pagination = [];
}
if (isPagination) refreshPagination(true);
}
const handleHistogramStreamingHits = (payload: WebSocketSearchPayload, response: WebSocketSearchResponse, isPagination: boolean, appendResult: boolean = false) => {
searchObj.loading = false;
if (!searchObj.data.queryResults.aggs) {
searchObj.data.queryResults.aggs = [];
}
if (
searchObj.data.queryResults.aggs.length == 0 &&
response.content.results.hits.length > 0
) {
let date = new Date();
const startDateTime =
searchObj.data.customDownloadQueryObj.query.start_time / 1000;
const endDateTime =
searchObj.data.customDownloadQueryObj.query.end_time / 1000;
const nowString = response.content.results.hits[0].zo_sql_key;
const now = new Date(nowString);
const day = String(now.getDate()).padStart(2, "0");
const month = String(now.getMonth() + 1).padStart(2, "0");
const year = now.getFullYear();
const dateToBePassed = `${day}-${month}-${year}`;
const hours = String(now.getHours()).padStart(2, "0");
let minutes = String(now.getMinutes()).padStart(2, "0");
if (searchObj.data.histogramInterval / 1000 <= 9999) {
minutes = String(now.getMinutes() + 1).padStart(2, "0");
}
const time = `${hours}:${minutes}`;
const currentTimeToBePassed = convertDateToTimestamp(
dateToBePassed,
time,
"UTC",
);
for (
let currentTime: any = currentTimeToBePassed.timestamp / 1000;
currentTime < endDateTime;
currentTime += searchObj.data.histogramInterval / 1000
) {
date = new Date(currentTime);
histogramResults.push({
zo_sql_key: date.toISOString().slice(0, 19),
zo_sql_num: 0,
});
}
for (
let currentTime: any = currentTimeToBePassed.timestamp / 1000;
currentTime > startDateTime;
currentTime -= searchObj.data.histogramInterval / 1000
) {
date = new Date(currentTime);
histogramResults.push({
zo_sql_key: date.toISOString().slice(0, 19),
zo_sql_num: 0,
});
}
}
searchObj.data.queryResults.aggs.push(...response.content.results.hits);
(async () => {
try {
generateHistogramData();
if (!payload.meta?.isHistogramOnly) refreshPagination(true);
} catch (error) {
console.error("Error processing histogram data:", error);
searchObj.loadingHistogram = false;
}
})();
// queryReq.query.start_time =
// searchObj.data.queryResults.partitionDetail.paginations[
// searchObj.data.resultGrid.currentPage - 1
// ][0].startTime;
// queryReq.query.end_time =
// searchObj.data.queryResults.partitionDetail.paginations[
// searchObj.data.resultGrid.currentPage - 1
// ][0].endTime;
// if (hasAggregationFlag) {
// searchObj.data.queryResults.total = res.data.total;
// }
if (!payload.meta?.isHistogramOnly)
searchObj.data.histogram.chartParams.title = getHistogramTitle();
searchObjDebug["histogramProcessingEndTime"] = performance.now();
searchObjDebug["histogramEndTime"] = performance.now();
}
const handleHistogramStreamingMetadata = (payload: WebSocketSearchPayload, response: WebSocketSearchResponse, isPagination: boolean, appendResult: boolean = false) => {
searchObjDebug["histogramProcessingStartTime"] = performance.now();
searchObj.data.queryResults.scan_size += response.content.results.scan_size;
searchObj.data.queryResults.took += response.content.results.took;
searchObj.data.queryResults.result_cache_ratio +=
response.content.results.result_cache_ratio;
}
const handlePageCountStreamingHits = (payload: WebSocketSearchPayload, response: WebSocketSearchResponse, isPagination: boolean, appendResult: boolean = false) => {
let regeratePaginationFlag = false;
if (
response.content.results.hits.length !=
searchObj.meta.resultGrid.rowsPerPage
) {
regeratePaginationFlag = true;
}
if(response.content?.streaming_aggs || searchObj.data.queryResults.streaming_aggs) {
searchObj.data.queryResults.aggs = response.content.results.hits;
} else {
searchObj.data.queryResults.aggs.push(...response.content.results.hits);
}
// if total records in partition is greater than recordsPerPage then we need to update pagination
// setting up forceFlag to true to update pagination as we have check for pagination already created more than currentPage + 3 pages.
refreshPagination(regeratePaginationFlag);
searchObj.data.histogram.chartParams.title = getHistogramTitle();
}
const handlePageCountStreamingMetadata = (payload: WebSocketSearchPayload, response: WebSocketSearchResponse, isPagination: boolean, appendResult: boolean = false) => {
removeTraceId(response.content.traceId);
if (searchObj.data.queryResults.aggs == null) {
searchObj.data.queryResults.aggs = [];
}
searchObj.data.queryResults.streaming_aggs = response?.content?.streaming_aggs;
searchObj.data.queryResults.scan_size += response.content.results.scan_size;
searchObj.data.queryResults.took += response.content.results.took;
}
// Limit, aggregation, vrl function, pagination, function error and query error
const handleSearchResponse = (
payload: WebSocketSearchPayload,
response: WebSocketSearchResponse,
) => {
if(payload.type === "search" && response?.type === "search_response_hits") {
handleStreamingHits(payload, response, payload.isPagination, !(response.content?.streaming_aggs || searchObj.data.queryResults.streaming_aggs) && searchPartitionMap[payload.traceId] > 1);
return;
}
if(payload.type === "search" && response?.type === "search_response_metadata") {
searchPartitionMap[payload.traceId] = searchPartitionMap[payload.traceId]
? searchPartitionMap[payload.traceId]
: 0;
searchPartitionMap[payload.traceId]++;
handleStreamingMetadata(payload, response, payload.isPagination, !response.content?.streaming_aggs && searchPartitionMap[payload.traceId] > 1);
return;
}
if(payload.type === "histogram" && response?.type === "search_response_hits") {
handleHistogramStreamingHits(payload, response, payload.isPagination);
return;
}
if(payload.type === "histogram" && response?.type === "search_response_metadata") {
handleHistogramStreamingMetadata(payload, response, payload.isPagination);
return;
}
if(payload.type === "pageCount" && response?.type === "search_response_hits") {
handlePageCountStreamingHits(payload, response, payload.isPagination);
return;
}
if(payload.type === "pageCount" && response?.type === "search_response_metadata") {
handlePageCountStreamingMetadata(payload, response, payload.isPagination);
return;
}
if (response.type === "search_response") {
searchPartitionMap[payload.traceId] = searchPartitionMap[payload.traceId]
? searchPartitionMap[payload.traceId]
: 0;
searchPartitionMap[payload.traceId]++;
if (payload.type === "search") {
handleLogsResponse(
payload.queryReq,
payload.isPagination,
payload.traceId,
response,
!response.content?.streaming_aggs &&
searchPartitionMap[payload.traceId] > 1, // In aggregation query, we need to replace the results instead of appending
);
}
if (payload.type === "histogram") {
handleHistogramResponse(
payload.queryReq,
payload.traceId,
response,
payload.meta,
);
}
if (payload.type === "pageCount") {
handlePageCountResponse(payload.queryReq, payload.traceId, response);
}
}
// if (response.type === "error") {
// handleSearchError(payload.traceId, response);
// }
if (response.type === "cancel_response") {
searchObj.loading = false;
searchObj.loadingHistogram = false;
searchObj.data.isOperationCancelled = false;
showCancelSearchNotification();
setCancelSearchError();
}
};
const handleFunctionError = (queryReq: SearchRequestPayload, response: any) => {
if (
response.content.results.hasOwnProperty("function_error") &&
response.content.results.function_error != ""
) {
searchObj.data.functionError = response.content.results.function_error;
}
if (
response.content.results.hasOwnProperty("function_error") &&
response.content.results.function_error != "" &&
response.content.results.hasOwnProperty("new_start_time") &&
response.content.results.hasOwnProperty("new_end_time")
) {
response.content.results.function_error = getFunctionErrorMessage(
response.content.results.function_error,
response.content.results.new_start_time,
response.content.results.new_end_time,
store.state.timezone,
);
searchObj.data.datetime.startTime =
response.content.results.new_start_time;
searchObj.data.datetime.endTime = response.content.results.new_end_time;
searchObj.data.datetime.type = "absolute";
queryReq.query.start_time = response.content.results.new_start_time;
queryReq.query.end_time = response.content.results.new_end_time;
searchObj.data.histogramQuery.query.start_time =
response.content.results.new_start_time;
searchObj.data.histogramQuery.query.end_time =
response.content.results.new_end_time;
updateUrlQueryParams();
}
}
const handleAggregation = (queryReq: SearchRequestPayload, response: any) => {
const parsedSQL = fnParsedSQL();
if (searchObj.meta.sqlMode) {
if (hasAggregation(parsedSQL?.columns) || parsedSQL.groupby != null) {
searchAggData.hasAggregation = true;
searchObj.meta.resultGrid.showPagination = false;
if (response.content?.streaming_aggs) {
searchAggData.total = response.content?.results?.total;
} else {
searchAggData.total =
searchAggData.total + response.content?.results?.total;
}
}
}
}
const updateResult = async (queryReq: SearchRequestPayload, response: any, isPagination: boolean, appendResult: boolean = false) => {
if (
searchObj.meta.refreshInterval > 0 &&
router.currentRoute.value.name == "logs"
) {
searchObj.data.queryResults.from = response.content.results.from;
searchObj.data.queryResults.scan_size =
response.content.results.scan_size;
searchObj.data.queryResults.took = response.content.results.took;
searchObj.data.queryResults.aggs = response.content.results.aggs;
searchObj.data.queryResults.hits = response.content.results.hits;
}
if (!searchObj.meta.refreshInterval) {
// In page count we set track_total_hits
if (!queryReq.query.hasOwnProperty("track_total_hits")) {
delete response.content.total;
}
// Scan-size and took time in histogram title
// For the initial request, we get histogram and logs data. So, we need to sum the scan_size and took time of both the requests.
// For the pagination request, we only get logs data. So, we need to consider scan_size and took time of only logs request.
if (appendResult) {
await chunkedAppend(searchObj.data.queryResults.hits, response.content.results.hits);
searchObj.data.queryResults.total += response.content.results.total;
searchObj.data.queryResults.took += response.content.results.took;
searchObj.data.queryResults.scan_size +=
response.content.results.scan_size;
} else {
if (response.content?.streaming_aggs) {
searchObj.data.queryResults = {
...response.content.results,
took: (searchObj.data?.queryResults?.took || 0) + response.content.results.took,
scan_size: (searchObj.data?.queryResults?.scan_size || 0) + response.content.results.scan_size,
}
} else if (isPagination) {
searchObj.data.queryResults.hits = response.content.results.hits;
searchObj.data.queryResults.from = response.content.results.from;
searchObj.data.queryResults.scan_size =
response.content.results.scan_size;
searchObj.data.queryResults.took = response.content.results.took;
searchObj.data.queryResults.total = response.content.results.total;
} else {
searchObj.data.queryResults = response.content.results;
}
}
}
// We are storing time_offset for the context of pagecount, to get the partial pagecount
if (searchObj.data.queryResults) {
searchObj.data.queryResults.time_offset = response.content?.time_offset;
}
}
const handleLogsResponse = async (
queryReq: SearchRequestPayload,
isPagination: boolean,
traceId: string,
response: any,
appendResult: boolean = false,
) => {
try {
////// Handle function error ///////
handleFunctionError(queryReq, response);
////// Handle aggregation ///////
handleAggregation(queryReq, response);
////// Handle reset field values ///////
resetFieldValues();
////// Update results ///////
updateResult(queryReq, response, isPagination, appendResult);
// If its a pagination request, then append
if (!isPagination) {
searchObj.data.queryResults.pagination = [];
}
if (isPagination) refreshPagination(true);
processPostPaginationData();
searchObjDebug["paginatedDataReceivedEndTime"] = performance.now();
} catch (e: any) {
searchObj.loading = false;
console.log(e);
showErrorNotification(
notificationMsg.value || "Error occurred while handling logs response.",
);
notificationMsg.value = "";
}
};
const chunkedAppend = async (target: any, source: any, chunkSize = 5000) => {
for (let i = 0; i < source.length; i += chunkSize) {
target.push.apply(target, source.slice(i, i + chunkSize));
await new Promise(resolve => setTimeout(resolve, 0)); // Let UI update
}
};
const handlePageCountResponse = (
queryReq: SearchRequestPayload,
traceId: string,
response: any,
) => {
removeTraceId(traceId);
if (searchObj.data.queryResults.aggs == null) {
searchObj.data.queryResults.aggs = [];
}
let regeratePaginationFlag = false;
if (
response.content.results.hits.length !=
searchObj.meta.resultGrid.rowsPerPage
) {
regeratePaginationFlag = true;
}
searchObj.data.queryResults.aggs.push(...response.content.results.hits);
searchObj.data.queryResults.scan_size += response.content.results.scan_size;
searchObj.data.queryResults.took += response.content.results.took;
// if total records in partition is greater than recordsPerPage then we need to update pagination
// setting up forceFlag to true to update pagination as we have check for pagination already created more than currentPage + 3 pages.
refreshPagination(regeratePaginationFlag);
searchObj.data.histogram.chartParams.title = getHistogramTitle();
};
const handleHistogramResponse = (
queryReq: SearchRequestPayload,
traceId: string,
response: any,
meta?: any,
) => {
searchObjDebug["histogramProcessingStartTime"] = performance.now();
searchObj.loading = false;
if (searchObj.data.queryResults.aggs == null) {
searchObj.data.queryResults.aggs = [];
}
if (
searchObj.data.queryResults.aggs.length == 0 &&
response.content.results.hits.length > 0
) {
let date = new Date();
const startDateTime =
searchObj.data.customDownloadQueryObj.query.start_time / 1000;
const endDateTime =
searchObj.data.customDownloadQueryObj.query.end_time / 1000;
const nowString = response.content.results.hits[0].zo_sql_key;
const now = new Date(nowString);
const day = String(now.getDate()).padStart(2, "0");
const month = String(now.getMonth() + 1).padStart(2, "0");
const year = now.getFullYear();
const dateToBePassed = `${day}-${month}-${year}`;
const hours = String(now.getHours()).padStart(2, "0");
let minutes = String(now.getMinutes()).padStart(2, "0");
if (searchObj.data.histogramInterval / 1000 <= 9999) {
minutes = String(now.getMinutes() + 1).padStart(2, "0");
}
const time = `${hours}:${minutes}`;
const currentTimeToBePassed = convertDateToTimestamp(
dateToBePassed,
time,
"UTC",
);
for (
let currentTime: any = currentTimeToBePassed.timestamp / 1000;
currentTime < endDateTime;
currentTime += searchObj.data.histogramInterval / 1000
) {
date = new Date(currentTime);
histogramResults.push({
zo_sql_key: date.toISOString().slice(0, 19),
zo_sql_num: 0,
});
}
for (
let currentTime: any = currentTimeToBePassed.timestamp / 1000;
currentTime > startDateTime;
currentTime -= searchObj.data.histogramInterval / 1000
) {
date = new Date(currentTime);
histogramResults.push({
zo_sql_key: date.toISOString().slice(0, 19),
zo_sql_num: 0,
});
}
}
searchObj.data.queryResults.aggs.push(...response.content.results.hits);
searchObj.data.queryResults.scan_size += response.content.results.scan_size;
searchObj.data.queryResults.took += response.content.results.took;
searchObj.data.queryResults.result_cache_ratio +=
response.content.results.result_cache_ratio;
(async () => {
try {
generateHistogramData();
if (!meta?.isHistogramOnly) refreshPagination(true);
} catch (error) {
console.error("Error processing histogram data:", error);
searchObj.loadingHistogram = false;
}
})();
// queryReq.query.start_time =
// searchObj.data.queryResults.partitionDetail.paginations[
// searchObj.data.resultGrid.currentPage - 1
// ][0].startTime;
// queryReq.query.end_time =
// searchObj.data.queryResults.partitionDetail.paginations[
// searchObj.data.resultGrid.currentPage - 1
// ][0].endTime;
// if (hasAggregationFlag) {
// searchObj.data.queryResults.total = res.data.total;
// }
if (!meta?.isHistogramOnly)
searchObj.data.histogram.chartParams.title = getHistogramTitle();
searchObjDebug["histogramProcessingEndTime"] = performance.now();
searchObjDebug["histogramEndTime"] = performance.now();
};
const resetHistogramError = () => {
searchObj.data.histogram.errorMsg = "";
searchObj.data.histogram.errorCode = 0;
searchObj.data.histogram.errorDetail = "";
};
const shouldShowHistogram = (parsedSQL: any) => {
return (isHistogramDataMissing(searchObj) &&
isHistogramEnabled(searchObj) &&
isSingleStreamSelected(searchObj) &&
(!searchObj.meta.sqlMode ||
isNonAggregatedSQLMode(searchObj, parsedSQL))) ||
(isHistogramEnabled(searchObj) &&
isSingleStreamSelected(searchObj) &&
!searchObj.meta.sqlMode);
}
const processHistogramRequest = async (queryReq: SearchRequestPayload) => {
const parsedSQL: any = fnParsedSQL();
if (searchObj.data.stream.selectedStream.length > 1) {
const errMsg = "Histogram is not available for multi stream search.";
resetHistogramWithError(errMsg, 0);
}
if (!searchObj.data.queryResults?.hits?.length) {
return;
}
const isFromZero = queryReq.query.from == 0 &&
searchObj.data.queryResults.hits?.length > 0
const _shouldShowHistogram = shouldShowHistogram(parsedSQL);
searchObj.data.queryResults.aggs = [];
if (_shouldShowHistogram) {
searchObj.meta.refreshHistogram = false;
if (searchObj.data.queryResults.hits?.length > 0) {
searchObjDebug["histogramStartTime"] = performance.now();
resetHistogramError();
searchObj.loadingHistogram = true;
await generateHistogramSkeleton();
histogramResults = [];
const payload = buildWebSocketPayload(
searchObj.data.histogramQuery,
false,
"histogram",
);
payload.meta = {
isHistogramOnly: searchObj.meta.histogramDirtyFlag,
}
const requestId = initializeSearchConnection(payload);
addTraceId(payload.traceId);
}
} else if (searchObj.meta.sqlMode && isLimitQuery(parsedSQL)) {
resetHistogramWithError("Histogram unavailable for CTEs, DISTINCT and LIMIT queries.", -1);
searchObj.meta.histogramDirtyFlag = false;
} else if (searchObj.meta.sqlMode && (isDistinctQuery(parsedSQL) || isWithQuery(parsedSQL))) {
if (shouldGetPageCount(queryReq, parsedSQL) && isFromZero) {
setTimeout(async () => {
searchObjDebug["pagecountStartTime"] = performance.now();
getPageCountThroughSocket(queryReq);
searchObjDebug["pagecountEndTime"] = performance.now();
}, 0);
}
if(isWithQuery(parsedSQL)){
resetHistogramWithError(
"Histogram unavailable for CTEs, DISTINCT and LIMIT queries.",
-1
);
searchObj.meta.histogramDirtyFlag = false;
}
else{
resetHistogramWithError(
"Histogram unavailable for CTEs, DISTINCT and LIMIT queries.",
-1
);
}
searchObj.meta.histogramDirtyFlag = false;
} else {
if(shouldGetPageCount(queryReq, parsedSQL) && isFromZero) {
setTimeout(async () => {
searchObjDebug["pagecountStartTime"] = performance.now();
getPageCountThroughSocket(queryReq);
searchObjDebug["pagecountEndTime"] = performance.now();
}, 0);
}
}
};
function isHistogramEnabled(searchObj: any) {
return (
searchObj.meta.refreshHistogram &&
!searchObj.loadingHistogram &&
searchObj.meta.showHistogram
);
}
function isSingleStreamSelected(searchObj: any) {
return searchObj.data.stream.selectedStream.length <= 1;
}
function isNonAggregatedSQLMode(searchObj: any, parsedSQL: any) {
return !(
searchObj.meta.sqlMode &&
(isLimitQuery(parsedSQL) || isDistinctQuery(parsedSQL) || isWithQuery(parsedSQL))
);
}
function isHistogramDataMissing(searchObj: any) {
return searchObj.data.queryResults.aggs === undefined || searchObj.data.queryResults.aggs.length === 0;
}
const handleSearchClose = (payload: any, response: any) => {
if (payload.traceId) removeTraceId(payload.traceId);
// Any case where below logic may end in recursion
if (payload.traceId) delete searchPartitionMap[payload.traceId];
if (searchObj.data.isOperationCancelled) {
searchObj.loading = false;
searchObj.loadingHistogram = false;
searchObj.data.isOperationCancelled = false;
showCancelSearchNotification();
setCancelSearchError();
return;
}
//TODO Omkar: Remove the duplicate error codes, are present same in useSearchWebSocket.ts
const errorCodes = [1001, 1006, 1010, 1011, 1012, 1013];
if (errorCodes.includes(response.code)) {
handleSearchError(payload, {
content: {
message:
"WebSocket connection terminated unexpectedly. Please check your network and try again",
trace_id: payload.traceId,
code: response.code,
error_detail: "",
},
type: "error",
});
}
// if (searchObj.data.searchRetriesCount[payload.traceId]) {
// delete searchObj.data.searchRetriesCount[payload.traceId];
// }
if (payload.traceId) removeTraceId(payload.traceId);
if(!searchObj.data.queryResults.hasOwnProperty('hits')){
searchObj.data.queryResults.hits = [];
}
if (
payload.type === "search" &&
!payload.isPagination &&
!searchObj.data.isOperationCancelled
) {
processHistogramRequest(payload.queryReq);
}
if (payload.type === "search" && !response?.content?.should_client_retry) searchObj.loading = false;
if ((payload.type === "histogram" || payload.type === "pageCount") && !response?.content?.should_client_retry){
searchObj.loadingHistogram = false;
}
searchObj.data.isOperationCancelled = false;
};
const handleSearchError = (request: any, err: WebSocketErrorResponse) => {
searchObj.loading = false;
searchObj.loadingHistogram = false;
const { message, trace_id, code, error_detail, error } = err.content;
// 20009 is the code for query cancelled
if (code === 20009) {
showCancelSearchNotification();
setCancelSearchError();
}
if (trace_id) removeTraceId(trace_id);
let errorMsg = constructErrorMessage({
message,
code,
trace_id,
defaultMessage: "Error while processing request",
});
if(error === "rate_limit_exceeded") {
errorMsg = message;
}
searchObj.data.errorDetail = error_detail || "";
searchObj.data.errorMsg = errorMsg;
notificationMsg.value = errorMsg;
};
const constructErrorMessage = ({
message,
code,
trace_id,
defaultMessage,
}: {
message?: string;
code?: number;
trace_id?: string;
defaultMessage: string;
}): string => {
let errorMsg = message || defaultMessage;
const customMessage = logsErrorMessage(code || 0);
if (customMessage) {
errorMsg = t(customMessage);
}
if (trace_id) {
errorMsg += ` <br><span class='text-subtitle1'>TraceID: ${trace_id}</span>`;
}
return errorMsg;
};
const getAggsTotal = () => {
return (searchObj.data.queryResults.aggs || []).reduce(
(acc: number, item: { zo_sql_num: number; }) => acc + item.zo_sql_num,
0,
);
};
const refreshPagination = (regenrateFlag: boolean = false) => {
try {
const { rowsPerPage } = searchObj.meta.resultGrid;
const { currentPage } = searchObj.data.resultGrid;
if (searchObj.meta.jobId != "")
searchObj.meta.resultGrid.rowsPerPage = 100;
let total = 0;
let totalPages = 0;
total = getAggsTotal();
if((searchObj.data.queryResults.pageCountTotal || -1) > total) {
total = searchObj.data.queryResults.pageCountTotal;
}
searchObj.data.queryResults.total = total;
searchObj.data.queryResults.pagination = [];
totalPages = Math.ceil(total / rowsPerPage);
for (let i = 0; i < totalPages; i++) {
if (i + 1 > currentPage + 10) {
break;
}
searchObj.data.queryResults.pagination.push({
from: i * rowsPerPage + 1,
size: rowsPerPage,
});
}
} catch (e: any) {
console.log("Error while refreshing partition pagination", e);
notificationMsg.value = "Error while refreshing partition pagination.";
return false;
}
};
const refreshJobPagination = (regenrateFlag: boolean = false) => {
try {
const { rowsPerPage } = searchObj.meta.resultGrid;
const { currentPage } = searchObj.data.resultGrid;
if (searchObj.meta.jobId != "") {
// searchObj.meta.resultGrid.rowsPerPage = 100;
}
let totalPages = 0;
searchObj.data.queryResults.pagination = [];
totalPages = Math.ceil(searchObj.data.queryResults.total / rowsPerPage);
for (let i = 0; i < totalPages; i++) {
if (i + 1 > currentPage + 10) {
break;
}
searchObj.data.queryResults.pagination.push({
from: i * rowsPerPage + 1,
size: rowsPerPage,
});
}
// console.log("searchObj.data.queryResults.pagination", searchObj.data.queryResults.pagination);
} catch (e: any) {
console.log("Error while refreshing pagination", e);
notificationMsg.value = "Error while refreshing pagination.";
return false;
}
};
const shouldGetPageCount = (queryReq: any, parsedSQL: any) => {
if(shouldShowHistogram(parsedSQL) || (searchObj.meta.sqlMode && isLimitQuery(parsedSQL))) {
return false;
}
let aggFlag = false;
if (parsedSQL) {
aggFlag = hasAggregation(parsedSQL?.columns);
}
if (!aggFlag) {
return true;
}
return false;
}
const getPageCountThroughSocket = async (queryReq: any) => {
if (
searchObj.data.queryResults.total >
queryReq.query.from + queryReq.query.size
) {
return;
}
searchObj.data.countErrorMsg = "";
queryReq.query.size = 0;
delete queryReq.query.from;
delete queryReq.query.quick_mode;
if(delete queryReq.query.action_id) delete queryReq.query.action_id;
queryReq.query["sql_mode"] = "full";
queryReq.query.track_total_hits = true;
if (
searchObj.data?.queryResults?.time_offset?.start_time &&
searchObj.data?.queryResults?.time_offset?.end_time
) {
queryReq.query.start_time =
searchObj.data.queryResults.time_offset.start_time;
queryReq.query.end_time =
searchObj.data.queryResults.time_offset.end_time;
}
const payload = buildWebSocketPayload(queryReq, false, "pageCount");
searchObj.loadingHistogram = true;
const requestId = initializeSearchConnection(payload);
addTraceId(payload.traceId);
};
const sendCancelSearchMessage = (searchRequests: any[]) => {
try {
if (!searchRequests.length) {
searchObj.data.isOperationCancelled = false;
return;
}
searchObj.data.isOperationCancelled = true;
// loop on all requestIds
searchRequests.forEach((traceId) => {
cancelSearchQueryBasedOnRequestId({
trace_id: traceId,
org_id: store?.state?.selectedOrganization?.identifier,
});
});
} catch (error: any) {
console.error("Failed to cancel WebSocket searches:", error);
showErrorNotification("Failed to cancel search operations");
}
};
const showCancelSearchNotification = () => {
$q.notify({
message: "Running query cancelled successfully",
color: "positive",
position: "bottom",
timeout: 4000,
});
};
const setCancelSearchError = () => {
if (!searchObj.data?.queryResults.hasOwnProperty("hits")) {
searchObj.data.queryResults.hits = [];
}
if (!searchObj.data?.queryResults?.hits?.length) {
searchObj.data.errorMsg = "";
searchObj.data.errorCode = 0;
}
if (
searchObj.data?.queryResults?.hasOwnProperty("hits") &&
searchObj.data.queryResults?.hits?.length &&
!searchObj.data.queryResults?.aggs?.length
) {
searchObj.data.histogram.errorMsg =
"Histogram search query was cancelled";
}
};
const handlePageCountError = (err: any) => {
searchObj.loading = false;
let trace_id = "";
searchObj.data.countErrorMsg = "Error while retrieving total events: ";
if (err.response != undefined) {
if (err.response.data.hasOwnProperty("trace_id")) {
trace_id = err.response.data?.trace_id;
}
} else {
if (err.hasOwnProperty("trace_id")) {
trace_id = err?.trace_id;
}
}
const customMessage = logsErrorMessage(err?.response?.data.code);
searchObj.data.errorCode = err?.response?.data.code;
notificationMsg.value = searchObj.data.countErrorMsg;
if (err?.request?.status >= 429) {
notificationMsg.value = err?.response?.data?.message;
searchObj.data.countErrorMsg += err?.response?.data?.message;
}
if (trace_id) {
searchObj.data.countErrorMsg += " TraceID:" + trace_id;
notificationMsg.value += " TraceID:" + trace_id;
trace_id = "";
}
};
const isActionsEnabled = computed(() => {
return (config.isEnterprise == "true" || config.isCloud == "true") && store.state.zoConfig.actions_enabled;
});
return {
searchObj,
searchAggData,
getStreams,
resetSearchObj,
resetStreamData,
updatedLocalLogFilterField,
getFunctions,
getStreamList,
fieldValues,
extractFields,
getQueryData,
getJobData,
searchAroundData,
updateGridColumns,
refreshData,
updateUrlQueryParams,
loadLogsData,
restoreUrlQueryParams,
handleQueryData,
updateStreams,
handleRunQuery,
generateHistogramData,
extractFTSFields,
getSavedViews,
onStreamChange,
generateURLQuery,
buildSearch,
loadStreamLists,
refreshPartitionPagination,
filterHitsColumns,
getHistogramQueryData,
generateHistogramSkeleton,
fnParsedSQL,
fnUnparsedSQL,
getRegionInfo,
validateFilterForMultiStream,
cancelQuery,
reorderSelectedFields,
resetHistogramWithError,
isLimitQuery,
extractTimestamps,
getFilterExpressionByFieldType,
setSelectedStreams,
extractValueQuery,
initialQueryPayload,
refreshPagination,
loadJobData,
refreshJobPagination,
enableRefreshInterval,
buildWebSocketPayload,
initializeSearchConnection,
addTraceId,
routeToSearchSchedule,
isActionsEnabled,
sendCancelSearchMessage,
isDistinctQuery,
isWithQuery,
getStream
};
};
export default useLogs;