Skip to content
Snippets Groups Projects
Unverified Commit cab00648 authored by Tom Robinson's avatar Tom Robinson
Browse files

Only show valid aggregations and fields

parent aec6bcc5
No related branches found
No related tags found
No related merge requests found
...@@ -48,7 +48,7 @@ function formatField([, fieldId], { tableMetadata: { fields } }) { ...@@ -48,7 +48,7 @@ function formatField([, fieldId], { tableMetadata: { fields } }) {
if (!field) { if (!field) {
throw 'field with ID does not exist: ' + fieldId; throw 'field with ID does not exist: ' + fieldId;
} }
return formatFieldName(field.display_name); return formatFieldName(field);
} }
function formatMetric([, metricId], { tableMetadata: { metrics } }) { function formatMetric([, metricId], { tableMetadata: { metrics } }) {
...@@ -56,7 +56,7 @@ function formatMetric([, metricId], { tableMetadata: { metrics } }) { ...@@ -56,7 +56,7 @@ function formatMetric([, metricId], { tableMetadata: { metrics } }) {
if (!metric) { if (!metric) {
throw 'metric with ID does not exist: ' + metricId; throw 'metric with ID does not exist: ' + metricId;
} }
return formatMetricName(metric.name) + "()"; return formatMetricName(metric) + "()";
} }
function formatExpressionReference([, expressionName]) { function formatExpressionReference([, expressionName]) {
......
...@@ -16,23 +16,30 @@ export const VALID_AGGREGATIONS = new Map(Object.entries({ ...@@ -16,23 +16,30 @@ export const VALID_AGGREGATIONS = new Map(Object.entries({
"sum": "Sum", "sum": "Sum",
"cum_sum": "CumulativeSum", "cum_sum": "CumulativeSum",
"distinct": "Distinct", "distinct": "Distinct",
"stddev": "StandardDeviation",
"avg": "Average", "avg": "Average",
"min": "Min", "min": "Min",
"max": "Max" "max": "Max"
})); }));
export function formatMetricName(metricName) { export function formatAggregationName(aggregationOption) {
return titleize(metricName).replace(/\W+/g, "") return VALID_AGGREGATIONS.get(aggregationOption.short);
} }
export function formatFieldName(fieldName) { export function formatMetricName(metric) {
return /^\w+$/.test(fieldName) ? return titleize(metric.name).replace(/\W+/g, "")
fieldName : }
JSON.stringify(fieldName);
export function formatFieldName(field) {
return /^\w+$/.test(field.display_name) ?
field.display_name :
JSON.stringify(field.display_name);
} }
export function formatExpressionName(name) { export function formatExpressionName(name) {
return formatFieldName(name); return /^\w+$/.test(name) ?
name :
JSON.stringify(name);
} }
// move to query lib // move to query lib
......
const { Lexer, Parser, extendToken, getImage } = require("chevrotain"); const { Lexer, Parser, extendToken, getImage } = require("chevrotain");
const _ = require("underscore"); const _ = require("underscore");
import { VALID_AGGREGATIONS, formatFieldName, formatMetricName, formatExpressionName } from "../expressions"; import { VALID_AGGREGATIONS, formatFieldName, formatMetricName, formatExpressionName, formatAggregationName } from "../expressions";
export const AGGREGATION_ARITY = new Map([
["Count", 0],
["CumulativeCount", 0],
["Sum", 1],
["CumulativeSum", 1],
["Distinct", 1],
["Average", 1],
["Min", 1],
["Max", 1]
]);
const AdditiveOperator = extendToken("AdditiveOperator", Lexer.NA); const AdditiveOperator = extendToken("AdditiveOperator", Lexer.NA);
const Plus = extendToken("Plus", /\+/, AdditiveOperator); const Plus = extendToken("Plus", /\+/, AdditiveOperator);
...@@ -190,7 +179,7 @@ class ExpressionsParser extends Parser { ...@@ -190,7 +179,7 @@ class ExpressionsParser extends Parser {
getMetricForName(metricName) { getMetricForName(metricName) {
const metrics = this._options.tableMetadata && this._options.tableMetadata.metrics; const metrics = this._options.tableMetadata && this._options.tableMetadata.metrics;
return _.find(metrics, (metric) => formatMetricName(metric.name) === metricName); return _.find(metrics, (metric) => formatMetricName(metric) === metricName);
} }
} }
...@@ -245,6 +234,9 @@ export function suggest(source, { ...@@ -245,6 +234,9 @@ export function suggest(source, {
let finalSuggestions = [] let finalSuggestions = []
// TODO: is there a better way to figure out which aggregation we're inside of?
const currentAggregationToken = _.find(assistanceTokenVector.slice().reverse(), (t) => t instanceof Aggregation);
for (const suggestion of syntacticSuggestions) { for (const suggestion of syntacticSuggestions) {
const { nextTokenType, ruleStack } = suggestion; const { nextTokenType, ruleStack } = suggestion;
// no nesting of aggregations or field references outside of aggregations // no nesting of aggregations or field references outside of aggregations
...@@ -258,8 +250,8 @@ export function suggest(source, { ...@@ -258,8 +250,8 @@ export function suggest(source, {
type: "operators", type: "operators",
name: getTokenSource(token), name: getTokenSource(token),
text: " " + getTokenSource(token) + " ", text: " " + getTokenSource(token) + " ",
prefixTrim: /\s+$/, prefixTrim: /\s*$/,
postfixTrim: /^\s+/ postfixTrim: /^\s*[*/+-]?\s*/
}))) })))
} else if (nextTokenType === LParen) { } else if (nextTokenType === LParen) {
finalSuggestions.push({ finalSuggestions.push({
...@@ -267,53 +259,55 @@ export function suggest(source, { ...@@ -267,53 +259,55 @@ export function suggest(source, {
name: "(", name: "(",
text: " (", text: " (",
postfixText: ")", postfixText: ")",
prefixTrim: /\s+$/, prefixTrim: /\s*$/,
postfixTrim: /^\s+/ postfixTrim: /^\s*\(?\s*/
}); });
} else if (nextTokenType === RParen) { } else if (nextTokenType === RParen) {
finalSuggestions.push({ finalSuggestions.push({
type: "other", type: "other",
name: ")", name: ")",
text: ") ", text: ") ",
prefixTrim: /\s+$/, prefixTrim: /\s*$/,
postfixTrim: /^\s+/ postfixTrim: /^\s*\)?\s*/
}); });
} else if (nextTokenType === Identifier || nextTokenType === StringLiteral) { } else if (nextTokenType === Identifier || nextTokenType === StringLiteral) {
if (!outsideAggregation) { if (!outsideAggregation && currentAggregationToken) {
finalSuggestions.push(...tableMetadata.fields.map(field => ({ let aggregationShort = aggregationsMap.get(getImage(currentAggregationToken));
type: "fields", let aggregationOption = _.findWhere(tableMetadata.aggregation_options, { short: aggregationShort });
name: field.display_name, if (aggregationOption && aggregationOption.fields.length > 0) {
text: formatFieldName(field.display_name) + " ", finalSuggestions.push(...aggregationOption.fields[0].map(field => ({
prefixTrim: /\w+$/, type: "fields",
postfixTrim: /^\w+\s*/ name: field.display_name,
}))) text: formatFieldName(field) + " ",
finalSuggestions.push(...Object.keys(customFields || {}).map(expressionName => ({ prefixTrim: /\w+$/,
type: "fields", postfixTrim: /^\w+\s*/
name: expressionName, })))
text: formatExpressionName(expressionName) + " ", finalSuggestions.push(...Object.keys(customFields || {}).map(expressionName => ({
prefixTrim: /\w+$/, type: "fields",
postfixTrim: /^\w+\s*/ name: expressionName,
}))) text: formatExpressionName(expressionName) + " ",
prefixTrim: /\w+$/,
postfixTrim: /^\w+\s*/
})))
}
} }
} else if (nextTokenType === Aggregation) { } else if (nextTokenType === Aggregation) {
if (outsideAggregation) { if (outsideAggregation) {
let tokens = getSubTokenTypes(nextTokenType); finalSuggestions.push(...tableMetadata.aggregation_options.filter(a => formatAggregationName(a)).map(aggregationOption => {
finalSuggestions.push(...tokens.map(token => { const arity = aggregationOption.fields.length;
let text = getTokenSource(token);
let arity = AGGREGATION_ARITY.get(text);
return { return {
type: "aggregations", type: "aggregations",
name: text, name: formatAggregationName(aggregationOption),
text: text + "(" + (arity > 0 ? "" : ")"), text: formatAggregationName(aggregationOption) + "(" + (arity > 0 ? "" : ")"),
postfixText: arity > 0 ? ")" : "", postfixText: arity > 0 ? ")" : "",
prefixTrim: /\w+$/, prefixTrim: /\w+$/,
postfixTrim: /^\w+\(\)?/ postfixTrim: /^\w+\(\)?/
} };
})); }));
finalSuggestions.push(...tableMetadata.metrics.map(metric => ({ finalSuggestions.push(...tableMetadata.metrics.map(metric => ({
type: "metrics", type: "metrics",
name: metric.name, name: metric.name,
text: formatMetricName(metric.name) + "() ", text: formatMetricName(metric) + "() ",
prefixTrim: /\w+$/, prefixTrim: /\w+$/,
postfixTrim: /^\w+\(\)?/ postfixTrim: /^\w+\(\)?/
}))) })))
......
...@@ -245,7 +245,7 @@ export default class ExpressionEditorTextfield extends Component { ...@@ -245,7 +245,7 @@ export default class ExpressionEditorTextfield extends Component {
targetAttachment: 'bottom left' targetAttachment: 'bottom left'
}} }}
> >
<ul style={{minWidth: 150, maxHeight: 342, overflow: "hidden"}}> <ul style={{minWidth: 150, overflow: "hidden"}}>
{suggestions.map((suggestion, i) => {suggestions.map((suggestion, i) =>
// insert section title. assumes they're sorted by type // insert section title. assumes they're sorted by type
[(i === 0 || suggestion.type !== suggestions[i - 1].type) && [(i === 0 || suggestion.type !== suggestions[i - 1].type) &&
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment