Work with Valkey Search
Valkey-Search (BSD-3-Clause), provided as a Valkey module, is a high-performance Search engine optimized for AI-driven / Search / Analytics / Recommendation System related workloads.
Valkey GLIDE support Search as a built-in module API, allowing you to interact with Search module commands through dedicated client methods.
Requirements
Section titled “Requirements”Before using a module, including the supported modules, it will need to be loaded into Valkey. See Modules Introduction on how to load modules into your Valkey service.
For more on version requirements, see our introduction on Modules API.
Examples
Section titled “Examples”import static glide.api.models.GlideString.gs;import glide.api.GlideClient;import glide.api.models.configuration.GlideClientConfiguration;import glide.api.models.configuration.NodeAddress;import glide.api.commands.servermodules.FT;import glide.api.models.GlideString;import glide.api.models.commands.FT.FTCreateOptions;import glide.api.models.commands.FT.FTCreateOptions.DataType;import glide.api.models.commands.FT.FTCreateOptions.DistanceMetric;import glide.api.models.commands.FT.FTCreateOptions.FieldInfo;import glide.api.models.commands.FT.FTCreateOptions.VectorFieldHnsw;import glide.api.models.commands.FT.FTSearchOptions;import java.util.HashMap;import java.util.Map;
// Both standalone and cluster mode are supportedGlideClientConfiguration config = GlideClientConfiguration.builder() .address(NodeAddress.builder().host("localhost").port(6379).build()) .build();GlideClient client = GlideClient.createClient(config).get();
// FT.CREATE a Hash index with vector fieldsString prefix = "{hash}:";String index = prefix + "index";FieldInfo[] fields = new FieldInfo[] { new FieldInfo("vec", "VEC", VectorFieldHnsw.builder(DistanceMetric.L2, 2).build())};FTCreateOptions options = FTCreateOptions.builder().dataType(DataType.HASH).prefixes(new String[] {prefix}).build();FT.create(client, index, fields, options).get(); // returns "OK"
// Insert hash data with vector fields (2-dimensional float32 vectors = 8 bytes each)byte[] vec0 = new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0};byte[] vec1 = new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0x80, (byte) 0xBF};
Map<GlideString, GlideString> hash0 = new HashMap<>();hash0.put(gs("vec"), gs(vec0));client.hset(gs(prefix + "0"), hash0).get();
Map<GlideString, GlideString> hash1 = new HashMap<>();hash1.put(gs("vec"), gs(vec1));client.hset(gs(prefix + "1"), hash1).get();
Thread.sleep(1000); // let server digest the data and update index
// FT.SEARCHbyte[] queryVec = new byte[] { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0};String query = "*=>[KNN 2 @VEC $query_vec]";Map<GlideString, GlideString> params = new HashMap<>();params.put(gs("query_vec"), gs(queryVec));FTSearchOptions searchOptions = FTSearchOptions.builder() .params(params) .build();Object[] searchResponse = FT.search(client, index, query, searchOptions).get();// searchResponse[0] == 2L// searchResponse[1] contains a map with "{hash}:0" and "{hash}:1" vector search resultsimport { GlideJson, GlideClient, GlideFt, Field, FtSearchOptions, FtSearchReturnType,} from "@valkey/valkey-glide";
// Both standalone and cluster mode are supportedconst client = await GlideClient.createClient({ addresses: [{ host: "localhost", port: 6379 }],});const prefix = "{json}:";
await GlideJson.set(client, `${prefix}1`, "$", '[{"arr": 42}, {"val": "hello"}, {"val": "world"}]');
// FT.CREATEconst fields: Field[] = [ { type: "NUMERIC", name: "$..arr", alias: "arr" }, { type: "TEXT", name: "$..val", alias: "val" },];await GlideFt.create(client, `${prefix}index`, fields, { dataType: "JSON", prefixes: [prefix],});
// FT.SEARCHconst optionsWithLimit: FtSearchOptions = { returnFields: [ { fieldIdentifier: "$..arr", alias: "myarr" }, { fieldIdentifier: "$..val", alias: "myval" }, ], timeout: 10000, limit: { offset: 0, count: 2 },};const searchResult: FtSearchReturnType = await GlideFt.search( client, `${prefix}index`, "@arr:[-inf +inf]", optionsWithLimit,);console.log(searchResult[0]); // Output: 1console.log(searchResult[1]); // Output: search result containing "{json}:1"from glide import ( GlideClient, GlideClientConfiguration, NodeAddress, ft, glide_json, DataType, NumericField, ReturnField, FtCreateOptions, FtSearchOptions,)import jsonimport time
# Both standalone and cluster mode are supportedconfig = GlideClientConfiguration([NodeAddress("localhost", 6379)])client = await GlideClient.create(config)
prefix = "{json}:"json_key1 = prefix + "1"json_key2 = prefix + "2"json_value1 = {"a": 11111, "b": 2, "c": 3}json_value2 = {"a": 22222, "b": 2, "c": 3}index = prefix + "index"
# FT.CREATEawait ft.create( client, index, schema=[ NumericField("$.a", "a"), NumericField("$.b", "b"), ], options=FtCreateOptions(data_type=DataType.JSON, prefixes=[prefix]),)
await glide_json.set(client, json_key1, "$", json.dumps(json_value1))await glide_json.set(client, json_key2, "$", json.dumps(json_value2))
time.sleep(1) # let server digest the data and update index
# FT.SEARCHft_search_options = FtSearchOptions( return_fields=[ ReturnField(field_identifier="a", alias="a_new"), ReturnField(field_identifier="b", alias="b_new"), ])
search_result = await ft.search(client, index, "@a:[-inf +inf]", options=ft_search_options)# search_result[0] == 2# search_result[1] contains results with "{json}:1" and "{json}:2"import ( "context" "time" glide "github.com/valkey-io/valkey-glide/go/v2" "github.com/valkey-io/valkey-glide/go/v2/config" "github.com/valkey-io/valkey-glide/go/v2/constants" "github.com/valkey-io/valkey-glide/go/v2/options")
// Both standalone and cluster mode are supportedcfg := config.NewClientConfiguration(). WithAddress(&config.NodeAddress{Host: "localhost", Port: 6379})client, err := glide.NewClient(cfg)
ctx := context.Background()prefix := "{hash}:"index := prefix + "index"ft := client.FT()
// FT.CREATE a Hash index with a vector field_, err = ft.Create(ctx, index, []options.Field{ options.NewVectorFieldHNSW("vec", constants.DistanceMetricL2, 2).SetAlias("VEC"), }, &options.FtCreateOptions{ DataType: constants.IndexDataTypeHash, Prefixes: []string{prefix}, },)
// Insert hash data with vector fields (2-dimensional float32 vectors)vec0 := string([]byte{0, 0, 0, 0, 0, 0, 0, 0})vec1 := string([]byte{0, 0, 0, 0, 0, 0, 0x80, 0xBF})client.HSet(ctx, prefix+"0", map[string]string{"vec": vec0})client.HSet(ctx, prefix+"1", map[string]string{"vec": vec1})time.Sleep(time.Second) // let server digest the data and update index
// FT.SEARCHresult, err := ft.Search(ctx, index, "*=>[KNN 2 @VEC $query_vec]", &options.FtSearchOptions{ Params: []options.FtSearchParam{{Key: "query_vec", Value: vec0}}, ReturnFields: []options.FtSearchReturnField{{FieldIdentifier: "vec"}}, })// result.TotalResults == 2// result.Documents contains "{hash}:0" and "{hash}:1" with their vector valuesuse ValkeyGlide\Search\{FtCreateBuilder, FtVectorField, FtSearchBuilder};
$client = new ValkeyGlide();$client->connect(addresses: [['host' => 'localhost', 'port' => 6379]]);
$prefix = '{hash}:';$index = $prefix . 'index';
// FT.CREATE a Hash index with a vector field$client->ftCreate( (new FtCreateBuilder()) ->index($index) ->on('HASH') ->prefix([$prefix]) ->addField(FtVectorField::hnsw('vec', 2, 'L2')));
// Insert hash data with vector fields (2-dimensional float32 vectors)$vec0 = str_repeat("\x00", 8);$vec1 = "\x00\x00\x00\x00\x00\x00\x80\xBF";$client->hSet($prefix . '0', 'vec', $vec0);$client->hSet($prefix . '1', 'vec', $vec1);usleep(1000000); // let server digest the data and update index
// FT.SEARCH$result = $client->ftSearch( (new FtSearchBuilder()) ->index($index) ->query('*=>[KNN 2 @VEC $query_vec]') ->params(['query_vec' => $vec0]) ->returnFields(['vec']));// $result[0] == 2// $result[1] contains "{hash}:0" and "{hash}:1" with their vector valuesusing Valkey.Glide;using Valkey.Glide.ServerModules;
// Both standalone and cluster mode are supportedvar config = new ConnectionConfiguration.StandaloneClientConfigurationBuilder() .WithAddress("localhost", 6379) .Build();await using var client = await GlideClient.CreateClient(config);
var prefix = "{hash}:";var index = prefix + "index";
// FT.CREATE a Hash index with a vector fieldvar fields = new Ft.CreateField[]{ new Ft.CreateVectorFieldHnsw { Identifier = "vec", Alias = "VEC", Dimensions = 2, DistanceMetric = Ft.DistanceMetric.Euclidean, },};var createOptions = new Ft.CreateOptions{ DataType = Ft.DataType.Hash, Prefixes = [prefix],};await Ft.CreateAsync(client, index, fields, createOptions);
// Insert hash data with vector fields (2-dimensional float32 vectors = 8 bytes each)var vec0 = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };var vec1 = new byte[] { 0, 0, 0, 0, 0, 0, 0x80, 0xBF };await client.HashSetAsync($"{prefix}0", "vec", vec0);await client.HashSetAsync($"{prefix}1", "vec", vec1);
// Let server digest the data and update indexawait Task.Delay(TimeSpan.FromSeconds(1));
// FT.SEARCHvar query = "*=>[KNN 2 @VEC $query_vec]";var searchOptions = new Ft.SearchOptions{ Params = new Dictionary<ValkeyValue, ValkeyValue> { ["query_vec"] = vec0, },};var result = await Ft.SearchAsync(client, index, query, searchOptions);var vectors = result.Documents.Select(d => Convert.ToHexString((byte[])d.Fields["vec"]));
Console.WriteLine($"Total results: {result.TotalResults}"); // Total results: 2Console.WriteLine($"Vectors: [{string.Join(", ", vectors)}]"); // Vectors: [0000000000000000, 00000000000080BF]What’s Next
Section titled “What’s Next”For more details on the Search module, see the Valkey Search topic.