-
Notifications
You must be signed in to change notification settings - Fork 933
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: support PPL in vega visualization #7285
Changes from 2 commits
841f18b
3113c02
c2ee9e1
3bde9c0
63cdcad
e8f0e3b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { SharedGlobalConfig, Logger, ILegacyClusterClient } from 'opensearch-dashboards/server'; | ||
import { Observable } from 'rxjs'; | ||
import { ISearchStrategy, SearchUsage } from '../../../data/server'; | ||
import { shimSchemaRow } from '../utils'; | ||
|
||
export const pplRawSearchStrategyProvider = ( | ||
config$: Observable<SharedGlobalConfig>, | ||
logger: Logger, | ||
client: ILegacyClusterClient, | ||
usage?: SearchUsage | ||
): ISearchStrategy => { | ||
return { | ||
search: async (context, request: any, options) => { | ||
const runSearch = request.dataSourceId | ||
? context.dataSource.opensearch.legacy.getClient(request.dataSourceId).callAPI | ||
: client.asScoped(request.rawRequest).callAsCurrentUser; | ||
|
||
try { | ||
const rawResponse: any = await runSearch('ppl.pplQuery', { body: request.params.body }); | ||
const data = shimSchemaRow(rawResponse); | ||
rawResponse.jsonData = data.jsonData; | ||
|
||
return { | ||
rawResponse, | ||
}; | ||
} catch (e) { | ||
logger.error(`pplSearchStrategy: ${e.message}`); | ||
if (usage) usage.trackError(); | ||
throw e; | ||
} | ||
}, | ||
}; | ||
}; |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would it be possible to put like a parsers map in the search service? ideally. all non-out of the box experience is encapsulated within the query enhancements. so the parsers service can host the parser and take in a search strategy id. then in query enhancements we register the ppl avoids having to re-write shared logic too There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Agree, let's explore the reusability, especially with considering the vis builder+vega, but that requires to clarify the requirement from both technical and design perspective, I'll leave it for future iteration, this PR is just the first step to add ppl support in vega. For now, I'll keep the scope as small as possible :) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { i18n } from '@osd/i18n'; | ||
import { Data, UrlObject, PPLQueryRequest } from './types'; | ||
import { SearchAPI } from './search_api'; | ||
|
||
const getRequestName = (request: PPLQueryRequest, index: number) => | ||
request.dataObject.name || | ||
i18n.translate('visTypeVega.opensearchQueryParser.unnamedRequest', { | ||
defaultMessage: 'Unnamed request #{index}', | ||
values: { index }, | ||
}); | ||
|
||
export class PPLQueryParser { | ||
searchAPI: SearchAPI; | ||
|
||
constructor(searchAPI: SearchAPI) { | ||
this.searchAPI = searchAPI; | ||
} | ||
|
||
parseUrl(dataObject: Data, url: UrlObject) { | ||
// data.url.body.query must be defined | ||
if (!url.body || !url.body.query || typeof url.body.query !== 'string') { | ||
throw new Error( | ||
i18n.translate('visTypeVega.pplQueryParser.dataUrl.PPL.queryCannotBeEmpty', { | ||
defaultMessage: '{dataUrlParam} must have query specified', | ||
values: { | ||
dataUrlParam: '"data.url"', | ||
}, | ||
}) | ||
); | ||
} | ||
|
||
return { dataObject, url }; | ||
} | ||
|
||
async populateData(requests: PPLQueryRequest[]) { | ||
const searchRequests = requests.map((r, index) => ({ | ||
...r.url, | ||
name: getRequestName(r, index), | ||
})); | ||
|
||
const data$ = await this.searchAPI.search(searchRequests, { strategy: 'pplraw' }); | ||
const results = await data$.toPromise(); | ||
results.forEach((data, index) => { | ||
const requestObject = requests.find((item) => getRequestName(item, index) === data.name); | ||
|
||
if (requestObject) { | ||
requestObject.dataObject.url = requestObject.url; | ||
requestObject.dataObject.values = (data.rawResponse as any).jsonData; | ||
} | ||
}); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i wouldn't update this. when it's ppl, the whole request should be passed as long as you just send it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean we check the
strategy
here, and when it'sppl
, we send the whole request instead of request.body?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
client.asScoped(request)
we call in search strategy requires the http request object, currently I see no better way to get it unless pass it explicitly.