Implement FT.INFO command#3599
Conversation
da2321a to
2e197bf
Compare
|
@a-TODO-rov went over the review and did some polishing myself:
IMHO we are good to go |
🛡️ Jit Security Scan Results✅ No security findings were detected in this PR
Security scan by Jit
|
| private IndexInfo.TextField<V> createTextField(V identifier, V attribute, boolean sortable, boolean unNormalizedForm, | ||
| boolean noIndex, boolean indexEmpty, boolean indexMissing, Map<String, Object> attributeMap, | ||
| Map<String, Object> additionalFields) { | ||
| Double weight = parseDouble(attributeMap.get(FLAG_WEIGHT)); |
There was a problem hiding this comment.
TextField weight always 0.0, never null when absent
Medium Severity
createTextField uses parseDouble(attributeMap.get(FLAG_WEIGHT)) which returns primitive double (defaulting to 0.0 when the key is absent). This gets autoboxed to Double.valueOf(0.0), so TextField.getWeight() never returns null — contradicting its documented contract of "or null if not set." The getDouble helper method already exists and correctly returns null for missing keys, so it looks like it was meant to be used here instead of parseDouble.
Additional Locations (1)
| && !key.equals(FLAG_NOINDEX) && !key.equals(FLAG_INDEXEMPTY) && !key.equals(FLAG_INDEXMISSING)) { | ||
| vectorAttributes.put(key, entry.getValue()); | ||
| } | ||
| } |
There was a problem hiding this comment.
RESP3 flags key leaks into vector attributes
Low Severity
In createVectorField, the exclusion list when building vectorAttributes does not include ATTR_FLAGS ("flags"). In RESP3, the attribute map contains a flags key with a List value (e.g., ["SORTABLE", "NOINDEX"]). This list is incorrectly included in vectorAttributes alongside actual vector attributes like DIM and DISTANCE_METRIC, polluting the map that VectorField.getAttributes() returns to callers.
|
@tishun Can we include this in the next release? |
|
Hey, @jruaux
for (int i = 0; i < list.size(); i += 2) {
if (i + 1 < list.size()) {
String key = decodeStringAsString(list.get(i));
Object val = list.get(i + 1);
// Handle special case where SORTABLE has a flag as its value (e.g., SORTABLE NOSTEM)
// In this case, we need to add both SORTABLE and the flag (NOSTEM) as separate keys
if (FLAG_SORTABLE.equals(key)) {
attributeMap.put(key, val);
String valStr = decodeStringAsString(val);
// Check if the value is a known flag
if (isFieldFlag(valStr)) {
attributeMap.put(valStr, valStr);
}
} else {
attributeMap.put(key, val);
}
}
}How will this function behave for this response: 7) attributes
8) 1) 1) identifier
2) title
3) attribute
4) product_title
5) type
6) TEXT
7) WEIGHT
8) "2"
9) SORTABLE
10) NOSTEM
11) WITHSUFFIXTRIEWill WITHSUFFIXTRIE be true ? This response is from testFtInfoCommand() integration test BTW.
|
Implements FT.INFO command to retrieve index information from RediSearch. Returns a strongly-typed IndexInfo object instead of Map<String, Object> for better type safety and developer experience. Changes: - Add IndexInfo class with comprehensive index metadata - Add IndexInfoParser supporting RESP2 and RESP3 protocols - Implement ftInfo across all API layers (sync, async, reactive, coroutines) - Add unit tests for IndexInfo class - Update integration tests
- Changed IndexInfo from IndexInfo<K, V> to IndexInfo<V> - Index name is now always String type (decoded with StringCodec) - Updated all ftInfo method signatures across all command interfaces - Updated IndexInfoParser to decode index name as String - Updated all tests to use new signature - Removed IndexInfoParserUnitTests (integration tests provide better coverage) All integration tests passing (testFtInfoCommand*)
- Handle case where parseListValue already parsed ComplexData into List - Add support for String values in parseLong, parseDouble, and parseBoolean methods - Fix parsing of field flags when SORTABLE has a flag as its value (e.g., SORTABLE NOSTEM) - Update getBoolean to decode non-String flag values using decodeStringAsString - Add isFieldFlag helper method to identify known field flags
- Fix RESP2 field attribute parsing to handle standalone boolean flags (SORTABLE, NOSTEM, WITHSUFFIXTRIE, etc.) which are returned as single elements, not key-value pairs - Add case-insensitive handling for COORD_SYSTEM and ALGORITHM attributes (RESP2 returns lowercase, RESP3 returns uppercase) - Normalize vector attribute keys to uppercase and numeric values to Long for consistent types across protocol versions - Remove phonetic field from TextField as Redis FT.INFO never returns it even when set during index creation - Fix test to use non-default language (FRENCH) since Redis only returns default_language when it differs from the default (english) - Fix vector field DATA_TYPE attribute name (was incorrectly TYPE)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
There are 4 total unresolved issues (including 2 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 30c52c9. Configure here.
| if (isStandaloneFlag(valStr) && !isFlagWithValue(key)) { | ||
| attributeMap.put(key, key); | ||
| i++; | ||
| continue; |
There was a problem hiding this comment.
RESP2 field parsing misidentifies values matching flag names
Medium Severity
In parseResp2FieldAttributes, the heuristic that checks if the next element is a standalone flag to treat the current element as standalone can misparse valid key-value pairs. If a field's attribute value happens to match a standalone flag name (e.g., a field aliased as "SORTABLE"), the parser incorrectly treats both the key and value as standalone flags instead of a key-value pair, losing the actual value.
Reviewed by Cursor Bugbot for commit 30c52c9. Configure here.
| private boolean isFieldFlag(String str) { | ||
| return FLAG_NOSTEM.equals(str) || FLAG_UNF.equals(str) || FLAG_NOINDEX.equals(str) || FLAG_PHONETIC.equals(str) | ||
| || FLAG_WITHSUFFIXTRIE.equals(str) || FLAG_INDEXEMPTY.equals(str) || FLAG_INDEXMISSING.equals(str); | ||
| } |
There was a problem hiding this comment.
Unused isFieldFlag method is dead code
Low Severity
The isFieldFlag method is defined but never called anywhere in the codebase. It appears to be leftover code superseded by the isStandaloneFlag and isFlagWithValue methods which handle the same classification. Its flag list also differs from isStandaloneFlag (missing SORTABLE and CASESENSITIVE, includes PHONETIC), adding confusion about the intended semantics.
Reviewed by Cursor Bugbot for commit 30c52c9. Configure here.


This PR implements the FT.INFO command to retrieve index information from RediSearch.
The implementation returns a IndexInfo object.
Changes:
Testing:
All unit tests pass. Integration tests require a Redis instance with RediSearch module.
Note
Medium Risk
Adds a new RediSearch command and a fairly complex RESP2/RESP3 response parser with new public model types, which could affect client compatibility and parsing correctness across Redis versions.
Overview
Adds support for the RediSearch
FT.INFOcommand across sync/async/reactive/coroutines APIs, wiring it throughRediSearchCommandBuilderand introducing theFT_INFOCommandType.Introduces a new
IndexInfomodel (with nested typed stats/field definitions and a forward-compatibleadditionalFieldsmap) plus anIndexInfoParserthat parses both RESP2 and RESP3 responses. Adds unit tests covering option/definition/field/stat parsing scenarios and edge cases.Reviewed by Cursor Bugbot for commit 4e904ef. Bugbot is set up for automated code reviews on this repo. Configure here.