(query-service) Provide delegate of IndexApi's query functionality.

This is an intermediate step in the process of introducing the query-service as a proxy between search and index.
This commit is contained in:
Viktor Lofgren 2023-10-08 22:22:26 +02:00
parent 89c6d85f2f
commit 94c882af7d
8 changed files with 133 additions and 9 deletions

View File

@ -0,0 +1,32 @@
plugins {
id 'java'
id 'jvm-test-suite'
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(21))
}
}
dependencies {
implementation project(':code:common:model')
implementation project(':code:api:index-api')
implementation project(':code:common:config')
implementation project(':code:libraries:message-queue')
implementation project(':code:common:service-discovery')
implementation project(':code:common:service-client')
implementation libs.bundles.slf4j
implementation libs.prometheus
implementation libs.notnull
implementation libs.guice
implementation libs.rxjava
implementation libs.gson
testImplementation libs.bundles.slf4j.test
testImplementation libs.bundles.junit
testImplementation libs.mockito
}

View File

@ -0,0 +1,56 @@
package nu.marginalia.query.client;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import io.prometheus.client.Summary;
import io.reactivex.rxjava3.core.Observable;
import nu.marginalia.WmsaHome;
import nu.marginalia.client.AbstractDynamicClient;
import nu.marginalia.client.Context;
import nu.marginalia.index.client.model.query.SearchSpecification;
import nu.marginalia.index.client.model.results.SearchResultSet;
import nu.marginalia.model.gson.GsonFactory;
import nu.marginalia.mq.MessageQueueFactory;
import nu.marginalia.mq.outbox.MqOutbox;
import nu.marginalia.service.descriptor.ServiceDescriptors;
import nu.marginalia.service.id.ServiceId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.annotation.CheckReturnValue;
import java.util.UUID;
@Singleton
public class QueryClient extends AbstractDynamicClient {
private static final Summary wmsa_search_index_api_delegate_time = Summary.build().name("wmsa_search_index_api_delegate_time").help("-").register();
private final Logger logger = LoggerFactory.getLogger(getClass());
private final MqOutbox outbox;
@Inject
public QueryClient(ServiceDescriptors descriptors,
MessageQueueFactory messageQueueFactory) {
super(descriptors.forId(ServiceId.Query), WmsaHome.getHostsFile(), GsonFactory::get);
String inboxName = ServiceId.Query.name + ":" + "0";
String outboxName = System.getProperty("service-name", UUID.randomUUID().toString());
outbox = messageQueueFactory.createOutbox(inboxName, outboxName, UUID.randomUUID());
}
/** Delegate an Index API style query directly to the index service */
@CheckReturnValue
public SearchResultSet delegate(Context ctx, SearchSpecification specs) {
return wmsa_search_index_api_delegate_time.time(
() -> this.postGet(ctx, "/delegate/", specs, SearchResultSet.class).blockingFirst()
);
}
public MqOutbox outbox() {
return outbox;
}
}

View File

