This is an automated email from the ASF dual-hosted git repository.
ankitsultana pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new 45389974f6f [timeseries] Adding timeseries language endpoint and UI
integration (#16424)
45389974f6f is described below
commit 45389974f6fad5a5f35b074d06063673112e8dc2
Author: Shaurya Chaturvedi <[email protected]>
AuthorDate: Tue Jul 29 12:09:06 2025 -0700
[timeseries] Adding timeseries language endpoint and UI integration (#16424)
Co-authored-by: Shaurya Chaturvedi <[email protected]>
---
.../apache/pinot/controller/ControllerConf.java | 18 +++++++
.../PinotControllerTimeseriesResource.java | 58 ++++++++++++++++++++++
.../app/components/Query/TimeseriesQueryPage.tsx | 50 +++++++++++++++----
.../src/main/resources/app/requests/index.ts | 3 ++
4 files changed, 120 insertions(+), 9 deletions(-)
diff --git
a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
index e8ffa8ebc8c..67a48e679f0 100644
---
a/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
+++
b/pinot-controller/src/main/java/org/apache/pinot/controller/ControllerConf.java
@@ -19,6 +19,7 @@
package org.apache.pinot.controller;
import com.google.common.base.Preconditions;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
@@ -27,6 +28,7 @@ import java.util.Optional;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
import org.apache.commons.configuration2.Configuration;
import org.apache.commons.lang3.StringUtils;
import org.apache.helix.controller.rebalancer.strategy.AutoRebalanceStrategy;
@@ -37,6 +39,7 @@ import org.apache.pinot.spi.filesystem.LocalPinotFS;
import org.apache.pinot.spi.utils.CommonConstants;
import org.apache.pinot.spi.utils.Enablement;
import org.apache.pinot.spi.utils.TimeUtils;
+import org.apache.pinot.tsdb.spi.PinotTimeSeriesConfiguration;
import static
org.apache.pinot.spi.utils.CommonConstants.Controller.CONFIG_OF_CONTROLLER_METRICS_PREFIX;
import static
org.apache.pinot.spi.utils.CommonConstants.Controller.CONFIG_OF_INSTANCE_ID;
@@ -1333,4 +1336,19 @@ public class ControllerConf extends PinotConfiguration {
public int getMaxForceCommitZkJobs() {
return getProperty(CONFIG_OF_MAX_FORCE_COMMIT_JOBS_IN_ZK,
ControllerJob.DEFAULT_MAXIMUM_CONTROLLER_JOBS_IN_ZK);
}
+
+ /**
+ * Get the configured timeseries languages from controller configuration.
+ * @return List of enabled timeseries languages
+ */
+ public List<String> getTimeseriesLanguages() {
+ String languagesConfig =
getProperty(PinotTimeSeriesConfiguration.getEnabledLanguagesConfigKey());
+ if (languagesConfig == null) {
+ return new ArrayList<>();
+ }
+ return Arrays.stream(languagesConfig.split(","))
+ .map(String::trim)
+ .filter(lang -> !lang.isEmpty())
+ .collect(Collectors.toList());
+ }
}
diff --git
a/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotControllerTimeseriesResource.java
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotControllerTimeseriesResource.java
new file mode 100644
index 00000000000..74c159c1733
--- /dev/null
+++
b/pinot-controller/src/main/java/org/apache/pinot/controller/api/resources/PinotControllerTimeseriesResource.java
@@ -0,0 +1,58 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.pinot.controller.api.resources;
+
+import io.swagger.annotations.ApiOperation;
+import java.util.List;
+import javax.inject.Inject;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.pinot.controller.ControllerConf;
+import
org.apache.pinot.controller.api.exception.ControllerApplicationException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Path("/timeseries")
+public class PinotControllerTimeseriesResource {
+ public static final Logger LOGGER =
LoggerFactory.getLogger(PinotControllerTimeseriesResource.class);
+
+ @Inject
+ ControllerConf _controllerConf;
+
+ @GET
+ @Produces(MediaType.APPLICATION_JSON)
+ @Path("languages")
+ @ApiOperation(value = "Get timeseries languages from controller
configuration",
+ notes = "Get timeseries languages from controller configuration")
+ public List<String> getBrokerTimeSeriesLanguages(@Context HttpHeaders
headers) {
+ try {
+ return _controllerConf.getTimeseriesLanguages();
+ } catch (Exception e) {
+ LOGGER.error("Error fetching timeseries languages from controller
configuration", e);
+ throw new ControllerApplicationException(LOGGER,
+ "Error fetching timeseries languages from controller configuration:
" + e.getMessage(),
+ Response.Status.INTERNAL_SERVER_ERROR);
+ }
+ }
+}
diff --git
a/pinot-controller/src/main/resources/app/components/Query/TimeseriesQueryPage.tsx
b/pinot-controller/src/main/resources/app/components/Query/TimeseriesQueryPage.tsx
index 964945813be..7e63d556883 100644
---
a/pinot-controller/src/main/resources/app/components/Query/TimeseriesQueryPage.tsx
+++
b/pinot-controller/src/main/resources/app/components/Query/TimeseriesQueryPage.tsx
@@ -38,7 +38,7 @@ import { UnControlled as CodeMirror } from
'react-codemirror2';
import 'codemirror/lib/codemirror.css';
import 'codemirror/theme/material.css';
import 'codemirror/mode/javascript/javascript';
-import { getTimeSeriesQueryResult } from '../../requests';
+import { getTimeSeriesQueryResult, getTimeSeriesLanguages } from
'../../requests';
import { useHistory, useLocation } from 'react-router';
import TableToolbar from '../TableToolbar';
import { Resizable } from 're-resizable';
@@ -224,10 +224,6 @@ const jsonoptions = {
wordWrap: 'break-word',
};
-const SUPPORTED_QUERY_LANGUAGES = [
- { value: 'm3ql', label: 'M3QL' },
-];
-
interface TimeseriesQueryConfig {
queryLanguage: string;
query: string;
@@ -252,6 +248,9 @@ const TimeseriesQueryPage = () => {
timeout: 60000,
});
+ const [supportedLanguages, setSupportedLanguages] =
useState<Array<string>>([]);
+ const [languagesLoading, setLanguagesLoading] = useState(true);
+
const [rawOutput, setRawOutput] = useState<string>('');
const [rawData, setRawData] = useState<TimeseriesQueryResponse | null>(null);
const [chartSeries, setChartSeries] = useState<ChartSeries[]>([]);
@@ -264,6 +263,25 @@ const TimeseriesQueryPage = () => {
const [selectedMetric, setSelectedMetric] = useState<string | null>(null);
+ // Fetch supported languages from controller configuration
+ useEffect(() => {
+ const fetchLanguages = async () => {
+ try {
+ setLanguagesLoading(true);
+ const response = await getTimeSeriesLanguages();
+ const languages = response.data || [];
+
+ setSupportedLanguages(languages);
+ } catch (error) {
+ console.error('Error fetching timeseries languages:', error);
+ setSupportedLanguages([]);
+ } finally {
+ setLanguagesLoading(false);
+ }
+ };
+ fetchLanguages();
+ }, []);
+
// Update config when URL parameters change
useEffect(() => {
const urlParams = new URLSearchParams(location.search);
@@ -410,6 +428,15 @@ const TimeseriesQueryPage = () => {
return (
<Grid container>
+ {/* Banner for no enabled languages */}
+ {!languagesLoading && supportedLanguages.length === 0 && (
+ <Grid item xs={12}>
+ <Alert severity="warning" style={{ marginBottom: '16px' }}>
+ <strong>No timeseries languages enabled.</strong> Please configure
timeseries languages in your controller, broker and server configurations using
the <code>pinot.timeseries.languages</code> property.
+ </Alert>
+ </Grid>
+ )}
+
<Grid item xs={12} className={classes.rightPanel}>
<Resizable
defaultSize={{ width: '100%', height: 148 }}
@@ -434,6 +461,7 @@ const TimeseriesQueryPage = () => {
lineWrapping: true,
indentWithTabs: true,
smartIndent: true,
+ readOnly: supportedLanguages.length === 0,
}}
className={classes.codeMirror}
autoCursor={false}
@@ -449,10 +477,11 @@ const TimeseriesQueryPage = () => {
<Select
value={config.queryLanguage}
onChange={(e) => handleConfigChange('queryLanguage',
e.target.value as string)}
+ disabled={languagesLoading || supportedLanguages.length === 0}
>
- {SUPPORTED_QUERY_LANGUAGES.map((lang) => (
- <MenuItem key={lang.value} value={lang.value}>
- {lang.label}
+ {supportedLanguages.map((lang) => (
+ <MenuItem key={lang} value={lang}>
+ {lang}
</MenuItem>
))}
</Select>
@@ -467,6 +496,7 @@ const TimeseriesQueryPage = () => {
value={config.startTime}
onChange={(e) => handleConfigChange('startTime',
e.target.value as string)}
placeholder={getOneMinuteAgoTimestamp()}
+ disabled={supportedLanguages.length === 0}
/>
</FormControl>
</Grid>
@@ -479,6 +509,7 @@ const TimeseriesQueryPage = () => {
value={config.endTime}
onChange={(e) => handleConfigChange('endTime', e.target.value
as string)}
placeholder={getCurrentTimestamp()}
+ disabled={supportedLanguages.length === 0}
/>
</FormControl>
</Grid>
@@ -490,6 +521,7 @@ const TimeseriesQueryPage = () => {
type="text"
value={config.timeout}
onChange={(e) => handleConfigChange('timeout',
parseInt(e.target.value as string) || 60000)}
+ disabled={supportedLanguages.length === 0}
/>
</FormControl>
</Grid>
@@ -499,7 +531,7 @@ const TimeseriesQueryPage = () => {
variant="contained"
color="primary"
onClick={handleExecuteQuery}
- disabled={isLoading || !config.query.trim()}
+ disabled={isLoading || !config.query.trim() ||
supportedLanguages.length === 0}
endIcon={<span style={{fontSize: '0.8em', lineHeight:
1}}>{navigator.platform.includes('Mac') ? '⌘↵' : 'Ctrl+↵'}</span>}
>
{isLoading ? 'Running Query...' : 'Run Query'}
diff --git a/pinot-controller/src/main/resources/app/requests/index.ts
b/pinot-controller/src/main/resources/app/requests/index.ts
index a069c693dbe..4bef8fdff29 100644
--- a/pinot-controller/src/main/resources/app/requests/index.ts
+++ b/pinot-controller/src/main/resources/app/requests/index.ts
@@ -238,6 +238,9 @@ export const getQueryResult = (params: Object):
Promise<AxiosResponse<SQLResult>
export const getTimeSeriesQueryResult = (params: Object):
Promise<AxiosResponse<any>> =>
transformApi.get(`/timeseries/api/v1/query_range`, { params });
+export const getTimeSeriesLanguages = (): Promise<AxiosResponse<string[]>> =>
+ baseApi.get('/timeseries/languages');
+
export const getClusterInfo = (): Promise<AxiosResponse<ClusterName>> =>
baseApi.get('/cluster/info');
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]