Skip to content

fix search sorting #1666

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 53 additions & 10 deletions js/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,55 @@ export function md5(inputString) {
return rh(a)+rh(b)+rh(c)+rh(d);
}

const levenArray = [];
const levenCodeCache = [];
export const leven = (first, second) => {
if (first === second) {
return 0;
}
const swap = first;
if (first.length > second.length) {
first = second;
second = swap;
}
let firstLength = first.length;
let secondLength = second.length;
while (firstLength > 0 && (first.charCodeAt(~-firstLength) === second.charCodeAt(~-secondLength))) {
firstLength--;
secondLength--;
}
let start = 0;
while (start < firstLength && (first.charCodeAt(start) === second.charCodeAt(start))) {
start++;
}
firstLength -= start;
secondLength -= start;
if (firstLength === 0) {
return secondLength;
}
let bCharacterCode;
let result;
let temporary;
let temporary2;
let index = 0;
let index2 = 0;
while (index < firstLength) {
levenCodeCache[index] = first.charCodeAt(start + index);
levenArray[index] = ++index;
}
while (index2 < secondLength) {
bCharacterCode = second.charCodeAt(start + index2);
temporary = index2++;
result = index2;
for (index = 0; index < firstLength; index++) {
temporary2 = bCharacterCode === levenCodeCache[index] ? temporary : temporary + 1;
temporary = levenArray[index];
result = levenArray[index] = temporary > result ? (temporary2 > result ? result + 1 : temporary2) : (temporary2 > temporary ? temporary + 1 : temporary2);
}
}
return result;
}

