Skip to content

Commit 65ab012

Browse files
committed
Version 2.0 release
1 parent 49ef02e commit 65ab012

File tree

14 files changed

+378
-106
lines changed

14 files changed

+378
-106
lines changed

.release-it.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"hooks": {
66
"before:init": ["npm test"],
77
"after:bump": "npm run build",
8-
"after:release": "cd docs && npm install soql-parser-js@${version} && git add package*.json && git commit -m \"Updated docs version\" && git push && npm run deploy"
8+
"after:release": "npm run copy-tc-to-docs && cd docs && npm install soql-parser-js@${version} && git add package*.json && git commit -m \"Updated docs version\" && git push && npm run deploy"
99
}
1010
}

CHANGELOG.md

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ With this change, the data model was reviewed and analyzed, and there are some s
1010

1111
#### Bundle Size
1212

13-
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.
13+
To compare the bundle size, the following small program was written and then compiled using the default configuration of webpack, and the resulting webpack bundle was compared to determine the full size of the library.
14+
15+
Minified, uncompressed:
1416

1517
- Version 1.x: **545kb**
16-
- Version 2.0: **197kb**
18+
- Version 2.0: **207kb**
1719

1820
```javascript
1921
var soqlParser = require('soql-parser-js');
@@ -26,11 +28,24 @@ console.log('soql', soql);
2628

2729
#### Benchmarks
2830

29-
Here is an example benchmark of parsing all the unit tests 1,000 times
31+
Performance testing was done by iterating the unit tests 60K times, here are the results:
32+
33+
**Version 1.x parser**
3034

3135
```
32-
OLD PARSER: ~6.2 seconds for ~60K parses
33-
NEW PARSER: ~2.25 seconds for 60K parses
36+
Library import (startup time): 0.8671 milliseconds
37+
Parsing: 58 X 1000 = 58000 iterations.
38+
Duration: 5.7648 seconds
39+
Average of 0.0994 milliseconds per query
40+
```
41+
42+
**Version 2.0 parser**
43+
44+
```
45+
Library import (startup time): 1.3793 milliseconds
46+
Parsing: 87 X 1000 = 87000 iterations.
47+
Duration: 3.6582 seconds
48+
Average of 0.0420 milliseconds per query
3449
```
3550

3651
### Breaking Changes 🔥
@@ -44,9 +59,9 @@ NEW PARSER: ~2.25 seconds for 60K parses
4459
- `TRUE`, `FALSE`, and all functions except those listed below will always be returned in uppercase, regardless of case of input.
4560
- **Exceptions**:
4661
- `toLabel`, `convertTimezone`, `convertCurrency` will always be in camelCase.
47-
- Added new available types for `DateLiteral` and `DateNLiteral`.
62+
- Added types for `DateLiteral` and `DateNLiteral` values. If you are using TypeScript, you can utilize these types.
4863
- A new `LiteralType` value was added for `APEX_BIND_VARIABLE`.
49-
- When composing functions in a where clause or group by clause, the `rawValue` will be preferred (if exists) (no change here), but if rawValue is not provided, then the function will be composed using the `functionName` and `parameters`.
64+
- When composing functions in a where clause or group by clause, the `rawValue` will be preferred (if it exists) (no change here), but if rawValue is not provided, then the function will be composed using the `functionName` and `parameters`.
5065
- A new `LiteralType` value was added for `INTEGER_WITH_CURRENCY_PREFIX` and `DECIMAL_WITH_CURRENCY_PREFIX`. e.x. `USD500.01`
5166

5267
#### Compose Query
@@ -93,6 +108,69 @@ export interface FormatOptions {
93108
- Added support for `usingScope` - https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_using_scope.htm?search_text=format()
94109

95110
```diff
111+
export type LiteralType =
112+
| 'STRING'
113+
| 'INTEGER'
114+
| 'DECIMAL'
115+
+ | 'INTEGER_WITH_CURRENCY_PREFIX'
116+
+ | 'DECIMAL_WITH_CURRENCY_PREFIX'
117+
| 'BOOLEAN'
118+
| 'NULL'
119+
| 'DATETIME'
120+
| 'DATE'
121+
| 'DATE_LITERAL'
122+
| 'DATE_N_LITERAL'
123+
+ | 'APEX_BIND_VARIABLE';
124+
125+
+ export type DateLiteral =
126+
+ | 'YESTERDAY'
127+
+ | 'TODAY'
128+
+ | 'TOMORROW'
129+
+ | 'LAST_WEEK'
130+
+ | 'THIS_WEEK'
131+
+ | 'NEXT_WEEK'
132+
+ | 'LAST_MONTH'
133+
+ | 'THIS_MONTH'
134+
+ | 'NEXT_MONTH'
135+
+ | 'LAST_90_DAYS'
136+
+ | 'NEXT_90_DAYS'
137+
+ | 'THIS_QUARTER'
138+
+ | 'LAST_QUARTER'
139+
+ | 'NEXT_QUARTER'
140+
+ | 'THIS_YEAR'
141+
+ | 'LAST_YEAR'
142+
+ | 'NEXT_YEAR'
143+
+ | 'THIS_FISCAL_QUARTER'
144+
+ | 'LAST_FISCAL_QUARTER'
145+
+ | 'NEXT_FISCAL_QUARTER'
146+
+ | 'THIS_FISCAL_YEAR'
147+
+ | 'LAST_FISCAL_YEAR'
148+
+ | 'NEXT_FISCAL_YEAR';
149+
150+
+ export type DateNLiteral =
151+
+ | 'YESTERDAY'
152+
+ | 'NEXT_N_DAYS'
153+
+ | 'LAST_N_DAYS'
154+
+ | 'N_DAYS_AGO'
155+
+ | 'NEXT_N_WEEKS'
156+
+ | 'LAST_N_WEEKS'
157+
+ | 'N_WEEKS_AGO'
158+
+ | 'NEXT_N_MONTHS'
159+
+ | 'LAST_N_MONTHS'
160+
+ | 'N_MONTHS_AGO'
161+
+ | 'NEXT_N_QUARTERS'
162+
+ | 'LAST_N_QUARTERS'
163+
+ | 'N_QUARTERS_AGO'
164+
+ | 'NEXT_N_YEARS'
165+
+ | 'LAST_N_YEARS'
166+
+ | 'N_YEARS_AGO'
167+
+ | 'NEXT_N_FISCAL_QUARTERS'
168+
+ | 'LAST_N_FISCAL_QUARTERS'
169+
+ | 'N_FISCAL_QUARTERS_AGO'
170+
+ | 'NEXT_N_FISCAL_YEARS'
171+
+ | 'LAST_N_FISCAL_YEARS'
172+
+ | 'N_FISCAL_YEARS_AGO';
173+
96174
export interface Field {
97175
type: 'Field';
98176
field: string;
@@ -106,7 +184,7 @@ export interface FieldFunctionExpression {
106184
- fn: string;
107185
+ functionName: string;
108186
- parameters?: string[] | FieldFunctionExpression[];
109-
+ parameters?: (string | FieldFunctionExpression)[];
187+
+ parameters: (string | FieldFunctionExpression)[];
110188
alias?: string;
111189
isAggregateFn?: boolean;
112190
rawValue?: string;

README.md

Lines changed: 67 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,11 @@
1212

1313
1. Parse SOQL queries into a common `Query` data structure.
1414
2. Compose a `Query` data structure back into a SOQL query.
15-
3. Validate a query to check if the syntax is valid. (Note: even if a query is valid, it might still be invalid based on your org configuration)
15+
3. Validate a query to check if the syntax is valid. (Note: even if a query is returned as valid, it might still be invalid based on your Salesforce configuration)
1616

17-
This library uses [Chevrotain](https://github.com/SAP/chevrotain) to parse queries. Prior to version 2.0.0, [antlr4](https://github.com/antlr/antlr4) was used. The move to Chevrotain provided a 35% performance improvement and resulted in a 36% decrease in bundle size. :tada:
17+
This library uses [Chevrotain](https://github.com/SAP/chevrotain) to parse queries. Prior to version 2.0.0, [antlr4](https://github.com/antlr/antlr4) was used. The move to Chevrotain provided a significant performance increase and decrease in bundle size. :tada:
18+
19+
Migrating from version 1 to version 2? [Check out the changelog](CHANGELOG.md#200) for a full list of changes.
1820

1921
Want to try it out? [Check out the demo](https://paustint.github.io/soql-parser-js/).
2022

@@ -39,24 +41,32 @@ isQueryValid('SELECT Id Foo FROM Baz'); // false
3941
| ------------ | ------------------------------------------------------ | ------------------------------------------ |
4042
| parseQuery | Parse a SOQL query string into a Query data structure. | soql: Query<br> config?: ParseQueryConfig |
4143
| isQueryValid | Returns true if the query was able to be parsed. | soql: Query<br> config?: ParseQueryConfig |
42-
| composeQuery | Turn a Query object back into a SOQL statement | soql: Query<br> config?: SoqlComposeConfig |
44+
| composeQuery | Turn a Query object back into a SOQL statement. | soql: Query<br> config?: SoqlComposeConfig |
4345
| formatQuery | Format a SOQL query string. | soql: Query<br> config?: FormatOptions |
4446

47+
## Utility Functions
48+
49+
| Function | Description | Arguments |
50+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
51+
| getField | Convenience method to construct fields in the correct format when using `composeQuery()`. Look in the data models section below for the structure of `ComposeFieldInput`. | input: `string | ComposeFieldInput` |
52+
| isSubquery | Returns `true` if the data passed in is a subquery. | query: `Query | Subquery` |
53+
| getFlattenedFields | Flatten a Salesforce record based on the parsed SOQL Query. | soql: Query<br> config?: SoqlComposeConfig |
54+
4555
**ParseQueryConfig**
4656

47-
| Property | Type | Description | required | default |
48-
| ---------------------- | ------- | --------------------------------------------------------------------------------------------- | -------- | ------- |
49-
| allowApexBindVariables | boolean | Determines if apex variables are allowed in parsed query. Example: `WHERE Id IN :accountIds`. | FALSE | FALSE |
57+
| Property | Type | Description | required | default |
58+
| ---------------------- | ------- | ---------------------------------------------------------------------------------------------------- | -------- | ------- |
59+
| allowApexBindVariables | boolean | Determines if apex variables are allowed in parsed query. Example: `WHERE Id IN :accountIds`. | FALSE | FALSE |
60+
| logErrors | boolean | If true, then additional detail will be logged to the console if there is a lexing or parsing error. | FALSE | FALSE |
5061

5162
**SoqlComposeConfig**
5263

53-
| Property | Type | Description | required | default |
54-
| ---------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------- |
55-
| format | boolean | Apply formatting the the composed query. This will result in a multi-line soql statement. | FALSE | TRUE |
56-
| formatOptions | FormatOptions | Options to apply to the formatter. | FALSE | |
57-
| autoCompose | boolean | If you need to compose just part of a query, you can create your own instance of the Compose class and set this to false, then call any methods that you need to just for what you would like to turn into a SOQL query. | FALSE | TRUE |
58-
| useRawValueForFn | boolean | When composing WHERE/GROUP BY functions, prefer the `rawValue` property instead of using `functionName` and `parameters` to compose the query. | FALSE | TRUE |
59-
| logging | boolean | Print out logging statements to the console about the format operation. | FALSE | FALSE |
64+
| Property | Type | Description | required | default |
65+
| ------------- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------- | ------- |
66+
| format | boolean | Apply formatting the the composed query. This will result in a multi-line soql statement. | FALSE | TRUE |
67+
| formatOptions | FormatOptions | Options to apply to the formatter. | FALSE | |
68+
| autoCompose | boolean | If you need to compose just part of a query, you can create your own instance of the Compose class and set this to false, then call any methods that you need to just for what you would like to turn into a SOQL query. | FALSE | TRUE |
69+
| logging | boolean | Print out logging statements to the console about the format operation. | FALSE | FALSE |
6070

6171
**FormatOptions**
6272

@@ -65,7 +75,7 @@ isQueryValid('SELECT Id Foo FROM Baz'); // false
6575
| numIndent | number | The number of tab characters to indent. | FALSE | 1 |
6676
| fieldMaxLineLength | number | The number of characters that the fields should take up before making a new line. Set this to 1 to have every field on its own line. | FALSE | 60 |
6777
| fieldSubqueryParensOnOwnLine | boolean | If true, the opening and closing parentheses will be on their own line for subqueries. | FALSE | TRUE |
68-
| whereClauseOperatorsIndented | boolean | If true, indents the where clause operators | FALSE | FALSE |
78+
| whereClauseOperatorsIndented | boolean | If true, indents the where clause operators. | FALSE | FALSE |
6979
| logging | boolean | Print out logging statements to the console about the format operation. | FALSE | FALSE |
7080

7181
## Examples
@@ -139,7 +149,6 @@ console.log(JSON.stringify(soqlQuery, null, 2));
139149

140150
```typescript
141151
import { isQueryValid } from 'soql-parser-js';
142-
// var soqlParserJs = require('soql-parser-js'); // node's require format - usage: soqlParserJs.parseQuery()
143152

144153
const invalidSoql = `SELECT UserId, COUNT(Id) Account`;
145154
const validSoql = `SELECT UserId, COUNT(Id) Account`;
@@ -158,7 +167,7 @@ These examples show building your own Query object with the minimum required fie
158167

159168
Some utility methods have been provided to make it easier to build the field data structures.
160169

161-
**Note:** Some operators may be converted to upper case (e.x. NOT, AND)
170+
**Note:** Some operators may be converted to uppercase (e.x. NOT, AND)
162171

163172
**Note:** There are a number of fields populated on the Query object when `parseQuery()` is called that are not required to compose a query. Look at the examples below and the comments in the data model for more information.
164173

@@ -369,12 +378,14 @@ WHERE Name LIKE 'a%'
369378
The following utility functions are available:
370379

371380
1. `getField(input: string | ComposeFieldInput)`
372-
1. Convenience method to construct fields in the correct data format. See example usage in the Compose example.
381+
1. Convenience method to construct fields in the correct format. See data model below for the various input types as well as example usage in the Compose examples above.
382+
2. returns one of the following data structures: `SoqlModels.FieldFunctionExpression | SoqlModels.Field | SoqlModels.FieldRelationship | SoqlModels.FieldSubquery | SoqlModels.FieldTypeOf`.
373383
2. `isSubquery(query: Query | Subquery)`
374-
1. Returns true if the data passed in is a subquery
384+
1. Returns `true` if the data passed in is a subquery
375385
3. `getFlattenedFields(query: Query)`
376386
1. Flatten a Salesforce record based on the parsed SOQL Query. this is useful if you have relationships in your query and want to show the results in a table, using `.` dot notation for the relationship field headings.
377-
2. Refer to `tests/publicUtils.spec.ts` for usage examples.
387+
2. Returns an array of strings.
388+
3. Refer to `tests/publicUtils.spec.ts` for usage examples.
378389

379390
## Data Models
380391

@@ -563,10 +574,10 @@ export interface HavingClause {
563574
}
564575

565576
export interface FunctionExp {
566-
rawValue?: string; // only used for compose fields if useRawValueForFn=true. Should be formatted like this: Count(Id)
567-
functionName?: string; // only used for compose fields if useRawValueForFn=false, not used for compose, will be populated if SOQL is parsed
577+
rawValue?: string; // This is the entire text of the function, such as Count(Id). When composing a query, if this is populated this will be used to build output SOQL query.
578+
functionName?: string; // When composing a query, if rawValue is undefined/null this will be used to build the SOQL query.
568579
alias?: string;
569-
parameters?: (string | FunctionExp)[]; // only used for compose fields if useRawValueForFn=false, not used for compose, will be populated if SOQL is parsed
580+
parameters?: (string | FunctionExp)[]; // When composing a query, if rawValue is undefined/null this will be used to build the SOQL query.
570581
isAggregateFn?: boolean; // not used for compose, will be populated if SOQL is parsed
571582
}
572583

@@ -581,6 +592,40 @@ export interface WithDataCategoryCondition {
581592
}
582593
```
583594

595+
## Compose / Utility
596+
597+
```typescript
598+
export type ComposeFieldInput = ComposeField | ComposeFieldFunction | ComposeFieldRelationship | ComposeFieldSubquery | ComposeFieldTypeof;
599+
600+
export interface ComposeField {
601+
field: string;
602+
objectPrefix?: string;
603+
}
604+
605+
export interface ComposeFieldFunction {
606+
// @Deprecated - will still be used if populated, but `functionName` is checked first and preferred
607+
fn?: string;
608+
functionName: string;
609+
parameters?: string | SoqlModels.FieldFunctionExpression | (string | SoqlModels.FieldFunctionExpression)[];
610+
alias?: string;
611+
}
612+
613+
export interface ComposeFieldRelationship {
614+
field: string;
615+
relationships: string[];
616+
objectPrefix?: string;
617+
}
618+
619+
export interface ComposeFieldSubquery {
620+
subquery?: SoqlModels.Subquery;
621+
}
622+
623+
export interface ComposeFieldTypeof {
624+
field: string;
625+
conditions: SoqlModels.FieldTypeOfCondition[];
626+
}
627+
```
628+
584629
## Contributing
585630

586631
All contributions are welcome on the project. Please read the [contribution guidelines](https://github.com/paustint/soql-parser-js/blob/master/CONTRIBUTING.md).

0 commit comments

Comments
 (0)