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

li.strolch.search.SearchBuilder Maven / Gradle / Ivy

The newest version!
package li.strolch.search;

import static li.strolch.search.ExpressionsSupport.*;
import static li.strolch.search.PredicatesSupport.containsIgnoreCase;
import static li.strolch.search.PredicatesSupport.isEqualTo;
import static li.strolch.utils.helper.StringHelper.trimOrEmpty;

import java.util.List;
import java.util.function.Function;

import li.strolch.model.ParameterBag;
import li.strolch.model.State;
import li.strolch.model.StrolchRootElement;
import li.strolch.model.Tags;
import li.strolch.model.parameter.Parameter;
import li.strolch.utils.helper.StringHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 

A helper class to build search expressions on {@link StrolchRootElement}

* *

This is often used in a web context where searches are performed on multiple parameters etc. of an element

* *

Note that the query string is parsed using the following rules:

*
    *
  • query is trimmed
  • *
  • empty query means search for everything, i.e. no {@link SearchExpression SearchExpressions} are added
  • *
  • query is split by space, and each part is handled further:
  • *
  • format {@code param:::} adds search expression for given * bag/param
  • *
  • otherwise search expression for id and name are added
  • *
  • all added search expressions are ANDed
  • *
*/ public class SearchBuilder { private static final Logger logger = LoggerFactory.getLogger(SearchBuilder.class); /** * Builds an {@link OrderSearch} for the given types with the given query * * @param query * the query * @param types * the type of orders to search * * @return the {@link OrderSearch} */ public static OrderSearch buildOrderSearch(String query, String... types) { return buildSearch(new OrderSearch().types(types), query); } /** * Builds an {@link ResourceSearch} for the given types with the given query * * @param query * the query * @param types * the type of resources to search * * @return the {@link OrderSearch} */ public static ResourceSearch buildResourceSearch(String query, String... types) { return buildSearch(new ResourceSearch().types(types), query); } /** * Builds an {@link ActivitySearch} for the given types with the given query * * @param query * the query * @param types * the type of activities to search * * @return the {@link OrderSearch} */ public static ActivitySearch buildActivitySearch(String query, String... types) { return buildSearch(new ActivitySearch().types(types), query); } public static ValueSearch buildSimpleValueSearch(ValueSearch search, String query, List> extractors) { if (extractors.isEmpty() || query == null) return search; query = query.trim(); if (query.isEmpty()) return search; boolean negate = false; if (query.startsWith("!")) { negate = true; query = query.substring(1); } String[] parts = query.split(" "); ValueSearchExpression searchExpression = null; for (String part : parts) { ValueSearchExpression partSearchExpression = null; for (Function extractor : extractors) { if (partSearchExpression == null) { partSearchExpression = ValueSearchExpressionBuilder.containsIgnoreCase(extractor, part); } else { partSearchExpression = partSearchExpression .or(ValueSearchExpressionBuilder.containsIgnoreCase(extractor, part)); } } if (searchExpression == null) { searchExpression = partSearchExpression; } else { searchExpression = searchExpression.and(partSearchExpression); } } if (searchExpression != null) { if (negate) searchExpression = searchExpression.not(); search.where(searchExpression); } return search; } @SuppressWarnings("unchecked") private static > U buildSearch(U search, String query) { query = trimOrEmpty(query); if (query.isEmpty()) return search; boolean and = true; if (query.startsWith("&")) { query = query.substring(1); } else if (query.startsWith("and ")) { query = query.substring("and ".length()); } else if (query.startsWith("|")) { and = false; query = query.substring(1); } else if (query.startsWith("or ")) { and = false; query = query.substring("or ".length()); } query = trimOrEmpty(query); SearchExpression se = null; String[] parts = query.split(" "); for (String part : parts) { part = part.trim(); boolean negate = false; if (part.startsWith("!")) { negate = true; part = part.substring(1); } String[] paramParts = parseParts(part); if (paramParts.length != 3) { SearchExpression expression; if (part.startsWith("$state") && (search instanceof OrderSearch || search instanceof ActivitySearch)) { part = part.substring("$state".length() + 1); if (part.isEmpty() || State.parseAllowNull(part) == null) { expression = id(containsIgnoreCase(part)).or(name(containsIgnoreCase(part))); } else { expression = state(isEqualTo(part)); } } else { expression = id(containsIgnoreCase(part)).or(name(containsIgnoreCase(part))); } se = add(and, negate, se, expression); } else { String bagId = paramParts[0]; String paramId = paramParts[1]; String value = paramParts[2]; SearchPredicate predicate; if (value.isEmpty()) predicate = isEqualTo(value); else predicate = containsIgnoreCase(value); if (!bagId.equals("?")) { se = add(and, negate, se, param(bagId, paramId, predicate)); } else { se = add(and, negate, se, element -> { for (String b : element.getParameterBagKeySet()) { ParameterBag bag = element.getParameterBag(b); Parameter param = bag.getParameter(paramId); if (param != null && predicate.matches(param.getValue())) return true; } return false; }); } } } if (se == null) throw new IllegalArgumentException("search expression not evaluated for string " + query); search = (U) search.where(se); return search; } private static String[] parseParts(String part) { int pos1 = part.indexOf(':'); if (pos1 == -1) return new String[0]; int pos2 = part.indexOf(':', pos1 + 1); if (pos2 == -1) return new String[0]; String bagId = part.substring(0, pos1); String paramId = part.substring(pos1 + 1, pos2); String query = part.substring(pos2 + 1); if (bagId.isEmpty() || paramId.isEmpty() || query.isEmpty()) return new String[0]; return new String[] { bagId, paramId, query }; } private static SearchExpression add(boolean and, boolean negate, SearchExpression se, SearchExpression expression) { if (negate) expression = expression.not(); if (se == null) return expression; if (and) return se.and(expression); else return se.or(expression); } public static RootElementSearchResult orderBy( RootElementSearchResult searchResult, String orderBy, boolean descending) { if (StringHelper.isEmpty(orderBy)) return searchResult.orderById(descending); switch (orderBy) { case Tags.Json.ID -> { return searchResult.orderById(descending); } case Tags.Json.NAME -> { return searchResult.orderByName(descending); } default -> logger.warn("Unhandled ordering " + orderBy); } return searchResult; } }