All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.lenni0451.commons.strings.search.SearchEngine Maven / Gradle / Ivy

The newest version!
package net.lenni0451.commons.strings.search;

import net.lenni0451.commons.math.MathUtils;
import net.lenni0451.commons.strings.search.tokens.ISearchToken;
import net.lenni0451.commons.strings.search.tokens.QueryTokenizer;

import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;

public class SearchEngine {

    private final Supplier> supplier;
    private final Function toStringFunction;

    public SearchEngine(final Supplier> supplier, final Function toStringFunction) {
        this.supplier = supplier;
        this.toStringFunction = toStringFunction;
    }

    /**
     * Searches for objects in the supplier that match the given query.
* The query can contain: *
    *
  • Keywords: Words that are separated by spaces
  • *
  • Required Words: Words that are surrounded by single or double quotes
  • *
  • OR: The keyword {@code OR} to combine two keywords with a binary or operator
  • *
* * @param filter The filter that should be applied to the results * @param query The query to search for * @return The search result containing the results and metadata */ public SearchResult search(final ResultFilter filter, final String query) { List> results = new ArrayList<>(); List tokens = QueryTokenizer.tokenize(query); if (tokens.isEmpty()) return new SearchResult<>(query, 0, filter, Collections.emptyList(), true); Iterator it = this.supplier.get().iterator(); boolean hasElements = false; while (it.hasNext()) { hasElements = true; T object = it.next(); SearchScore result = this.calculateScore(object, tokens); if (result.score > 0) results.add(result); } if (!hasElements) return new SearchResult<>(query, tokens.size(), filter, Collections.emptyList(), true); results.sort((o1, o2) -> Integer.compare(o2.score, o1.score)); switch (filter) { case ALL: break; case GAP: this.filterScoreGap(results); break; case HIGHEST: if (!results.isEmpty()) { int maxScore = results.get(0).score; results.removeIf(result -> result.score < maxScore); } break; } List sortedResults = new ArrayList<>(); for (SearchScore result : results) sortedResults.add(result.object); return new SearchResult<>(query, tokens.size(), filter, sortedResults, false); } private SearchScore calculateScore(final T object, final List tokens) { String string = this.toStringFunction.apply(object).toLowerCase(Locale.ROOT); int score = 0; for (ISearchToken token : tokens) { int tokenScore = 0; tokenScore += token.matches(string); tokenScore += token.contains(string); if (token.required() && tokenScore == 0) return new SearchScore<>(object, 0); score += tokenScore; } return new SearchScore<>(object, score); } private void filterScoreGap(final List> results) { if (results.size() <= 1) return; int maxScore = results.get(0).score; int minScore = results.get(results.size() - 1).score; int gap = maxScore - minScore; if (gap <= 0) return; int minGap = MathUtils.ceilInt(gap / 2F); for (int i = results.size() - 1; i >= 0; i--) { if (results.get(i).score < minScore + minGap) results.remove(i); } } private static class SearchScore { private final T object; private final int score; private SearchScore(final T object, final int score) { this.object = object; this.score = score; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy