// A is the type of object being listed by the search sclass ScoredSearcher_stable { int maxResults = 1000; bool returnAll; // special mode to return all items LS preparedTerms; MultiMap byScore = descTreeMultiMap(); *() {} *(S query, O... _) { maxResults = optPar maxResults(_, maxResults); setQuery(query); } void setQuery(S query) { preparedTerms = scoredSearch_prepare(query); } void put(A object, S s) { putScored(object, score(s)); } void putScored(A object, double score) { if (score != 0) byScore.add(score, object); } 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 returnAll ? 1 : 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) { putScored(object, score); } L get() { ret pairsB(takeFirst(maxResults, multiMapPairIterator(byScore))); } L get_transformListWithinScore(IF1> f) { ret pairsB(takeFirst(maxResults, multiMapPairIterator_transformValueList(byScore, f))); } L> withScores() { ret map(takeFirst(maxResults, multiMapPairIterator(byScore)), p -> Scored(p.b, p.a)); } A best() { ret firstValue(byScore); } }