Skip to content

Commit

Permalink
Add clienv-java module with ElastiknnNearestNeighborsQueryBuilder for…
Browse files Browse the repository at this point in the history
… running queries via Java REST client (#260)
  • Loading branch information
alexklibisz committed Jun 26, 2021
1 parent 878e9ab commit c28cc6b
Show file tree
Hide file tree
Showing 10 changed files with 445 additions and 5 deletions.
13 changes: 13 additions & 0 deletions build.gradle
Expand Up @@ -47,6 +47,7 @@ subprojects {
Project api4s = project(':api4s')
Project benchmarks = project(':benchmarks')
Project clientElastic4s = project(':client-elastic4s')
Project clientJava = project(':client-java')
Project lucene = project(':lucene')
Project models = project(':models')
Project plugin = project(':plugin')
Expand Down Expand Up @@ -204,6 +205,16 @@ configure(clientElastic4s, List.of(
}
}))

configure(clientJava, List.of(
publishConfig("Java APIs for Elastiknn, intended for use with Elasticsearch REST clients", false),
{
dependencies {
implementation "org.elasticsearch:elasticsearch:${esVersion}"
}
}
))


configure(models, List.of(
publishConfig("Exact and approximate similarity models used in Elastiknn", false)))

Expand Down Expand Up @@ -266,6 +277,7 @@ configure(testing, List.of(scalaProjectConfig, {
dependencies {
implementation models
implementation clientElastic4s
implementation clientJava
implementation plugin
implementation lucene
implementation 'com.typesafe:config:1.4.0'
Expand All @@ -276,6 +288,7 @@ configure(testing, List.of(scalaProjectConfig, {
implementation "org.apache.lucene:lucene-codecs:${luceneVersion}"
implementation "org.apache.lucene:lucene-analyzers-common:${luceneVersion}"
implementation "org.elasticsearch:elasticsearch:${esVersion}"
implementation "org.elasticsearch.client:elasticsearch-rest-high-level-client:${esVersion}"
implementation "com.storm-enroute:scalameter_${scalaShortVersion}:0.19"
implementation "org.scalanlp:breeze_${scalaShortVersion}:1.0"
implementation "com.klibisz.futil:futil_${scalaShortVersion}:0.1.2"
Expand Down
2 changes: 1 addition & 1 deletion docs/Taskfile.yml
Expand Up @@ -14,7 +14,7 @@ tasks:
- install-bundler
cmds:
- bundle install
- bundle exec jekyll serve
- bundle exec jekyll serve --port 4001

compile:
desc: Compile docs into a static site.
Expand Down
29 changes: 26 additions & 3 deletions docs/pages/libraries.md
Expand Up @@ -34,9 +34,9 @@ This includes a low level client that roughly mirrors the [Scala client](/scala-
|:--|:--|
|Release|[![Python Release][Badge-Python-Release]][Link-Python-Release]|

## Java library with exact and approximate similarity models
## Java library with exact and approximate nearest neighbor search models

This library contains the exact and approximate similarity models used by Elastiknn.
This library contains the exact and approximate nearest neighbor search models used by Elastiknn.

**Install**

Expand Down Expand Up @@ -68,9 +68,27 @@ implementation 'com.klibisz.elastiknn:lucene:<version below>'
**Versions**

|:--|:--|
|Rekease|[![Lucene Release][Badge-Lucene-Release]][Link-Lucene-Release]|
|Release|[![Lucene Release][Badge-Lucene-Release]][Link-Lucene-Release]|
|Snapshot|[![Lucene Snapshot][Badge-Lucene-Snapshot]][Link-Lucene-Snapshot]|

## Java library with Elasticsearch query builder for Elastiknn queries

This library contains a custom [query builder](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/current/java-rest-high-query-builders.html)
for defining Elastiknn queries in Java.

**Install**

In a Gradle project:

```groovy
implementation 'com.klibisz.elastiknn:client-java:<version below>'
```

**Versions**

|:--|:--|
|Release|[![Lucene Release][Badge-Java-Client-Release]][Link-Java-Client-Release]|
|Snapshot|[![Lucene Snapshot][Badge-Java-Client-Snapshot]][Link-Java-Client-Snapshot]|

## Scala client

Expand Down Expand Up @@ -142,6 +160,11 @@ libraryDependencies += "com.klibisz.elastiknn" %% "api4s" % <version below>
[Link-Lucene-Release]: https://search.maven.org/artifact/com.klibisz.elastiknn/lucene
[Link-Lucene-Snapshot]: https://oss.sonatype.org/#nexus-search;gav~com.klibisz.elastiknn~lucene~~~

[Badge-Java-Client-Release]: https://img.shields.io/nexus/r/com.klibisz.elastiknn/client-java?server=http%3A%2F%2Foss.sonatype.org&style=flat-square "lucene release"
[Badge-Java-Client-Snapshot]: https://img.shields.io/nexus/s/com.klibisz.elastiknn/client-java?server=http%3A%2F%2Foss.sonatype.org&style=flat-square "lucene snapshot"
[Link-Java-Client-Release]: https://search.maven.org/artifact/com.klibisz.elastiknn/client-java
[Link-Java-Client-Snapshot]: https://oss.sonatype.org/#nexus-search;gav~com.klibisz.elastiknn~client-java~~~

[Badge-Api4s-Release]: https://img.shields.io/nexus/r/com.klibisz.elastiknn/api4s_2.12?server=http%3A%2F%2Foss.sonatype.org&style=flat-square "api4s_2.12 release"
[Badge-Api4s-Snapshot]: https://img.shields.io/nexus/s/com.klibisz.elastiknn/api4s_2.12?server=http%3A%2F%2Foss.sonatype.org&style=flat-square "api4s_2.12 snapshot"
[Link-Api4s-Release]: https://search.maven.org/artifact/com.klibisz.elastiknn/api4s_2.12
Expand Down
@@ -0,0 +1,86 @@
package com.klibisz.elastiknn;

import com.klibisz.elastiknn.api4j.ElastiknnNearestNeighborsQuery;
import com.klibisz.elastiknn.api4j.Vector;
import org.apache.lucene.search.Query;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.SearchExecutionContext;

import java.io.IOException;
import java.util.Objects;

public class ElastiknnNearestNeighborsQueryBuilder extends AbstractQueryBuilder<ElastiknnNearestNeighborsQueryBuilder> {

private final ElastiknnNearestNeighborsQuery query;
private final String field;

public ElastiknnNearestNeighborsQueryBuilder(ElastiknnNearestNeighborsQuery query, String field) {
this.query = query;
this.field = field;
}

@Override
protected void doWriteTo(StreamOutput out) {
throw new UnsupportedOperationException("doWriteTo is not implemented");
}

@Override
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(getWriteableName());
builder.field("field", field);
builder.field("similarity", query.getSimilarity().toString());
if (query instanceof ElastiknnNearestNeighborsQuery.Exact) {
builder.field("model", "exact");
} else if (query instanceof ElastiknnNearestNeighborsQuery.AngularLsh) {
ElastiknnNearestNeighborsQuery.AngularLsh q = (ElastiknnNearestNeighborsQuery.AngularLsh) query;
builder.field("model", "lsh");
builder.field("candidates", q.getCandidates());
} else if (query instanceof ElastiknnNearestNeighborsQuery.L2Lsh) {
ElastiknnNearestNeighborsQuery.L2Lsh q = (ElastiknnNearestNeighborsQuery.L2Lsh) query;
builder.field("model", "lsh");
builder.field("candidates", q.getCandidates());
builder.field("probes", q.getProbes());
} else if (query instanceof ElastiknnNearestNeighborsQuery.PermutationLsh) {
ElastiknnNearestNeighborsQuery.PermutationLsh q = (ElastiknnNearestNeighborsQuery.PermutationLsh) query;
builder.field("model", "permutation_lsh");
builder.field("candidates", q.getCandidates());
} else {
throw new RuntimeException(String.format("Unexpected query type [%s]", query.getClass().toString()));
}
if (query.getVector() instanceof Vector.DenseFloat) {
Vector.DenseFloat dfv = (Vector.DenseFloat) query.getVector();
builder.field("vec", dfv.values);
} else if (query.getVector() instanceof Vector.SparseBool) {
Vector.SparseBool sbv = (Vector.SparseBool) query.getVector();
builder.startArray("vec");
builder.value(sbv.trueIndices);
builder.value(sbv.totalIndices);
builder.endArray();
} else {
throw new RuntimeException(String.format("Unexpected vector type [%s]", query.getVector().getClass().toString()));
}
builder.endObject();
}

@Override
protected Query doToQuery(SearchExecutionContext context) {
throw new UnsupportedOperationException("doToQuery is not implemented");
}

@Override
protected boolean doEquals(ElastiknnNearestNeighborsQueryBuilder other) {
return other != null && ((this == other) || (query.equals(other.query) && field.equals(other.field)));
}

@Override
protected int doHashCode() {
return Objects.hash(query, field);
}

@Override
public String getWriteableName() {
return "elastiknn_nearest_neighbors";
}
}
@@ -0,0 +1,159 @@
package com.klibisz.elastiknn.api4j;

import java.util.Objects;

public abstract class ElastiknnNearestNeighborsQuery {

private ElastiknnNearestNeighborsQuery() {}

public abstract Vector getVector();
public abstract Similarity getSimilarity();

public static final class Exact extends ElastiknnNearestNeighborsQuery {
private final Similarity similarity;
private final Vector vector;
public Exact(Vector vector, Similarity similarity) {
this.similarity = similarity;
this.vector = vector;
}

@Override
public Vector getVector() {
return vector;
}

@Override
public Similarity getSimilarity() {
return similarity;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Exact exact = (Exact) o;
return getSimilarity() == exact.getSimilarity() && Objects.equals(getVector(), exact.getVector());
}

@Override
public int hashCode() {
return Objects.hash(getSimilarity(), getVector());
}
}

public static final class AngularLsh extends ElastiknnNearestNeighborsQuery {
private final Vector vector;
private final Integer candidates;
public AngularLsh(Vector vector, Integer candidates) {
this.vector = vector;
this.candidates = candidates;
}

public Integer getCandidates() {
return candidates;
}

@Override
public Vector getVector() {
return vector;
}

@Override
public Similarity getSimilarity() {
return Similarity.ANGULAR;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AngularLsh that = (AngularLsh) o;
return Objects.equals(getVector(), that.getVector()) && Objects.equals(getCandidates(), that.getCandidates()) && getSimilarity() == that.getSimilarity();
}

@Override
public int hashCode() {
return Objects.hash(getVector(), getCandidates(), getSimilarity());
}
}

public static final class L2Lsh extends ElastiknnNearestNeighborsQuery {
private final Vector.DenseFloat vector;
private final Integer candidates;
private final Integer probes;
public L2Lsh(Vector.DenseFloat vector, Integer candidates, Integer probes) {
this.vector = vector;
this.candidates = candidates;
this.probes = probes;
}

public Integer getProbes() {
return probes;
}

public Integer getCandidates() {
return candidates;
}

@Override
public Vector getVector() {
return vector;
}

@Override
public Similarity getSimilarity() {
return Similarity.L2;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
L2Lsh l2Lsh = (L2Lsh) o;
return Objects.equals(getVector(), l2Lsh.getVector()) && Objects.equals(getCandidates(), l2Lsh.getCandidates()) && Objects.equals(getProbes(), l2Lsh.getProbes()) && getSimilarity() == l2Lsh.getSimilarity();
}

@Override
public int hashCode() {
return Objects.hash(getVector(), getCandidates(), getProbes(), getSimilarity());
}
}

public final static class PermutationLsh extends ElastiknnNearestNeighborsQuery {
private final Vector.DenseFloat vector;
private final Similarity similarity;
private final Integer candidates;
public PermutationLsh(Vector.DenseFloat vector, Similarity similarity, Integer candidates) {
this.vector = vector;
this.similarity = similarity;
this.candidates = candidates;
}

public Integer getCandidates() {
return candidates;
}

@Override
public Vector getVector() {
return vector;
}

@Override
public Similarity getSimilarity() {
return similarity;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PermutationLsh that = (PermutationLsh) o;
return Objects.equals(getVector(), that.getVector()) && getSimilarity() == that.getSimilarity() && Objects.equals(getCandidates(), that.getCandidates());
}

@Override
public int hashCode() {
return Objects.hash(getVector(), getSimilarity(), getCandidates());
}
}
}
@@ -0,0 +1,9 @@
package com.klibisz.elastiknn.api4j;

public enum Similarity {
JACCARD,
HAMMING,
L1,
L2,
ANGULAR
}

0 comments on commit c28cc6b

Please sign in to comment.