Skip to content

Commit

Permalink
Added sqlFormatFunc, mongoFormatFunc, renderBrackets, `renderSe…
Browse files Browse the repository at this point in the history
…ps` (for func), `funcs` (for field) (#131)
  • Loading branch information
ukrbublik authored Dec 19, 2019
1 parent 3f150f7 commit 7aaa13e
Show file tree
Hide file tree
Showing 14 changed files with 112 additions and 37 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Changelog
- 1.0.12
- Added `sqlFormatFunc`, `mongoFormatFunc`, `renderBrackets`, `renderSeps` (for func), `funcs` (for field)
- 1.0.11
- Added css-class `qb-lite` for query builder (see readme if you wanna use it)
- 1.0.10
Expand Down
30 changes: 17 additions & 13 deletions CONFIG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ const myConfig = {

[cols="1m,1,1,5a",options="header"]
|===
|key |requred |default |meaning
|key |required |default |meaning
|type |+ | |One of types described in link:#configtypes[config.types] or `!struct` for complex field
|subfields |+ for `!struct` type | |Config for subfields of complex field (multiple nesting is supported)
|label |+ | |Label to be displayed in field list +
Expand All @@ -168,6 +168,7 @@ const myConfig = {
|operators, defaultOperator, widgets, valueSources | | |You can override config of corresponding type (see below at section link:#configtypes[config.types])
|mainWidgetProps | | |Shorthand for `widgets.<main>.widgetProps`
|excludeOperators | | |Can exclude some operators, eg. `proximity` for `text` type
|funcs | | |If comparing with funcs is enabled for this field (`valueSources` contains `func`), you can also limit list of funcs to be compared (by default will be available all funcs from link:#configfuncs[config.funcs] with `returnType` matching field's `type`)
|hideForSelect | |false |If true, field will appear only at right side (when you compare field with another field)
|hideForCompare | |false |If true, field will appear only at left side
|===
Expand Down Expand Up @@ -343,7 +344,7 @@ where `AND` and `OR` - available conjuctions (logical operators). You can add `N

[cols="1m,1,4a",options="header",]
|===
|key |requred |meaning
|key |required |meaning
|label |+ |Label to be displayed in conjunctions swicther
|formatConj |+ |Function for formatting query, used to join rules into group with conjunction. +
`(Immultable.List children, string conj, bool not, bool isForDisplay) => string` +
Expand Down Expand Up @@ -379,7 +380,7 @@ where `AND` and `OR` - available conjuctions (logical operators). You can add `N

[cols="1m,1,1,5a",options="header",]
|===
|key |requred |default |meaning
|key |required |default |meaning
|label |+ | |Label to be displayed in operators select component
|reversedOp |+ | |Opposite operator.
|isUnary | |false |true for `is_empty` operator only
Expand Down Expand Up @@ -464,7 +465,7 @@ const {

[cols="1m,1,1,5a",options="header",]
|===
|key |requred |default |meaning
|key |required |default |meaning
|type |+ | |One of types described in link:#configtypes[config.types]
|factory |+ | |React function component
|formatValue |+ | |Function for formatting widget's value in query string. +
Expand All @@ -483,9 +484,7 @@ const {
|valueFormat | | |Option for `<TimeWidget>`, `<DateWidget>`, `<DateTimeWidget>` to format value to be passed in `formatValue()`. Example: `YYYY-MM-DD HH:mm`
|labelYes, labelNo | | |Option for `<BooleanWidget>`
|customProps | | |You can pass any props directly to widget with `customProps`. +
For example enable search for https://ant.design/components/select/[`<Select>`] widget: `widgetProps: {customProps: {showSearch: true}}`
|singleWidget | | |Special option for `rangeslider` widget (`<RangeWidget>`), value equals to `slider` (`<SliderWidget>`) to connect them. +
Used together with operator `range_between` having `isSpecialRange=true` option.
For example enable search for https://ant.design/components/select/[`<Select>`] widget: `customProps: {showSearch: true}`
|===

NOTE: There is special `field` widget, rendered by `<ValueFieldWidget>`. +
Expand Down Expand Up @@ -531,7 +530,7 @@ To enable this feature set `valueSources` of type to `['value', 'func'']` (see b

[cols="1m,1,1,5a",options="header",]
|===
|key |requred |default |meaning
|key |required |default |meaning
|valueSources | |keys of `valueSourcesInfo` at link:#configsettings[config.settings] |Array with values `'value'`, `'field'`, `'func'`. If `'value'` is included, you can compare field with values. If `'field'` is included, you can compare field with another field of same type. If `'func'` is included, you can compare field with result of function (see link:#configfuncs[config.funcs]).
|defaultOperator | | |If specified, it will be auto selected when user selects field
|widgets.* |+ | |Available widgets for current type and their config. +
Expand Down Expand Up @@ -570,15 +569,18 @@ To enable this feature set `valueSources` of type to `['value', 'func'']` (see b

[cols="1m,1,1,5a",options="header",]
|===
|key |requred |default |meaning
|key |required |default |meaning
|returnType |+ | |One of types described in link:#configtypes[config.types]
|label | |same as func key |Label to be displayed in functions list
|formatFunc | |For `isForDisplay==false` - `FUNC(val1, val2)`, for `isForDisplay==true` - `FUNC(arg1: val1, arg2: val2)` |Function for formatting func expression in query rule. +
|formatFunc | |Example: for `isForDisplay==false` - `FUNC(val1, val2)`, for `isForDisplay==true` - `FUNC(arg1: val1, arg2: val2)` |Function for formatting func expression in query rule. +
`(Object args, bool isForDisplay) => string` +
where `args` is map `{<arg name> => <arg value>}``
|sqlFunc |+ for SQL format |same as func key |Func name in SQL
|mongoFunc |+ for MongoDB format |same as func key |Func name in Mongo
where `args` is object `{<arg name> : <arg value>}`
|sqlFunc |- for SQL format |same as func key |Func name in SQL
|sqlFormatFunc |- for SQL format | |Can be used instead of `sqlFunc`. Function with 1 param - args object `{<arg name> : <arg value>}`, should return formatted function expression string. +
Example: SUM function can be formatted with `({a, b}) => a + " + " + b`
|mongoFunc |- for MongoDB format |same as func key |Func name in Mongo
|mongoArgsAsObject | |false |Some functions like https://docs.mongodb.com/manual/reference/operator/aggregation/rtrim/[$rtrim] supports named args, other ones like https://docs.mongodb.com/manual/reference/operator/aggregation/slice/[$slice] takes args as array
|mongoFormatFunc |- for MongoDB format | |Can be used instead of `mongoFunc`. Function with 1 param - args object `{<arg name> : <arg value>}`, should return formatted function expression object.
|args.* | | |Arguments of function. Config is almost same as for simple link:#configfields[fields]
|args.<arg>.label | |arg's key |Label to be displayed in arg's label or placeholder (if `config.settings.showLabels` is false)
|args.<arg>.type |+ | |One of types described in link:#configtypes[config.types]
Expand All @@ -589,4 +591,6 @@ To enable this feature set `valueSources` of type to `['value', 'func'']` (see b
Example: `{ yellow: 'Yellow', green: 'Green' }` where `Yellow` - label to display at list of options
|args.<arg>.fieldSettings | | |Settings for widgets, will be passed as props. Example: `{min: 1, max: 10}`
|args.<arg>.isOptional | |false |Last args can be optional
|renderBrackets | |`['(', ')']` |Can render custom function brackets in UI (or not render).
|renderSeps | |`[', ']` |Can render custom arguments separators in UI (other than `,`).
|===
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class DemoQueryBuilder extends Component {
- Please wrap `<Builder />` in `div.query-builder`.
Optionally you can add class `.qb-lite` to it for showing action buttons (like delete rule/group, add, etc.) only on hover, which will look cleaner.
Wrapping in `div.query-builder-container` in not necessary, but if you want to make query builder scrollable, it's best place to apply appropriate styles.
- Use can save query value in `onChange` callback.
- You can save query value in `onChange` callback.
Note that value will be in [`Immutable`](https://immutable-js.github.io/immutable-js/) format, so you can use `QbUtils.getTree()` to convert it into JS object.
You can store it on backend, and load later by passing in `value` prop of `<Query />`.

Expand Down
31 changes: 30 additions & 1 deletion examples/demo/config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ const fields: Fields = {
min: -1,
max: 5
},
funcs: ['LINEAR_REGRESSION'],
},
slider: {
label: 'Slider',
Expand Down Expand Up @@ -335,7 +336,35 @@ const funcs: Funcs = {
valueSources: ['value', 'field'],
},
}
}
},
LINEAR_REGRESSION: {
label: 'Linear regression',
returnType: 'number',
formatFunc: ({coef, bias, val}, _) => `(${coef} * ${val} + ${bias})`,
sqlFormatFunc: ({coef, bias, val}) => `(${coef} * ${val} + ${bias})`,
mongoFormatFunc: ({coef, bias, val}) => ({'$sum': [{'$multiply': [coef, val]}, bias]}),
renderBrackets: ['', ''],
renderSeps: [' * ', ' + '],
args: {
coef: {
label: "Coef",
type: 'number',
defaultValue: 1,
valueSources: ['value'],
},
val: {
label: "Value",
type: 'number',
valueSources: ['value'],
},
bias: {
label: "Bias",
type: 'number',
defaultValue: 0,
valueSources: ['value'],
}
}
},
};


Expand Down
2 changes: 2 additions & 0 deletions modules/components/FuncSelect.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,8 @@ export default class FuncSelect extends PureComponent {
delete list[funcKey];
} else {
let canUse = funcConfig.returnType == expectedType;
if (leftFieldConfig.funcs)
canUse = canUse && leftFieldConfig.funcs.includes(funcFullkey);
if (canUseFuncForField)
canUse = canUse && canUseFuncForField(leftFieldFullkey, leftFieldConfig, funcFullkey, funcConfig, operator);
if (!canUse)
Expand Down
14 changes: 7 additions & 7 deletions modules/components/FuncWidget.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,27 +130,27 @@ export default class FuncWidget extends PureComponent {
);
};

renderArgSep = (argKey, argDefinition, argIndex) => {
renderArgSep = (argKey, argDefinition, argIndex, {renderSeps}) => {
if (!argIndex) return null;
return (
<Col className="rule--func--arg-sep">
{", "}
{renderSeps ? renderSeps[argIndex - 1] : ", "}
</Col>
);
};

renderBracketBefore = (funcDefinition) => {
renderBracketBefore = ({renderBrackets}) => {
return (
<Col key="before_args" className="rule--func--bracket-before">
{"("}
{renderBrackets ? renderBrackets[0] : "("}
</Col>
);
};

renderBracketAfter = (funcDefinition) => {
renderBracketAfter = ({renderBrackets}) => {
return (
<Col key="after_args" className="rule--func--bracket-after">
{")"}
{renderBrackets ? renderBrackets[1] : ")"}
</Col>
);
};
Expand All @@ -167,7 +167,7 @@ export default class FuncWidget extends PureComponent {
<Col key="args" className="rule--func--args">
{Object.keys(args).map((argKey, argIndex) => (
<Col key={`arg-${argKey}-${argIndex}`} className="rule--func--arg">
{this.renderArgSep(argKey, args[argKey], argIndex)}
{this.renderArgSep(argKey, args[argKey], argIndex, funcDefinition)}
{this.renderArgLabel(argKey, args[argKey])}
{this.renderArgLabelSep(argKey, args[argKey])}
{this.renderArgVal(funcKey, argKey, args[argKey])}
Expand Down
5 changes: 4 additions & 1 deletion modules/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ interface ValueField extends BaseField {
type: String,
preferWidgets?: Array<String>,
valueSources?: Array<ValueSource>,
funcs?: Array<String>,
tableName?: String,
fieldSettings?: FieldSettings,
defaultValue?: RuleValue,
Expand Down Expand Up @@ -441,7 +442,7 @@ export type Settings = LocaleSettings & RenderSettings & BehaviourSettings & Oth

type SqlFormatFunc = (formattedArgs: { [key: string]: string }) => String;
type FormatFunc = (formattedArgs: { [key: string]: string }, isForDisplay: Boolean) => String;
type MongoFormatFunc = (formattedArgs: { [key: string]: MongoValue }) => String;
type MongoFormatFunc = (formattedArgs: { [key: string]: MongoValue }) => MongoValue;

interface FuncGroup {
type?: "!struct",
Expand All @@ -459,6 +460,8 @@ export interface Func {
formatFunc?: FormatFunc,
sqlFormatFunc?: SqlFormatFunc,
mongoFormatFunc?: MongoFormatFunc,
renderBrackets?: Array<ReactElement | String>,
renderSeps?: Array<ReactElement | String>,
};
export interface FuncArg extends ValueField {
isOptional?: Boolean,
Expand Down
6 changes: 5 additions & 1 deletion modules/utils/configUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,11 @@ export const getValueSourcesForFieldOp = (config, field, operator, fieldDefiniti
return config._fieldsCntByType[fieldDefinition.type] > 1;
}
if (vs == "func" && fieldDefinition) {
return config._funcsCntByType[fieldDefinition.type] > 0;
if (!config._funcsCntByType[fieldDefinition.type])
return false;
if (fieldDefinition.funcs)
return fieldDefinition.funcs.length > 0;
return true;
}
return true;
});
Expand Down
3 changes: 1 addition & 2 deletions modules/utils/mongodbFormat.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,12 @@ const mongoFormatValue = (config, currentValue, valueSrc, valueType, fieldWidget
const argVal = args ? args.get(argKey) : undefined;
const argValue = argVal ? argVal.get('value') : undefined;
const argValueSrc = argVal ? argVal.get('valueSrc') : undefined;
const argName = argKey;
const [formattedArgVal, _argUseExpr] = mongoFormatValue(config, argValue, argValueSrc, argConfig.type, fieldDef, argConfig, null, null);
if (argValue != undefined && formattedArgVal === undefined)
return [undefined, false];
argsCnt++;
if (formattedArgVal !== undefined) { // skip optional in the end
formattedArgs[argName] = formattedArgVal;
formattedArgs[argKey] = formattedArgVal;
lastArg = formattedArgVal;
}
}
Expand Down
11 changes: 7 additions & 4 deletions modules/utils/queryString.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ const formatValue = (config, currentValue, valueSrc, valueType, fieldWidgetDefin
const args = currentValue.get('args');
const funcConfig = getFuncConfig(funcKey, config);
const funcName = isForDisplay && funcConfig.label || funcKey;
const formattedArgs = [];
const formattedArgs = {};
const formattedArgsWithNames = {};
for (const argKey in funcConfig.args) {
const argConfig = funcConfig.args[argKey];
const fieldDef = getFieldConfig(argConfig, config);
Expand All @@ -43,8 +44,10 @@ const formatValue = (config, currentValue, valueSrc, valueType, fieldWidgetDefin
const argValueSrc = argVal ? argVal.get('valueSrc') : undefined;
const formattedArgVal = formatValue(config, argValue, argValueSrc, argConfig.type, fieldDef, argConfig, null, null, isForDisplay);
const argName = isForDisplay && argConfig.label || argKey;
if (formattedArgVal !== undefined) // skip optional in the end
formattedArgs.push([argName, formattedArgVal]);
if (formattedArgVal !== undefined) { // skip optional in the end
formattedArgs[argKey] = formattedArgVal;
formattedArgsWithNames[argName] = formattedArgVal;
}
}
if (typeof funcConfig.formatFunc === 'function') {
const fn = funcConfig.formatFunc;
Expand All @@ -54,7 +57,7 @@ const formatValue = (config, currentValue, valueSrc, valueType, fieldWidgetDefin
];
ret = fn(...args);
} else {
ret = `${funcName}(${formattedArgs.map(([k, v]) => (isForDisplay ? `${k}: ${v}` : `${v}`)).join(', ')})`;
ret = `${funcName}(${Object.entries(formattedArgsWithNames).map(([k, v]) => (isForDisplay ? `${k}: ${v}` : `${v}`)).join(', ')})`;
}
} else {
if (typeof fieldWidgetDefinition.formatValue === 'function') {
Expand Down
6 changes: 3 additions & 3 deletions modules/utils/sqlFormat.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ const sqlFormatValue = (config, currentValue, valueSrc, valueType, fieldWidgetDe
const args = currentValue.get('args');
const funcConfig = getFuncConfig(funcKey, config);
const funcName = funcConfig.sqlFunc || funcKey;
const formattedArgs = [];
const formattedArgs = {};
for (const argKey in funcConfig.args) {
const argConfig = funcConfig.args[argKey];
const fieldDef = getFieldConfig(argConfig, config);
Expand All @@ -73,7 +73,7 @@ const sqlFormatValue = (config, currentValue, valueSrc, valueType, fieldWidgetDe
const argValueSrc = argVal ? argVal.get('valueSrc') : undefined;
const formattedArgVal = sqlFormatValue(config, argValue, argValueSrc, argConfig.type, fieldDef, argConfig, null, null);
if (formattedArgVal !== undefined) // skip optional in the end
formattedArgs.push([argKey, formattedArgVal]);
formattedArgs[argKey] = formattedArgVal;
}
if (typeof funcConfig.sqlFormatFunc === 'function') {
const fn = funcConfig.sqlFormatFunc;
Expand All @@ -82,7 +82,7 @@ const sqlFormatValue = (config, currentValue, valueSrc, valueType, fieldWidgetDe
];
ret = fn(...args);
} else {
ret = `${funcName}(${formattedArgs.map(([k, v]) => v).join(', ')})`;
ret = `${funcName}(${Object.entries(formattedArgs).map(([k, v]) => v).join(', ')})`;
}
} else {
if (typeof fieldWidgetDefinition.sqlFormatValue === 'function') {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-awesome-query-builder",
"version": "1.0.11",
"version": "1.0.12",
"description": "User-friendly query builder for React. Demo: https://ukrbublik.github.io/react-awesome-query-builder",
"keywords": [
"reactjs",
Expand Down
4 changes: 2 additions & 2 deletions sandbox/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-awesome-query-builder-demo",
"version": "0.2.4",
"version": "0.2.5",
"description": "Demo for react-awesome-query-builder",
"main": "src/index.jsx",
"readme": "README.md",
Expand All @@ -19,7 +19,7 @@
"dependencies": {
"react": "^16.4.1",
"react-dom": "^16.4.1",
"react-awesome-query-builder": "^1.0.11",
"react-awesome-query-builder": "^1.0.12",
"react-scripts": "3.0.1"
},
"browserslist": {
Expand Down
31 changes: 30 additions & 1 deletion sandbox/src/demo/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ const fields = {
min: -1,
max: 5
},
funcs: ['LINEAR_REGRESSION'],
},
slider: {
label: 'Slider',
Expand Down Expand Up @@ -317,7 +318,35 @@ const funcs = {
valueSources: ['value', 'field'],
},
}
}
},
LINEAR_REGRESSION: {
label: 'Linear regression',
returnType: 'number',
formatFunc: ({coef, bias, val}, _) => `(${coef} * ${val} + ${bias})`,
sqlFormatFunc: ({coef, bias, val}) => `(${coef} * ${val} + ${bias})`,
mongoFormatFunc: ({coef, bias, val}) => ({'$sum': [{'$multiply': [coef, val]}, bias]}),
renderBrackets: ['', ''],
renderSeps: [' * ', ' + '],
args: {
coef: {
label: "Coef",
type: 'number',
defaultValue: 1,
valueSources: ['value'],
},
val: {
label: "Value",
type: 'number',
valueSources: ['value'],
},
bias: {
label: "Bias",
type: 'number',
defaultValue: 0,
valueSources: ['value'],
}
}
},
};


Expand Down

0 comments on commit 7aaa13e

Please sign in to comment.