mirror of
https://github.com/MarginaliaSearch/MarginaliaSearch.git
synced 2025-02-23 04:58:59 +00:00
Added rudimentary !bang-support
This commit is contained in:
parent
cfd01c7dbe
commit
275e42197c
@ -233,4 +233,12 @@ public class EdgeSearchE2ETest extends E2ETestBase {
|
||||
|
||||
Files.move(driver.getScreenshotAs(OutputType.FILE).toPath(), screenshotFilename("eval"));
|
||||
}
|
||||
@Test
|
||||
public void testBang() throws IOException {
|
||||
var driver = chrome.getWebDriver();
|
||||
|
||||
driver.get("http://proxyNginx/search?query=!g test");
|
||||
|
||||
Files.move(driver.getScreenshotAs(OutputType.FILE).toPath(), screenshotFilename("bang"));
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import nu.marginalia.wmsa.edge.index.client.EdgeIndexClient;
|
||||
import nu.marginalia.wmsa.edge.search.command.CommandEvaluator;
|
||||
import nu.marginalia.wmsa.edge.search.command.ResponseType;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchParameters;
|
||||
import nu.marginalia.wmsa.edge.search.exceptions.RedirectException;
|
||||
import nu.marginalia.wmsa.edge.search.query.model.EdgeUserSearchParameters;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -165,6 +166,9 @@ public class EdgeSearchService extends Service {
|
||||
try {
|
||||
return searchCommandEvaulator.eval(ctx, params, humanQuery);
|
||||
}
|
||||
catch (RedirectException ex) {
|
||||
response.redirect(ex.newUrl);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
logger.error("Error", ex);
|
||||
serveError(ctx, response);
|
||||
|
@ -2,14 +2,15 @@ package nu.marginalia.wmsa.edge.search.command;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import nu.marginalia.wmsa.configuration.server.Context;
|
||||
import nu.marginalia.wmsa.edge.search.command.commands.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class CommandEvaluator {
|
||||
|
||||
List<SearchCommandInterface> commands = new ArrayList<>();
|
||||
|
||||
private final List<SearchCommandInterface> commands = new ArrayList<>();
|
||||
private final SearchCommand search;
|
||||
|
||||
@Inject
|
||||
public CommandEvaluator(
|
||||
@ -17,13 +18,16 @@ public class CommandEvaluator {
|
||||
ConvertCommand convert,
|
||||
DefinitionCommand define,
|
||||
SiteSearchCommand site,
|
||||
BangCommand bang,
|
||||
SearchCommand search
|
||||
) {
|
||||
commands.add(browse);
|
||||
commands.add(convert);
|
||||
commands.add(define);
|
||||
commands.add(site);
|
||||
commands.add(search);
|
||||
commands.add(bang);
|
||||
|
||||
this.search = search;
|
||||
}
|
||||
|
||||
public Object eval(Context ctx, SearchParameters parameters, String query) {
|
||||
@ -33,8 +37,10 @@ public class CommandEvaluator {
|
||||
return ret.get();
|
||||
}
|
||||
}
|
||||
// Search command *should* always evaluate
|
||||
throw new IllegalStateException("Search Command returned Optional.empty()");
|
||||
|
||||
// Always process the search command last
|
||||
return search.process(ctx, parameters, query)
|
||||
.orElseThrow(() -> new IllegalStateException("Search Command returned Optional.empty()!") /* This Should Not be Possible™ */ );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,73 @@
|
||||
package nu.marginalia.wmsa.edge.search.command.commands;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import nu.marginalia.wmsa.configuration.server.Context;
|
||||
import nu.marginalia.wmsa.edge.assistant.screenshot.ScreenshotService;
|
||||
import nu.marginalia.wmsa.edge.data.dao.EdgeDataStoreDao;
|
||||
import nu.marginalia.wmsa.edge.data.dao.task.EdgeDomainBlacklist;
|
||||
import nu.marginalia.wmsa.edge.model.EdgeDomain;
|
||||
import nu.marginalia.wmsa.edge.model.EdgeId;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchCommandInterface;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchParameters;
|
||||
import nu.marginalia.wmsa.edge.search.exceptions.RedirectException;
|
||||
import nu.marginalia.wmsa.edge.search.model.BrowseResultSet;
|
||||
import nu.marginalia.wmsa.renderer.mustache.MustacheRenderer;
|
||||
import nu.marginalia.wmsa.renderer.mustache.RendererFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class BangCommand implements SearchCommandInterface {
|
||||
private final Map<String, String> bangsToPattern = new HashMap<>();
|
||||
|
||||
@Inject
|
||||
public BangCommand()
|
||||
{
|
||||
bangsToPattern.put("!g", "https://www.google.com/search?q=%s");
|
||||
bangsToPattern.put("!ddg", "https://duckduckgo.com/search?q=%s");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Object> process(Context ctx, SearchParameters parameters, String query) {
|
||||
|
||||
for (var entry : bangsToPattern.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
matchBangPattern(query, entry.getKey(), entry.getValue());
|
||||
|
||||
}
|
||||
|
||||
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)) {
|
||||
|
||||
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();
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void redirect(String pattern, String terms) {
|
||||
var url = String.format(pattern, URLEncoder.encode(terms.trim(), StandardCharsets.UTF_8));
|
||||
throw new RedirectException(url);
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package nu.marginalia.wmsa.edge.search.command;
|
||||
package nu.marginalia.wmsa.edge.search.command.commands;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import nu.marginalia.wmsa.configuration.server.Context;
|
||||
@ -7,6 +7,8 @@ import nu.marginalia.wmsa.edge.data.dao.EdgeDataStoreDao;
|
||||
import nu.marginalia.wmsa.edge.data.dao.task.EdgeDomainBlacklist;
|
||||
import nu.marginalia.wmsa.edge.model.EdgeDomain;
|
||||
import nu.marginalia.wmsa.edge.model.EdgeId;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchCommandInterface;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchParameters;
|
||||
import nu.marginalia.wmsa.edge.search.model.BrowseResultSet;
|
||||
import nu.marginalia.wmsa.renderer.mustache.MustacheRenderer;
|
||||
import nu.marginalia.wmsa.renderer.mustache.RendererFactory;
|
@ -1,8 +1,11 @@
|
||||
package nu.marginalia.wmsa.edge.search.command;
|
||||
package nu.marginalia.wmsa.edge.search.command.commands;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import nu.marginalia.wmsa.configuration.server.Context;
|
||||
import nu.marginalia.wmsa.edge.search.UnitConversion;
|
||||
import nu.marginalia.wmsa.edge.search.command.ResponseType;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchCommandInterface;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchParameters;
|
||||
import nu.marginalia.wmsa.renderer.mustache.MustacheRenderer;
|
||||
import nu.marginalia.wmsa.renderer.mustache.RendererFactory;
|
||||
|
@ -1,11 +1,14 @@
|
||||
|
||||
package nu.marginalia.wmsa.edge.search.command;
|
||||
package nu.marginalia.wmsa.edge.search.command.commands;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import lombok.SneakyThrows;
|
||||
import nu.marginalia.wmsa.configuration.server.Context;
|
||||
import nu.marginalia.wmsa.edge.assistant.client.AssistantClient;
|
||||
import nu.marginalia.wmsa.edge.assistant.dict.DictionaryResponse;
|
||||
import nu.marginalia.wmsa.edge.search.command.ResponseType;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchCommandInterface;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchParameters;
|
||||
import nu.marginalia.wmsa.renderer.mustache.MustacheRenderer;
|
||||
import nu.marginalia.wmsa.renderer.mustache.RendererFactory;
|
||||
import org.slf4j.Logger;
|
@ -1,4 +1,4 @@
|
||||
package nu.marginalia.wmsa.edge.search.command;
|
||||
package nu.marginalia.wmsa.edge.search.command.commands;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import nu.marginalia.wmsa.configuration.server.Context;
|
||||
@ -6,6 +6,9 @@ import nu.marginalia.wmsa.edge.data.dao.EdgeDataStoreDao;
|
||||
import nu.marginalia.wmsa.edge.data.dao.task.EdgeDomainBlacklist;
|
||||
import nu.marginalia.wmsa.edge.search.EdgeSearchOperator;
|
||||
import nu.marginalia.wmsa.edge.search.UnitConversion;
|
||||
import nu.marginalia.wmsa.edge.search.command.ResponseType;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchCommandInterface;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchParameters;
|
||||
import nu.marginalia.wmsa.edge.search.model.DecoratedSearchResults;
|
||||
import nu.marginalia.wmsa.edge.search.query.model.EdgeUserSearchParameters;
|
||||
import nu.marginalia.wmsa.renderer.mustache.MustacheRenderer;
|
||||
@ -17,10 +20,10 @@ import java.util.Optional;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
public class SearchCommand implements SearchCommandInterface {
|
||||
private EdgeDomainBlacklist blacklist;
|
||||
private EdgeDataStoreDao dataStoreDao;
|
||||
private EdgeSearchOperator searchOperator;
|
||||
private UnitConversion unitConversion;
|
||||
private final EdgeDomainBlacklist blacklist;
|
||||
private final EdgeDataStoreDao dataStoreDao;
|
||||
private final EdgeSearchOperator searchOperator;
|
||||
private final UnitConversion unitConversion;
|
||||
private final MustacheRenderer<DecoratedSearchResults> searchResultsRenderer;
|
||||
private final MustacheRenderer<DecoratedSearchResults> searchResultsRendererGmi;
|
||||
|
@ -1,4 +1,4 @@
|
||||
package nu.marginalia.wmsa.edge.search.command;
|
||||
package nu.marginalia.wmsa.edge.search.command.commands;
|
||||
|
||||
import com.google.inject.Inject;
|
||||
import nu.marginalia.wmsa.configuration.server.Context;
|
||||
@ -8,6 +8,9 @@ import nu.marginalia.wmsa.edge.index.model.IndexBlock;
|
||||
import nu.marginalia.wmsa.edge.model.crawl.EdgeDomainIndexingState;
|
||||
import nu.marginalia.wmsa.edge.search.EdgeSearchOperator;
|
||||
import nu.marginalia.wmsa.edge.search.EdgeSearchProfile;
|
||||
import nu.marginalia.wmsa.edge.search.command.ResponseType;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchCommandInterface;
|
||||
import nu.marginalia.wmsa.edge.search.command.SearchParameters;
|
||||
import nu.marginalia.wmsa.edge.search.model.DecoratedSearchResultSet;
|
||||
import nu.marginalia.wmsa.edge.search.model.DecoratedSearchResults;
|
||||
import nu.marginalia.wmsa.edge.search.model.DomainInformation;
|
||||
@ -27,7 +30,6 @@ import java.util.function.Predicate;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SiteSearchCommand implements SearchCommandInterface {
|
||||
private EdgeDomainBlacklist blacklist;
|
||||
private final EdgeDataStoreDao dataStoreDao;
|
||||
private final EdgeSearchOperator searchOperator;
|
||||
private DomainInformationService domainInformationService;
|
||||
@ -39,21 +41,18 @@ public class SiteSearchCommand implements SearchCommandInterface {
|
||||
private final Predicate<String> queryPatternPredicate = Pattern.compile("^site:[.A-Za-z\\-0-9]+$").asPredicate();
|
||||
@Inject
|
||||
public SiteSearchCommand(
|
||||
EdgeDomainBlacklist blacklist,
|
||||
DomainInformationService domainInformationService,
|
||||
EdgeDataStoreDao dataStoreDao,
|
||||
RendererFactory rendererFactory,
|
||||
EdgeSearchOperator searchOperator,
|
||||
DomainInformationService domainInformationService)
|
||||
EdgeSearchOperator searchOperator)
|
||||
throws IOException
|
||||
{
|
||||
this.blacklist = blacklist;
|
||||
this.dataStoreDao = dataStoreDao;
|
||||
this.searchOperator = searchOperator;
|
||||
this.domainInformationService = domainInformationService;
|
||||
|
||||
siteInfoRenderer = rendererFactory.renderer("edge/site-info");
|
||||
siteInfoRendererGmi = rendererFactory.renderer("edge/site-info-gmi");
|
||||
|
||||
this.searchOperator = searchOperator;
|
||||
this.domainInformationService = domainInformationService;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -63,9 +62,7 @@ public class SiteSearchCommand implements SearchCommandInterface {
|
||||
}
|
||||
|
||||
var results = siteInfo(ctx, query);
|
||||
|
||||
var domain = results.getDomain();
|
||||
logger.info("Domain: {}", domain);
|
||||
|
||||
DecoratedSearchResultSet resultSet;
|
||||
Path screenshotPath = null;
|
@ -0,0 +1,14 @@
|
||||
package nu.marginalia.wmsa.edge.search.exceptions;
|
||||
|
||||
public class RedirectException extends RuntimeException {
|
||||
public final String newUrl;
|
||||
|
||||
public RedirectException(String newUrl) {
|
||||
this.newUrl = newUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public StackTraceElement[] getStackTrace() {
|
||||
return new StackTraceElement[0];
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package nu.marginalia.wmsa.edge.search.command.commands;
|
||||
|
||||
import nu.marginalia.wmsa.edge.search.exceptions.RedirectException;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
class BangCommandTest {
|
||||
|
||||
@Test
|
||||
public void testBang() {
|
||||
var bc = new BangCommand();
|
||||
|
||||
expectRedirectUrl("https://www.google.com/search?q=search+terms", () -> bc.process(null, null, "search terms !g"));
|
||||
expectNoRedirect(() -> bc.process(null, null, "search terms!g"));
|
||||
expectNoRedirect(() -> bc.process(null, null, "!gsearch terms"));
|
||||
expectRedirectUrl("https://www.google.com/search?q=search+terms", () -> bc.process(null, null, "!g search terms"));
|
||||
}
|
||||
|
||||
void expectNoRedirect(Runnable op) {
|
||||
try {
|
||||
op.run();
|
||||
}
|
||||
catch (RedirectException ex) {
|
||||
fail("Expected no redirection, but got " + ex.newUrl);
|
||||
}
|
||||
}
|
||||
void expectRedirectUrl(String expectedUrl, Runnable op) {
|
||||
try {
|
||||
op.run();
|
||||
fail("Didn't intercept exception");
|
||||
}
|
||||
catch (RedirectException ex) {
|
||||
Assertions.assertEquals(expectedUrl, ex.newUrl, "Unexpected redirect");
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user