11from __future__ import annotations
22
33from typing import Any , TYPE_CHECKING
4- from google .protobuf import json_format
54from google .protobuf .message import Message
65
76import uuid
@@ -56,6 +55,61 @@ def dict_to_proto_struct(d: dict | None) -> "Struct":
5655 return s
5756
5857
58+ def _struct_to_dict (struct : "Struct" ) -> dict [str , Any ]:
59+ """Convert a protobuf Struct to dict by directly accessing fields.
60+
61+ This optimized version is ~2x faster than json_format.MessageToDict
62+ by avoiding JSON serialization/deserialization overhead.
63+
64+ Args:
65+ struct: A protobuf Struct message.
66+
67+ Returns:
68+ Dictionary representation of the Struct.
69+ """
70+
71+ result : dict [str , Any ] = {}
72+ for key , value in struct .fields .items ():
73+ # Directly access the Value fields based on which one is set
74+ if value .HasField ("null_value" ):
75+ result [key ] = None
76+ elif value .HasField ("number_value" ):
77+ result [key ] = value .number_value
78+ elif value .HasField ("string_value" ):
79+ result [key ] = value .string_value
80+ elif value .HasField ("bool_value" ):
81+ result [key ] = value .bool_value
82+ elif value .HasField ("struct_value" ):
83+ result [key ] = _struct_to_dict (value .struct_value )
84+ elif value .HasField ("list_value" ):
85+ # Convert ListValue to Python list
86+ list_result = []
87+ for item in value .list_value .values :
88+ if item .HasField ("null_value" ):
89+ list_result .append (None )
90+ elif item .HasField ("number_value" ):
91+ list_result .append (item .number_value )
92+ elif item .HasField ("string_value" ):
93+ list_result .append (item .string_value )
94+ elif item .HasField ("bool_value" ):
95+ list_result .append (item .bool_value )
96+ elif item .HasField ("struct_value" ):
97+ list_result .append (_struct_to_dict (item .struct_value ))
98+ elif item .HasField ("list_value" ):
99+ # Nested lists
100+ nested_list = []
101+ for nested_item in item .list_value .values :
102+ if nested_item .HasField ("number_value" ):
103+ nested_list .append (nested_item .number_value )
104+ elif nested_item .HasField ("string_value" ):
105+ nested_list .append (nested_item .string_value )
106+ elif nested_item .HasField ("bool_value" ):
107+ nested_list .append (nested_item .bool_value )
108+ list_result .append (nested_list )
109+ result [key ] = list_result
110+ return result
111+
112+
59113def parse_sparse_values (sparse_values : dict | None ) -> SparseValues :
60114 from typing import cast
61115
@@ -76,33 +130,33 @@ def parse_fetch_response(
76130 """
77131 # Extract response info from initial metadata
78132 from pinecone .utils .response_info import extract_response_info
133+ from pinecone .db_data .dataclasses import SparseValues
79134
80135 metadata = initial_metadata or {}
81136 response_info = extract_response_info (metadata )
82137
83138 # Directly access protobuf fields instead of converting entire message to dict
139+ vectors = response .vectors
84140 vd = {}
85141 # namespace is a required string field, so it will always have a value (default empty string)
86142 namespace = response .namespace
87143
88144 # Iterate over vectors map directly
89- for vec_id , vec in response . vectors .items ():
145+ for vec_id , vec in vectors .items ():
90146 # Convert vector.values (RepeatedScalarFieldContainer) to list
91147 values = list (vec .values ) if vec .values else []
92148
93149 # Handle sparse_values if present (check if field is set and not empty)
94150 parsed_sparse = None
95151 if vec .HasField ("sparse_values" ) and vec .sparse_values :
96- from pinecone .db_data .dataclasses import SparseValues
97-
98152 parsed_sparse = SparseValues (
99153 indices = list (vec .sparse_values .indices ), values = list (vec .sparse_values .values )
100154 )
101155
102- # Convert metadata Struct to dict only when needed
156+ # Convert metadata Struct to dict only when needed using optimized conversion
103157 metadata_dict = None
104158 if vec .HasField ("metadata" ) and vec .metadata :
105- metadata_dict = json_format . MessageToDict (vec .metadata )
159+ metadata_dict = _struct_to_dict (vec .metadata )
106160
107161 vd [vec_id ] = Vector (
108162 id = vec .id , values = values , sparse_values = parsed_sparse , metadata = metadata_dict
@@ -152,10 +206,10 @@ def parse_fetch_by_metadata_response(
152206 }
153207 )
154208
155- # Convert metadata Struct to dict only when needed
209+ # Convert metadata Struct to dict only when needed using optimized conversion
156210 metadata_dict = None
157211 if vec .HasField ("metadata" ) and vec .metadata :
158- metadata_dict = json_format . MessageToDict (vec .metadata )
212+ metadata_dict = _struct_to_dict (vec .metadata )
159213
160214 vd [vec_id ] = _Vector (
161215 id = vec .id ,
@@ -289,9 +343,9 @@ def query_response_to_dict(response: "ProtoQueryResponse") -> dict[str, Any]:
289343 "values" : list (match .sparse_values .values ),
290344 }
291345
292- # Convert metadata if present
346+ # Convert metadata if present using optimized conversion
293347 if match .HasField ("metadata" ) and match .metadata :
294- match_dict ["metadata" ] = json_format . MessageToDict (match .metadata )
348+ match_dict ["metadata" ] = _struct_to_dict (match .metadata )
295349
296350 result ["matches" ].append (match_dict )
297351
@@ -342,10 +396,10 @@ def parse_query_response(
342396 indices = list (match .sparse_values .indices ), values = list (match .sparse_values .values )
343397 )
344398
345- # Convert metadata Struct to dict only when needed
399+ # Convert metadata Struct to dict only when needed using optimized conversion
346400 metadata_dict = None
347401 if match .HasField ("metadata" ) and match .metadata :
348- metadata_dict = json_format . MessageToDict (match .metadata )
402+ metadata_dict = _struct_to_dict (match .metadata )
349403
350404 sc = ScoredVector (
351405 id = match .id ,
0 commit comments