// A is the type of object being listed by the search sclass ScoredSearcher { int maxResults = 1000; LS preparedTerms; new Map scores; *() {} *(S query, O... _) { maxResults = optPar maxResults(_, maxResults); setQuery(query); } void setQuery(S query) { preparedTerms = scoredSearch_prepare(query); } void put(A object, S s) { putUnlessZero(scores, object, scoredSearch_score(s, preparedTerms)); } void put(A object, Cl fields) { add(object, scoreFields(fields)); } // process fields with weights void putWithWeights(A object, Cl> fields) { scoredSearch_scoreWeighted2(fields, preparedTerms); } int scoreFields(Cl fields) { ret scoredSearch_score(fields, preparedTerms); } int score(S text) { ret scoredSearch_score(text, preparedTerms); } void add(A object) { put(object, str(object)); } void add(A object, Cl fields) { put(object, fields); } void put(A object, double score) { add(object, score); } void add(A object, double score) { putUnlessZero(scores, object, score); } L get() { ret takeFirst(maxResults, keysSortedByValuesDesc(scores)); } L> withScores() { ret map(get(), a -> scoredWithMapValue(scores, a)); } A best() { ret keyWithHighestValue(scores); } }