mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 21:18:58 +00:00
(search) Refactor search parameters to include query
This commit is contained in:
parent
01621c6344
commit
a258f0af7a
@ -109,12 +109,14 @@ public class AbstractClientTest {
|
||||
|
||||
assertError(client.post(Context.internal(), 0,"/post", "test"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGet404() {
|
||||
testServer.get(this::error404);
|
||||
|
||||
assertError(client.get(Context.internal(), 0,"/get"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDelete404() {
|
||||
testServer.delete(this::error404);
|
||||
|
@ -9,10 +9,11 @@ import nu.marginalia.model.EdgeDomain;
|
||||
import nu.marginalia.db.DbDomainQueries;
|
||||
import nu.marginalia.query.client.QueryClient;
|
||||
import nu.marginalia.query.model.QueryResponse;
|
||||
import nu.marginalia.search.command.SearchParameters;
|
||||
import nu.marginalia.search.model.SearchProfile;
|
||||
import nu.marginalia.search.model.UrlDetails;
|
||||
import nu.marginalia.client.Context;
|
||||
import nu.marginalia.search.model.DecoratedSearchResults;
|
||||
import nu.marginalia.search.model.UserSearchParameters;
|
||||
import nu.marginalia.search.svc.SearchQueryIndexService;
|
||||
import nu.marginalia.search.svc.SearchUnitConversionService;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
@ -70,9 +71,9 @@ public class SearchOperator {
|
||||
return searchQueryService.getResultsFromQuery(queryResponse);
|
||||
}
|
||||
|
||||
public DecoratedSearchResults doSearch(Context ctx, UserSearchParameters userParams) {
|
||||
public DecoratedSearchResults doSearch(Context ctx, SearchParameters userParams) {
|
||||
|
||||
Future<String> eval = searchUnitConversionService.tryEval(ctx, userParams.humanQuery());
|
||||
Future<String> eval = searchUnitConversionService.tryEval(ctx, userParams.query());
|
||||
var queryParams = paramFactory.forRegularSearch(userParams);
|
||||
var queryResponse = queryClient.search(ctx, queryParams);
|
||||
|
||||
|
@ -5,20 +5,20 @@ import nu.marginalia.index.client.model.query.SearchSubquery;
|
||||
import nu.marginalia.index.query.limit.QueryLimits;
|
||||
import nu.marginalia.index.query.limit.SpecificationLimit;
|
||||
import nu.marginalia.query.model.QueryParams;
|
||||
import nu.marginalia.search.model.UserSearchParameters;
|
||||
import nu.marginalia.search.command.SearchParameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SearchQueryParamFactory {
|
||||
|
||||
public QueryParams forRegularSearch(UserSearchParameters userParams) {
|
||||
public QueryParams forRegularSearch(SearchParameters userParams) {
|
||||
SearchSubquery prototype = new SearchSubquery();
|
||||
var profile = userParams.profile();
|
||||
profile.addTacitTerms(prototype);
|
||||
userParams.jsSetting().addTacitTerms(prototype);
|
||||
userParams.js().addTacitTerms(prototype);
|
||||
|
||||
return new QueryParams(
|
||||
userParams.humanQuery(),
|
||||
userParams.query(),
|
||||
null,
|
||||
prototype.searchTermsInclude,
|
||||
prototype.searchTermsExclude,
|
||||
|
@ -30,16 +30,16 @@ public class CommandEvaluator {
|
||||
defaultCommand = search;
|
||||
}
|
||||
|
||||
public Object eval(Context ctx, SearchParameters parameters, String query) {
|
||||
public Object eval(Context ctx, SearchParameters parameters) {
|
||||
for (var cmd : specialCommands) {
|
||||
var ret = cmd.process(ctx, parameters, query);
|
||||
var ret = cmd.process(ctx, parameters);
|
||||
if (ret.isPresent()) {
|
||||
return ret.get();
|
||||
}
|
||||
}
|
||||
|
||||
// Always process the search command last
|
||||
return defaultCommand.process(ctx, parameters, query)
|
||||
return defaultCommand.process(ctx, parameters)
|
||||
.orElseThrow(() -> new IllegalStateException("Search Command returned Optional.empty()!") /* This Should Not be Possible™ */ );
|
||||
}
|
||||
|
||||
|
@ -6,5 +6,5 @@ import nu.marginalia.client.Context;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface SearchCommandInterface {
|
||||
Optional<Object> process(Context ctx, SearchParameters parameters, String query);
|
||||
Optional<Object> process(Context ctx, SearchParameters parameters);
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ package nu.marginalia.search.command;
|
||||
|
||||
import nu.marginalia.search.model.SearchProfile;
|
||||
|
||||
public record SearchParameters(SearchProfile profile, SearchJsParameter js, boolean detailedResults) {
|
||||
public record SearchParameters(String query, SearchProfile profile, SearchJsParameter js) {
|
||||
public String profileStr() {
|
||||
return profile.name;
|
||||
}
|
||||
|
@ -23,36 +23,75 @@ public class BangCommand implements SearchCommandInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters, String query) {
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters) {
|
||||
|
||||
for (var entry : bangsToPattern.entrySet()) {
|
||||
matchBangPattern(query, entry.getKey(), entry.getValue());
|
||||
String bangPattern = entry.getKey();
|
||||
String redirectPattern = entry.getValue();
|
||||
|
||||
var match = matchBangPattern(parameters.query(), bangPattern);
|
||||
|
||||
if (match.isPresent()) {
|
||||
var url = String.format(redirectPattern, URLEncoder.encode(match.get(), StandardCharsets.UTF_8));
|
||||
throw new RedirectException(url);
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void matchBangPattern(String query, String bangKey, String urlPattern) {
|
||||
for (int idx = query.indexOf(bangKey); idx >= 0; idx = query.indexOf(bangKey, idx + 1)) {
|
||||
private Optional<String> matchBangPattern(String query, String bangKey) {
|
||||
var bm = new BangMatcher(query);
|
||||
|
||||
if (idx > 0) { // Don't match "search term!b", require either "!b term" or "search term !b"
|
||||
if (!Character.isSpaceChar(query.charAt(idx-1))) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
int nextIdx = idx + bangKey.length();
|
||||
while (bm.findNext(bangKey)) {
|
||||
|
||||
if (nextIdx >= query.length()) { // allow "search term !b"
|
||||
redirect(urlPattern, query.substring(0, idx));
|
||||
}
|
||||
else if (Character.isSpaceChar(query.charAt(nextIdx))) { // skip matches on pattern "!bsearch term" for !b
|
||||
redirect(urlPattern, query.substring(0, idx).stripTrailing() + " " + query.substring(nextIdx).stripLeading());
|
||||
}
|
||||
if (bm.isRelativeSpaceOrInvalid(-1))
|
||||
continue;
|
||||
if (bm.isRelativeSpaceOrInvalid(bangKey.length()))
|
||||
continue;
|
||||
|
||||
String queryWithoutBang = bm.prefix().trim() + " " + bm.suffix(bangKey.length()).trim();
|
||||
return Optional.of(queryWithoutBang);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private void redirect(String pattern, String terms) {
|
||||
var url = String.format(pattern, URLEncoder.encode(terms.trim(), StandardCharsets.UTF_8));
|
||||
throw new RedirectException(url);
|
||||
private static class BangMatcher {
|
||||
private final String str;
|
||||
private int pos;
|
||||
|
||||
public String prefix() {
|
||||
return str.substring(0, pos);
|
||||
}
|
||||
|
||||
public String suffix(int offset) {
|
||||
if (pos+offset < str.length())
|
||||
return str.substring(pos + offset);
|
||||
return "";
|
||||
}
|
||||
|
||||
public BangMatcher(String str) {
|
||||
this.str = str;
|
||||
this.pos = -1;
|
||||
}
|
||||
|
||||
public boolean findNext(String pattern) {
|
||||
if (pos + 1 >= str.length())
|
||||
return false;
|
||||
|
||||
return (pos = str.indexOf(pattern, pos + 1)) >= 0;
|
||||
}
|
||||
|
||||
public boolean isRelativeSpaceOrInvalid(int offset) {
|
||||
if (offset + pos < 0)
|
||||
return true;
|
||||
if (offset + pos >= str.length())
|
||||
return true;
|
||||
|
||||
return Character.isSpaceChar(str.charAt(offset + pos));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -56,14 +56,14 @@ public class BrowseCommand implements SearchCommandInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters, String query) {
|
||||
if (!queryPatternPredicate.test(query)) {
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters) {
|
||||
if (!queryPatternPredicate.test(parameters.query())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.ofNullable(browseSite(ctx, query))
|
||||
return Optional.ofNullable(browseSite(ctx, parameters.query()))
|
||||
.map(results -> browseResultsRenderer.render(results,
|
||||
Map.of("query", query,
|
||||
Map.of("query", parameters.query(),
|
||||
"profile", parameters.profileStr(),
|
||||
"focusDomain", results.focusDomain())));
|
||||
}
|
||||
|
@ -24,12 +24,16 @@ public class ConvertCommand implements SearchCommandInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters, String query) {
|
||||
var conversion = searchUnitConversionService.tryConversion(ctx, query);
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters) {
|
||||
var conversion = searchUnitConversionService.tryConversion(ctx, parameters.query());
|
||||
if (conversion.isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return Optional.of(conversionRenderer.render(Map.of("query", query, "result", conversion.get(), "profile", parameters.profileStr())));
|
||||
return Optional.of(conversionRenderer.render(Map.of(
|
||||
"query", parameters.query(),
|
||||
"result", conversion.get(),
|
||||
"profile", parameters.profileStr()))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -38,14 +38,17 @@ public class DefinitionCommand implements SearchCommandInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters, String query) {
|
||||
if (!queryPatternPredicate.test(query.trim())) {
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters) {
|
||||
if (!queryPatternPredicate.test(parameters.query())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
var results = lookupDefinition(ctx, query);
|
||||
var results = lookupDefinition(ctx, parameters.query());
|
||||
|
||||
return Optional.of(dictionaryRenderer.render(results, Map.of("query", query, "profile", parameters.profileStr())));
|
||||
return Optional.of(dictionaryRenderer.render(results,
|
||||
Map.of("query", parameters.query(),
|
||||
"profile", parameters.profileStr())
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
|
@ -8,7 +8,6 @@ import nu.marginalia.search.command.SearchCommandInterface;
|
||||
import nu.marginalia.search.command.SearchParameters;
|
||||
import nu.marginalia.search.model.DecoratedSearchResults;
|
||||
import nu.marginalia.search.model.UrlDetails;
|
||||
import nu.marginalia.search.model.UserSearchParameters;
|
||||
import nu.marginalia.renderer.MustacheRenderer;
|
||||
import nu.marginalia.renderer.RendererFactory;
|
||||
|
||||
@ -33,10 +32,8 @@ public class SearchCommand implements SearchCommandInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters, String query) {
|
||||
UserSearchParameters params = new UserSearchParameters(query, parameters.profile(), parameters.js());
|
||||
|
||||
DecoratedSearchResults results = searchOperator.doSearch(ctx, params);
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters) {
|
||||
DecoratedSearchResults results = searchOperator.doSearch(ctx, parameters);
|
||||
|
||||
return Optional.of(searchResultsRenderer.render(results));
|
||||
}
|
||||
|
@ -51,12 +51,12 @@ public class SiteListCommand implements SearchCommandInterface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters, String query) {
|
||||
if (!queryPatternPredicate.test(query)) {
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters) {
|
||||
if (!queryPatternPredicate.test(parameters.query())) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
var results = siteInfo(ctx, query);
|
||||
var results = siteInfo(ctx, parameters.query());
|
||||
var domain = results.getDomain();
|
||||
|
||||
List<UrlDetails> resultSet;
|
||||
@ -81,7 +81,7 @@ public class SiteListCommand implements SearchCommandInterface {
|
||||
|
||||
Map<String, Object> renderObject = new HashMap<>(10);
|
||||
|
||||
renderObject.put("query", query);
|
||||
renderObject.put("query", parameters.query());
|
||||
renderObject.put("hideRanking", true);
|
||||
renderObject.put("profile", parameters.profileStr());
|
||||
renderObject.put("results", resultSet);
|
||||
|
@ -3,12 +3,13 @@ package nu.marginalia.search.model;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import nu.marginalia.search.command.SearchParameters;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@AllArgsConstructor @Getter @Builder
|
||||
public class DecoratedSearchResults {
|
||||
private final UserSearchParameters params;
|
||||
private final SearchParameters params;
|
||||
private final List<String> problems;
|
||||
private final String evalResult;
|
||||
|
||||
@ -18,12 +19,12 @@ public class DecoratedSearchResults {
|
||||
private final int focusDomainId;
|
||||
|
||||
public String getQuery() {
|
||||
return params.humanQuery();
|
||||
return params.query();
|
||||
}
|
||||
public String getProfile() {
|
||||
return params.profile().name;
|
||||
}
|
||||
public String getJs() {
|
||||
return params.jsSetting().value;
|
||||
return params.js().value;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +0,0 @@
|
||||
package nu.marginalia.search.model;
|
||||
|
||||
import nu.marginalia.search.command.SearchJsParameter;
|
||||
import nu.marginalia.search.model.SearchProfile;
|
||||
|
||||
public record UserSearchParameters(String humanQuery, SearchProfile profile, SearchJsParameter jsSetting) {
|
||||
}
|
@ -48,13 +48,13 @@ public class SearchQueryService {
|
||||
final String humanQuery = queryParam.trim();
|
||||
|
||||
var params = new SearchParameters(
|
||||
humanQuery,
|
||||
SearchProfile.getSearchProfile(profileStr),
|
||||
SearchJsParameter.parse(request.queryParams("js")),
|
||||
Boolean.parseBoolean(request.queryParams("detailed"))
|
||||
SearchJsParameter.parse(request.queryParams("js"))
|
||||
);
|
||||
|
||||
try {
|
||||
return searchCommandEvaulator.eval(ctx, params, humanQuery);
|
||||
return searchCommandEvaulator.eval(ctx, params);
|
||||
}
|
||||
catch (RedirectException ex) {
|
||||
response.redirect(ex.newUrl);
|
||||
|
Loading…
Reference in New Issue
Block a user