Skip to content

Commit f03bdc7

Browse files
committed
RDBC-766 BasicTimeSeriesIndexes_StrongSyntaxTest::basicMapIndex
1 parent ecd13ab commit f03bdc7

File tree

3 files changed

+313
-10
lines changed

3 files changed

+313
-10
lines changed

ravendb/documents/indexes/index_creation.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def __init__(self, index_name: str):
155155
if len(self._index_name) > 256:
156156
raise ValueError(f"The index name is limited to 256 characters, but was: {self._index_name}")
157157

158-
self.reduce: Union[None, str] = None
158+
self.reduce: Optional[str] = None
159159

160160
self.stores_strings: Dict[str, FieldStorage] = {}
161161
self.indexes_strings: Dict[str, FieldIndexing] = {}
@@ -164,17 +164,17 @@ def __init__(self, index_name: str):
164164
self.term_vectors_strings: Dict[str, FieldTermVector] = {}
165165
self.spatial_indexes_strings: Dict[str, SpatialOptions] = {}
166166

167-
self.lock_mode: Union[None, IndexLockMode] = None
168-
self.priority: Union[None, IndexLockMode] = None
169-
self.state: Union[None, IndexState] = None
170-
self.deployment_mode: Union[None, IndexDeploymentMode] = None
167+
self.lock_mode: Optional[IndexLockMode] = None
168+
self.priority: Optional[IndexLockMode] = None
169+
self.state: Optional[IndexState] = None
170+
self.deployment_mode: Optional[IndexDeploymentMode] = None
171171

172-
self.output_reduce_to_collection: Union[None, str] = None
173-
self.pattern_for_output_reduce_to_collection_references: Union[None, str] = None
174-
self.pattern_references_collection_name: Union[None, str] = None
172+
self.output_reduce_to_collection: Optional[str] = None
173+
self.pattern_for_output_reduce_to_collection_references: Optional[str] = None
174+
self.pattern_references_collection_name: Optional[str] = None
175175

176-
self.additional_sources: Union[None, Dict[str, str]] = None
177-
self.additional_assemblies: Union[None, Set[AdditionalAssembly]] = None
176+
self.additional_sources: Optional[Dict[str, str]] = None
177+
self.additional_assemblies: Optional[Set[AdditionalAssembly]] = None
178178
self.configuration: Dict[str, str] = {}
179179

