Skip to content

Commit

Permalink
Complete partial column names
Browse files Browse the repository at this point in the history
  • Loading branch information
danvk committed Nov 14, 2014
1 parent 7823518 commit e355045
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 24 deletions.
17 changes: 10 additions & 7 deletions __tests__/js/query-completion-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,10 @@ var _ = require('underscore'),

describe('Query Completion', function() {
var columns = ['A', 'B', 'INFO.DP'];
var completer = QueryCompletion.createTypeaheadSource(QueryLanguage.parse, columns);

// Returns a list of possible complete queries.
function getCompletions(prefix) {
var results = null;
// In principle the callback could be async, but we know that it's not.
completer(prefix, (v) => { results = v; });
assert.ok(results);
return results.map((v) => v.value);
return QueryCompletion.getCompletions(prefix, QueryLanguage.parse, columns);
}

function assertCompletions(prefix, expectedCompletions) {
Expand Down Expand Up @@ -44,6 +39,15 @@ describe('Query Completion', function() {
assertCompletions('ORDER BY A A', ['ORDER BY A ASC']);
});

it('Should auto-complete field names in ORDER BY', function() {
assertCompletions('ORDER BY I', ['ORDER BY INFO.DP']);
assertCompletions('ORDER BY IN', ['ORDER BY INFO.DP']);
assertCompletions('ORDER BY INF', ['ORDER BY INFO.DP']);
assertCompletions('ORDER BY INFO', ['ORDER BY INFO.DP']);
assertCompletions('ORDER BY INFO.D', ['ORDER BY INFO.DP']);
assertCompletions('ORDER BY INFO.DP', []); // valid query
});

/*
// This is difficult to implement for now because of the errors that peg.js returns.
it('Should offer range suggestions', function() {
Expand Down Expand Up @@ -77,7 +81,6 @@ describe('Query Completion', function() {

/*
To-do:
- "ORDER BY I" doesn't complete to "ORDER BY INFO.DP".
- Mandatory space: "ORDER BYA" and "A<10 ANDA>10" shouldn't be valid.
- Value completion that's aware of the current field
- Completion when the query is already valid.
Expand Down
51 changes: 34 additions & 17 deletions cycledash/static/js/QueryCompletion.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ function firstToken(str) {
}
}

// Returns the string w/o the last token.
// e.g. "ORDER BY IN" --> "ORDER BY "
function withoutLastToken(str) {
var m = str.match(/^(.*)[^ ]+$/);
if (m) {
return m[1];
} else {
return null;
}
}

// Given a PEG.js expectation object, return possible strings which could
// fulfill that expectation, e.g. 'filter' -> 'A = 0'.
function completionsForExpectation(expectation, columnNames, rejectedText) {
Expand Down Expand Up @@ -112,20 +123,8 @@ function completionsForExpectation(expectation, columnNames, rejectedText) {
return [];
}

/**
* Returns a typeahead.js-compatible completion source for a CQL grammar.
* - parse is the PEG.js parse function
* - columnNames is a list of column names which may be used in the grammar.
*
* The returned function takes a query prefix and calls a callback with
* possible completions.
*
* For example, if columnNames=['A', 'B'], then:
* 'ORDER BY ' -> callback([{value: 'ORDER BY A'}, {value: 'ORDER BY B'}])
*/
function createTypeaheadSource(parse, columnNames) {

return function (query, callback) {
// Workhorse function: given a query prefix, return a list of completions
function getCompletions(query, parse, columnNames) {
var completions = [];

if (query == '') {
Expand All @@ -150,7 +149,9 @@ return function (query, callback) {
});
completions = completions.concat(newCompletions);
} else {
// Probably an invalid column name.
// Probably an invalid column name. Pop off the last token and try to get completions.
var subquery = withoutLastToken(query);
completions = getCompletions(subquery, parse, columnNames);
}
} else {
// It's a valid query! Nothing to do...
Expand All @@ -171,11 +172,27 @@ return function (query, callback) {
return token && query + token; // nulls are dropped by _.compact
})));

callback(valueify(completions));
return completions;
};

/**
* Returns a typeahead.js-compatible completion source for a CQL grammar.
* - parse is the PEG.js parse function
* - columnNames is a list of column names which may be used in the grammar.
*
* The returned function takes a query prefix and calls a callback with
* possible completions.
*
* For example, if columnNames=['A', 'B'], then:
* 'ORDER BY ' -> callback([{value: 'ORDER BY A'}, {value: 'ORDER BY B'}])
*/
function createTypeaheadSource(parse, columnNames) {
return function(query, callback) {
callback(getCompletions(query, parse, columnNames).map((v) => ({value:v})));
}
}

module.exports = {
createTypeaheadSource
createTypeaheadSource,
getCompletions
};

0 comments on commit e355045

Please sign in to comment.