Skip to content

Commit 1b60672

Browse files
committed
Allow custom scoring functions (for ranking results).
1 parent 79c0d91 commit 1b60672

File tree

4 files changed

+244
-197
lines changed

4 files changed

+244
-197
lines changed

jquery.selectize.js

+105-78
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@
146146
inputClass: 'selectize-input',
147147
dropdownClass: 'selectize-dropdown',
148148

149+
score : null, // function(data)
149150
onChange : null, // function(value)
150151
onItemAdd : null, // function(value, $item) { ... }
151152
onItemRemove : null, // function(value) { ... }
@@ -908,6 +909,81 @@
908909
return tokens;
909910
};
910911

912+
/**
913+
* Returns a function to be used to score individual results.
914+
* Results will be sorted by the score (descending). Scores less
915+
* than or equal to zero (no match) will not be included in the results.
916+
*
917+
* @param {object} data
918+
* @param {object} search
919+
* @returns {function}
920+
*/
921+
Selectize.prototype.getScoreCallback = function(search) {
922+
var self = this;
923+
var tokens = search.tokens;
924+
925+
var calculateFieldScore = (function() {
926+
if (!tokens.length) {
927+
return function() { return 0; };
928+
} else if (tokens.length === 1) {
929+
return function(value) {
930+
var score, pos;
931+
932+
value = String(value || '').toLowerCase();
933+
pos = value.search(tokens[0].regex);
934+
if (pos === -1) return 0;
935+
score = tokens[0].string.length / value.length;
936+
if (pos === 0) score += 0.5;
937+
return score;
938+
};
939+
} else {
940+
return function(value) {
941+
var score, pos, i, j;
942+
943+
value = String(value || '').toLowerCase();
944+
score = 0;
945+
for (i = 0, j = tokens.length; i < j; i++) {
946+
pos = value.search(tokens[i].regex);
947+
if (pos === -1) return 0;
948+
if (pos === 0) score += 0.5;
949+
score += tokens[i].string.length / value.length;
950+
}
951+
return score / tokens.length;
952+
};
953+
}
954+
})();
955+
956+
var calculateScore = (function() {
957+
var fields = self.settings.searchField;
958+
if (typeof fields === 'string') {
959+
fields = [fields];
960+
}
961+
if (!fields || !fields.length) {
962+
return function() { return 0; };
963+
} else if (fields.length === 1) {
964+
var field = fields[0];
965+
return function(data) {
966+
if (!data.hasOwnProperty(field)) return 0;
967+
return calculateFieldScore(data[field]);
968+
};
969+
} else {
970+
return function(data) {
971+
var n = 0;
972+
var score = 0;
973+
for (var i = 0, j = fields.length; i < j; i++) {
974+
if (data.hasOwnProperty(fields[i])) {
975+
score += calculateFieldScore(data[fields[i]]);
976+
n++;
977+
}
978+
}
979+
return score / n;
980+
};
981+
}
982+
})();
983+
984+
return calculateScore;
985+
};
986+
911987
/**
912988
* Searches through available options and returns
913989
* a sorted array of matches. Includes options that
@@ -931,108 +1007,51 @@
9311007
* @returns {object}
9321008
*/
9331009
Selectize.prototype.search = function(query, settings) {
1010+
var self = this;
1011+
var value, score, search, calculateScore;
1012+
9341013
settings = settings || {};
9351014
query = $.trim(String(query || '').toLowerCase());
9361015

937-
var self = this;
938-
var tokens, value, score, results;
939-
9401016
if (query !== this.lastQuery) {
9411017
this.lastQuery = query;
942-
tokens = this.parseSearchTokens(query);
9431018

944-
results = {
1019+
search = {
9451020
query : query,
946-
tokens : tokens,
1021+
tokens : this.parseSearchTokens(query),
9471022
total : 0,
9481023
items : []
9491024
};
9501025

951-
var calculateFieldScore = (function() {
952-
if (!tokens.length) {
953-
return function() { return 0; };
954-
} else if (tokens.length === 1) {
955-
return function(value) {
956-
var score, pos;
957-
958-
value = String(value || '').toLowerCase();
959-
pos = value.search(tokens[0].regex);
960-
if (pos === -1) return 0;
961-
score = tokens[0].string.length / value.length;
962-
if (pos === 0) score += 0.5;
963-
return score;
964-
};
965-
} else {
966-
return function(value) {
967-
var score, pos, i, j;
968-
969-
value = String(value || '').toLowerCase();
970-
score = 0;
971-
for (i = 0, j = tokens.length; i < j; i++) {
972-
pos = value.search(tokens[i].regex);
973-
if (pos === -1) return 0;
974-
if (pos === 0) score += 0.5;
975-
score += tokens[i].string.length / value.length;
976-
}
977-
return score / tokens.length;
978-
};
979-
}
980-
})();
981-
982-
var calculateScore = (function() {
983-
var fields = self.settings.searchField;
984-
if (typeof fields === 'string') {
985-
fields = [fields];
986-
}
987-
if (!fields || !fields.length) {
988-
return function() { return 0; };
989-
} else if (fields.length === 1) {
990-
var field = fields[0];
991-
return function(data) {
992-
if (!data.hasOwnProperty(field)) return 0;
993-
return calculateFieldScore(data[field]);
994-
};
995-
} else {
996-
return function(data) {
997-
var n = 0;
998-
var score = 0;
999-
for (var i = 0, j = fields.length; i < j; i++) {
1000-
if (data.hasOwnProperty(fields[i])) {
1001-
score += calculateFieldScore(data[fields[i]]);
1002-
n++;
1003-
}
1004-
}
1005-
return score / n;
1006-
};
1007-
}
1008-
})();
1026+
calculateScore = this.settings.score || this.getScoreCallback(search);
10091027

1028+
// perform search and sort
10101029
if (query.length) {
10111030
for (value in this.options) {
10121031
if (this.options.hasOwnProperty(value)) {
10131032
score = calculateScore(this.options[value]);
10141033
if (score > 0) {
1015-
results.items.push({
1034+
search.items.push({
10161035
score: score,
10171036
value: value
10181037
});
10191038
}
10201039
}
10211040
}
1022-
results.items.sort(function(a, b) {
1041+
search.items.sort(function(a, b) {
10231042
return b.score - a.score;
10241043
});
10251044
} else {
10261045
for (value in this.options) {
10271046
if (this.options.hasOwnProperty(value)) {
1028-
results.items.push({
1047+
search.items.push({
10291048
score: 1,
10301049
value: value
10311050
});
10321051
}
10331052
}
10341053
if (this.settings.sortField) {
1035-
results.items.sort((function() {
1054+
search.items.sort((function() {
10361055
var field = self.settings.sortField;
10371056
var multiplier = self.settings.sortDirection === 'desc' ? -1 : 1;
10381057
return function(a, b) {
@@ -1045,12 +1064,13 @@
10451064
})());
10461065
}
10471066
}
1048-
this.currentResults = results;
1067+
this.currentResults = search;
10491068
} else {
1050-
results = $.extend(true, {}, this.currentResults);
1069+
search = $.extend(true, {}, this.currentResults);
10511070
}
10521071

1053-
return this.prepareResults(results, settings);
1072+
// apply limits and return
1073+
return this.prepareResults(search, settings);
10541074
};
10551075

10561076
/**
@@ -1061,21 +1081,21 @@
10611081
* @param {object} settings
10621082
* @returns {object}
10631083
*/
1064-
Selectize.prototype.prepareResults = function(results, settings) {
1084+
Selectize.prototype.prepareResults = function(search, settings) {
10651085
if (this.settings.hideSelected) {
1066-
for (var i = results.items.length - 1; i >= 0; i--) {
1067-
if (this.items.indexOf(String(results.items[i].value)) !== -1) {
1068-
results.items.splice(i, 1);
1086+
for (var i = search.items.length - 1; i >= 0; i--) {
1087+
if (this.items.indexOf(String(search.items[i].value)) !== -1) {
1088+
search.items.splice(i, 1);
10691089
}
10701090
}
10711091
}
10721092

1073-
results.total = results.items.length;
1093+
search.total = search.items.length;
10741094
if (typeof settings.limit === 'number') {
1075-
results.items = results.items.slice(0, settings.limit);
1095+
search.items = search.items.slice(0, settings.limit);
10761096
}
10771097

1078-
return results;
1098+
return search;
10791099
};
10801100

10811101
/**
@@ -1147,6 +1167,13 @@
11471167
* @param {object} data
11481168
*/
11491169
Selectize.prototype.addOption = function(value, data) {
1170+
if ($.isArray(value)) {
1171+
for (var i = 0, n = value.length; i < n; i++) {
1172+
this.addOption(value[i][this.settings.valueField], value[i]);
1173+
}
1174+
return;
1175+
}
1176+
11501177
if (this.options.hasOwnProperty(value)) return;
11511178
value = String(value);
11521179
this.userOptions[value] = true;

0 commit comments

Comments
 (0)