Skip to content

Commit 2700a00

Browse files
committed
feat: add skip_decode parameter to return_fields method (#252)
Implements skip_decode parameter for return_fields() method to improve field deserialization UX. This allows users to skip decoding of binary fields like embeddings while still returning them in query results. - Added optional skip_decode parameter to BaseQuery.return_fields() - Parameter accepts string or list of field names to skip decoding - Maintains backward compatibility when skip_decode is not provided - Comprehensive unit test coverage for all query types - Enhanced skip_decode to use parent's return_field with decode_field=False - Added comprehensive integration tests with real Redis - Maintained full backward compatibility with return_field(decode_field=False) - Tests confirm proper binary field handling (embeddings, image data)
1 parent 2660e2f commit 2700a00

File tree

3 files changed

+553
-0
lines changed

3 files changed

+553
-0
lines changed

redisvl/query/query.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class BaseQuery(RedisQuery):
3030
_params: Dict[str, Any] = {}
3131
_filter_expression: Union[str, FilterExpression] = FilterExpression("*")
3232
_built_query_string: Optional[str] = None
33+
_skip_decode_fields: Set[str] = set()
3334

3435
def __init__(self, query_string: str = "*"):
3536
"""
@@ -47,6 +48,9 @@ def __init__(self, query_string: str = "*"):
4748
# has not been built yet.
4849
self._built_query_string = None
4950

51+
# Initialize skip_decode_fields set
52+
self._skip_decode_fields = set()
53+
5054
def __str__(self) -> str:
5155
"""Return the string representation of the query."""
5256
return " ".join([str(x) for x in self.get_args()])
@@ -107,6 +111,60 @@ def _query_string(self, value: Optional[str]):
107111
"""Setter for _query_string to maintain compatibility with parent class."""
108112
self._built_query_string = value
109113

114+
def return_fields(
115+
self, *fields, skip_decode: Optional[Union[str, List[str]]] = None
116+
):
117+
"""
118+
Set the fields to return with search results.
119+
120+
Args:
121+
*fields: Variable number of field names to return.
122+
skip_decode: Optional field name or list of field names that should not be
123+
decoded. Useful for binary data like embeddings.
124+
125+
Returns:
126+
self: Returns the query object for method chaining.
127+
128+
Raises:
129+
TypeError: If skip_decode is not a string, list, or None.
130+
"""
131+
# Only clear fields when skip_decode is provided (indicating user is explicitly setting fields)
132+
# This preserves backward compatibility when return_fields is called multiple times
133+
if skip_decode is not None:
134+
# Clear existing fields to provide replacement behavior
135+
self._return_fields = []
136+
self._return_fields_decode_as = {}
137+
138+
# Process skip_decode parameter to prepare decode settings
139+
if isinstance(skip_decode, str):
140+
skip_decode_set = {skip_decode}
141+
self._skip_decode_fields = {skip_decode}
142+
elif isinstance(skip_decode, list):
143+
skip_decode_set = set(skip_decode)
144+
self._skip_decode_fields = set(skip_decode)
145+
else:
146+
raise TypeError(
147+
"skip_decode must be a string, list of strings, or None"
148+
)
149+
150+
# Add fields using parent's return_field method with proper decode settings
151+
for field in fields:
152+
if field in skip_decode_set:
153+
# Use return_field with decode_field=False for skip_decode fields
154+
super().return_field(field, decode_field=False)
155+
else:
156+
# Use normal return_field for other fields
157+
super().return_field(field)
158+
else:
159+
# Standard additive behavior (backward compatible)
160+
super().return_fields(*fields)
161+
162+
# Initialize skip_decode_fields if not already set
163+
if not hasattr(self, "_skip_decode_fields"):
164+
self._skip_decode_fields = set()
165+
166+
return self
167+
110168

111169
class FilterQuery(BaseQuery):
112170
def __init__(

0 commit comments

Comments
 (0)