Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Java: FT.CREATE #2414

Merged
merged 5 commits into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#### Changes

* Java: Added `FT.CREATE` ([#2414](https://github.com/valkey-io/valkey-glide/pull/2414))

#### Breaking Changes

#### Fixes
Expand Down
4 changes: 2 additions & 2 deletions java/client/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ jar.dependsOn('copyNativeLib')
javadoc.dependsOn('copyNativeLib')
copyNativeLib.dependsOn('buildRustRelease')
compileTestJava.dependsOn('copyNativeLib')
test.dependsOn('buildRust')
testFfi.dependsOn('buildRust')
test.dependsOn('buildRustRelease')
testFfi.dependsOn('buildRustRelease')

test {
exclude "glide/ffi/FfiTest.class"
Expand Down
164 changes: 164 additions & 0 deletions java/client/src/main/java/glide/api/commands/servermodules/FT.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/** Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0 */
package glide.api.commands.servermodules;

import static glide.api.models.GlideString.gs;

import glide.api.BaseClient;
import glide.api.GlideClient;
import glide.api.GlideClusterClient;
import glide.api.models.ClusterValue;
import glide.api.models.GlideString;
import glide.api.models.commands.FT.FTCreateOptions;
import glide.api.models.commands.FT.FTCreateOptions.FieldInfo;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
import lombok.NonNull;

/** Module for vector search commands. */
public class FT {
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
/**
* Creates an index and initiates a backfill of that index.
*
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
* @param client The client to execute the command.
* @param indexName The index name.
* @param fields Fields to populate into the index.
* @return <code>OK</code>.
* @example
* <pre>{@code
* // Create an index for vectors of size 2:
* FT.create(client, "my_idx1", new FieldInfo[] {
* new FieldInfo("vec", VectorFieldFlat.builder(DistanceMetric.L2, 2).build())
* }).get();
*
* // Create a 6-dimensional JSON index using the HNSW algorithm:
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
* FT.create(client, "my_idx2",
* new FieldInfo[] { new FieldInfo("$.vec", "VEC",
* VectorFieldHnsw.builder(DistanceMetric.L2, 6).numberOfEdges(32).build())
* }).get();
* }</pre>
*/
public static CompletableFuture<String> create(
@NonNull BaseClient client, @NonNull String indexName, @NonNull FieldInfo[] fields) {
// Node: bug in meme DB - command fails if cmd is too short even though all mandatory args are
// present
// TODO confirm is it fixed or not and update docs if needed
return create(client, indexName, fields, FTCreateOptions.builder().build());
}

/**
* Creates an index and initiates a backfill of that index.
*
* @param client The client to execute the command.
* @param indexName The index name.
Yury-Fridlyand marked this conversation as resolved.
Show resolved Hide resolved
* @param fields Fields to populate into the index.
* @param options Additional parameters for the command - see {@link FTCreateOptions}.
* @return <code>OK</code>.
* @example
* <pre>{@code
* // Create a 6-dimensional JSON index using the HNSW algorithm:
* FT.create(client, "json_idx1",
* new FieldInfo[] { new FieldInfo("$.vec", "VEC",
* VectorFieldHnsw.builder(DistanceMetric.L2, 6).numberOfEdges(32).build())
* },
* FTCreateOptions.builder().indexType(JSON).prefixes(new String[] {"json:"}).build(),
* ).get();
* }</pre>
*/
public static CompletableFuture<String> create(
@NonNull BaseClient client,
@NonNull String indexName,
@NonNull FieldInfo[] fields,
@NonNull FTCreateOptions options) {
return create(client, gs(indexName), fields, options);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will accepting String parameter (indexName here) but passing the GlideString version to customCommand() affect the output of the command anyhow?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a binary version of custom command is used. I hope no.

}

/**
* Creates an index and initiates a backfill of that index.
*
* @param client The client to execute the command.
* @param indexName The index name.
* @param fields Fields to populate into the index.
* @return <code>OK</code>.
* @example
* <pre>{@code
* // Create an index for vectors of size 2:
* FT.create(client, gs("my_idx1"), new FieldInfo[] {
* new FieldInfo("vec", VectorFieldFlat.builder(DistanceMetric.L2, 2).build())
* }).get();
*
* // Create a 6-dimensional JSON index using the HNSW algorithm:
* FT.create(client, gs("my_idx2"),
* new FieldInfo[] { new FieldInfo(gs("$.vec"), gs("VEC"),
* VectorFieldHnsw.builder(DistanceMetric.L2, 6).numberOfEdges(32).build())
* }).get();
* }</pre>
*/
public static CompletableFuture<String> create(
@NonNull BaseClient client, @NonNull GlideString indexName, @NonNull FieldInfo[] fields) {
// Node: bug in meme DB - command fails if cmd is too short even though all mandatory args are
// present
// TODO confirm is it fixed or not and update docs if needed
return create(client, indexName, fields, FTCreateOptions.builder().build());
}

/**
* Creates an index and initiates a backfill of that index.
*
* @param client The client to execute the command.
* @param indexName The index name.
* @param fields Fields to populate into the index.
* @param options Additional parameters for the command - see {@link FTCreateOptions}.
* @return <code>OK</code>.
* @example
* <pre>{@code
* // Create a 6-dimensional JSON index using the HNSW algorithm:
* FT.create(client, gs("json_idx1"),
* new FieldInfo[] { new FieldInfo(gs("$.vec"), gs("VEC"),
* VectorFieldHnsw.builder(DistanceMetric.L2, 6).numberOfEdges(32).build())
* },
* FTCreateOptions.builder().indexType(JSON).prefixes(new String[] {"json:"}).build(),
* ).get();
* }</pre>
*/
public static CompletableFuture<String> create(
@NonNull BaseClient client,
@NonNull GlideString indexName,
@NonNull FieldInfo[] fields,
@NonNull FTCreateOptions options) {
var args =
Stream.of(
new GlideString[] {gs("FT.CREATE"), indexName},
options.toArgs(),
new GlideString[] {gs("SCHEMA")},
Arrays.stream(fields)
.map(FieldInfo::toArgs)
.flatMap(Arrays::stream)
.toArray(GlideString[]::new))
.flatMap(Arrays::stream)
.toArray(GlideString[]::new);
return executeCommand(client, args, false);
}

/**
* A wrapper for custom command API.
*
* @param client The client to execute the command.
* @param args The command line.
* @param returnsMap - true if command returns a map
*/
@SuppressWarnings("unchecked")
private static <T> CompletableFuture<T> executeCommand(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't work when the response returns null.
Doesn't work when T is a complex object.
Should we expose the handleXXResponse functions?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?
Why?
No!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Casting a null value throws an error
  2. String[] doesn't cast the internal values
  3. Why no? This would prevent us from duplicating code.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. no
  2. Yes, a command returns Object[], to create a String[] we need additional casting. A developer should be aware of that.
  3. Those functions aren't user API, we can't make them public.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's talk about this after push - but those functions could be put in a Utils package that is not exposed to the module api

BaseClient client, GlideString[] args, boolean returnsMap) {
if (client instanceof GlideClient) {
return ((GlideClient) client).customCommand(args).thenApply(r -> (T) r);
} else if (client instanceof GlideClusterClient) {
return ((GlideClusterClient) client)
.customCommand(args)
.thenApply(returnsMap ? ClusterValue::getMultiValue : ClusterValue::getSingleValue)
.thenApply(r -> (T) r);
}
throw new IllegalArgumentException(
"Unknown type of client, should be either `GlideClient` or `GlideClusterClient`");
}
}
Loading
Loading