180180
@abstractmethod
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
from abc import ABC
2+
from typing import Optional, Set, Dict, Callable, Union, TypeVar, List
3+
4+
from ravendb import IndexDefinition, IndexSourceType
5+
from ravendb.documents.conventions import DocumentConventions
6+
from ravendb.documents.indexes.definitions import (
7+
IndexPriority,
8+
IndexLockMode,
9+
IndexDeploymentMode,
10+
IndexState,
11+
FieldStorage,
12+
FieldIndexing,
13+
FieldTermVector,
14+
)
15+
from ravendb.documents.indexes.index_creation import AbstractIndexCreationTaskBase, AbstractIndexDefinitionBuilder
16+
from ravendb.documents.indexes.spatial.configuration import SpatialOptions, SpatialOptionsFactory
17+
from ravendb.primitives import constants
18+
19+
20+
_T_IndexDefinition = TypeVar("_T_IndexDefinition", bound=IndexDefinition)
21+
22+
23+
class TimeSeriesIndexDefinition(IndexDefinition):
24+
@property
25+
def source_type(self) -> IndexSourceType:
26+
return IndexSourceType.TIME_SERIES
27+
28+
29+
class TimeSeriesIndexDefinitionBuilder(AbstractIndexDefinitionBuilder[TimeSeriesIndexDefinition]):
30+
def __init__(self, index_name: Optional[str] = None):
31+
super().__init__(index_name)
32+
self.map: Optional[str] = None
33+
34+
def _new_index_definition(self) -> TimeSeriesIndexDefinition:
35+
return TimeSeriesIndexDefinition()
36+
37+
def to_index_definition(
38+
self, conventions: DocumentConventions, validate_map: bool = True
39+
) -> TimeSeriesIndexDefinition:
40+
if self.map is None and validate_map:
41+
raise RuntimeError(
42+
f"Map is required to generate an index, "
43+
f"you cannot create an index without a valid Map property (in index {self._index_name})."
44+
)
45+
return super().to_index_definition(conventions, validate_map)
46+
47+
def _to_index_definition(
48+
self, index_definition: TimeSeriesIndexDefinition, conventions: DocumentConventions
49+
) -> None:
50+
if map is None:
51+
return
52+
53+
index_definition.maps.add(self.map)
54+
55+
56+
class AbstractGenericTimeSeriesIndexCreationTask(AbstractIndexCreationTaskBase[TimeSeriesIndexDefinition], ABC):
57+
def __init__(
58+
self,
59+
conventions: DocumentConventions = None,
60+
priority: IndexPriority = None,
61+
lock_mode: IndexLockMode = None,
62+
deployment_mode: IndexDeploymentMode = None,
63+
state: IndexState = None,
64+
):
65+
super().__init__(conventions, priority, lock_mode, deployment_mode, state)
66+
self._reduce: Optional[str] = None
67+
self.stores_strings: Dict[str, FieldStorage] = {}
68+
self.indexes_strings: Dict[str, FieldIndexing] = {}
69+
self.analyzers_strings: Dict[str, str] = {}
70+
self.index_suggestions: Set[str] = set()
71+
self.term_vectors_strings: Dict[str, FieldTermVector] = {}
72+
self.spatial_options_strings: Dict[str, SpatialOptions] = {}
73+
74+
self._output_reduce_to_collection: Optional[str] = None
75+
self._pattern_for_output_reduce_to_collection_references: Optional[str] = None
76+
self._pattern_references_collection_name: Optional[str] = None
77+
78+
@property
79+
def is_map_reduce(self) -> bool:
80+
return self._reduce is not None
81+
82+
def _index(self, field: str, indexing: FieldIndexing) -> None:
83+
self.indexes_strings[field] = indexing
84+
85+
def _spatial(self, field: str, indexing: Callable[[SpatialOptionsFactory], SpatialOptions]) -> None:
86+
self.spatial_options_strings[field] = indexing(SpatialOptionsFactory())
87+
88+
def _store_all_fields(self, storage: FieldStorage) -> None:
89+
self.stores_strings[constants.Documents.Indexing.Fields.ALL_FIELDS] = storage
90+
91+
def _store(self, field: str, storage: FieldStorage) -> None:
92+
self.stores_strings[field] = storage
93+
94+
def _analyze(self, field: str, analyzer: str) -> None:
95+
self.analyzers_strings[field] = analyzer
96+
97+
def _term_vector(self, field: str, term_vector: FieldTermVector) -> None:
98+
self.term_vectors_strings[field] = term_vector
99+
100+
def _suggestion(self, field: str) -> None:
101+
self.index_suggestions.add(field)
102+
103+
104+
class AbstractTimeSeriesIndexCreationTask(AbstractGenericTimeSeriesIndexCreationTask):
105+
def __init__(
106+
self,
107+
conventions: DocumentConventions = None,
108+
priority: IndexPriority = None,
109+
lock_mode: IndexLockMode = None,
110+
deployment_mode: IndexDeploymentMode = None,
111+
state: IndexState = None,
112+
):
113+
super().__init__(conventions, priority, lock_mode, deployment_mode, state)
114+
self.map: Optional[str] = None
115+
116+
def create_index_definition(self) -> Union[IndexDefinition, _T_IndexDefinition]:
117+
if self.conventions is None:
118+
self.conventions = DocumentConventions()
119+
120+
index_definition_builder = TimeSeriesIndexDefinitionBuilder(self.index_name)
121+
index_definition_builder.indexes_strings = self.indexes_strings
122+
index_definition_builder.analyzers_strings = self.analyzers_strings
123+
index_definition_builder.map = self.map
124+
index_definition_builder.reduce = self._reduce
125+
index_definition_builder.stores_strings = self.stores_strings
126+
index_definition_builder.suggestions_options = self.index_suggestions
127+
index_definition_builder.term_vector_strings = self.term_vectors_strings
128+
index_definition_builder.spatial_indexes_strings = self.spatial_options_strings
129+
index_definition_builder.output_reduce_to_collection = self._output_reduce_to_collection
130+
index_definition_builder.pattern_for_output_reduce_to_collection_references = (
131+
self._pattern_for_output_reduce_to_collection_references
132+
)
133+
index_definition_builder.pattern_references_collection_name = self._pattern_references_collection_name
134+
index_definition_builder.additional_sources = self.additional_sources
135+
index_definition_builder.additional_assemblies = self.additional_assemblies
136+
index_definition_builder.configuration = self.configuration
137+
index_definition_builder.lock_mode = self.lock_mode
138+
index_definition_builder.priority = self.priority
139+
index_definition_builder.state = self.state
140+
index_definition_builder.deployment_mode = self.deployment_mode
141+
142+
return index_definition_builder.to_index_definition(self.conventions)
143+
144+
145+
class AbstractMultiMapTimeSeriesIndexCreationTask(AbstractGenericTimeSeriesIndexCreationTask):
146+
def __init__(
147+
self,
148+
conventions: DocumentConventions = None,
149+
priority: IndexPriority = None,
150+
lock_mode: IndexLockMode = None,
151+
deployment_mode: IndexDeploymentMode = None,
152+
state: IndexState = None,
153+
):
154+
super().__init__(conventions, priority, lock_mode, deployment_mode, state)
155+
self.maps: List[str] = []
156+
157+
def _add_map(self, map: str) -> None:
158+
if map is None:
159+
raise ValueError("Map cannot be None.")
160+
self.maps.append(map)
161+
162+
def create_index_definition(self) -> Union[IndexDefinition, _T_IndexDefinition]:
163+
if self.conventions is None:
164+
self.conventions = DocumentConventions()
165+
166+
index_definition_builder = TimeSeriesIndexDefinitionBuilder(self.index_name)
167+
index_definition_builder.indexes_strings = self.indexes_strings
168+
index_definition_builder.analyzers_strings = self.analyzers_strings
169+
index_definition_builder.reduce = self._reduce
170+
index_definition_builder.stores_strings = self.stores_strings
171+
index_definition_builder.suggestions_options = self.index_suggestions
172+
index_definition_builder.term_vector_strings = self.term_vectors_strings
173+
index_definition_builder.spatial_indexes_strings = self.spatial_options_strings
174+
index_definition_builder.output_reduce_to_collection = self._output_reduce_to_collection
175+
index_definition_builder.pattern_for_output_reduce_to_collection_references = (
176+
self._pattern_for_output_reduce_to_collection_references
177+
)
178+
index_definition_builder.pattern_references_collection_name = self._pattern_references_collection_name
179+
index_definition_builder.additional_sources = self.additional_sources
180+
index_definition_builder.additional_assemblies = self.additional_assemblies
181+
index_definition_builder.configuration = self.configuration
182+
index_definition_builder.lock_mode = self.lock_mode
183+
index_definition_builder.priority = self.priority
184+
index_definition_builder.state = self.state
185+
index_definition_builder.deployment_mode = self.deployment_mode
186+
187+
index_definition = index_definition_builder.to_index_definition(self.conventions, False)
188+
index_definition.maps = set(self.maps)
189+
190+
return index_definition
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
from __future__ import annotations
2+
import datetime
3+
from typing import Optional, Any, Dict
4+
5+
from ravendb.documents.indexes.time_series import (
6+
AbstractTimeSeriesIndexCreationTask,
7+
AbstractMultiMapTimeSeriesIndexCreationTask,
8+
)
9+
from ravendb.infrastructure.orders import Company
10+
from ravendb.tests.test_base import TestBase
11+
from ravendb.tools.raven_test_helper import RavenTestHelper
12+
from ravendb.tools.utils import Utils
13+
14+
15+
class TestBasicTimeSeriesIndexes_StrongSyntax(TestBase):
16+
def setUp(self):
17+
super().setUp()
18+
19+
def test_basic_map_index(self):
20+
now1 = RavenTestHelper.utc_today()
21+
22+
with self.store.open_session() as session:
23+
company = Company()
24+
session.store(company, "companies/1")
25+
session.time_series_for_entity(company, "HeartRate").append_single(now1, 7, "tag")
26+
session.save_changes()
27+
28+
time_series_index = MyTsIndex()
29+
index_definition = time_series_index.create_index_definition()
30+
31+
self.assertIsNotNone(index_definition)
32+
time_series_index.execute(self.store)
33+
34+
self.wait_for_indexing(self.store)
35+
36+
with self.store.open_session() as session:
37+
results = list(session.query_index_type(MyTsIndex, MyMultiMapTsIndex.Result))
38+
39+
self.assertEqual(1, len(results))
40+
41+
result = results[0]
42+
43+
self.assertEqual(now1, result.date)
44+
self.assertIsNotNone(result.user)
45+
self.assertGreater(result.heart_beat, 0)
46+
47+
48+
class MyTsIndex(AbstractTimeSeriesIndexCreationTask):
49+
def __init__(self):
50+
super().__init__()
51+
self.map = (
52+
"from ts in timeSeries.Companies.HeartRate "
53+
"from entry in ts.Entries "
54+
"select new { "
55+
" heartBeat = entry.Values[0], "
56+
" date = entry.Timestamp.Date, "
57+
" user = ts.DocumentId "
58+
"}"
59+
)
60+
61+
62+
class MyMultiMapTsIndex(AbstractMultiMapTimeSeriesIndexCreationTask):
63+
class Result:
64+
def __init__(
65+
self,
66+
heart_beat: Optional[float] = None,
67+
date: Optional[datetime.datetime] = None,
68+
user: Optional[str] = None,
69+
):
70+
self.heart_beat = heart_beat
71+
self.date = date
72+
self.user = user
73+
74+
@classmethod
75+
def from_json(cls, json_dict:Dict[str, Any]) -> MyMultiMapTsIndex.Result:
76+
return cls(
77+
json_dict["heartBeat"],
78+
Utils.string_to_datetime(json_dict["date"]),
79+
json_dict["user"]
80+
)
81+
82+
83+
def __init__(self):
84+
super(MyMultiMapTsIndex, self).__init__()
85+
self._add_map(
86+
"from ts in timeSeries.Companies.HeartRate "
87+
+ "from entry in ts.Entries "
88+
+ "select new { "
89+
+ " heartBeat = entry.Values[0], "
90+
+ " date = entry.Timestamp.Date, "
91+
+ " user = ts.DocumentId "
92+
+ "}"
93+
)
94+
95+
self._add_map(
96+
"from ts in timeSeries.Companies.HeartRate2 "
97+
+ "from entry in ts.Entries "
98+
+ "select new { "
99+
+ " heartBeat = entry.Values[0], "
100+
+ " date = entry.Timestamp.Date, "
101+
+ " user = ts.DocumentId "
102+
+ "}"
103+
)
104+
105+
self._add_map(
106+
"from ts in timeSeries.Users.HeartRate "
107+
+ "from entry in ts.Entries "
108+
+ "select new { "
109+
+ " heartBeat = entry.Values[0], "
110+
+ " date = entry.Timestamp.Date, "
111+
+ " user = ts.DocumentId "
112+
+ "}"
113+
)

0 commit comments

Comments
 (0)