diff options
12 files changed, 455 insertions, 0 deletions
diff --git a/sample-apps/http-api-using-searcher/README.md b/sample-apps/http-api-using-searcher/README.md new file mode 100644 index 00000000000..6511db54b7e --- /dev/null +++ b/sample-apps/http-api-using-searcher/README.md @@ -0,0 +1,4 @@ +# Vespa sample applications - Building a HTTP API using a searcher + +Refer to search/handler-tutorial.html for documentation FIXME real link soon + diff --git a/sample-apps/http-api-using-searcher/feed.json b/sample-apps/http-api-using-searcher/feed.json new file mode 100644 index 00000000000..bbd91c314de --- /dev/null +++ b/sample-apps/http-api-using-searcher/feed.json @@ -0,0 +1,14 @@ +[ + { + "put": "id:test:basic::http://demo/0/", + "fields": { + "description": "red hat smurf demo" + } + }, + { + "put": "id:test:basic::http://demo/1/", + "fields": { + "description": "red smurf hat demo something" + } + } +] diff --git a/sample-apps/http-api-using-searcher/pom.xml b/sample-apps/http-api-using-searcher/pom.xml new file mode 100644 index 00000000000..7ae8549c385 --- /dev/null +++ b/sample-apps/http-api-using-searcher/pom.xml @@ -0,0 +1,85 @@ +<?xml version="1.0"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>com.yahoo.demo</groupId> + <artifactId>sample-app</artifactId> + <version>1.0.1</version> + <packaging>container-plugin</packaging> <!-- Use Vespa packaging --> + + <properties> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + <test.hide>true</test.hide> + <vespa_version>6-SNAPSHOT</vespa_version> + </properties> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.11</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>application</artifactId> <!-- Is this needed? --> + <version>${vespa_version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.yahoo.vespa</groupId> + <artifactId>container-dev</artifactId> <!-- not container-dev --> + <version>${vespa_version}</version> + <scope>provided</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.6.1</version> + <configuration> + <optimize>true</optimize> + <showDeprecation>true</showDeprecation> + <showWarnings>true</showWarnings> + <source>1.8</source> + <target>1.8</target> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.19.1</version> + <configuration> + <systemPropertyVariables> + <isMavenSurefirePlugin>true</isMavenSurefirePlugin> + </systemPropertyVariables> + <redirectTestOutputToFile>${test.hide}</redirectTestOutputToFile> + </configuration> + </plugin> + <plugin> + <groupId>com.yahoo.vespa</groupId> + <artifactId>vespa-application-maven-plugin</artifactId> <!-- Zip the application package --> + <version>${vespa_version}</version> + <executions> + <execution> + <goals> + <goal>packageApplication</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <groupId>com.yahoo.vespa</groupId> + <artifactId>bundle-plugin</artifactId> + <version>${vespa_version}</version> + <extensions>true</extensions> + </plugin> + </plugins> + </build> +</project> diff --git a/sample-apps/http-api-using-searcher/src/main/application/searchdefinitions/basic.sd b/sample-apps/http-api-using-searcher/src/main/application/searchdefinitions/basic.sd new file mode 100644 index 00000000000..3c4920a1912 --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/main/application/searchdefinitions/basic.sd @@ -0,0 +1,16 @@ +search basic { + + document basic { + + field description type string { + indexing: summary | index + } + + } + + fieldset default { + fields: description + } + +} + diff --git a/sample-apps/http-api-using-searcher/src/main/application/services.xml b/sample-apps/http-api-using-searcher/src/main/application/services.xml new file mode 100644 index 00000000000..fe16ec42260 --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/main/application/services.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. --> +<services version="1.0"> + <admin version="2.0"> + <adminserver hostalias="node1"/> + </admin> + + <container id="default" version="1.0"> + + <document-api /> <!-- Enable feed endpoint --> + + <search> + <chain inherits="vespa" id="default"> + <searcher id="com.yahoo.demo.DemoSearcher" bundle="demo"> + <config name="demo.demo"> + <demo> + <item> + <term>smurf</term> + </item> + </demo> + </config> + </searcher> + </chain> + <renderer id="demo" class="com.yahoo.demo.DemoRenderer" bundle="demo" /> + </search> + + <handler id="com.yahoo.demo.DemoHandler" bundle="demo"> + <binding>http://*:8080/demo</binding> + </handler> + + <component id="com.yahoo.demo.DemoComponent" bundle="demo"/> + + <nodes> + <node hostalias="node1"/> + </nodes> + </container> + + <content id="logical" version="1.0"> + <redundancy>1</redundancy> + <documents> + <document mode="index" type="basic"/> + </documents> + + <group name="mygroup" distribution-key="0"> + <node distribution-key="0" hostalias="node1"/> + </group> + + <engine> + <proton> + <searchable-copies>1</searchable-copies> + </proton> + </engine> + </content> + +</services> diff --git a/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoComponent.java b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoComponent.java new file mode 100644 index 00000000000..2182cebecb9 --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoComponent.java @@ -0,0 +1,45 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.demo; + +import com.yahoo.component.AbstractComponent; + +import java.text.Normalizer; +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +/** + * A shared component with an "expensive" constructor exposing a shared, + * thread-safe service. + */ +public class DemoComponent extends AbstractComponent { + private final Set<Integer> illegalHashes; + + public DemoComponent() { + illegalHashes = new HashSet<Integer>(); + Random r = new Random(); + // generate up to 1e6 unique hashes + for (int i = 0; i < 1000 * 1000; ++i) { + illegalHashes.add(r.nextInt()); + } + } + + /** + * NFKC-normalize term, or replace it with "smurf" with a low probability. + * Will change choice for each run, but will be constant in a single run of + * the container. + * + * @param term + * term to normalize or replace with "smurf" + * @return NFKC-normalized term or "smurf" + */ + public String normalize(String term) { + String normalized = Normalizer.normalize(term, Normalizer.Form.NFKC); + if (illegalHashes.contains(normalized.hashCode())) { + return "smurf"; + } else { + return normalized; + } + } + +} diff --git a/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoHandler.java b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoHandler.java new file mode 100644 index 00000000000..cf14f6d58cd --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoHandler.java @@ -0,0 +1,50 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.demo; + +import com.google.inject.Inject; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.LoggingRequestHandler; +import com.yahoo.container.logging.AccessLog; +import com.yahoo.search.handler.SearchHandler; +import com.yahoo.search.query.Model; +import com.yahoo.search.query.Presentation; + +import java.util.concurrent.Executor; + +/** + * Forward requests to search handler after adding "Red Hat" as query and "demo" + * as renderer ID. + */ +public class DemoHandler extends LoggingRequestHandler { + + private final SearchHandler searchHandler; + + /** + * Constructor for use in injection. The requested objects are subclasses of + * component or have dedicated providers, so the container will know how to + * create this handler. + * + * @param executor + * threadpool, provided by Vespa + * @param accessLog + * access log for incoming queries, provided by Vespa + * @param searchHandler + * the Vespa search handler, also automatically injected + */ + @Inject + public DemoHandler(Executor executor, AccessLog accessLog, + SearchHandler searchHandler) { + super(executor, accessLog, null, true); + this.searchHandler = searchHandler; + } + + @Override + public HttpResponse handle(HttpRequest request) { + HttpRequest searchRequest = new HttpRequest.Builder(request) + .put(Model.QUERY_STRING, "Red Hat") + .put(Presentation.FORMAT, "demo").createDirectRequest(); + HttpResponse r = searchHandler.handle(searchRequest); + return r; + } +} diff --git a/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoRenderer.java b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoRenderer.java new file mode 100644 index 00000000000..979c50f7f8f --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoRenderer.java @@ -0,0 +1,74 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.demo; + +import com.yahoo.search.Result; +import com.yahoo.search.rendering.Renderer; +import com.yahoo.search.result.Hit; +import com.yahoo.search.result.HitGroup; + +import java.io.IOException; +import java.io.Writer; +import java.util.Iterator; + +/** + * Render result sets as plain text. First line is whether an error occurred, + * second rendering initialization time stamp, then each line is the ID of each + * document returned, and the last line is time stamp for when the renderer was + * finished. + */ +public class DemoRenderer extends Renderer { + private String heading; + + /** + * No global, shared state to set. + */ + public DemoRenderer() { + } + + @Override + protected void render(Writer writer, Result result) throws IOException { + if (result.hits().getErrorHit() == null) { + writer.write("OK\n"); + } else { + writer.write("Oops!\n"); + } + writer.write(heading); + writer.write("\n"); + renderHits(writer, result.hits()); + writer.write("Rendering finished work: " + System.currentTimeMillis()); + writer.write("\n"); + } + + private void renderHits(Writer writer, HitGroup hits) throws IOException { + for (Iterator<Hit> i = hits.deepIterator(); i.hasNext();) { + Hit h = i.next(); + if (h.types().contains("summary")) { + String id = h.getDisplayId(); + if (id != null) { + writer.write(id); + writer.write("\n"); + } + } + } + } + + @Override + public String getEncoding() { + return "utf-8"; + } + + @Override + public String getMimeType() { + return "text/plain"; + } + + /** + * Initialize mutable, per-result set state here. + */ + @Override + public void init() { + long time = System.currentTimeMillis(); + heading = "Renderer initialized: " + time; + } + +} diff --git a/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoSearcher.java b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoSearcher.java new file mode 100644 index 00000000000..e749a3c1556 --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/main/java/com/yahoo/demo/DemoSearcher.java @@ -0,0 +1,76 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.demo; + +import com.yahoo.component.chain.dependencies.After; +import com.yahoo.component.chain.dependencies.Before; +import com.yahoo.component.chain.dependencies.Provides; +import com.yahoo.demo.DemoConfig.Demo; +import com.yahoo.prelude.query.AndItem; +import com.yahoo.prelude.query.CompositeItem; +import com.yahoo.prelude.query.Item; +import com.yahoo.prelude.query.WordItem; +import com.yahoo.processing.request.CompoundName; +import com.yahoo.search.Query; +import com.yahoo.search.Result; +import com.yahoo.search.Searcher; +import com.yahoo.search.query.QueryTree; +import com.yahoo.search.searchchain.Execution; +import com.yahoo.search.searchchain.PhaseNames; + +import java.util.List; + +/** + * A searcher for adding a set of configured terms as AND terms, and add a + * single term from the request to the query tree (after running the term + * through a shared component). + */ +@After(PhaseNames.RAW_QUERY) +@Before(PhaseNames.TRANSFORMED_QUERY) +@Provides(DemoSearcher.DEMO_TRANSFORM) +public class DemoSearcher extends Searcher { + public static final String DEMO_TRANSFORM = "com.yahoo.demo.DemoSearcher.NothingUseful"; + + /** + * The request property with this name will be filtered and added to the + * query as an AND term. + */ + public static final CompoundName EXTRA_TERM = new CompoundName("extraTerm"); + + private final List<Demo> extraTerms; + + private final DemoComponent infrastructure; + + public DemoSearcher(DemoComponent infrastructure, DemoConfig extraTerms) { + this.extraTerms = extraTerms.demo(); + this.infrastructure = infrastructure; + } + + /** + * Programmatic query transform, add terms from config and the EXTRA_TERM + * request property. + */ + @Override + public Result search(Query query, Execution execution) { + QueryTree q = query.getModel().getQueryTree(); + addAndItem(q, infrastructure.normalize( + query.properties().getString(EXTRA_TERM))); + for (Demo d : extraTerms) { + addAndItem(q, d.term()); + } + return execution.search(query); + } + + private void addAndItem(QueryTree q, String term) { + Item root = q.getRoot(); + CompositeItem compositeRoot; + if (root instanceof AndItem) { + compositeRoot = (CompositeItem) root; + } else { + compositeRoot = new AndItem(); + compositeRoot.addItem(root); + q.setRoot(compositeRoot); + } + compositeRoot.addItem(new WordItem(term)); + } + +} diff --git a/sample-apps/http-api-using-searcher/src/main/resources/configdefinitions/demo.def b/sample-apps/http-api-using-searcher/src/main/resources/configdefinitions/demo.def new file mode 100644 index 00000000000..9a804089433 --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/main/resources/configdefinitions/demo.def @@ -0,0 +1,5 @@ +# Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +namespace=demo + +demo[].term string + diff --git a/sample-apps/http-api-using-searcher/src/test/java/com/yahoo/example/ApplicationMain.java b/sample-apps/http-api-using-searcher/src/test/java/com/yahoo/example/ApplicationMain.java new file mode 100644 index 00000000000..da85e486602 --- /dev/null +++ b/sample-apps/http-api-using-searcher/src/test/java/com/yahoo/example/ApplicationMain.java @@ -0,0 +1,27 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.example; + +import com.yahoo.application.Networking; +import org.junit.Test; + +import java.nio.file.FileSystems; + +import static org.junit.Assume.assumeTrue; + +public class ApplicationMain { + + @Test + public void runFromMaven() throws Exception { + assumeTrue(Boolean.valueOf(System.getProperty("isMavenSurefirePlugin"))); + main(null); + } + + public static void main(String[] args) throws Exception { + try (com.yahoo.application.Application app = com.yahoo.application.Application.fromApplicationPackage( + FileSystems.getDefault().getPath("src/main/application"), + Networking.enable)) { + app.getClass(); // throws NullPointerException + Thread.sleep(Long.MAX_VALUE); + } + } +}
\ No newline at end of file diff --git a/sample-apps/pom.xml b/sample-apps/pom.xml index 1114ff2ec4b..91d1b7c7580 100644 --- a/sample-apps/pom.xml +++ b/sample-apps/pom.xml @@ -13,5 +13,9 @@ <module>basic-search-java</module> <module>blog-recommendation</module> <module>boolean-search</module> +<!-- + <module>http-api-using-request-handlers-and-processors</module> + <module>http-api-using-searcher</module> +--> </modules> </project> |