diff --git a/content/develop/clients/ruby/queryjson.md b/content/develop/clients/ruby/queryjson.md new file mode 100644 index 0000000000..b6f0323e03 --- /dev/null +++ b/content/develop/clients/ruby/queryjson.md @@ -0,0 +1,166 @@ +--- +categories: +- docs +- develop +- stack +- oss +- rs +- rc +- oss +- kubernetes +- clients +description: Learn how to use Redis Search with JSON and hash documents. +linkTitle: Index and query documents +title: Index and query documents +scope: example +relatedPages: +- /develop/ai/search-and-query +topics: +- Redis Search +- JSON +- hash +weight: 2 +--- + +This example shows how to create a +[search index]({{< relref "/develop/ai/search-and-query/indexing" >}}) +for [JSON]({{< relref "/develop/data-types/json" >}}) documents and +run queries against the index. It then goes on to show the slight differences +in the equivalent code for [hash]({{< relref "/develop/data-types/hashes" >}}) +documents. + +{{< note >}}The redis-rb Query Engine requires redis-rb v6.0.0 or later. +{{< /note >}} + +{{< note >}}`redis-rb` uses query dialect 2 by default. +Redis Search methods such as [`search()`]({{< relref "/commands/ft.search" >}}) +will explicitly request this dialect, overriding the default set for the server. +See +[Query dialects]({{< relref "/develop/ai/search-and-query/advanced-concepts/dialects" >}}) +for more information. +{{< /note >}} + +## Initialize + +Make sure that you have [Redis Open Source]({{< relref "/operate/oss_and_stack/" >}}) +or another Redis server available. Also install the +[`redis-rb`]({{< relref "/develop/clients/ruby" >}}) client library if you +haven't already done so. + +Require the `redis` gem. The Query Engine classes live under the +`Redis::Commands::Search` namespace, so the example below aliases it to +`Search` to keep the code concise. + +{{< clients-example set="ruby_home_json" step="import" description="Foundational: Require the redis gem and alias the Query Engine namespace" difficulty="beginner" >}} +{{< /clients-example >}} + +## Create data + +Create some test data to add to the database: + +{{< clients-example set="ruby_home_json" step="create_data" description="Foundational: Define sample user data structures for indexing and querying" difficulty="beginner" >}} +{{< /clients-example >}} + +## Add the index + +Connect to your Redis database. The code below shows the most +basic connection but see the +[`redis-rb` guide]({{< relref "/develop/clients/ruby" >}}) +to learn more about the available connection options. + +{{< clients-example set="ruby_home_json" step="connect" description="Foundational: Establish a connection to a Redis server for query operations" difficulty="beginner" >}} +{{< /clients-example >}} + +Delete any existing index called `idx:users` and any keys that start with `user:`. + +{{< clients-example set="ruby_home_json" step="cleanup_json" description="Foundational: Clean up existing indexes and data before creating new indexes" difficulty="beginner" >}} +{{< /clients-example >}} + +Create an index. In this example, only JSON documents with the key prefix `user:` are indexed. For more information, see [Query syntax]({{< relref "/develop/ai/search-and-query/query/" >}}). + +Build the schema with the field type helpers (`text_field`, `tag_field`, +`numeric_field`) inside a `Search::Schema.build` block. Each field's first +argument is the [JSON path]({{< relref "/develop/data-types/json/path" >}}) to +the value, and the `as:` option gives the field an alias you can refer to in +queries. + +{{< clients-example set="ruby_home_json" step="make_index" description="Foundational: Create a search index for JSON documents with field definitions and key prefix filtering" difficulty="intermediate" >}} +{{< /clients-example >}} + +## Add the data + +Add the three sets of user data to the database as +[JSON]({{< relref "/develop/data-types/json" >}}) objects. +If you use keys with the `user:` prefix then Redis will index the +objects automatically as you add them: + +{{< clients-example set="ruby_home_json" step="add_data" description="Foundational: Store JSON documents in Redis with automatic indexing based on key prefix" difficulty="beginner" >}} +{{< /clients-example >}} + +## Query the data + +You can now use the index to search the JSON objects. The +[query]({{< relref "/develop/ai/search-and-query/query" >}}) +below searches for objects that have the text "Paul" in any field +and have an `age` value in the range 30 to 40: + +{{< clients-example set="ruby_home_json" step="query1" description="Query with filters: Search JSON documents using text matching and numeric range filters to find specific records" difficulty="intermediate" >}} +{{< /clients-example >}} + +Because the index has the key prefix `user:`, the client strips that prefix +from the returned document IDs and reports the logical ID (for example, `3` +rather than `user:3`). + +Use a `Search::Query` object to specify query options, such as returning only +the `city` field: + +{{< clients-example set="ruby_home_json" step="query2" description="Query with field projection: Retrieve only specific fields from search results to reduce data transfer" difficulty="intermediate" >}} +{{< /clients-example >}} + +Use an +[aggregation query]({{< relref "/develop/ai/search-and-query/query/aggregation" >}}) +to count all users in each city. + +{{< clients-example set="ruby_home_json" step="query3" description="Aggregation query: Group and count results by field values to analyze data patterns" difficulty="intermediate" >}} +{{< /clients-example >}} + +## Differences with hash documents + +Indexing for hash documents is very similar to JSON indexing but you +need to specify some slightly different options. + +When you create the schema for a hash index, you don't need to +add aliases for the fields, since you use the basic names to access +the fields anyway. Also, you must use `Search::IndexType::HASH` for the +`index_type:` option of the `IndexDefinition` when you create the index. The code +below shows these changes with a new index called `hash-idx:users`, which is +otherwise the same as the `idx:users` index used for JSON documents in the +previous examples. + +First, delete any existing index called `hash-idx:users` and any keys that start with `huser:`. + +{{< clients-example set="ruby_home_json" step="cleanup_hash" description="Foundational: Clean up existing hash indexes and data before creating new indexes" difficulty="beginner" >}} +{{< /clients-example >}} + +Now create the new index: + +{{< clients-example set="ruby_home_json" step="make_hash_index" description="Foundational: Create a search index for hash documents with field definitions and key prefix filtering" difficulty="intermediate" >}} +{{< /clients-example >}} + +Use [`hset()`]({{< relref "/commands/hset" >}}) to add the hash +documents instead of [`json_set()`]({{< relref "/commands/json.set" >}}). + +{{< clients-example set="ruby_home_json" step="add_hash_data" description="Foundational: Store hash documents in Redis with automatic indexing based on key prefix" difficulty="beginner" >}} +{{< /clients-example >}} + +The query commands work the same here for hash as they do for JSON (but +the name of the hash index is different). The results are returned as +`Document` objects, as with JSON: + +{{< clients-example set="ruby_home_json" step="query1_hash" description="Query with filters: Search hash documents using text matching and numeric range filters (same as JSON queries)" difficulty="intermediate" >}} +{{< /clients-example >}} + +## More information + +See the [Redis Search]({{< relref "/develop/ai/search-and-query" >}}) docs +for a full description of all query features with examples. diff --git a/data/command-api-mapping.json b/data/command-api-mapping.json index 0fa1dd6c0c..875c9fca43 100644 --- a/data/command-api-mapping.json +++ b/data/command-api-mapping.json @@ -34286,6 +34286,51 @@ "description": "The aggregation results" } } + ], + "redis_rb": [ + { + "signature": "aggregate(query, *args)", + "params": [ + { + "name": "query", + "type": "Search::AggregateRequest, Search::Cursor, String", + "description": "an aggregate request, a cursor to read, or a raw query string followed by raw pipeline args" + }, + { + "name": "args", + "type": "Array", + "description": "raw pipeline tokens when query is a String" + } + ], + "returns": { + "type": "Search::AggregateResult", + "description": "the result rows (each a Hash) and the cursor id (or nil)" + } + }, + { + "signature": "ft_aggregate(index_name, query, *args)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "Search::AggregateRequest, Search::Cursor, String", + "description": "an aggregate request, a cursor to read, or a raw query string followed by raw pipeline args" + }, + { + "name": "args", + "type": "Array", + "description": "raw pipeline tokens when query is a String, e.g. \"GROUPBY\", 1, \"@x\", \"REDUCE\", \"COUNT\", 0, \"AS\", \"n\"" + } + ], + "returns": { + "type": "Search::AggregateResult", + "description": "the result rows (each a Hash) and the cursor id (or nil)" + } + } ] } }, @@ -34443,6 +34488,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_aliasadd(alias_name, index_name)", + "params": [ + { + "name": "alias_name", + "type": "String", + "description": "the alias" + }, + { + "name": "index_name", + "type": "String", + "description": "the index the alias points to" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } }, @@ -34570,6 +34636,22 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_aliasdel(alias_name)", + "params": [ + { + "name": "alias_name", + "type": "String", + "description": "the alias" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } }, @@ -34727,6 +34809,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_aliasupdate(alias_name, index_name)", + "params": [ + { + "name": "alias_name", + "type": "String", + "description": "the alias" + }, + { + "name": "index_name", + "type": "String", + "description": "the index the alias should point to" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } }, @@ -34923,6 +35026,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_alter(index_name, field_or_args)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "field_or_args", + "type": "Search::Field, Array", + "description": "a Search::Field (rendered via #to_args) or a raw token array" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } }, @@ -35177,6 +35301,131 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "create_index(name, schema, storage_type: \"hash\", prefix: nil, stopwords: nil, max_text_fields: false, skip_initial_scan: false, definition: nil)", + "params": [ + { + "name": "name", + "type": "String", + "description": "the index name" + }, + { + "name": "schema", + "type": "Search::Schema", + "description": "the field schema, built with Search::Schema.build" + }, + { + "name": "storage_type", + "type": "String", + "description": "the data type to index (\"hash\" or \"json\")" + }, + { + "name": "prefix", + "type": "String", + "description": "key prefix for indexed/added documents" + }, + { + "name": "stopwords", + "type": "Array", + "description": "custom stopword list" + }, + { + "name": "max_text_fields", + "type": "Boolean", + "description": "emit MAXTEXTFIELDS" + }, + { + "name": "skip_initial_scan", + "type": "Boolean", + "description": "emit SKIPINITIALSCAN (do not backfill existing keys)" + }, + { + "name": "definition", + "type": "Search::IndexDefinition", + "description": "a prebuilt ON/PREFIX/FILTER clause; takes precedence over storage_type/prefix" + } + ], + "returns": { + "type": "Search::Index", + "description": "a stateful index bound to the client, exposing #add/#search/#aggregate/#drop" + } + }, + { + "signature": "ft_create(index_name, schema, storage_type = nil, prefix: nil, stopwords: nil, max_text_fields: false, skip_initial_scan: false, definition: nil, temporary: nil, no_term_offsets: false, no_highlight: false, no_field_flags: false, no_term_frequencies: false)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "schema", + "type": "Search::Schema", + "description": "the field schema; the SCHEMA clause is rendered from it" + }, + { + "name": "storage_type", + "type": "String, Symbol", + "description": "the data type to index, e.g. \"HASH\" or \"JSON\" (ON ); ignored when definition is given" + }, + { + "name": "prefix", + "type": "String", + "description": "index keys whose name starts with \":\"; ignored when definition is given" + }, + { + "name": "stopwords", + "type": "Array", + "description": "a custom stopword list (STOPWORDS)" + }, + { + "name": "max_text_fields", + "type": "Boolean", + "description": "emit MAXTEXTFIELDS" + }, + { + "name": "skip_initial_scan", + "type": "Boolean", + "description": "emit SKIPINITIALSCAN" + }, + { + "name": "definition", + "type": "Search::IndexDefinition", + "description": "a prebuilt ON/PREFIX/FILTER clause; takes precedence over storage_type/prefix" + }, + { + "name": "temporary", + "type": "Integer", + "description": "index lifetime in seconds (TEMPORARY )" + }, + { + "name": "no_term_offsets", + "type": "Boolean", + "description": "emit NOOFFSETS" + }, + { + "name": "no_highlight", + "type": "Boolean", + "description": "emit NOHL" + }, + { + "name": "no_field_flags", + "type": "Boolean", + "description": "emit NOFIELDS" + }, + { + "name": "no_term_frequencies", + "type": "Boolean", + "description": "emit NOFREQS" + } + ], + "returns": { + "type": "String", + "description": "\"OK\" on success" + } + } ] } }, @@ -35324,6 +35573,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_cursor_del(index_name, cursor_id)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "cursor_id", + "type": "Integer", + "description": "the cursor id" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } }, @@ -35506,6 +35776,27 @@ "description": "The next batch of aggregation results" } } + ], + "redis_rb": [ + { + "signature": "ft_cursor_read(index_name, cursor_id)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "cursor_id", + "type": "Integer", + "description": "the cursor id returned by a previous WITHCURSOR aggregation" + } + ], + "returns": { + "type": "Search::AggregateResult", + "description": "the next rows, with #cursor set to the next cursor id (0 when exhausted)" + } + } ] } }, @@ -35668,6 +35959,27 @@ "description": "Number of terms added" } } + ], + "redis_rb": [ + { + "signature": "ft_dictadd(dict_name, *terms)", + "params": [ + { + "name": "dict_name", + "type": "String", + "description": "the dictionary name" + }, + { + "name": "terms", + "type": "Array", + "description": "the terms to add" + } + ], + "returns": { + "type": "Integer", + "description": "the number of new terms added" + } + } ] } }, @@ -35830,6 +36142,27 @@ "description": "Number of terms deleted" } } + ], + "redis_rb": [ + { + "signature": "ft_dictdel(dict_name, *terms)", + "params": [ + { + "name": "dict_name", + "type": "String", + "description": "the dictionary name" + }, + { + "name": "terms", + "type": "Array", + "description": "the terms to remove" + } + ], + "returns": { + "type": "Integer", + "description": "the number of terms removed" + } + } ] } }, @@ -35957,6 +36290,22 @@ "description": "All terms in the dictionary" } } + ], + "redis_rb": [ + { + "signature": "ft_dictdump(dict_name)", + "params": [ + { + "name": "dict_name", + "type": "String", + "description": "the dictionary name" + } + ], + "returns": { + "type": "Array", + "description": "the terms in the dictionary" + } + } ] } }, @@ -36142,6 +36491,41 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "drop(delete_documents: false)", + "params": [ + { + "name": "delete_documents", + "type": "Boolean", + "description": "also delete the indexed documents (DD)" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + }, + { + "signature": "ft_dropindex(index_name, delete_documents: false)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "delete_documents", + "type": "Boolean", + "description": "also delete the indexed documents (DD)" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } }, @@ -36319,6 +36703,27 @@ "description": "Query execution plan" } } + ], + "redis_rb": [ + { + "signature": "ft_explain(index_name, query)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "String", + "description": "the query string" + } + ], + "returns": { + "type": "String", + "description": "a human-readable description of the query plan" + } + } ] } }, @@ -36543,19 +36948,65 @@ "signature": "fthybrid(string $index, HybridSearchQuery $query)", "params": [ { - "name": "$index", - "type": "string", - "description": "The index name" + "name": "$index", + "type": "string", + "description": "The index name" + }, + { + "name": "$query", + "type": "HybridSearchQuery", + "description": "The hybrid search query" + } + ], + "returns": { + "type": "array", + "description": "Hybrid search results" + } + } + ], + "redis_rb": [ + { + "signature": "ft_hybrid_search(index_name, query:, combine_method: nil, post_processing: nil, params_substitution: nil, timeout: nil, cursor: nil)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "Search::HybridQuery", + "description": "the combined SEARCH + VSIM query" }, { - "name": "$query", - "type": "HybridSearchQuery", - "description": "The hybrid search query" + "name": "combine_method", + "type": "Search::CombineResultsMethod", + "description": "the fusion strategy (RRF/LINEAR)" + }, + { + "name": "post_processing", + "type": "Search::HybridPostProcessingConfig", + "description": "a post-fusion pipeline" + }, + { + "name": "params_substitution", + "type": "Hash", + "description": "query parameter substitutions (PARAMS)" + }, + { + "name": "timeout", + "type": "Integer", + "description": "query timeout in milliseconds (TIMEOUT)" + }, + { + "name": "cursor", + "type": "Search::HybridCursorQuery", + "description": "cursor/pagination config (WITHCURSOR)" } ], "returns": { - "type": "array", - "description": "Hybrid search results" + "type": "Search::HybridResult", + "description": "the fused result rows, total, warnings and execution time (or, for a WITHCURSOR query, the per-leg cursor ids)" } } ] @@ -36679,6 +37130,22 @@ "description": "Index information" } } + ], + "redis_rb": [ + { + "signature": "ft_info(index_name)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + } + ], + "returns": { + "type": "Hash", + "description": "index metadata (e.g. \"index_name\", \"num_docs\", \"attributes\")" + } + } ] } }, @@ -36972,6 +37439,27 @@ "description": "Query results and profiling information" } } + ], + "redis_rb": [ + { + "signature": "ft_profile(index_name, *args)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "args", + "type": "Array", + "description": "the profile arguments, e.g. \"SEARCH\", \"QUERY\", \"\"" + } + ], + "returns": { + "type": "Array", + "description": "the raw profile reply (results plus a profiling tree)" + } + } ] } }, @@ -37197,6 +37685,81 @@ "description": "Search results" } } + ], + "redis_rb": [ + { + "signature": "search(query = nil, return_fields: nil, nocontent: nil, sort_by: nil, asc: nil, dialect: nil, with_scores: nil, params: nil)", + "params": [ + { + "name": "query", + "type": "String, Search::Query", + "description": "a query string or a Search::Query object; a block builds a Query via the predicate DSL" + }, + { + "name": "return_fields", + "type": "Array", + "description": "only return these fields (RETURN)" + }, + { + "name": "nocontent", + "type": "Boolean", + "description": "return ids only (NOCONTENT)" + }, + { + "name": "sort_by", + "type": "String", + "description": "sort field (SORTBY); pair with asc:" + }, + { + "name": "asc", + "type": "Boolean", + "description": "sort ascending (default); pass false for descending" + }, + { + "name": "dialect", + "type": "Integer", + "description": "query dialect version (DIALECT); defaults to 2" + }, + { + "name": "with_scores", + "type": "Boolean", + "description": "include the relevance score on each document (WITHSCORES)" + }, + { + "name": "params", + "type": "Hash", + "description": "query parameter substitutions (PARAMS)" + } + ], + "returns": { + "type": "Search::SearchResult", + "description": "the total count and the matching Search::Document objects" + } + }, + { + "signature": "ft_search(index_name, query, **options)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index (or alias) name" + }, + { + "name": "query", + "type": "String", + "description": "the query string" + }, + { + "name": "options", + "type": "Hash", + "description": "search options translated to FT.SEARCH tokens (:no_content, :with_scores, :limit, :sortby, :filter, :geo_filter, :return, :params, :dialect, ...)" + } + ], + "returns": { + "type": "Search::SearchResult", + "description": "the total count and the matching Search::Document objects" + } + } ] } }, @@ -37442,6 +38005,42 @@ "description": "Spell check suggestions" } } + ], + "redis_rb": [ + { + "signature": "ft_spellcheck(index_name, query, distance: nil, include: nil, exclude: nil)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "String", + "description": "the query whose terms are checked" + }, + { + "name": "distance", + "type": "Integer", + "description": "maximum Levenshtein distance for suggestions (DISTANCE)" + }, + { + "name": "include", + "type": "String", + "description": "a custom dictionary to include terms from (TERMS INCLUDE)" + }, + { + "name": "exclude", + "type": "String", + "description": "a custom dictionary to exclude terms from (TERMS EXCLUDE)" + } + ], + "returns": { + "type": "Hash", + "description": "each misspelled term mapped to an array of { \"suggestion\" => String, \"score\" => Numeric }" + } + } ] } }, @@ -37722,6 +38321,37 @@ "description": "The current size of the suggestion dictionary" } } + ], + "redis_rb": [ + { + "signature": "ft_sugadd(key, string, score, options = {})", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + }, + { + "name": "string", + "type": "String", + "description": "the suggestion text" + }, + { + "name": "score", + "type": "Numeric", + "description": "the suggestion weight" + }, + { + "name": "options", + "type": "Hash", + "description": "accepts :incr (increment the existing score, INCR) and :payload (opaque payload, PAYLOAD)" + } + ], + "returns": { + "type": "Integer", + "description": "the current size of the suggestion dictionary" + } + } ] } }, @@ -37884,6 +38514,27 @@ "description": "1 if deleted, 0 if not found" } } + ], + "redis_rb": [ + { + "signature": "ft_sugdel(key, string)", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + }, + { + "name": "string", + "type": "String", + "description": "the suggestion text to delete" + } + ], + "returns": { + "type": "Integer", + "description": "1 if the suggestion existed and was deleted, 0 otherwise" + } + } ] } }, @@ -38227,6 +38878,32 @@ "description": "List of suggestions" } } + ], + "redis_rb": [ + { + "signature": "ft_sugget(key, prefix, options = {})", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + }, + { + "name": "prefix", + "type": "String", + "description": "the prefix to complete" + }, + { + "name": "options", + "type": "Hash", + "description": "accepts :fuzzy (FUZZY), :with_scores (WITHSCORES), :with_payloads (WITHPAYLOADS) and :max (MAX)" + } + ], + "returns": { + "type": "Array", + "description": "the suggestions, interleaved with scores/payloads when requested" + } + } ] } }, @@ -38354,6 +39031,22 @@ "description": "The number of suggestions in the dictionary" } } + ], + "redis_rb": [ + { + "signature": "ft_suglen(key)", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + } + ], + "returns": { + "type": "Integer", + "description": "the number of suggestions" + } + } ] } }, @@ -38475,6 +39168,22 @@ "description": "Synonym groups and their terms" } } + ], + "redis_rb": [ + { + "signature": "ft_syndump(index_name)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + } + ], + "returns": { + "type": "Hash", + "description": "each term mapped to the synonym group ids it belongs to" + } + } ] } }, @@ -38692,6 +39401,37 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_synupdate(index_name, group_id, *terms, skip_initial_scan: false)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "group_id", + "type": "String", + "description": "the synonym group id" + }, + { + "name": "terms", + "type": "Array", + "description": "the terms to add to the group" + }, + { + "name": "skip_initial_scan", + "type": "Boolean", + "description": "do not re-scan existing documents (SKIPINITIALSCAN)" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } }, @@ -38849,6 +39589,27 @@ "description": "Distinct tag values" } } + ], + "redis_rb": [ + { + "signature": "ft_tagvals(index_name, field_name)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "field_name", + "type": "String", + "description": "the TAG field name" + } + ], + "returns": { + "type": "Array", + "description": "the distinct tag values (normalized to lowercase unless the field is case-sensitive)" + } + } ] } }, diff --git a/data/command-api-mapping/FT.AGGREGATE.json b/data/command-api-mapping/FT.AGGREGATE.json index e91ee2f998..ebdae48ebf 100644 --- a/data/command-api-mapping/FT.AGGREGATE.json +++ b/data/command-api-mapping/FT.AGGREGATE.json @@ -196,6 +196,51 @@ "description": "The aggregation results" } } + ], + "redis_rb": [ + { + "signature": "aggregate(query, *args)", + "params": [ + { + "name": "query", + "type": "Search::AggregateRequest, Search::Cursor, String", + "description": "an aggregate request, a cursor to read, or a raw query string followed by raw pipeline args" + }, + { + "name": "args", + "type": "Array", + "description": "raw pipeline tokens when query is a String" + } + ], + "returns": { + "type": "Search::AggregateResult", + "description": "the result rows (each a Hash) and the cursor id (or nil)" + } + }, + { + "signature": "ft_aggregate(index_name, query, *args)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "Search::AggregateRequest, Search::Cursor, String", + "description": "an aggregate request, a cursor to read, or a raw query string followed by raw pipeline args" + }, + { + "name": "args", + "type": "Array", + "description": "raw pipeline tokens when query is a String, e.g. \"GROUPBY\", 1, \"@x\", \"REDUCE\", \"COUNT\", 0, \"AS\", \"n\"" + } + ], + "returns": { + "type": "Search::AggregateResult", + "description": "the result rows (each a Hash) and the cursor id (or nil)" + } + } ] } } diff --git a/data/command-api-mapping/FT.ALIASADD.json b/data/command-api-mapping/FT.ALIASADD.json index 47189e33be..a9ac38b5ed 100644 --- a/data/command-api-mapping/FT.ALIASADD.json +++ b/data/command-api-mapping/FT.ALIASADD.json @@ -152,6 +152,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_aliasadd(alias_name, index_name)", + "params": [ + { + "name": "alias_name", + "type": "String", + "description": "the alias" + }, + { + "name": "index_name", + "type": "String", + "description": "the index the alias points to" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } } diff --git a/data/command-api-mapping/FT.ALIASDEL.json b/data/command-api-mapping/FT.ALIASDEL.json index b323fc4184..32fc16ba03 100644 --- a/data/command-api-mapping/FT.ALIASDEL.json +++ b/data/command-api-mapping/FT.ALIASDEL.json @@ -122,6 +122,22 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_aliasdel(alias_name)", + "params": [ + { + "name": "alias_name", + "type": "String", + "description": "the alias" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } } diff --git a/data/command-api-mapping/FT.ALIASUPDATE.json b/data/command-api-mapping/FT.ALIASUPDATE.json index e2a40a718e..f0bbfaff50 100644 --- a/data/command-api-mapping/FT.ALIASUPDATE.json +++ b/data/command-api-mapping/FT.ALIASUPDATE.json @@ -152,6 +152,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_aliasupdate(alias_name, index_name)", + "params": [ + { + "name": "alias_name", + "type": "String", + "description": "the alias" + }, + { + "name": "index_name", + "type": "String", + "description": "the index the alias should point to" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } } diff --git a/data/command-api-mapping/FT.ALTER.json b/data/command-api-mapping/FT.ALTER.json index 814efe25a2..6f213e11a9 100644 --- a/data/command-api-mapping/FT.ALTER.json +++ b/data/command-api-mapping/FT.ALTER.json @@ -191,6 +191,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_alter(index_name, field_or_args)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "field_or_args", + "type": "Search::Field, Array", + "description": "a Search::Field (rendered via #to_args) or a raw token array" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } } diff --git a/data/command-api-mapping/FT.CREATE.json b/data/command-api-mapping/FT.CREATE.json index 80d6c06369..40bda2461c 100644 --- a/data/command-api-mapping/FT.CREATE.json +++ b/data/command-api-mapping/FT.CREATE.json @@ -249,6 +249,131 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "create_index(name, schema, storage_type: \"hash\", prefix: nil, stopwords: nil, max_text_fields: false, skip_initial_scan: false, definition: nil)", + "params": [ + { + "name": "name", + "type": "String", + "description": "the index name" + }, + { + "name": "schema", + "type": "Search::Schema", + "description": "the field schema, built with Search::Schema.build" + }, + { + "name": "storage_type", + "type": "String", + "description": "the data type to index (\"hash\" or \"json\")" + }, + { + "name": "prefix", + "type": "String", + "description": "key prefix for indexed/added documents" + }, + { + "name": "stopwords", + "type": "Array", + "description": "custom stopword list" + }, + { + "name": "max_text_fields", + "type": "Boolean", + "description": "emit MAXTEXTFIELDS" + }, + { + "name": "skip_initial_scan", + "type": "Boolean", + "description": "emit SKIPINITIALSCAN (do not backfill existing keys)" + }, + { + "name": "definition", + "type": "Search::IndexDefinition", + "description": "a prebuilt ON/PREFIX/FILTER clause; takes precedence over storage_type/prefix" + } + ], + "returns": { + "type": "Search::Index", + "description": "a stateful index bound to the client, exposing #add/#search/#aggregate/#drop" + } + }, + { + "signature": "ft_create(index_name, schema, storage_type = nil, prefix: nil, stopwords: nil, max_text_fields: false, skip_initial_scan: false, definition: nil, temporary: nil, no_term_offsets: false, no_highlight: false, no_field_flags: false, no_term_frequencies: false)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "schema", + "type": "Search::Schema", + "description": "the field schema; the SCHEMA clause is rendered from it" + }, + { + "name": "storage_type", + "type": "String, Symbol", + "description": "the data type to index, e.g. \"HASH\" or \"JSON\" (ON ); ignored when definition is given" + }, + { + "name": "prefix", + "type": "String", + "description": "index keys whose name starts with \":\"; ignored when definition is given" + }, + { + "name": "stopwords", + "type": "Array", + "description": "a custom stopword list (STOPWORDS)" + }, + { + "name": "max_text_fields", + "type": "Boolean", + "description": "emit MAXTEXTFIELDS" + }, + { + "name": "skip_initial_scan", + "type": "Boolean", + "description": "emit SKIPINITIALSCAN" + }, + { + "name": "definition", + "type": "Search::IndexDefinition", + "description": "a prebuilt ON/PREFIX/FILTER clause; takes precedence over storage_type/prefix" + }, + { + "name": "temporary", + "type": "Integer", + "description": "index lifetime in seconds (TEMPORARY )" + }, + { + "name": "no_term_offsets", + "type": "Boolean", + "description": "emit NOOFFSETS" + }, + { + "name": "no_highlight", + "type": "Boolean", + "description": "emit NOHL" + }, + { + "name": "no_field_flags", + "type": "Boolean", + "description": "emit NOFIELDS" + }, + { + "name": "no_term_frequencies", + "type": "Boolean", + "description": "emit NOFREQS" + } + ], + "returns": { + "type": "String", + "description": "\"OK\" on success" + } + } ] } } diff --git a/data/command-api-mapping/FT.CURSOR DEL.json b/data/command-api-mapping/FT.CURSOR DEL.json index 758d224dc5..2b2b2ab316 100644 --- a/data/command-api-mapping/FT.CURSOR DEL.json +++ b/data/command-api-mapping/FT.CURSOR DEL.json @@ -142,6 +142,27 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_cursor_del(index_name, cursor_id)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "cursor_id", + "type": "Integer", + "description": "the cursor id" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } } diff --git a/data/command-api-mapping/FT.CURSOR READ.json b/data/command-api-mapping/FT.CURSOR READ.json index dd6c280f51..62bcf9f702 100644 --- a/data/command-api-mapping/FT.CURSOR READ.json +++ b/data/command-api-mapping/FT.CURSOR READ.json @@ -177,6 +177,27 @@ "description": "The next batch of aggregation results" } } + ], + "redis_rb": [ + { + "signature": "ft_cursor_read(index_name, cursor_id)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "cursor_id", + "type": "Integer", + "description": "the cursor id returned by a previous WITHCURSOR aggregation" + } + ], + "returns": { + "type": "Search::AggregateResult", + "description": "the next rows, with #cursor set to the next cursor id (0 when exhausted)" + } + } ] } } diff --git a/data/command-api-mapping/FT.DICTADD.json b/data/command-api-mapping/FT.DICTADD.json index fd48f1a61e..40ed927d7f 100644 --- a/data/command-api-mapping/FT.DICTADD.json +++ b/data/command-api-mapping/FT.DICTADD.json @@ -157,6 +157,27 @@ "description": "Number of terms added" } } + ], + "redis_rb": [ + { + "signature": "ft_dictadd(dict_name, *terms)", + "params": [ + { + "name": "dict_name", + "type": "String", + "description": "the dictionary name" + }, + { + "name": "terms", + "type": "Array", + "description": "the terms to add" + } + ], + "returns": { + "type": "Integer", + "description": "the number of new terms added" + } + } ] } } diff --git a/data/command-api-mapping/FT.DICTDEL.json b/data/command-api-mapping/FT.DICTDEL.json index af290d6a3b..036eb29189 100644 --- a/data/command-api-mapping/FT.DICTDEL.json +++ b/data/command-api-mapping/FT.DICTDEL.json @@ -157,6 +157,27 @@ "description": "Number of terms deleted" } } + ], + "redis_rb": [ + { + "signature": "ft_dictdel(dict_name, *terms)", + "params": [ + { + "name": "dict_name", + "type": "String", + "description": "the dictionary name" + }, + { + "name": "terms", + "type": "Array", + "description": "the terms to remove" + } + ], + "returns": { + "type": "Integer", + "description": "the number of terms removed" + } + } ] } } diff --git a/data/command-api-mapping/FT.DICTDUMP.json b/data/command-api-mapping/FT.DICTDUMP.json index 1f3f400e12..c703e1b3eb 100644 --- a/data/command-api-mapping/FT.DICTDUMP.json +++ b/data/command-api-mapping/FT.DICTDUMP.json @@ -122,6 +122,22 @@ "description": "All terms in the dictionary" } } + ], + "redis_rb": [ + { + "signature": "ft_dictdump(dict_name)", + "params": [ + { + "name": "dict_name", + "type": "String", + "description": "the dictionary name" + } + ], + "returns": { + "type": "Array", + "description": "the terms in the dictionary" + } + } ] } } diff --git a/data/command-api-mapping/FT.DROPINDEX.json b/data/command-api-mapping/FT.DROPINDEX.json index c38d574e7c..f7dc73e0d7 100644 --- a/data/command-api-mapping/FT.DROPINDEX.json +++ b/data/command-api-mapping/FT.DROPINDEX.json @@ -180,6 +180,41 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "drop(delete_documents: false)", + "params": [ + { + "name": "delete_documents", + "type": "Boolean", + "description": "also delete the indexed documents (DD)" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + }, + { + "signature": "ft_dropindex(index_name, delete_documents: false)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "delete_documents", + "type": "Boolean", + "description": "also delete the indexed documents (DD)" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } } diff --git a/data/command-api-mapping/FT.EXPLAIN.json b/data/command-api-mapping/FT.EXPLAIN.json index 3bcde12c94..ea2d5b0149 100644 --- a/data/command-api-mapping/FT.EXPLAIN.json +++ b/data/command-api-mapping/FT.EXPLAIN.json @@ -172,6 +172,27 @@ "description": "Query execution plan" } } + ], + "redis_rb": [ + { + "signature": "ft_explain(index_name, query)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "String", + "description": "the query string" + } + ], + "returns": { + "type": "String", + "description": "a human-readable description of the query plan" + } + } ] } } diff --git a/data/command-api-mapping/FT.HYBRID.json b/data/command-api-mapping/FT.HYBRID.json index f613fe6efd..7c189e7d8f 100644 --- a/data/command-api-mapping/FT.HYBRID.json +++ b/data/command-api-mapping/FT.HYBRID.json @@ -82,6 +82,52 @@ "description": "Hybrid search results" } } + ], + "redis_rb": [ + { + "signature": "ft_hybrid_search(index_name, query:, combine_method: nil, post_processing: nil, params_substitution: nil, timeout: nil, cursor: nil)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "Search::HybridQuery", + "description": "the combined SEARCH + VSIM query" + }, + { + "name": "combine_method", + "type": "Search::CombineResultsMethod", + "description": "the fusion strategy (RRF/LINEAR)" + }, + { + "name": "post_processing", + "type": "Search::HybridPostProcessingConfig", + "description": "a post-fusion pipeline" + }, + { + "name": "params_substitution", + "type": "Hash", + "description": "query parameter substitutions (PARAMS)" + }, + { + "name": "timeout", + "type": "Integer", + "description": "query timeout in milliseconds (TIMEOUT)" + }, + { + "name": "cursor", + "type": "Search::HybridCursorQuery", + "description": "cursor/pagination config (WITHCURSOR)" + } + ], + "returns": { + "type": "Search::HybridResult", + "description": "the fused result rows, total, warnings and execution time (or, for a WITHCURSOR query, the per-leg cursor ids)" + } + } ] } } diff --git a/data/command-api-mapping/FT.INFO.json b/data/command-api-mapping/FT.INFO.json index 8d5672cca2..f4cefd45ad 100644 --- a/data/command-api-mapping/FT.INFO.json +++ b/data/command-api-mapping/FT.INFO.json @@ -116,6 +116,22 @@ "description": "Index information" } } + ], + "redis_rb": [ + { + "signature": "ft_info(index_name)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + } + ], + "returns": { + "type": "Hash", + "description": "index metadata (e.g. \"index_name\", \"num_docs\", \"attributes\")" + } + } ] } } diff --git a/data/command-api-mapping/FT.PROFILE.json b/data/command-api-mapping/FT.PROFILE.json index edafb5be73..aef0e459b1 100644 --- a/data/command-api-mapping/FT.PROFILE.json +++ b/data/command-api-mapping/FT.PROFILE.json @@ -288,6 +288,27 @@ "description": "Query results and profiling information" } } + ], + "redis_rb": [ + { + "signature": "ft_profile(index_name, *args)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "args", + "type": "Array", + "description": "the profile arguments, e.g. \"SEARCH\", \"QUERY\", \"\"" + } + ], + "returns": { + "type": "Array", + "description": "the raw profile reply (results plus a profiling tree)" + } + } ] } } diff --git a/data/command-api-mapping/FT.SEARCH.json b/data/command-api-mapping/FT.SEARCH.json index a94eafca84..3ecb272f73 100644 --- a/data/command-api-mapping/FT.SEARCH.json +++ b/data/command-api-mapping/FT.SEARCH.json @@ -220,6 +220,81 @@ "description": "Search results" } } + ], + "redis_rb": [ + { + "signature": "search(query = nil, return_fields: nil, nocontent: nil, sort_by: nil, asc: nil, dialect: nil, with_scores: nil, params: nil)", + "params": [ + { + "name": "query", + "type": "String, Search::Query", + "description": "a query string or a Search::Query object; a block builds a Query via the predicate DSL" + }, + { + "name": "return_fields", + "type": "Array", + "description": "only return these fields (RETURN)" + }, + { + "name": "nocontent", + "type": "Boolean", + "description": "return ids only (NOCONTENT)" + }, + { + "name": "sort_by", + "type": "String", + "description": "sort field (SORTBY); pair with asc:" + }, + { + "name": "asc", + "type": "Boolean", + "description": "sort ascending (default); pass false for descending" + }, + { + "name": "dialect", + "type": "Integer", + "description": "query dialect version (DIALECT); defaults to 2" + }, + { + "name": "with_scores", + "type": "Boolean", + "description": "include the relevance score on each document (WITHSCORES)" + }, + { + "name": "params", + "type": "Hash", + "description": "query parameter substitutions (PARAMS)" + } + ], + "returns": { + "type": "Search::SearchResult", + "description": "the total count and the matching Search::Document objects" + } + }, + { + "signature": "ft_search(index_name, query, **options)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index (or alias) name" + }, + { + "name": "query", + "type": "String", + "description": "the query string" + }, + { + "name": "options", + "type": "Hash", + "description": "search options translated to FT.SEARCH tokens (:no_content, :with_scores, :limit, :sortby, :filter, :geo_filter, :return, :params, :dialect, ...)" + } + ], + "returns": { + "type": "Search::SearchResult", + "description": "the total count and the matching Search::Document objects" + } + } ] } } diff --git a/data/command-api-mapping/FT.SPELLCHECK.json b/data/command-api-mapping/FT.SPELLCHECK.json index e187e297ab..e360296de0 100644 --- a/data/command-api-mapping/FT.SPELLCHECK.json +++ b/data/command-api-mapping/FT.SPELLCHECK.json @@ -240,6 +240,42 @@ "description": "Spell check suggestions" } } + ], + "redis_rb": [ + { + "signature": "ft_spellcheck(index_name, query, distance: nil, include: nil, exclude: nil)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "query", + "type": "String", + "description": "the query whose terms are checked" + }, + { + "name": "distance", + "type": "Integer", + "description": "maximum Levenshtein distance for suggestions (DISTANCE)" + }, + { + "name": "include", + "type": "String", + "description": "a custom dictionary to include terms from (TERMS INCLUDE)" + }, + { + "name": "exclude", + "type": "String", + "description": "a custom dictionary to exclude terms from (TERMS EXCLUDE)" + } + ], + "returns": { + "type": "Hash", + "description": "each misspelled term mapped to an array of { \"suggestion\" => String, \"score\" => Numeric }" + } + } ] } } diff --git a/data/command-api-mapping/FT.SUGADD.json b/data/command-api-mapping/FT.SUGADD.json index 24309ed3d5..d8b782b926 100644 --- a/data/command-api-mapping/FT.SUGADD.json +++ b/data/command-api-mapping/FT.SUGADD.json @@ -275,6 +275,37 @@ "description": "The current size of the suggestion dictionary" } } + ], + "redis_rb": [ + { + "signature": "ft_sugadd(key, string, score, options = {})", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + }, + { + "name": "string", + "type": "String", + "description": "the suggestion text" + }, + { + "name": "score", + "type": "Numeric", + "description": "the suggestion weight" + }, + { + "name": "options", + "type": "Hash", + "description": "accepts :incr (increment the existing score, INCR) and :payload (opaque payload, PAYLOAD)" + } + ], + "returns": { + "type": "Integer", + "description": "the current size of the suggestion dictionary" + } + } ] } } diff --git a/data/command-api-mapping/FT.SUGDEL.json b/data/command-api-mapping/FT.SUGDEL.json index b9f8efeb95..cc9e54e38c 100644 --- a/data/command-api-mapping/FT.SUGDEL.json +++ b/data/command-api-mapping/FT.SUGDEL.json @@ -157,6 +157,27 @@ "description": "1 if deleted, 0 if not found" } } + ], + "redis_rb": [ + { + "signature": "ft_sugdel(key, string)", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + }, + { + "name": "string", + "type": "String", + "description": "the suggestion text to delete" + } + ], + "returns": { + "type": "Integer", + "description": "1 if the suggestion existed and was deleted, 0 otherwise" + } + } ] } } diff --git a/data/command-api-mapping/FT.SUGGET.json b/data/command-api-mapping/FT.SUGGET.json index a3f64ec320..5abf3638af 100644 --- a/data/command-api-mapping/FT.SUGGET.json +++ b/data/command-api-mapping/FT.SUGGET.json @@ -338,6 +338,32 @@ "description": "List of suggestions" } } + ], + "redis_rb": [ + { + "signature": "ft_sugget(key, prefix, options = {})", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + }, + { + "name": "prefix", + "type": "String", + "description": "the prefix to complete" + }, + { + "name": "options", + "type": "Hash", + "description": "accepts :fuzzy (FUZZY), :with_scores (WITHSCORES), :with_payloads (WITHPAYLOADS) and :max (MAX)" + } + ], + "returns": { + "type": "Array", + "description": "the suggestions, interleaved with scores/payloads when requested" + } + } ] } } diff --git a/data/command-api-mapping/FT.SUGLEN.json b/data/command-api-mapping/FT.SUGLEN.json index ccc9883444..859803fcfa 100644 --- a/data/command-api-mapping/FT.SUGLEN.json +++ b/data/command-api-mapping/FT.SUGLEN.json @@ -122,6 +122,22 @@ "description": "The number of suggestions in the dictionary" } } + ], + "redis_rb": [ + { + "signature": "ft_suglen(key)", + "params": [ + { + "name": "key", + "type": "String", + "description": "the suggestion dictionary key" + } + ], + "returns": { + "type": "Integer", + "description": "the number of suggestions" + } + } ] } } diff --git a/data/command-api-mapping/FT.SYNDUMP.json b/data/command-api-mapping/FT.SYNDUMP.json index 6a82c2a475..fcd04ee5a4 100644 --- a/data/command-api-mapping/FT.SYNDUMP.json +++ b/data/command-api-mapping/FT.SYNDUMP.json @@ -116,6 +116,22 @@ "description": "Synonym groups and their terms" } } + ], + "redis_rb": [ + { + "signature": "ft_syndump(index_name)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + } + ], + "returns": { + "type": "Hash", + "description": "each term mapped to the synonym group ids it belongs to" + } + } ] } } diff --git a/data/command-api-mapping/FT.SYNUPDATE.json b/data/command-api-mapping/FT.SYNUPDATE.json index 5fb535671c..1a3e63a9db 100644 --- a/data/command-api-mapping/FT.SYNUPDATE.json +++ b/data/command-api-mapping/FT.SYNUPDATE.json @@ -212,6 +212,37 @@ "description": "OK on success" } } + ], + "redis_rb": [ + { + "signature": "ft_synupdate(index_name, group_id, *terms, skip_initial_scan: false)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "group_id", + "type": "String", + "description": "the synonym group id" + }, + { + "name": "terms", + "type": "Array", + "description": "the terms to add to the group" + }, + { + "name": "skip_initial_scan", + "type": "Boolean", + "description": "do not re-scan existing documents (SKIPINITIALSCAN)" + } + ], + "returns": { + "type": "String", + "description": "\"OK\"" + } + } ] } } diff --git a/data/command-api-mapping/FT.TAGVALS.json b/data/command-api-mapping/FT.TAGVALS.json index 7c9aad31ad..2838253513 100644 --- a/data/command-api-mapping/FT.TAGVALS.json +++ b/data/command-api-mapping/FT.TAGVALS.json @@ -152,6 +152,27 @@ "description": "Distinct tag values" } } + ], + "redis_rb": [ + { + "signature": "ft_tagvals(index_name, field_name)", + "params": [ + { + "name": "index_name", + "type": "String", + "description": "the index name" + }, + { + "name": "field_name", + "type": "String", + "description": "the TAG field name" + } + ], + "returns": { + "type": "Array", + "description": "the distinct tag values (normalized to lowercase unless the field is case-sensitive)" + } + } ] } } diff --git a/local_examples/client-specific/ruby/home_json.rb b/local_examples/client-specific/ruby/home_json.rb new file mode 100644 index 0000000000..9dc68bdc2c --- /dev/null +++ b/local_examples/client-specific/ruby/home_json.rb @@ -0,0 +1,179 @@ +# EXAMPLE: ruby_home_json +# STEP_START import +require 'redis' + +# A short alias for the Query Engine namespace to keep the code below readable. +Search = Redis::Commands::Search +# STEP_END + +# REMOVE_START +def assert_equal(expected, actual) + raise "Expected #{expected.inspect}, got #{actual.inspect}" unless actual == expected +end +# REMOVE_END + +# STEP_START create_data +user1 = { + 'name' => 'Paul John', + 'email' => 'paul.john@example.com', + 'age' => 42, + 'city' => 'London' +} + +user2 = { + 'name' => 'Eden Zamir', + 'email' => 'eden.zamir@example.com', + 'age' => 29, + 'city' => 'Tel Aviv' +} + +user3 = { + 'name' => 'Paul Zamir', + 'email' => 'paul.zamir@example.com', + 'age' => 35, + 'city' => 'Tel Aviv' +} +# STEP_END + +# STEP_START connect +r = Redis.new +# STEP_END + +# STEP_START cleanup_json +begin + r.ft_dropindex('idx:users', delete_documents: true) +rescue Redis::CommandError + # Index doesn't exist, so there is nothing to drop. +end + +r.del('user:1', 'user:2', 'user:3') +# STEP_END + +# STEP_START make_index +schema = Search::Schema.build do + text_field '$.name', as: 'name' + tag_field '$.city', as: 'city' + numeric_field '$.age', as: 'age' +end + +definition = Search::IndexDefinition.new( + prefix: ['user:'], + index_type: Search::IndexType::JSON +) + +index = r.create_index('idx:users', schema, definition: definition) +puts index.name # >>> idx:users +# STEP_END + +# STEP_START add_data +user1_set = r.json_set('user:1', '$', user1) +user2_set = r.json_set('user:2', '$', user2) +user3_set = r.json_set('user:3', '$', user3) +puts [user1_set, user2_set, user3_set].inspect # >>> ["OK", "OK", "OK"] +# STEP_END +# REMOVE_START +assert_equal('OK', user1_set) +assert_equal('OK', user2_set) +assert_equal('OK', user3_set) +# REMOVE_END + +# STEP_START query1 +find_paul_result = index.search('Paul @age:[30 40]') + +puts find_paul_result.total # >>> 1 +# The index has the key prefix `user:`, so the client returns the +# logical document id with that prefix removed. +find_paul_result.each { |doc| puts doc.id } # >>> 3 +# STEP_END +# REMOVE_START +assert_equal(1, find_paul_result.total) +assert_equal('3', find_paul_result.documents.first.id) +# REMOVE_END + +# STEP_START query2 +cities_query = Search::Query.new('Paul').return_field('$.city', as_field: 'city') +cities_result = index.search(cities_query) + +cities_result.documents.sort_by(&:id).each do |doc| + puts "#{doc.id}: #{doc['city']}" +end +# >>> 1: London +# >>> 3: Tel Aviv +# STEP_END +# REMOVE_START +sorted_cities = cities_result.documents.sort_by(&:id) +assert_equal(%w[1 3], sorted_cities.map(&:id)) +assert_equal(['London', 'Tel Aviv'], sorted_cities.map { |doc| doc['city'] }) +# REMOVE_END + +# STEP_START query3 +request = Search::AggregateRequest.new('*') + .group_by('@city', Search::Reducers.count.as('count')) + +agg_result = index.aggregate(request) + +agg_result.rows.sort_by { |row| row['city'] }.each do |row| + puts "#{row['city']} - #{row['count']}" +end +# >>> London - 1 +# >>> Tel Aviv - 2 +# STEP_END +# REMOVE_START +sorted_rows = agg_result.rows.sort_by { |row| row['city'] } +assert_equal(['London', 'Tel Aviv'], sorted_rows.map { |row| row['city'] }) +assert_equal(%w[1 2], sorted_rows.map { |row| row['count'] }) +# REMOVE_END + +# STEP_START cleanup_hash +begin + r.ft_dropindex('hash-idx:users', delete_documents: true) +rescue Redis::CommandError + # Index doesn't exist, so there is nothing to drop. +end + +r.del('huser:1', 'huser:2', 'huser:3') +# STEP_END + +# STEP_START make_hash_index +hash_schema = Search::Schema.build do + text_field 'name' + tag_field 'city' + numeric_field 'age' +end + +hash_definition = Search::IndexDefinition.new( + prefix: ['huser:'], + index_type: Search::IndexType::HASH +) + +hash_index = r.create_index('hash-idx:users', hash_schema, definition: hash_definition) +puts hash_index.name # >>> hash-idx:users +# STEP_END + +# STEP_START add_hash_data +huser1_set = r.hset('huser:1', user1) +huser2_set = r.hset('huser:2', user2) +huser3_set = r.hset('huser:3', user3) +puts [huser1_set, huser2_set, huser3_set].inspect # >>> [4, 4, 4] +# STEP_END +# REMOVE_START +assert_equal(4, huser1_set) +assert_equal(4, huser2_set) +assert_equal(4, huser3_set) +# REMOVE_END + +# STEP_START query1_hash +find_paul_hash_result = hash_index.search('Paul @age:[30 40]') + +puts find_paul_hash_result.total # >>> 1 +find_paul_hash_result.each do |doc| + puts "#{doc.id}: #{doc['name']}, #{doc['city']}" +end +# >>> 3: Paul Zamir, Tel Aviv +# STEP_END +# REMOVE_START +assert_equal(1, find_paul_hash_result.total) +assert_equal('3', find_paul_hash_result.documents.first.id) +# REMOVE_END + +r.close