This adds in support to the lattice UI application to use the SQL3 (#2338)

* This adds in support to the lattice UI application to use the SQL3
endpoint. If the `/sql` endpoint returns 404, it will use the SQL1
endpoint. If the `/sql` endpoint is available, it will send SQL queries
to that. It does not try the SQL1 endpoint if the SQL3 endpoint returns
an error processing the query. That is, it is all SQL3 or SQL1.

- Below are the specific changes:
- Adds a file that contains functions for interacting with http services as opposed to just grpc/event-based services. As of this commit, it is only the SQL3 endpoint.
- This adds a variable to track if we are using the SQL3 endpoint or not
- This adds a function to handle the response from the SQL3 endpoint
- Adds a function to eventServices to query the sql3 HTTP endpoint
- Fixed a missing semicolon in grpcServices

Co-authored-by: Fletcher Haynes <fletcher.haynes@generalassemb.ly>
(cherry picked from commit 7ab453e289)
This commit is contained in:
Fletcher Haynes 2022-12-08 12:47:21 -08:00
parent 4e3856348c
commit bf0486503a
5 changed files with 61 additions and 14 deletions

View file

@ -27,6 +27,7 @@ type QueryProps = {
results: ResultType[];
error?: ResultType;
loading: boolean;
isSQL3: boolean;
onRemoveResult: (resultIdx: number) => void;
onClear: () => void;
onQuery: (query: string, type: 'PQL' | 'SQL', index?: string) => void;
@ -37,6 +38,7 @@ export const Query: FC<QueryProps> = ({
results,
error,
loading,
isSQL3,
onRemoveResult,
onClear,
onQuery
@ -172,6 +174,7 @@ export const Query: FC<QueryProps> = ({
{results.map((result, idx) => (
<Paper key={`query-result-${idx}`} className={css.results}>
<QueryResults
isSQL3={isSQL3}
collapsibleQuery={false}
results={result}
onRemoveResult={() => onRemoveResult(idx)}

View file

@ -41,6 +41,7 @@ export const QueryContainer: FC<{}> = () => {
const [results, setResults] = useState<ResultType[]>([]);
const [errorResult, setErrorResult] = useState<ResultType>();
const [loading, setLoading] = useState<boolean>(false);
const [isSQL3, setIsSQL3] = useState<boolean>(false)
useEffectOnce(() => {
pilosa.get.schema().then((res) => {
@ -48,6 +49,15 @@ export const QueryContainer: FC<{}> = () => {
});
});
const handleHTTPQueryMessages = (response) => {
setIsSQL3(true)
streamingResults.headers = response.data.schema.fields;
streamingResults.rows = response.data.data;
setErrorResult(undefined);
setResults([streamingResults]);
setLoading(false);
}
const handleQueryMessages = (message: RowResponse) => {
if (streamingResults.totalMessageCount < MAX_MESSAGES) {
const response = message.toObject();
@ -84,6 +94,7 @@ export const QueryContainer: FC<{}> = () => {
streamingResults.roundtrip = moment
.duration(moment().diff(startTime))
.as('milliseconds');
setErrorResult(undefined);
setResults([streamingResults, ...results]);
}
@ -119,7 +130,18 @@ export const QueryContainer: FC<{}> = () => {
setLoading(false);
}
} else {
querySQL(query, handleQueryMessages, handleQueryEnd);
pilosa.post.sql(query)
.then((res) => {
setIsSQL3(true);
handleHTTPQueryMessages(res);
}).catch((e) => {
if (e.response.status === 404) {
setIsSQL3(false);
querySQL(query, handleQueryMessages, handleQueryEnd);
}
});
}
}
};
@ -139,6 +161,7 @@ export const QueryContainer: FC<{}> = () => {
loading={loading}
onClear={() => setResults([])}
onRemoveResult={removeResultItem}
isSQL3={isSQL3}
/>
);
};

View file

@ -14,12 +14,14 @@ import css from './QueryResults.module.scss';
type QueryResultsProps = {
collapsibleQuery?: boolean;
results: ResultType;
isSQL3?: boolean
onRemoveResult?: () => void;
};
export const QueryResults: FC<QueryResultsProps> = ({
collapsibleQuery = true,
results,
isSQL3,
onRemoveResult,
}) => {
const [showQuery, setShowQuery] = useState<boolean>(false);
@ -31,22 +33,38 @@ export const QueryResults: FC<QueryResultsProps> = ({
const headers = results.headers;
const data = results.rows.map((row) => {
let rowData = {};
row.forEach((col, colIdx) => {
const header = headers[colIdx];
if (header.datatype.includes('[]')) {
const dataTypeVal = `${header.datatype.slice(2)}arrayval`;
rowData[header.name] = col[dataTypeVal].valsList.join(', ');
} else if (header.datatype === 'decimal') {
const decimalVal = col[`${header.datatype}val`];
if (decimalVal) {
const { value, scale } = decimalVal;
rowData[header.name] = value / Math.pow(10, scale);
if (isSQL3) {
const header = headers[colIdx];
if (Array.isArray(col)) {
let displayString = col.join(", ");
rowData[header.name] = displayString;
} else if (headers[colIdx]['base-type'] == "decimal") {
const scale = headers[colIdx]['type-info']['scale'];
rowData[header.name] = Number(col) / Math.pow(10, scale);
} else {
rowData[header.name] = decimalVal;
rowData[header.name] = col;
}
} else {
rowData[header.name] = col[`${header.datatype}val`];
const header = headers[colIdx];
if (header.datatype.includes('[]')) {
const dataTypeVal = `${header.datatype.slice(2)}arrayval`;
rowData[header.name] = col[dataTypeVal].valsList.join(', ');
} else if (header.datatype === 'decimal') {
const decimalVal = col[`${header.datatype}val`];
if (decimalVal) {
const { value, scale } = decimalVal;
rowData[header.name] = value / Math.pow(10, scale);
} else {
rowData[header.name] = decimalVal;
}
} else {
rowData[header.name] = col[`${header.datatype}val`];
}
}
});

View file

@ -44,7 +44,7 @@ export const pilosa = {
},
queryHistory() {
return api.get('/query-history');
},
}
},
post: {
finishTransaction(id) {
@ -53,5 +53,8 @@ export const pilosa = {
query(index, query) {
return api.post(`/index/${index}/query`, query);
},
sql(query) {
return api.post(`/sql`, query);
}
},
};

View file

@ -25,4 +25,4 @@ export const querySQL = (sql: string, onMessage, onEnd) => {
const querySQLRequest = new QuerySQLRequest();
querySQLRequest.setSql(sql);
invokeStream(Pilosa.QuerySQL, querySQLRequest, onMessage, onEnd);
}
};