@ -26,11 +26,14 @@ dependencies {
implementation project(':code:common:model') implementation project(':code:common:model')
implementation project(':code:common:db') implementation project(':code:common:db')
implementation project(':code:common:service') implementation project(':code:common:service')
implementation project(':code:common:service-client')
implementation project(':code:api:index-api') implementation project(':code:api:index-api')
implementation project(':code:common:service-discovery') implementation project(':code:common:service-discovery')
implementation libs.bundles.slf4j implementation libs.bundles.slf4j
implementation libs.spark
implementation libs.gson
implementation libs.prometheus implementation libs.prometheus
implementation libs.notnull implementation libs.notnull
implementation libs.guice implementation libs.guice

View File

@ -1,6 +1,11 @@
package nu.marginalia.query; package nu.marginalia.query;
import com.google.gson.Gson;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import nu.marginalia.model.gson.GsonFactory;
public class QueryModule extends AbstractModule { public class QueryModule extends AbstractModule {
public void configure() {
bind(Gson.class).toProvider(GsonFactory::get);
}
} }

View File

@ -1,12 +1,41 @@
package nu.marginalia.query; package nu.marginalia.query;
import com.google.gson.Gson;
import com.google.inject.Inject; import com.google.inject.Inject;
import nu.marginalia.client.Context;
import nu.marginalia.index.client.IndexClient;
import nu.marginalia.index.client.model.query.SearchSpecification;
import nu.marginalia.index.client.model.results.SearchResultSet;
import nu.marginalia.service.server.BaseServiceParams; import nu.marginalia.service.server.BaseServiceParams;
import nu.marginalia.service.server.Service; import nu.marginalia.service.server.Service;
import spark.Request;
import spark.Response;
import spark.Spark;
public class QueryService extends Service { public class QueryService extends Service {
private final IndexClient indexClient;
private final Gson gson;
@Inject @Inject
public QueryService(BaseServiceParams params) { public QueryService(BaseServiceParams params,
IndexClient indexClient,
Gson gson)
{
super(params); super(params);
this.indexClient = indexClient;
this.gson = gson;
Spark.post("/delegate/", this::delegateToIndex, gson::toJson);
} }
private SearchResultSet delegateToIndex(Request request, Response response) {
String json = request.body();
SearchSpecification specsSet = gson.fromJson(json, SearchSpecification.class);
response.type("application/json");
return indexClient.query(Context.fromRequest(request), specsSet);
}
} }

View File

@ -33,6 +33,7 @@ dependencies {
implementation project(':code:libraries:term-frequency-dict') implementation project(':code:libraries:term-frequency-dict')
implementation project(':code:api:assistant-api') implementation project(':code:api:assistant-api')
implementation project(':code:api:query-api')
implementation project(':code:api:index-api') implementation project(':code:api:index-api')
implementation project(':code:api:search-api') implementation project(':code:api:search-api')
implementation project(':code:common:service-discovery') implementation project(':code:common:service-discovery')

View File

@ -2,16 +2,13 @@ package nu.marginalia.search.svc;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import nu.marginalia.index.client.IndexClient;
import nu.marginalia.index.client.model.query.SearchSpecification; import nu.marginalia.index.client.model.query.SearchSpecification;
import nu.marginalia.index.client.model.results.DecoratedSearchResultItem; import nu.marginalia.index.client.model.results.DecoratedSearchResultItem;
import nu.marginalia.index.client.model.results.SearchResultSet; import nu.marginalia.query.client.QueryClient;
import nu.marginalia.search.model.PageScoreAdjustment;
import nu.marginalia.search.model.UrlDetails; import nu.marginalia.search.model.UrlDetails;
import nu.marginalia.search.results.SearchResultDecorator; import nu.marginalia.search.results.SearchResultDecorator;
import nu.marginalia.search.results.UrlDeduplicator; import nu.marginalia.search.results.UrlDeduplicator;
import nu.marginalia.client.Context; import nu.marginalia.client.Context;
import nu.marginalia.search.query.model.SearchQuery;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.slf4j.Marker; import org.slf4j.Marker;
@ -24,17 +21,17 @@ import java.util.regex.Pattern;
public class SearchQueryIndexService { public class SearchQueryIndexService {
private final SearchResultDecorator resultDecorator; private final SearchResultDecorator resultDecorator;
private final Comparator<UrlDetails> resultListComparator; private final Comparator<UrlDetails> resultListComparator;
private final IndexClient indexClient; private final QueryClient queryClient;
private final SearchQueryCountService searchVisitorCount; private final SearchQueryCountService searchVisitorCount;
private final Marker queryMarker = MarkerFactory.getMarker("QUERY"); private final Marker queryMarker = MarkerFactory.getMarker("QUERY");
private final Logger logger = LoggerFactory.getLogger(getClass()); private final Logger logger = LoggerFactory.getLogger(getClass());
@Inject @Inject
public SearchQueryIndexService(SearchResultDecorator resultDecorator, public SearchQueryIndexService(SearchResultDecorator resultDecorator,
IndexClient indexClient, QueryClient queryClient,
SearchQueryCountService searchVisitorCount) { SearchQueryCountService searchVisitorCount) {
this.resultDecorator = resultDecorator; this.resultDecorator = resultDecorator;
this.indexClient = indexClient; this.queryClient = queryClient;
this.searchVisitorCount = searchVisitorCount; this.searchVisitorCount = searchVisitorCount;
resultListComparator = Comparator.comparing(UrlDetails::getTermScore) resultListComparator = Comparator.comparing(UrlDetails::getTermScore)
@ -45,7 +42,7 @@ public class SearchQueryIndexService {
public List<UrlDetails> executeQuery(Context ctx, SearchSpecification specs) { public List<UrlDetails> executeQuery(Context ctx, SearchSpecification specs) {
// Send the query // Send the query
final var queryResponse = indexClient.query(ctx, specs); final var queryResponse = queryClient.delegate(ctx, specs);
// Remove duplicates and other chaff // Remove duplicates and other chaff
final var results = limitAndDeduplicateResults(specs, queryResponse.results); final var results = limitAndDeduplicateResults(specs, queryResponse.results);

View File

@ -46,6 +46,7 @@ include 'code:features-index:index-reverse'
include 'code:features-index:domain-ranking' include 'code:features-index:domain-ranking'
include 'code:api:search-api' include 'code:api:search-api'
include 'code:api:query-api'
include 'code:api:index-api' include 'code:api:index-api'
include 'code:api:assistant-api' include 'code:api:assistant-api'
include 'code:api:process-mqapi' include 'code:api:process-mqapi'