Releases: jetstreamapp/soql-parser-js
Release 2.4.0
Release 2.3.0
Release 2.2.3
Release 2.2.2
Release 2.2.1
Release 2.2.0
Release 2.1.0
Release 2.0.0
2.0.0
Summary
Version 2.0 brings some significant bundle size and performance improvements. This library now uses Chevrotain instead of antlr4. With this change, everything related to parsing had to be re-written from scratch. Chevrotain uses pure javascript to handle lexing, parsing, and visiting the generated ast/cst as opposed to using a grammar file and generating a javascript parser based on the grammar.
With this change, the data model was reviewed and analyzed, and there are some significant breaking changes to the data structures. Review the 🔥breaking changes🔥 below for a detailed description of each breaking change.
Bundle Size
soql-parser-js bundles all of the library code and three dependencies chevrotain (which relies on regexp-to-ast) and lodash.get (required by chevrotain) into the javascript bundle. Previously, antlr4 was not bundled and was required to be installed separately.
To compare the bundle size, the following small program was written and then compiled using the default configuration of webpack, and the output bundle was compared.
- Version 1.x: 545kb (this includes all required dependencies)
- Version 2.0: 197kb (this includes all required dependencies)
var soqlParser = require('soql-parser-js');
const query = soqlParser.parseQuery(`SELECT Id FROM Account WHERE Id = 'FOO'`);
console.log('query', query);
const soql = soqlParser.composeQuery(query);
console.log('soql', soql);Benchmarks
Here is an example benchmark of parsing all the unit tests 1,000 times
OLD PARSER: ~6.2 seconds for ~60K parses
NEW PARSER: ~2.25 seconds for 60K parses
Breaking Changes 🔥
General Changes
- The CLI was removed.
- The
parseQuery()function no longer acceptsoptionsas a second parameter. rawValuewill always have a space between parametersGROUPING(Id, BillingCountry)- Some
literalTypevalues may have differing case from prior versions, regardless of the data input.TRUE,FALSE, and all functions except those listed below will always be returned in uppercase, regardless of case of input.- Exceptions:
toLabel,convertTimezone,convertCurrencywill always be in camelCase.
- Added new available types for
DateLiteralandDateNLiteral.
- A new
LiteralTypevalue was added forAPEX_BIND_VARIABLE.
Compose Query
getComposedField()is deprecated, you should now usegetField().getComposedField()will remain available for backward compatibility.getField()/getComposedField()has the following changes:fnproperty is has been deprecated (but still exists), you should now usefunctionNameinstead.- The
fromproperty has been removed for subqueries. TherelationshipNameis required to be populated to compose a subquery.
- On the FormatOptions interface
fieldMaxLineLenwas renamed tofieldMaxLineLength.
export interface FormatOptions {
numIndent?: number;
- fieldMaxLineLen?: number;
+ fieldMaxLineLength?: number;
fieldSubqueryParensOnOwnLine?: boolean;
whereClauseOperatorsIndented?: boolean;
logging?: boolean;
}Parse Query
rawValuewill now be included onFieldifobjectPrefixis defined.aliasmay be included onField, if defined.- On
FieldFunctionExpression,fnwas renamed tofunctionName. this was done because all other usages offnwereFunctionExp, but it was a string in this case. - The
parameterstype onFieldFunctionExpressionwas modified to allow an array of varying types. - Removed
fromproperty fromFieldSubquery. havingwas removed fromQueryBaseand now lives as a property onGroupByClause.- On the
Conditionobject,literalTypemay be an array. This will be an array ifvalueis an array and there are variable types within thevalue. For example:WHERE Foo IN ('a', null, 'b')would produceliteralType: ['STRING', 'NULL', 'STRING']. - The
GroupByClausehas the following modifications:fieldis now optional, and will be populated only if the grouping is on a single field.typehas been renamed tofnand will be populated whenCUBEandROLLUPare used.- The
havingclause has been moved as a top-level property to theGroupByClauseand will be populated only if ahavingclause is present.
- The
HavingConditionnow has aliteralTypethat will be populated with the type of thevalueproperty. FunctionExphas the following modificationstextwas renamed torawValueto be more consistent with other places in the data model.namewas renamed tofunctionName.parameterwas renamed toparametersand the type was changed to(string | FunctionExp)[]to support nested functions. This will ALWAYS be an array now even if there is only one parameter.fnwas removed, as nested functionParameters are always stored as an entry in theparametersarray.
export interface Field {
type: 'Field';
field: string;
objectPrefix?: string;
+ rawValue?: string;
+ alias?: string;
}
export interface FieldFunctionExpression {
type: 'FieldFunctionExpression';
- fn: string;
+ functionName: string;
- parameters?: string[] | FieldFunctionExpression[];
+ parameters?: (string | FieldFunctionExpression)[];
alias?: string;
isAggregateFn?: boolean;
rawValue?: string;
}
export interface FieldRelationship {
type: 'FieldRelationship';
field: string;
relationships: string[];
objectPrefix?: string;
rawValue?: string;
+ alias?: string;
}
export interface FieldSubquery {
type: 'FieldSubquery';
subquery: Subquery;
- from?: string;
}
export interface QueryBase {
fields: FieldType[];
sObjectAlias?: string;
where?: WhereClause;
limit?: number;
offset?: number;
groupBy?: GroupByClause;
- having?: HavingClause;
orderBy?: OrderByClause | OrderByClause[];
withDataCategory?: WithDataCategoryClause;
withSecurityEnforced?: boolean;
for?: ForClause;
update?: UpdateClause;
}
export interface Condition {
openParen?: number;
closeParen?: number;
logicalPrefix?: LogicalPrefix;
field?: string;
fn?: FunctionExp;
operator: Operator;
value?: string | string[];
valueQuery?: Query;
- literalType?: LiteralType;
+ literalType?: LiteralType | LiteralType[];
dateLiteralVariable?: number;parsed
}
export interface GroupByClause {
- field: string | string[];
+ field?: string | string[];
- type?: GroupByType;
+ fn?: FunctionExp;
+ having?: HavingClause;
}
export interface HavingCondition {
openParen?: number;
closeParen?: number;
field?: string;
fn?: FunctionExp;
operator: string;
value: string | number;
+ literalType?: String;
}
export interface FunctionExp {
- text?: string;
+ rawValue?: string;
- name?: string;
+ functionName?: string;
alias?: string;
- parameter?: string | string[];
+ parameters?: (string | FunctionExp)[];
isAggregateFn?: boolean;
- fn?: FunctionExp;
}