Skip to content

Commit c338b67

Browse files
committed
RDBC-642 Various tests
1 parent 949c67b commit c338b67

File tree

19 files changed

+1489
-47
lines changed

19 files changed

+1489
-47
lines changed

ravendb/documents/indexes/definitions.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,11 @@ def __str__(self):
5959

6060

6161
class FieldStorage(Enum):
62-
YES = " Yes"
62+
YES = "Yes"
6363
NO = "No"
6464

6565

6666
class FieldIndexing(Enum):
67-
YES = " Yes"
6867
NO = "No"
6968
SEARCH = "Search"
7069
EXACT = "Exact"
@@ -87,7 +86,7 @@ def __str__(self):
8786

8887

8988
class FieldTermVector(Enum):
90-
YES = " Yes"
89+
YES = "Yes"
9190
NO = "No"
9291
WITH_POSITIONS = "WithPositions"
9392
WITH_OFFSETS = "WithOffsets"
@@ -158,7 +157,7 @@ def to_json(self):
158157
"Storage": self.storage,
159158
"Indexing": self.indexing,
160159
"TermVector": self.term_vector,
161-
"Spatial": self.spatial,
160+
"Spatial": self.spatial.to_json() if self.spatial else None,
162161
"Analyzer": self.analyzer,
163162
"Suggestions": self.suggestions,
164163
}

