1717 overload ,
1818)
1919
20+ import numpy as np
2021from pydantic import BaseConfig , parse_obj_as
2122from typing_inspect import typingGenericAlias
2223
3435if TYPE_CHECKING :
3536 from pydantic .fields import ModelField
3637
37- from docarray .proto import DocVecProto
38+ from docarray .proto import (
39+ DocVecProto ,
40+ ListOfDocArrayProto ,
41+ ListOfDocVecProto ,
42+ NdArrayProto ,
43+ )
3844
3945torch_available = is_torch_available ()
4046if torch_available :
5460T = TypeVar ('T' , bound = 'DocVec' )
5561IndexIterType = Union [slice , Iterable [int ], Iterable [bool ], None ]
5662
63+ NONE_NDARRAY_PROTO_SHAPE = (0 ,)
64+ NONE_NDARRAY_PROTO_DTYPE = 'None'
65+
66+
67+ def _none_ndarray_proto () -> 'NdArrayProto' :
68+ from docarray .proto import NdArrayProto
69+
70+ zeros_arr = parse_obj_as (NdArray , np .zeros (NONE_NDARRAY_PROTO_SHAPE ))
71+ nd_proto = NdArrayProto ()
72+ nd_proto .dense .buffer = zeros_arr .tobytes ()
73+ nd_proto .dense .ClearField ('shape' )
74+ nd_proto .dense .shape .extend (list (zeros_arr .shape ))
75+ nd_proto .dense .dtype = NONE_NDARRAY_PROTO_DTYPE
76+
77+ return nd_proto
78+
79+
80+ def _none_docvec_proto () -> 'DocVecProto' :
81+ from docarray .proto import DocVecProto
82+
83+ return DocVecProto ()
84+
85+
86+ def _none_list_of_docvec_proto () -> 'ListOfDocArrayProto' :
87+ from docarray .proto import ListOfDocVecProto
88+
89+ return ListOfDocVecProto ()
90+
91+
92+ def _is_none_ndarray_proto (proto : 'NdArrayProto' ) -> bool :
93+ return (
94+ proto .dense .shape == list (NONE_NDARRAY_PROTO_SHAPE )
95+ and proto .dense .dtype == NONE_NDARRAY_PROTO_DTYPE
96+ )
97+
98+
99+ def _is_none_docvec_proto (proto : 'DocVecProto' ) -> bool :
100+ return (
101+ proto .tensor_columns == {}
102+ and proto .doc_columns == {}
103+ and proto .docs_vec_columns == {}
104+ and proto .any_columns == {}
105+ )
106+
107+
108+ def _is_none_list_of_docvec_proto (proto : 'ListOfDocVecProto' ) -> bool :
109+ from docarray .proto import ListOfDocVecProto
110+
111+ return isinstance (proto , ListOfDocVecProto ) and len (proto .data ) == 0
112+
57113
58114class DocVec (AnyDocArray [T_doc ]):
59115 """
@@ -243,7 +299,7 @@ def _check_doc_field_not_none(field_name, doc):
243299 elif issubclass (field_type , AnyDocArray ):
244300 if first_doc_is_none :
245301 _verify_optional_field_of_docs (docs )
246- doc_columns [field_name ] = None
302+ docs_vec_columns [field_name ] = None
247303 else :
248304 docs_list = list ()
249305 for doc in docs :
@@ -534,48 +590,104 @@ def __len__(self):
534590
535591 @classmethod
536592 def from_protobuf (cls : Type [T ], pb_msg : 'DocVecProto' ) -> T :
537- """create a Document from a protobuf message"""
593+ """create a DocVec from a protobuf message"""
594+
595+ tensor_columns : Dict [str , Optional [AbstractTensor ]] = {}
596+ doc_columns : Dict [str , Optional ['DocVec' ]] = {}
597+ docs_vec_columns : Dict [str , Optional [ListAdvancedIndexing ['DocVec' ]]] = {}
598+ any_columns : Dict [str , ListAdvancedIndexing ] = {}
599+
600+ for tens_col_name , tens_col_proto in pb_msg .tensor_columns .items ():
601+ if _is_none_ndarray_proto (tens_col_proto ):
602+ # handle values that were None before serialization
603+ tensor_columns [tens_col_name ] = None
604+ else :
605+ # TODO(johannes): handle torch, tf, numpy
606+ tensor_columns [tens_col_name ] = NdArray .from_protobuf (tens_col_proto )
607+
608+ for doc_col_name , doc_col_proto in pb_msg .doc_columns .items ():
609+ if _is_none_docvec_proto (doc_col_proto ):
610+ # handle values that were None before serialization
611+ doc_columns [doc_col_name ] = None
612+ else :
613+ col_doc_type : Type = cls .doc_type ._get_field_type (doc_col_name )
614+ doc_columns [doc_col_name ] = DocVec .__class_getitem__ (
615+ col_doc_type
616+ ).from_protobuf (doc_col_proto )
617+
618+ for docs_vec_col_name , docs_vec_col_proto in pb_msg .docs_vec_columns .items ():
619+ vec_list : Optional [ListAdvancedIndexing ]
620+ if _is_none_list_of_docvec_proto (docs_vec_col_proto ):
621+ # handle values that were None before serialization
622+ vec_list = None
623+ else :
624+ vec_list = ListAdvancedIndexing ()
625+ for doc_list_proto in docs_vec_col_proto .data :
626+ col_doc_type = cls .doc_type ._get_field_type (
627+ docs_vec_col_name
628+ ).doc_type
629+ vec_list .append (
630+ DocVec .__class_getitem__ (col_doc_type ).from_protobuf (
631+ doc_list_proto
632+ )
633+ )
634+ docs_vec_columns [docs_vec_col_name ] = vec_list
635+
636+ for any_col_name , any_col_proto in pb_msg .any_columns .items ():
637+ any_column : ListAdvancedIndexing = ListAdvancedIndexing ()
638+ for node_proto in any_col_proto .data :
639+ content = cls .doc_type ._get_content_from_node_proto (
640+ node_proto , any_col_name
641+ )
642+ any_column .append (content )
643+ any_columns [any_col_name ] = any_column
644+
538645 storage = ColumnStorage (
539- pb_msg . tensor_columns ,
540- pb_msg . doc_columns ,
541- pb_msg . docs_vec_columns ,
542- pb_msg . any_columns ,
646+ tensor_columns = tensor_columns ,
647+ doc_columns = doc_columns ,
648+ docs_vec_columns = docs_vec_columns ,
649+ any_columns = any_columns ,
543650 )
544651
545652 return cls .from_columns_storage (storage )
546653
547654 def to_protobuf (self ) -> 'DocVecProto' :
548655 """Convert DocVec into a Protobuf message"""
549656 from docarray .proto import (
550- DocListProto ,
551657 DocVecProto ,
552658 ListOfAnyProto ,
553659 ListOfDocArrayProto ,
660+ ListOfDocVecProto ,
554661 NdArrayProto ,
555662 )
556663
557- da_proto = DocListProto ()
558- for doc in self :
559- da_proto .docs .append (doc .to_protobuf ())
560-
561664 doc_columns_proto : Dict [str , DocVecProto ] = dict ()
562665 tensor_columns_proto : Dict [str , NdArrayProto ] = dict ()
563666 da_columns_proto : Dict [str , ListOfDocArrayProto ] = dict ()
564667 any_columns_proto : Dict [str , ListOfAnyProto ] = dict ()
565668
566669 for field , col_doc in self ._storage .doc_columns .items ():
567- doc_columns_proto [field ] = (
568- col_doc .to_protobuf () if col_doc is not None else None
569- )
670+ if col_doc is None :
671+ # put dummy empty DocVecProto for serialization
672+ doc_columns_proto [field ] = _none_docvec_proto ()
673+ else :
674+ doc_columns_proto [field ] = col_doc .to_protobuf ()
570675 for field , col_tens in self ._storage .tensor_columns .items ():
571- tensor_columns_proto [field ] = (
572- col_tens .to_protobuf () if col_tens is not None else None
573- )
676+ if col_tens is None :
677+ # put dummy empty NdArrayProto for serialization
678+ tensor_columns_proto [field ] = _none_ndarray_proto ()
679+ else :
680+ tensor_columns_proto [field ] = (
681+ col_tens .to_protobuf () if col_tens is not None else None
682+ )
574683 for field , col_da in self ._storage .docs_vec_columns .items ():
575- list_proto = ListOfDocArrayProto ()
684+ list_proto = ListOfDocVecProto ()
576685 if col_da :
577686 for docs in col_da :
578687 list_proto .data .append (docs .to_protobuf ())
688+ else :
689+ # put dummy empty ListOfDocVecProto for serialization
690+ list_proto = _none_list_of_docvec_proto ()
579691 da_columns_proto [field ] = list_proto
580692 for field , col_any in self ._storage .any_columns .items ():
581693 list_proto = ListOfAnyProto ()
0 commit comments