cheetah-framework
Version:
Cheetah Framework JS used in all our applications
272 lines (238 loc) • 7.33 kB
JavaScript
import { Model } from '@cheetah/models/Model'
import { BUILDER_OPERATORS, RELATION_TYPE_OPERATORS, OPERATORS } from '@cheetah/components/QueryBuilder/utils/QueryBuilderHelper'
class Criterion extends Model {
constructor (data) {
super(data)
this.initBuilderFields()
}
/**
* Default values should consist of every possible key:value
* a criterion can have to ensure the reactivity is
* properly bound the every props.
*
* @return {object}
*/
get defaultValue () {
return {
/**
* Aggregation field key
*/
relation: null,
/**
* Criterion field key
*/
field: null,
/**
* Operator for simple criterion OR operator for criteria
* if the current criterion is an aggregator.
*/
operator: null,
/**
* Comparison value for simple criterion
*/
value: null,
/**
* Comparison column for criterion comparing
* two fields
*/
value_of: null,
/**
* List of aggregation conditions.Support only
* a single condition.
*/
aggregators: [{
/**
* Aggregation type
*/
aggregation: null,
/**
* Aggregated column
*/
field: null,
/**
* Aggregation operator for comparison
*/
operator: null,
/**
* Aggregation value to compare
*/
value: null
}],
/**
* Array of criteria for an aggregation. Expect to receive an array
* of criteria that will be converted to:
*
* {
* operator: {string},
* criteria: {Criteria[]|Criterion[]}
* }
*/
criteria: [],
/**
* Props used by the query builder
*/
builder_operator: null,
builder_compare_field: false,
is_editing: false
}
}
/**
* Set the criterion builder operator which will, in the end,
* define the criterion structure.
*
* @param value
*/
setBuilderOperatorAttribute (value) {
this._attributes.builder_operator = value
}
/**
* Guesses builder fields based on received data. This function should be
* called only on new criterion instances.
*
* Should basically be reverting what this.toJSON() does.
*/
initBuilderFields () {
if (this.value_of) {
this.builder_compare_field = true
}
if (_.includes([BUILDER_OPERATORS.LIKE, BUILDER_OPERATORS.NOT_LIKE], this.operator)) {
/**
* Fail safely check if the value ends or starts
* with "%" before doing an operation.
*/
if (_.startsWith(this.value, '%') && _.endsWith(this.value, '%')) {
this.value = this.value.slice(1, -1)
}
}
if (this.aggregators[0].aggregation) {
this.builder_operator = this.aggregators[0].aggregation
this.field = this.relation
this.criteria = _.pick(this, ['operator', 'criteria'])
} else if (_.includes(_.values(RELATION_TYPE_OPERATORS), this.type)) {
this.field = this.relation
this.builder_operator = BUILDER_OPERATORS[this.type === RELATION_TYPE_OPERATORS.WITH ? 'EXIST' : 'NOT_EXIST']
this.criteria = _.pick(this, ['operator', 'criteria'])
} else {
this.criteria = {
operator: OPERATORS.AND,
criteria: []
}
this.builder_operator = this.operator
}
}
get isAggregation () {
return _.includes(_.pick(BUILDER_OPERATORS, ['COUNT', 'MAX', 'MIN']), this.builder_operator)
}
get hasSubQuery () {
return this.isAggregation || _.includes(_.pick(BUILDER_OPERATORS, ['EXIST', 'NOT_EXIST']), this.builder_operator)
}
/**
* is_editing getter used by the query builder.
* Use native getter to prevent sending it in the payload
*
* @return {boolean}
*/
get is_editing () {
return this._attributes.is_editing
}
/**
* is_editing setter used by the query builder.
*
* @param {boolean} value
*/
set is_editing (value) {
this._attributes.is_editing = value
}
/**
* Parse builder_* fields to get the true
* criterion structure.
*
* @return {object}
*/
toJSON () {
const baseObject = _.cloneDeep(super.toJSON())
if (this.isAggregation) {
if (baseObject.builder_operator === BUILDER_OPERATORS.COUNT) {
_.unset(baseObject, 'aggregators.0.field')
}
_.set(baseObject, 'aggregators.0.aggregation', baseObject.builder_operator)
return {
relation: baseObject.field,
..._.pick(baseObject, ['aggregators']),
...baseObject.criteria
}
}
if (_.includes(_.pick(BUILDER_OPERATORS, ['EXIST', 'NOT_EXIST']), baseObject.builder_operator)) {
switch (baseObject.builder_operator) {
case BUILDER_OPERATORS.EXIST:
_.set(baseObject, 'type', RELATION_TYPE_OPERATORS.WITH)
_.set(baseObject, 'value', BUILDER_OPERATORS.EXIST)
break
case BUILDER_OPERATORS.NOT_EXIST:
_.set(baseObject, 'type', RELATION_TYPE_OPERATORS.WITHOUT)
_.set(baseObject, 'value', BUILDER_OPERATORS.EXIST)
break
}
return {
relation: baseObject.field,
..._.pick(baseObject, ['type', 'value']),
...baseObject.criteria
}
}
if (baseObject.builder_compare_field) {
return {
operator: baseObject.builder_operator,
..._.pick(baseObject, ['field', 'value_of', 'builder_compare_field'])
}
}
if (_.includes([BUILDER_OPERATORS.LIKE, BUILDER_OPERATORS.NOT_LIKE], baseObject.builder_operator)) {
return {
operator: baseObject.builder_operator,
field: baseObject.field,
value: `%${baseObject.value}%`
}
}
return {
operator: baseObject.builder_operator,
..._.pick(baseObject, ['field', 'value'])
}
}
static getOperatorsFor (fieldType) {
let operators = []
switch (fieldType) {
case 'string' :
operators = ['EQUAL', 'NOT_EQUAL', 'LIKE', 'NOT_LIKE', 'IN', 'NOT_IN', 'NULL', 'NOT_NULL']
break
case 'boolean' :
operators = ['EQUAL', 'NOT_EQUAL', 'NULL', 'NOT_NULL']
break
case 'enum' :
operators = ['EQUAL', 'NOT_EQUAL', 'IN', 'NOT_IN', 'NULL', 'NOT_NULL']
break
case 'relation' :
operators = ['EXIST', 'NOT_EXIST', 'COUNT']
break
case 'integer' :
case 'float' :
case 'decimal' :
operators = ['EQUAL', 'NOT_EQUAL', 'LT', 'LTE', 'GT', 'GTE', 'IN', 'NOT_IN', 'NULL', 'NOT_NULL']
break
case 'date':
case 'datetime':
operators = ['EQUAL', 'NOT_EQUAL', 'LT', 'LTE', 'GT', 'GTE', 'NULL', 'NOT_NULL']
break
case 'model' :
operators = ['EQUAL', 'NOT_EQUAL', 'IN', 'NOT_IN', 'NULL', 'NOT_NULL']
break
case 'aggregation':
operators = ['EQUAL', 'NOT_EQUAL', 'LT', 'LTE', 'GT', 'GTE']
break
}
return _.values(_.pick(Criterion.BUILDER_OPERATORS, operators))
}
}
Criterion.BUILDER_OPERATORS = BUILDER_OPERATORS
Criterion.RELATION_TYPE_OPERATORS = RELATION_TYPE_OPERATORS
Criterion.allBuilderOperators = _.values(BUILDER_OPERATORS)
Criterion.arrayableBuilderOperators = _.values(_.pick(BUILDER_OPERATORS, ['IN', 'NOT_IN']))
export default Criterion