ravendb/documents/indexes/index_creation.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
from ravendb import constants
66
from ravendb.documents.conventions import DocumentConventions
7+
from ravendb.documents.indexes.spatial.configuration import SpatialOptionsFactory
78
from ravendb.documents.store.definition import DocumentStore, DocumentStoreBase
89
from ravendb.documents.indexes.definitions import (
910
IndexDefinition,
@@ -105,9 +106,8 @@ def is_map_reduce(self) -> bool:
105106
def _index(self, field: str, indexing: FieldIndexing) -> None:
106107
self._indexes_strings[field] = indexing
107108

108-
# todo: def _spatial(self, field:str, indexing:Callable[[SpatialOptionsFactory],SpatialOptions]) -> None
109-
def _spatial(self, field: str, indexing: Callable) -> None:
110-
pass
109+
def _spatial(self, field: str, indexing: Callable[[SpatialOptionsFactory], SpatialOptions]) -> None:
110+
self._spatial_options_strings[field] = indexing(SpatialOptionsFactory())
111111

112112
def _store_all_fields(self, storage: FieldStorage) -> None:
113113
self._stores_strings[constants.Documents.Indexing.Fields.ALL_FIELDS] = storage

ravendb/documents/indexes/spatial/configuration.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
from __future__ import annotations
23
from enum import Enum
34
from typing import Union, List, Dict
45

@@ -56,6 +57,18 @@ def __init__(
5657
self.max_y = max_y
5758
self.units = units
5859

60+
def to_json(self) -> Dict:
61+
return {
62+
"Type": self.type.value,
63+
"Strategy": self.strategy.value,
64+
"MaxTreeLevel": self.max_tree_level,
65+
"MinX": self.min_x,
66+
"MaxX": self.max_x,
67+
"MinY": self.min_y,
68+
"MaxY": self.max_y,
69+
"Units": self.units,
70+
}
71+
5972
@classmethod
6073
def from_copy(cls, options: SpatialOptions) -> SpatialOptions:
6174
return cls(
@@ -126,3 +139,99 @@ def __init__(
126139
@classmethod
127140
def from_json(cls, json_dict: Dict) -> AutoSpatialOptions:
128141
return cls.from_copy(SpatialOptions.from_copy(json_dict.get("Options")))
142+
143+
144+
class SpatialOptionsFactory:
145+
class GeographySpatialOptionsFactory:
146+
def default_option(self, circle_radius_units: SpatialUnits = SpatialUnits.KILOMETERS) -> SpatialOptions:
147+
return self.geohash_prefix_tree_index(0, circle_radius_units)
148+
149+
def bounding_box_index(self, circle_radius_units: SpatialUnits = SpatialUnits.KILOMETERS) -> SpatialOptions:
150+
ops = SpatialOptions()
151+
ops.type = SpatialFieldType.GEOGRAPHY
152+
ops.strategy = SpatialSearchStrategy.BOUNDING_BOX
153+
ops.units = circle_radius_units
154+
return ops
155+
156+
def geohash_prefix_tree_index(
157+
self, max_tree_level: int, circle_radius_units: SpatialUnits = SpatialUnits.KILOMETERS
158+
) -> SpatialOptions:
159+
if max_tree_level == 0:
160+
max_tree_level = SpatialOptions.DEFAULT_GEOHASH_LEVEL
161+
162+
opts = SpatialOptions()
163+
opts.type = SpatialFieldType.GEOGRAPHY
164+
opts.max_tree_level = max_tree_level
165+
opts.strategy = SpatialSearchStrategy.GEOHASH_PREFIX_TREE
166+
opts.units = circle_radius_units
167+
return opts
168+
169+
def quad_prefix_tree_level(
170+
self, max_tree_level: int, circle_radius_units: SpatialUnits = SpatialUnits.KILOMETERS
171+
) -> SpatialOptions:
172+
if max_tree_level == 0:
173+
max_tree_level = SpatialOptions.DEFAULT_QUAD_TREE_LEVEL
174+
175+
opts = SpatialOptions()
176+
opts.type = SpatialFieldType.GEOGRAPHY
177+
opts.max_tree_level = max_tree_level
178+
opts.strategy = SpatialSearchStrategy.QUAD_PREFIX_TREE
179+
opts.units = circle_radius_units
180+
return opts
181+
182+
class CartesianSpatialOptionsFactory:
183+
def bounding_box_index(self) -> SpatialOptions:
184+
opts = SpatialOptions()
185+
opts.type = SpatialFieldType.CARTESIAN
186+
opts.strategy = SpatialSearchStrategy.BOUNDING_BOX
187+
return opts
188+
189+
def quad_prefix_tree_index(
190+
self, max_tree_level: int, bounds: SpatialOptionsFactory.SpatialBounds
191+
) -> SpatialOptions:
192+
if max_tree_level == 0:
193+
raise ValueError("maxTreeLevel")
194+
195+
opts = SpatialOptions()
196+
opts.type = SpatialFieldType.CARTESIAN
197+
opts.max_tree_level = max_tree_level
198+
opts.strategy = SpatialSearchStrategy.QUAD_PREFIX_TREE
199+
opts.min_x = bounds.min_x
200+
opts.max_x = bounds.max_x
201+
opts.min_y = bounds.min_y
202+
opts.max_y = bounds.max_y
203+
204+
return opts
205+
206+
def geography(self) -> GeographySpatialOptionsFactory:
207+
return SpatialOptionsFactory.GeographySpatialOptionsFactory()
208+
209+
def cartesian(self) -> CartesianSpatialOptionsFactory:
210+
return SpatialOptionsFactory.CartesianSpatialOptionsFactory()
211+
212+
class SpatialBounds:
213+
def __init__(self, min_x: float, min_y: float, max_x: float, max_y: float):
214+
self.min_x = min_x
215+
self.min_y = min_y
216+
self.max_x = max_x
217+
self.max_y = max_y
218+
219+
def __hash__(self):
220+
hash((self.max_x, self.min_x, self.max_y, self.min_y))
221+
222+
def __eq__(self, other: object):
223+
if self == other:
224+
return True
225+
if other == None:
226+
return False
227+
if self.__class__ != other.__class__:
228+
return False
229+
other: SpatialOptionsFactory.SpatialBounds
230+
return all(
231+
[
232+
self.max_x == other.max_x,
233+
self.max_y == other.max_y,
234+
self.min_x == other.min_x,
235+
self.min_y == other.min_y,
236+
]
237+
)

ravendb/documents/queries/explanation.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from __future__ import annotations
12
from typing import Dict, List
23

34
from ravendb.documents.queries.query import QueryResult
@@ -14,3 +15,10 @@ def update(self, query_result: QueryResult) -> None:
1415
class ExplanationOptions:
1516
def __init__(self, group_key: str = None):
1617
self.group_key = group_key
18+
19+
@classmethod
20+
def from_json(cls, json_dict: Dict) -> ExplanationOptions:
21+
return cls(json_dict["groupKey"])
22+
23+
def to_json(self) -> Dict:
24+
return {"groupKey": self.group_key}

ravendb/documents/session/entity_to_json.py

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ravendb.documents.session.document_info import DocumentInfo
88
from ravendb.documents.session.event_args import (
99
BeforeConversionToDocumentEventArgs,
10-
AfterConversionToDocumentEventArgs,
10+
AfterConversionToDocumentEventArgs, BeforeConversionToEntityEventArgs, AfterConversionToEntityEventArgs,
1111
)
1212
from ravendb.exceptions import exceptions
1313
from ravendb.exceptions.exceptions import InvalidOperationException
@@ -72,10 +72,10 @@ def _convert_entity_to_json_internal(
7272

7373
# todo: refactor this method, make it more useful/simple and less ugly (like this return...[0])
7474
def convert_to_entity(
75-
self, entity_type: Type[_T], key: str, document: dict, track_entity: bool, nested_object_types=None
75+
self, entity_type: Type[_T], key: str, document: dict, track_entity: bool
7676
) -> _T:
7777
conventions = self._session.conventions
78-
return self.convert_to_entity_static(document, entity_type, conventions, nested_object_types)
78+
return self.convert_to_entity_static(document, entity_type, conventions, self._session)
7979

8080
@staticmethod
8181
def populate_entity_static(entity, document: dict) -> None:
@@ -148,25 +148,28 @@ def convert_to_entity_by_key_static(
148148

149149
@staticmethod
150150
def convert_to_entity_static(
151-
document: dict, object_type: [_T], conventions: "DocumentConventions", nested_object_types=None
151+
document: dict, object_type: [_T], conventions: "DocumentConventions", session_hook: Optional["InMemoryDocumentSessionOperations"] = None
152152
) -> _T:
153153
metadata = document.pop("@metadata")
154154
document_deepcopy = deepcopy(document)
155155
type_from_metadata = conventions.try_get_type_from_metadata(metadata)
156156
is_inherit = False
157-
# todo: events
158-
# events.before_conversion_to_entity(document, metadata, type_from_metadata)
157+
key = metadata.get(constants.Documents.Metadata.ID, None)
158+
if session_hook:
159+
session_hook.before_conversion_to_entity_invoke(BeforeConversionToEntityEventArgs(session_hook,key,object_type,document_deepcopy))
159160

160161
if object_type == dict or type_from_metadata == "builtins.dict":
161-
# events.after_conversion_to_entity(document, document, metadata)
162+
session_hook.after_conversion_to_entity_invoke(AfterConversionToEntityEventArgs(session_hook, key, document_deepcopy, document_deepcopy))
162163
return document_deepcopy
163164

164165
if type_from_metadata is None:
165166
if object_type is not None:
166167
metadata["Raven-Python-Type"] = "{0}.{1}".format(object_type.__module__, object_type.__name__)
167168
else: # no type defined on document or during load, return a dict
168169
dyn = _DynamicStructure(**document_deepcopy)
169-
# events.after_conversion_to_entity(dyn, document, metadata)
170+
if session_hook:
171+
session_hook.after_conversion_to_entity_invoke(
172+
AfterConversionToEntityEventArgs(session_hook, key, document_deepcopy,dyn))
170173
return dyn
171174
else:
172175
object_from_metadata = Utils.import_class(type_from_metadata)
@@ -187,8 +190,9 @@ def convert_to_entity_static(
187190
if "from_json" in object_type.__dict__ and inspect.ismethod(object_type.from_json):
188191
entity = object_type.from_json(document_deepcopy)
189192

190-
elif nested_object_types is None and is_inherit:
193+
elif is_inherit:
191194
entity = Utils.convert_json_dict_to_object(document_deepcopy, object_type)
195+
192196
else:
193197
entity = _DynamicStructure(**document_deepcopy)
194198
entity.__class__ = object_type
@@ -197,33 +201,11 @@ def convert_to_entity_static(
197201
except TypeError as e:
198202
raise InvalidOperationException("Probably projection error", e)
199203

200-
if nested_object_types:
201-
for key in nested_object_types:
202-
attr = getattr(entity, key)
203-
if attr:
204-
try:
205-
if isinstance(attr, list):
206-
nested_list = []
207-
for attribute in attr:
208-
nested_list.append(Utils.initialize_object(attribute, nested_object_types[key]))
209-
setattr(entity, key, nested_list)
210-
elif nested_object_types[key] is datetime:
211-
setattr(entity, key, Utils.string_to_datetime(attr))
212-
elif nested_object_types[key] is timedelta:
213-
setattr(entity, key, Utils.string_to_timedelta(attr))
214-
else:
215-
setattr(
216-
entity,
217-
key,
218-
Utils.initialize_object(attr, nested_object_types[key]),
219-
)
220-
except TypeError as e:
221-
print(e)
222-
pass
223-
224204
if "Id" in entity.__dict__:
225205
entity.Id = metadata.get("@id", None)
226-
# events.after_conversion_to_entity(entity, document, metadata)
206+
if session_hook:
207+
session_hook.after_conversion_to_entity_invoke(
208+
AfterConversionToEntityEventArgs(session_hook, key, document_deepcopy, entity))
227209
return entity
228210

229211
def remove_from_missing(self, entity):

ravendb/documents/session/in_memory_document_session_operations.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1635,7 +1635,7 @@ def hash_code(self) -> int:
16351635
def __deserialize_from_transformer(
16361636
self, object_type: Type[_T], key: Union[None, str], document: dict, track_entity: bool
16371637
) -> _T:
1638-
return self.entity_to_json.convert_to_entity(object_type, key, document, track_entity, None)
1638+
return self.entity_to_json.convert_to_entity(object_type, key, document, track_entity)
16391639

16401640
def check_if_id_already_included(self, ids: List[str], includes: Union[List[List], List[str]]) -> bool:
16411641
if includes and not isinstance(includes[0], str):

ravendb/documents/session/query.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -851,6 +851,10 @@ def _generate_index_query(self, query: str) -> IndexQuery:
851851
index_query.query_parameters = self._query_parameters
852852
index_query.disable_caching = self._disable_caching
853853
index_query.projection_behavior = self._projection_behavior
854+
855+
if self._page_size is not None:
856+
index_query.page_size = self._page_size
857+
854858
return index_query
855859

856860
def _search(self, field_name: str, search_terms: str, operator: SearchOperator) -> None:
@@ -901,11 +905,11 @@ def __build_with(self, query_text: List[str]) -> None:
901905
query_text.append(os.linesep)
902906

903907
def __build_pagination(self, query_text: List[str]) -> None:
904-
if self._start is not None and self._start > 0 or self._page_size is not None:
908+
if (self._start is not None and self._start > 0) or self._page_size is not None:
905909
query_text.append(" limit $")
906-
query_text.append(self.__add_query_parameter(self._start))
910+
query_text.append(self.__add_query_parameter(self._start or 0))
907911
query_text.append(", $")
908-
query_text.append(self.__add_query_parameter(self._page_size))
912+
query_text.append(self.__add_query_parameter(self._page_size or 0))
909913

910914
def __build_include(self, query_text: List[str]) -> None:
911915
if (
@@ -1709,7 +1713,7 @@ def order_by_score_descending(self) -> DocumentQuery[_T]:
17091713
return self
17101714

17111715
def include_explanations(
1712-
self, options: Optional[ExplanationOptions], explanations_callback: Callable[[Explanations], None]
1716+
self, options: Optional[ExplanationOptions] = None, explanations_callback: Callable[[Explanations], None] = None
17131717
) -> DocumentQuery[_T]:
17141718
self._include_explanations(options, explanations_callback)
17151719
return self

ravendb/infrastructure/faceted.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
from __future__ import annotations
2+
import datetime
3+
from enum import Enum
4+
from typing import Dict
5+
6+
7+
class Currency(Enum):
8+
EUR = "EUR"
9+
PLN = "PLN"
10+
NIS = "NIS"
11+
12+
13+
class Order:
14+
def __init__(
15+
self,
16+
product: str = None,
17+
total: float = None,
18+
currency: Currency = None,
19+
quantity: int = None,
20+
region: int = None,
21+
at: datetime.datetime = None,
22+
tax: float = None,
23+
):
24+
self.product = product
25+
self.total = total
26+
self.currency = currency
27+
self.quantity = quantity
28+
self.region = region
29+
self.at = at
30+
self.tax = tax
31+
32+
@classmethod
33+
def from_json(cls, json_dict: Dict) -> Order:
34+
return cls(
35+
json_dict["product"],
36+
json_dict["total"],
37+
Currency(json_dict["currency"]),
38+
json_dict["quantity"],
39+
json_dict["region"],
40+
datetime.datetime.fromisoformat(json_dict["at"]) if "at" in json_dict else None,
41+
json_dict["tax"],
42+
)
43+
44+
def to_json(self) -> Dict:
45+
return {
46+
"product": self.product,
47+
"total": self.total,
48+
"currency": self.currency.value,
49+
"quantity": self.quantity,
50+
"region": self.region,
51+
"at": self.at.isoformat() if self.at else None,
52+
"tax": self.tax,
53+
}

0 commit comments

Comments
 (0)