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