export async function fetchData(route, options) {
let err;
const res = await api.fetchApi(route, options).catch(e => {
Expand Down Expand Up @@ -595,12 +644,10 @@ export function showPopover(target, text, className, options) {
}

let $tooltip;
export function hideTooltip(target) {
export function hideTooltip() {
if ($tooltip) {
$tooltip.style.display = "none";
$tooltip.innerHTML = "";
$tooltip.style.top = "0px";
$tooltip.style.left = "0px";
$tooltip.remove();
$tooltip = null;
}
}
export function showTooltip(target, text, className = 'cn-tooltip', styleMap = {}) {
Expand Down Expand Up @@ -639,11 +686,7 @@ function initTooltip () {
}
};
const mouseleaveHandler = (e) => {
const target = e.target;
const text = target.getAttribute('tooltip');
if (text) {
hideTooltip(target);
}
hideTooltip();
};
document.body.removeEventListener('mouseenter', mouseenterHandler, true);
document.body.removeEventListener('mouseleave', mouseleaveHandler, true);
Expand Down
118 changes: 111 additions & 7 deletions js/custom-nodes-manager.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
fetchData, md5, icons, show_message, customConfirm, customAlert, customPrompt,
sanitizeHTML, infoToast, showTerminal, setNeedRestart,
storeColumnWidth, restoreColumnWidth, getTimeAgo, copyText, loadCss,
showPopover, hidePopover
showPopover, hidePopover, leven
} from "./common.js";

// https://cenfun.github.io/turbogrid/api.html
Expand Down Expand Up @@ -418,11 +418,7 @@ export class CustomNodesManager {

".cn-manager-keywords": {
input: (e) => {
const keywords = `${e.target.value}`.trim();
if (keywords !== this.keywords) {
this.keywords = keywords;
this.updateGrid();
}
this.onKeywordsChange(e);
},
focus: (e) => e.target.select()
},
Expand Down Expand Up @@ -539,7 +535,7 @@ export class CustomNodesManager {

this.addHighlight(d.rowItem);

if (d.columnItem.id === "nodes") {
if (d.columnItem && d.columnItem.id === "nodes") {
this.showNodes(d);
return;
}
Expand Down Expand Up @@ -596,6 +592,14 @@ export class CustomNodesManager {
return autoHeightColumns.includes(columnItem.id)
},

rowFilteredSort: () => {
if (this.keywords) {
return {
id: 'sort_score'
};
}
},

// updateGrid handler for filter and keywords
rowFilter: (rowItem) => {

Expand All @@ -612,12 +616,109 @@ export class CustomNodesManager {
}
}

// calculate sort score
if (shouldShown && this.keywords) {
rowItem.sort_score = this.calculateSortScore(rowItem, searchableColumns);
}

return shouldShown;
}
});

}

onKeywordsChange(e) {
this.grid.showLoading();
// debounce for performance
clearTimeout(this.timeKeywords);
this.timeKeywords = setTimeout(() => {
this.grid.hideLoading();
const keywords = `${e.target.value}`.trim();
if (keywords !== this.keywords) {
this.keywords = keywords;
if (keywords) {
this.grid.removeSortColumn();
} else {
this.grid.sortRows("id", {
sortAsc: true
});
}
this.updateGrid();
}
}, 300);
}

calculateSortScore(rowItem, searchableColumns) {
const keywords = this.keywords.split(/\s+/g).filter((s) => s);
const lowerKeywords = keywords.map(k => k.toLowerCase());
const matchedList = searchableColumns.map(id => {
const { highlightKey, textKey } = this.grid.options.highlightKeywords;
const highlight = rowItem[`${highlightKey}${id}`];
if (!highlight) {
return;
}
const text = `${rowItem[`${textKey}${id}`] || rowItem[id]}`;
const lowerText = text.toLowerCase();
const matchedItems = keywords.map((key, i) => {
// multiple matched
const lowerKey = lowerKeywords[i];
const len = lowerKey.length;
const matches = [];
let index = lowerText.indexOf(lowerKey);
while (index !== -1) {
matches.push(index);
index = lowerText.indexOf(lowerKey, index + len);
}
if (!matches.length) {
return
}
const distances = matches.map(start => {
const end = start + len
const str = text.slice(start, end);
let distance = leven(key, str);
const prev = text.slice(start - 1, start);
if (prev) {
if (/[A-Za-z]/.test(prev)) {
distance += 1;
} else if (/[0-9]/.test(prev)) {
distance += 0.8;
}
}
const next = text.slice(end, end + 1);
if (next) {
if (/[A-Za-z]/.test(next)) {
distance += 0.8;
} else if (/[0-9]/.test(next)) {
distance += 0.5;
}
}
return distance;
});
// console.log(rowItem.title, distances)
return {
// min
distance: Math.min.apply(null, distances)
}
}).filter(it => it);
if (matchedItems.length < keywords.length) {
return;
}
return {
// avg
distance: matchedItems.map(it => it.distance).reduce((p, v) => p + v, 0) / matchedItems.length
}
}).filter(it => it);
// by distance
let distance = Math.min.apply(null, matchedList.map(it => it.distance));
// by matched count
distance += 1 - matchedList.length / searchableColumns.length;
// by stars
const stars = TG.Util.toNum(rowItem.stars);
distance += 1 - stars / 10000;
// score
return 1 / distance;
}

hasAlternatives() {
return this.filter === ShowMode.ALTERNATIVES
}
Expand Down Expand Up @@ -756,6 +857,7 @@ export class CustomNodesManager {
id: "nodes",
name: "Nodes",
width: 100,
sortAsc: false,
formatter: (v, rowItem, columnItem) => {
if (!rowItem.nodes) {
return '';
Expand Down Expand Up @@ -796,6 +898,7 @@ export class CustomNodesManager {
id: 'stars',
name: '★',
align: 'center',
sortAsc: false,
classMap: "cn-pack-stars",
formatter: (stars) => {
if (stars < 0) {
Expand All @@ -811,6 +914,7 @@ export class CustomNodesManager {
name: 'Last Update',
align: 'center',
type: 'date',
sortAsc: false,
width: 100,
classMap: "cn-pack-last-update",
formatter: (last_update) => {
Expand Down