- Working on the parser
- Library Specification
- parseFilterString(filterString) ⇒ object
- setFilter(definition, { label,subtype,values }) ⇒ string
- Testing
This project uses the parser generator by Nearley.js . To work on the grammar and to modify the rules, you will need to have Nearley installed on your machine. To achieve that run:
npm install -g nearley
There are a few example grammar files provided in the folder lib/grammar/examples
. Going through these might give the reader an understanding of the grammar syntax and the grammar examples also include instructions on how to test them.
The main file containing the grammar rules is stored in lib/grammar/main.ne
. Whenever the file is updated, you need to compile it to generate the parser js file. To do this run:
nearleyc lib/grammar/main.ne -o lib/compiled-grammar/main.js
This will generate the parser file main.js
which is used by this library.
If you want to test the generated parser file against a particular output in the terminal without running the tests through jest, you can run:
nearley-test lib/compiled-grammar/main.js --input 'CATEGORY(true)=="abc"'
There are two main functions returned from the library with the following signatures:
- parseFilterString(filterString) ⇒
object
Parses the filter string to return a JSON object
- setFilter(definition, { label,subtype,values }) ⇒
string
Takes a filter string and a json object and updates filter string with the JSON object. Existing values for the quantity being set are blown away
Parses the filter string to return a JSON object
Kind: global function
Returns: object
- - a json representation of the string
Param | Type | Description |
---|---|---|
filterString | string |
the filter string |
Example
parseFilterString('CATEGORY(true)==["abc","cde"]&CATEGORY(false)==["xyz"]&CATEGORY(true)!=["123"]&SIV_ATTRIBUTE(id)==[12,23]&SIV_ATTRIBUTE(id)!=[65,34]')
-> returns: [
{
CATEGORY: {
includedWithSubcategories: ['abc', 'cde'],
includedWithoutSubcategories: ['xyz'],
excludedWithSubcategories: ['123'],
},
SIV_ATTRIBUTE: {
id: {
included: [12, 23],
excluded: [65, 34],
},
},
},
];
If we break it down to smaller chunks, we get:
These are definitions where including a parent category automatically includes all its subcategories to the filter. In the filter string they are represented by the boolean in the parenthesis immediately after the keyword CATEGORY
. For example in the category string CATEGORY(true)==['CAT_SYS_123']
, the true
indicates that all the subcategories are included.
These can be further divided into two types:
These filter strings are used to show the category codes which are added to the filter. These are filter strings of the form:
const filterString = 'CATEGORY(true)==["abc", "cde"]';
The resulting JSON for this string should look like:
const filterArray = [
{
CATEGORY: {
includedWithSubcategories: ['abc', 'cde'],
},
},
];
Very similar to the previous example but these strings denote category codes which are explicitly removed from the filter. Currently, these filter strings do not exist in the actual data but strings like these are possible. These are filter strings of the form:
const filterString = 'CATEGORY(true)!=["xyz", "pyf"]';
Note that the equality operator here is !=
and not ==
.
The resulting JSON for this string should look like:
const filterArray = [
{
CATEGORY: {
excludedWithSubcategories: ['xyz', 'pyf'],
},
},
];
The key here is called excluded
as opposed to included
in the previous example.
A category filter definition where adding or excluding a category only applies to items marked with that particular category and items marked with categories which are subcategories to the category mentioned in the definition are unaffected by this filter rule. An example of a filter string of this type would be CATEGORY(false)==['CAT_123']
and here the boolean false
next to the category indicates that the subcategories are not included in the definition. These have the same 2 types as the definitions where the subcategories are included:
- Adding Categories
- Removing Categories
They look exactly like their counterparts where the subcategories are included except for the boolean flag. For example, a filter string of this type where you are adding the categories would look like:
const filterString = 'CATEGORY(false)==["abc", "cde"]';
The parsed JSON object for this string would be:
const filterArray = [
{
CATEGORY: {
includedWithoutSubcategories: ['abc', 'cde'],
},
},
];
The only difference here being that the boolean value for the key subcategory
is false
.
const filterString = 'CATEGORY(false)==["abc", "cde"]|SIV_ATTRIBUTE(id)==[123, 432]';
The parsed JSON object for this string would be:
const filterArray = [
{
CATEGORY: {
includedWithoutSubcategories: ['abc', 'cde'],
},
},
{
SIV_ATTRIBUTE: {
id: {
included: [123, 432],
},
},
},
];
const filterString = 'SIV_ATTRIBUTE(id)==[123, 432]&(CATEGORY(true)==["pikachu", "raichu"])';
The parsed JSON object for this string would be:
const filterArray = [
{
CATEGORY: {
includedWithSubcategories: ['pikachu', 'raichu'],
},
SIV_ATTRIBUTE: {
id: {
excluded: [123, 432],
},
},
},
];
const filterString = 'SIV_ATTRIBUTE(id)!=[123, 432]&(CATEGORY(true)==["pikachu", "raichu"]|SIV_ATTRIBUTE(id)==[98])';
The parsed JSON object for this string would be:
const filterArray = [
{
SIV_ATTRIBUTE: {
id: {
excluded: [123, 432],
},
},
array: [
{
CATEGORY: {
includedWithSubcategories: ['pikachu', 'raichu'],
},
},
{
SIV_ATTRIBUTE: {
id: {
included: [98],
},
},
},
],
},
];
Takes a filter string and a json object and updates filter string with the JSON object. Existing values for the quantity being set are blown away.
Kind: global function
Returns: string
- The filter difinition with the values added to the initial definition.
Param | Type | Description |
---|---|---|
definition | string |
The filter definition to be modified |
{ label, subtype, values } | object |
An object which specifies what to set on the filter and the values to set |
Example
setFilter("SIV_ATTRIBUTE(id)=[123]", { label: 'CATEGORY', subtype: 'subcategory-included', values: ["cat1"]})
-> `CATEGORY(true)==["cat1"]|SIV_ATTRIBUTE(id)==[123]`
The currently supported labels and the valid subtypes are in this chart.
Label | Valid Subtypes |
---|---|
CATEGORY |
subcategory-included , subcategory-excluded |
SIV_ATTRIBUTE |
id-included , id-excluded , supplier-included |
The following section explains each of these label subtype combinations in further detail with examples.
Used to update or set category codes which are to be included in the filter string including their subcategories. Say the filter initially includes all items with the category code electronics
and its subcategories. You want to change the filter to include all items which belong to category codes electricals
and home-appliances
and their subcategories.
const initialDefinition = 'CATEGORY(true)==["electronics"]';
setFilter(initialDefintion, { label: 'CATEGORY', subtype: 'subcategory-included', values: ["electricals", "home-appliances"]});
-> 'CATEGORY(true)==["electricals", "home-appliances"]'
The initial value can also be a filter string not containing category. Say the initial filter was all items belonging to the supplier Apple
. And we wanted to add the same 2 category codes to this filter string.
const initialDefinition = `SIV_ATTRIBUTE(supplier)==["Apple"]`;
setFilter(initialDefintion, { label: 'CATEGORY', subtype: 'subcategory-included', values: ["electricals", "home-appliances"]});
-> `SIV_ATTRIBUTE(supplier)==["Apple"]&CATEGORY(true)==["electricals", "home-appliances"]`
Used to update or set category codes which are to be included in the filter string excluding their subcategories. Say the filter initially includes all items with the category code electronics
and its subcategories. You want to change the filter to include all items which belong to category codes electronics2
and home-appliances
but not their subcategories.
const initialDefinition = 'CATEGORY(true)==["electronics"]';
setFilter(initialDefintion, { label: 'CATEGORY', subtype: 'subcategory-excluded', values: ["electronics2", "home-appliances"]});
-> 'CATEGORY(false)==["electronics2", "home-appliances"]'
We use this to create a filter which includes all the SIV IDs provided. This is a special case because the other filter strings are all inclusive. For example, your filter may try to get all items belonging to a particular category, say Electronics AND is supplied by a particular supplier, say Apple. But in the case of SIV IDs, it is the other conditions OR all items with these IDs. It is not an AND relationship.
const initialDef = 'CATEGORY(true)==["electronics"]&SIV_ATTRIBUTE(supplier)==["Apple"]'
setFilter(initialDef, { label: 'SIV', subtype: 'id-included', values: [123]})
-> '(CATEGORY(true)==["electronics"]&SIV_ATTRIBUTE(supplier)==["Apple"])|SIV_ATTRIBUTE(id)==[123]'
Note: The resulting filter is joined with an "|" which denotes that its an OR condition.
If you want to select all items belonging to a particular filter except for a few IDs, thats when you use this rule. This will specify a set of SIV ID values to exclude from the filter.
const initialDef = 'CATEGORY(true)==["electronics"]&SIV_ATTRIBUTE(supplier)==["Apple"]'
setFilter(initialDef, { label: 'SIV', subtype: 'id-excluded', values: [123]})
-> '(CATEGORY(true)==["electronics"]&SIV_ATTRIBUTE(supplier)==["Apple"]&SIV_ATTRIBUTE(id)!=[123])'
Note the difference from the previous filter string. The IDs are denoted with a "!=" sign and they are joined with an & to denote that this rule works with the existing rules.
NOTE: Another thing to note is that currently, you cannot have a filter definiton with only ID excluded and ID included. In that case, all the ID excluded entries are automatically removed.
If you have a particular filter but if you want to restrict the filter result only to a list of suppliers, you will use this subtype. For example, say the filter currently returns all items belonging to the category electronics, but you want to just look at the items under electronics provided by the amazing supplier rewardos and say the supplier ID of rewardos was 1.
const initialDef = 'CATEGORY(true)==["electronics"]'
setFilter(initialDef, { label: 'SIV', subtype: 'supplier-included', values: [1]})
-> '(CATEGORY(true)==["electronics"]&SIV_ATTRIBUTE(supplier)==[1])'
If you have a particular filter but if you want to restrict the filter result only to exclude a list of suppliers, you will use this subtype. For example, say the filter currently returns all items belonging to the category electronics, but you want to just look at all the items under electronics except items provided by the amazing supplier rewardos and say the supplier ID of rewardos was 1.
const initialDef = 'CATEGORY(true)==["electronics"]'
setFilter(initialDef, { label: 'SIV', subtype: 'supplier-excluded', values: [1]})
-> '(CATEGORY(true)==["electronics"]&SIV_ATTRIBUTE(supplier)!=[1])'
This project implements jest for testing. To run the tests, simply run
npm run test
You can add debugger in your code and run your tests in debug mode by running:
npm run test:debug
It will start the test in debug mode. And then you can go to chrome and go to the inspect tab: chrome://inspect
, and you will see the debug session under Remote Targets. Click on it and it will open another chrome tab with the test running in a debugger. Once the test completes running, you need to close the chrome debug tab for the test to end on the terminal.
More details are here: Jest Troubleshooting