diff options
author | Marius David Wieschollek <passwords.public@mdns.eu> | 2021-12-15 00:03:14 +0300 |
---|---|---|
committer | Marius David Wieschollek <passwords.public@mdns.eu> | 2021-12-15 00:03:14 +0300 |
commit | 485bf5d647f367435487ac00adcdc2b9f77fed08 (patch) | |
tree | a4d570501fc0339e83b5c69e1956fa9be50219ab | |
parent | ccf33f8cdab77cc4a0c0d1979090898977863ae5 (diff) |
Add new search enginesearch
Signed-off-by: Marius David Wieschollek <passwords.public@mdns.eu>
19 files changed, 1032 insertions, 0 deletions
diff --git a/src/js/NextSearch/Condition/AbstractSearchCondition.js b/src/js/NextSearch/Condition/AbstractSearchCondition.js new file mode 100644 index 0000000..1e85e1c --- /dev/null +++ b/src/js/NextSearch/Condition/AbstractSearchCondition.js @@ -0,0 +1,80 @@ +export default class AbstractSearchCondition { + + /** + * @return {String} + * @constructor + */ + get TYPE() { + return 'abstract'; + } + + /** + * @return {Number} + */ + get length() { + return this._conditions.length; + } + + /** + * + * @return {(AbstractSearchCondition|AbstractSearchField)[]} + */ + get conditions() { + return this._conditions; + } + + /** + * @param {(AbstractSearchCondition|AbstractSearchField)[]} value + */ + set conditions(value) { + this._conditions = value; + } + + /** + * + * @param {AbstractSearchCondition|AbstractSearchField} conditions + */ + constructor(...conditions) { + if(!Array.isArray(conditions)) conditions = []; + + this._conditions = conditions; + } + + /** + * + * @param {(AbstractSearchField|AbstractSearchCondition)} conditions + * @return {AbstractSearchCondition} + */ + add(...conditions) { + this._conditions.push(...conditions); + + return this; + } + + /** + * + * @param {Object} item + * @return {({checks: number, passed: boolean, matches: number}|{passed: false})} + */ + evaluate(item) { + return {matches: 0, checks: 0, passed: false}; + } + + /** + * + * @return {{type: String, conditions: [], operator: String}} + */ + export() { + let data = { + type : 'condition', + operator : this.TYPE, + conditions: [] + }; + + for(let condition of this._conditions) { + data.conditions.push(condition.export()); + } + + return data; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Condition/AndCondition.js b/src/js/NextSearch/Condition/AndCondition.js new file mode 100644 index 0000000..8e4d36a --- /dev/null +++ b/src/js/NextSearch/Condition/AndCondition.js @@ -0,0 +1,25 @@ +import AbstractSearchCondition from '@js/NextSearch/Condition/AbstractSearchCondition'; + +export default class AndCondition extends AbstractSearchCondition { + + get TYPE() { + return 'and'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let result = {matches: 0, checks: 0, passed: true}; + + for(let condition of this._conditions) { + let partialResult = condition.evaluate(item); + + if(!partialResult.passed) return {passed: false}; + result.matches += partialResult.matches; + result.checks += partialResult.checks; + } + + return result; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Condition/ConditionBuilder.js b/src/js/NextSearch/Condition/ConditionBuilder.js new file mode 100644 index 0000000..02d8d51 --- /dev/null +++ b/src/js/NextSearch/Condition/ConditionBuilder.js @@ -0,0 +1,79 @@ +import FieldBuilder from '@js/NextSearch/Field/FieldBuilder'; +import AndCondition from '@js/NextSearch/Condition/AndCondition'; +import OrCondition from '@js/NextSearch/Condition/OrCondition'; +import XorCondition from '@js/NextSearch/Condition/XorCondition'; + +export default class ConditionBuilder { + + /** + * + * @param {AbstractSearchCondition} condition + */ + constructor(condition) { + this._condition = condition; + } + + /** + * + * @param {(String|Function)} [param] + * @returns {FieldBuilder|ConditionBuilder} + */ + where(param) { + if(typeof param === 'string') { + return new FieldBuilder(param, this._condition, this); + } else if(typeof param === 'function') { + param(this); + } + + return this; + } + + /** + * + * @param {(String|Function)} [param] + * @returns {FieldBuilder|ConditionBuilder} + */ + and(param) { + return this._createCondition(param, AndCondition); + } + + /** + * + * @param {(String|Function)} [param] + * @returns {FieldBuilder|ConditionBuilder} + */ + or(param) { + return this._createCondition(param, OrCondition); + } + + /** + * + * @param {(String|Function)} [param] + * @returns {FieldBuilder|ConditionBuilder} + */ + xor(param) { + return this._createCondition(param, XorCondition); + } + + /** + * + * @param param + * @param conditionClass + * @returns {FieldBuilder|ConditionBuilder} + * @private + */ + _createCondition(param, conditionClass) { + let condition = new conditionClass(), + builder = new ConditionBuilder(condition); + + this._condition.add(condition); + if(typeof param === 'string') { + return builder.where(param); + } else if(typeof param === 'function') { + param(builder); + return this; + } + + return builder; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Condition/OrCondition.js b/src/js/NextSearch/Condition/OrCondition.js new file mode 100644 index 0000000..c711cf7 --- /dev/null +++ b/src/js/NextSearch/Condition/OrCondition.js @@ -0,0 +1,28 @@ +import AbstractSearchCondition from '@js/NextSearch/Condition/AbstractSearchCondition'; + +export default class OrCondition extends AbstractSearchCondition { + + get TYPE() { + return 'or'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let result = {matches: 0, checks: 0, passed: false}; + + for(let condition of this._conditions) { + let partialResult = condition.evaluate(item); + + if(partialResult.passed) { + result.passed = true; + result.matches += partialResult.matches; + result.checks += partialResult.checks; + } + } + + if(!result.passed) return {passed: false}; + return result; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Condition/XorCondition.js b/src/js/NextSearch/Condition/XorCondition.js new file mode 100644 index 0000000..56a336e --- /dev/null +++ b/src/js/NextSearch/Condition/XorCondition.js @@ -0,0 +1,31 @@ +import AbstractSearchCondition from '@js/NextSearch/Condition/AbstractSearchCondition'; + +export default class XorCondition extends AbstractSearchCondition { + + get TYPE() { + return 'xor'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let result = {matches: 0, checks: 0, passed: false}; + + for(let condition of this._conditions) { + let partialResult = condition.evaluate(item); + + if(partialResult.passed) { + if(result.passed) return {passed: false}; + + result.passed = true; + result.matches = partialResult.matches; + result.checks = partialResult.checks; + } + } + + if(!result.passed) return {passed: false}; + + return result; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/AbstractSearchField.js b/src/js/NextSearch/Field/AbstractSearchField.js new file mode 100644 index 0000000..4f1e0d2 --- /dev/null +++ b/src/js/NextSearch/Field/AbstractSearchField.js @@ -0,0 +1,41 @@ +export default class AbstractSearchField { + + /** + * @return {String} + * @constructor + */ + get TYPE() { + return 'abstract'; + } + + /** + * + * @param {String} field + * @param {*} value + */ + constructor(field, value) { + this._name = field; + this._value = value; + } + + /** + * + * @param {AbstractModel} item + * @return {({checks: number, passed: boolean, matches: number}|{passed: false})} + */ + evaluate(item) { + return {matches: 0, checks: 0, passed: false}; + } + + /** + * @return {{field: String, type: String, value: *, operator: String}} + */ + export() { + return { + type : 'field', + operator: this.TYPE, + value : this._value, + field : this._name + }; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldBuilder.js b/src/js/NextSearch/Field/FieldBuilder.js new file mode 100644 index 0000000..8dbe743 --- /dev/null +++ b/src/js/NextSearch/Field/FieldBuilder.js @@ -0,0 +1,147 @@ +import FieldEquals from '@js/NextSearch/Field/FieldEquals'; +import FieldContains from '@js/NextSearch/Field/FieldContains'; +import FieldMatches from '@js/NextSearch/Field/FieldMatches'; +import FieldIn from '@js/NextSearch/Field/FieldIn'; +import FieldNotEquals from '@js/NextSearch/Field/FieldNotEquals'; +import FieldNotContains from '@js/NextSearch/Field/FieldNotContains'; +import FieldNotMatches from '@js/NextSearch/Field/FieldNotMatches'; +import FieldNotIn from '@js/NextSearch/Field/FieldNotIn'; + +export default class FieldBuilder { + + /** + * + * @param {String} field + * @param {AbstractSearchCondition} condition + * @param {ConditionBuilder} query + */ + constructor(field, condition, query) { + this._field = field; + this._query = query; + this._condition = condition; + } + + /** + * + * @param {String} value + * @returns {FieldBuilder} + */ + equals(value) { + this._condition.add(new FieldEquals(this._field, value)); + + return this; + } + + /** + * + * @param {String} value + * @returns {FieldBuilder} + */ + contains(value) { + this._condition.add(new FieldContains(this._field, value)); + + return this; + } + + /** + * + * @param {String} value + * @returns {FieldBuilder} + */ + matches(value) { + this._condition.add(new FieldMatches(this._field, value)); + + return this; + } + + /** + * + * @param {String[]} values + * @returns {FieldBuilder} + */ + in(values) { + this._condition.add(new FieldIn(this._field, values)); + + return this; + } + + /** + * + * @param {String} value + * @returns {FieldBuilder} + */ + notEquals(value) { + this._condition.add(new FieldNotEquals(this._field, value)); + + return this; + } + + /** + * + * @param {String} value + * @returns {FieldBuilder} + */ + notContains(value) { + this._condition.add(new FieldNotContains(this._field, value)); + + return this; + } + + /** + * + * @param {String} value + * @returns {FieldBuilder} + */ + notMatches(value) { + this._condition.add(new FieldNotMatches(this._field, value)); + + return this; + } + + /** + * + * @param {String[]} values + * @returns {FieldBuilder} + */ + notIn(values) { + this._condition.add(new FieldNotIn(this._field, values)); + + return this; + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + where(...props) { + return this._query.where(...props); + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + and(...props) { + return this._query.and(...props); + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + or(...props) { + return this._query.or(...props); + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + xor(...props) { + return this._query.xor(...props); + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldContains.js b/src/js/NextSearch/Field/FieldContains.js new file mode 100644 index 0000000..1ab66e1 --- /dev/null +++ b/src/js/NextSearch/Field/FieldContains.js @@ -0,0 +1,24 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldContains extends AbstractSearchField { + + get TYPE() { + return 'contains'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {passed: false}; + + let search = this._value.toString().toLowerCase(); + if(value.toString().toLowerCase().indexOf(search) !== -1) { + return {matches: 1, checks: 1, passed: true}; + } + + return {passed: false}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldEquals.js b/src/js/NextSearch/Field/FieldEquals.js new file mode 100644 index 0000000..08f694e --- /dev/null +++ b/src/js/NextSearch/Field/FieldEquals.js @@ -0,0 +1,20 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldEquals extends AbstractSearchField { + + get TYPE() { + return 'equals'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {passed: false}; + if(value === this._value) return {matches: 1, checks: 1, passed: true}; + + return {passed: false}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldIn.js b/src/js/NextSearch/Field/FieldIn.js new file mode 100644 index 0000000..aeebc67 --- /dev/null +++ b/src/js/NextSearch/Field/FieldIn.js @@ -0,0 +1,23 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldIn extends AbstractSearchField { + + get TYPE() { + return 'in'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {passed: false}; + + if(this._value.indexOf(value) !== -1) { + return {matches: 1, checks: 1, passed: true}; + } + + return {passed: false}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldMatches.js b/src/js/NextSearch/Field/FieldMatches.js new file mode 100644 index 0000000..18f14ef --- /dev/null +++ b/src/js/NextSearch/Field/FieldMatches.js @@ -0,0 +1,25 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldMatches extends AbstractSearchField { + + get TYPE() { + return 'matches'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {passed: false}; + + let regexp = new RegExp(this._value, 'g'), + matches = regexp.exec(value); + if(matches.length > 1) { + return {matches: 1, checks: 1, passed: true}; + } + + return {passed: false}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldNotContains.js b/src/js/NextSearch/Field/FieldNotContains.js new file mode 100644 index 0000000..d52d5f2 --- /dev/null +++ b/src/js/NextSearch/Field/FieldNotContains.js @@ -0,0 +1,24 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldNotContains extends AbstractSearchField { + + get TYPE() { + return 'notContains'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {matches: 1, checks: 1, passed: true}; + + let search = this._value.toString().toLowerCase(); + if(value.toString().toLowerCase().indexOf(search) !== -1) { + return {passed: false}; + } + + return {matches: 1, checks: 1, passed: true}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldNotEquals.js b/src/js/NextSearch/Field/FieldNotEquals.js new file mode 100644 index 0000000..21973b1 --- /dev/null +++ b/src/js/NextSearch/Field/FieldNotEquals.js @@ -0,0 +1,20 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldNotEquals extends AbstractSearchField { + + get TYPE() { + return 'notEquals'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {matches: 1, checks: 1, passed: true}; + if(value === this._value) return {passed: false}; + + return {matches: 1, checks: 1, passed: true}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldNotIn.js b/src/js/NextSearch/Field/FieldNotIn.js new file mode 100644 index 0000000..ffa73e5 --- /dev/null +++ b/src/js/NextSearch/Field/FieldNotIn.js @@ -0,0 +1,23 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldNotIn extends AbstractSearchField { + + get TYPE() { + return 'notIn'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {matches: 1, checks: 1, passed: true}; + + if(this._value.indexOf(value) !== -1) { + return {passed: false}; + } + + return {matches: 1, checks: 1, passed: true}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Field/FieldNotMatches.js b/src/js/NextSearch/Field/FieldNotMatches.js new file mode 100644 index 0000000..8eb4ed2 --- /dev/null +++ b/src/js/NextSearch/Field/FieldNotMatches.js @@ -0,0 +1,26 @@ +import AbstractSearchField from '@js/NextSearch/Field/AbstractSearchField'; + +export default class FieldNotMatches extends AbstractSearchField { + + get TYPE() { + return 'notMatches'; + } + + /** + * @inheritDoc + */ + evaluate(item) { + let value = item.getProperty(this._name); + + if(!value) return {matches: 1, checks: 1, passed: true}; + + let regexp = new RegExp(this._value, 'g'); + let matches = regexp.exec(value); + + if(matches.length > 1) { + return {passed: false}; + } + + return {matches: 1, checks: 1, passed: true}; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Search.js b/src/js/NextSearch/Search.js new file mode 100644 index 0000000..43389c4 --- /dev/null +++ b/src/js/NextSearch/Search.js @@ -0,0 +1,307 @@ +import AndCondition from '@js/NextSearch/Condition/AndCondition'; +import OrCondition from '@js/NextSearch/Condition/OrCondition'; +import XorCondition from '@js/NextSearch/Condition/XorCondition'; +import SortAscending from '@js/NextSearch/Sort/SortAscending'; +import SortDescending from '@js/NextSearch/Sort/SortDescending'; +import ConditionBuilder from '@js/NextSearch/Condition/ConditionBuilder'; + +export default class Search { + + /** + * + * @return {Boolean} + */ + get sorted() { + return this._sort.length !== 0; + } + + /** + * + * @return {Boolean} + */ + get filtered() { + return this._condition.length !== 0; + } + + /** + * + * @return {AbstractSearchSort[]} + */ + get sort() { + return this._sort; + } + + /** + * + * @param {AbstractSearchSort[]} value + */ + set sort(value) { + this._sort = value; + } + + /** + * + * @return {AbstractSearchCondition[]} + */ + get conditions() { + return this._condition.conditions; + } + + /** + * + * @param {AbstractSearchCondition[]} value + */ + set conditions(value) { + this._condition.conditions = value; + } + + /** + * @return {Number} + */ + get limit() { + return this._limit; + } + + /** + * @param {Number} value + */ + set limit(value) { + this._limit = value; + } + + /** + * @return {Number} + */ + get score() { + return this._score; + } + + /** + * @param {Number} value + */ + set score(value) { + this._score = value; + } + + get type() { + return this._condition.TYPE; + } + + /** + * + * @param value + */ + set type(value) { + let conditions = this.conditions; + this._condition = this._makeCondition(value); + this._builder = new ConditionBuilder(this._condition); + this.conditions = conditions; + } + + /** + * + * @param {String} [condition=and] + */ + constructor(condition = 'and') { + this._condition = this._makeCondition(condition); + this._builder = new ConditionBuilder(this._condition); + this._sort = []; + this._score = 0; + this._limit = 0; + } + + clear() { + this._condition.conditions = []; + this._sort = []; + this._score = 0; + this._limit = 0; + } + + /** + * @api + * @public + * + * @param {String} field + * @param {Boolean} ascending + * @return {Search} + */ + sortBy(field, ascending = false) { + if(ascending) { + this._sort.push(new SortAscending(field)); + } else { + this._sort.push(new SortDescending(field)); + } + + return this; + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + where(...props) { + return this._builder.where(...props); + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + and(...props) { + return this._builder.and(...props); + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + or(...props) { + return this._builder.or(...props); + } + + /** + * + * @param props + * @returns {FieldBuilder|ConditionBuilder} + */ + xor(...props) { + return this._builder.xor(...props); + } + + /** + * @api + * @public + * + * @param {(AbstractView|AbstractCollection)} view + * + * @return {AbstractModel[]} + */ + execute(view) { + let items = view.getClone(), + matches = []; + + for(let item of items) { + let result = this._condition.evaluate(item); + + if(result.passed) { + let score = result.checks === 0 ? 0 : result.matches / result.checks; + if(score >= this._score) { + matches.push({item, score}); + } + } + } + + if(matches.length !== 0 && this._sort.length !== 0) { + matches.sort( + (a, b) => { return this._sortFunction(a, b); } + ); + } + + if(this._limit > 0 && matches.length > this._limit) { + matches = matches.splice(0, this._limit); + } + + let result = []; + for(let match of matches) { + result.push(match.item); + } + + return result; + } + + /** + * + * @return {{score: Number, condition: {type: String, conditions: *[], operator: String}, limit: Number, sort: []}} + */ + export() { + let data = { + condition: this._condition.export(), + limit : this._limit, + score : this._score, + sort : [] + }; + + for(let sort of this._sort) { + data.sort.push(sort.export()); + } + + return data; + } + + /** + * + * @return {{score: Number, condition: {type: String, conditions: *[], operator: String}, limit: Number, sort: []}} + */ + import(data) { + if(data.hasOwnProperty('limit')) this._limit = data.limit; + if(data.hasOwnProperty('score')) this._score = data.score; + + if(data.hasOwnProperty('sort')) { + this._sort = []; + for(let sort of data.sort) { + this.sortBy(sort.field, sort.ascending); + } + } + + if(data.hasOwnProperty('condition')) { + this._condition = this._makeCondition(data.condition.operator); + this._builder = new ConditionBuilder(this._condition); + + this._importConditions(this._builder, data.condition.conditions); + } + } + + /** + * + * @param {ConditionBuilder} builder + * @param {Object[]} conditions + * @private + */ + _importConditions(builder, conditions) { + for(let condition of conditions) { + if(condition.type === 'field') { + builder.where(condition.field)[condition.operator](condition.value); + } else { + builder[condition.operator]((condBuilder) => { + this._importConditions(condBuilder, condition.conditions); + }); + } + } + } + + + /** + * + * @param {AbstractModel} a + * @param {AbstractModel} b + * @return {Number} + * @private + */ + _sortFunction(a, b) { + for(let sort of this._sort) { + let result = sort.compare(a, b); + if(result !== 0) return result; + } + + return 0; + } + + /** + * + * @param type + * @return {OrCondition|XorCondition|AndCondition} + * @private + */ + _makeCondition(type) { + if(type === 'or') { + return new OrCondition(); + } else if(type === 'xor') { + return new XorCondition(); + } else { + return new AndCondition(); + } + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Sort/AbstractSearchSort.js b/src/js/NextSearch/Sort/AbstractSearchSort.js new file mode 100644 index 0000000..1e8352f --- /dev/null +++ b/src/js/NextSearch/Sort/AbstractSearchSort.js @@ -0,0 +1,75 @@ +export default class AbstractSearchSort { + + /** + * @return {String} + * @constructor + */ + get TYPE() { + return 'abstract'; + } + + /** + * @return {String} + */ + get field() { + return this._field; + } + + /** + * + * @param {String} field + */ + constructor(field) { + this._field = field; + } + + /** + * + * @param {Object} a + * @param {Object} b + * @return {Number} + */ + compare(a, b) { + let valueA = this._getFieldValue(a), + valueB = this._getFieldValue(b); + + return this._compareValues(valueA, valueB); + } + + /** + * + * @return {{field: String, type: string, ascending: boolean}} + */ + export() { + return { + type : 'sort', + ascending: this.TYPE === 'ascending', + field : this._field + }; + } + + /** + * + * @param {*} a + * @param {*} b + * @return {Number} + * @private + */ + _compareValues(a, b) { + return 0; + } + + /** + * + * @param {Object} item + * @return {*} + * @private + */ + _getFieldValue(item) { + if(this._field === 'score') { + return item.score; + } + + return item.item.getProperty(this._field); + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Sort/SortAscending.js b/src/js/NextSearch/Sort/SortAscending.js new file mode 100644 index 0000000..b86d131 --- /dev/null +++ b/src/js/NextSearch/Sort/SortAscending.js @@ -0,0 +1,17 @@ +import AbstractSearchSort from '@js/NextSearch/Sort/AbstractSearchSort'; + +export default class SortAscending extends AbstractSearchSort { + + get TYPE() { + return 'ascending'; + } + + _compareValues(a, b) { + if(a === b) return 0; + if(typeof a === 'string') { + if(b === null) return -1; + return a.localeCompare(b, undefined, {numeric: true, sensitivity: 'base'}); + } + return a < b ? -1 : 1; + } +}
\ No newline at end of file diff --git a/src/js/NextSearch/Sort/SortDescending.js b/src/js/NextSearch/Sort/SortDescending.js new file mode 100644 index 0000000..2b75024 --- /dev/null +++ b/src/js/NextSearch/Sort/SortDescending.js @@ -0,0 +1,17 @@ +import AbstractSearchSort from '@js/NextSearch/Sort/AbstractSearchSort'; + +export default class SortDescending extends AbstractSearchSort { + + get TYPE() { + return 'descending'; + } + + _compareValues(a, b) { + if(a === b) return 0; + if(typeof a === 'string') { + if(b === null) return 1; + return b.localeCompare(a, undefined, {numeric: true, sensitivity: 'base'}); + } + return b < a ? -1 : 1; + } +}
\ No newline at end of file |