Skip to content

Commit 49dd546

Browse files
committed
RDBC-766 Indexes
1 parent de1d362 commit 49dd546

File tree

3 files changed

+372
-2
lines changed

3 files changed

+372
-2
lines changed

ravendb/documents/indexes/time_series.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
FieldStorage,
1212
FieldIndexing,
1313
FieldTermVector,
14+
IndexFieldOptions,
15+
IndexType,
1416
)
1517
from ravendb.documents.indexes.index_creation import AbstractIndexCreationTaskBase, AbstractIndexDefinitionBuilder
1618
from ravendb.documents.indexes.spatial.configuration import SpatialOptions, SpatialOptionsFactory
@@ -188,3 +190,81 @@ def create_index_definition(self) -> Union[IndexDefinition, _T_IndexDefinition]:
188190
index_definition.maps = set(self.maps)
189191

190192
return index_definition
193+
194+
195+
class AbstractJavaScriptTimeSeriesIndexCreationTask(AbstractIndexCreationTaskBase[TimeSeriesIndexDefinition]):
196+
def __init__(
197+
self,
198+
conventions: DocumentConventions = None,
199+
priority: IndexPriority = None,
200+
lock_mode: IndexLockMode = None,
201+
deployment_mode: IndexDeploymentMode = None,
202+
state: IndexState = None,
203+
):
204+
super().__init__(conventions, priority, lock_mode, deployment_mode, state)
205+
self._definition = TimeSeriesIndexDefinition()
206+
207+
@property
208+
def maps(self) -> Set[str]:
209+
return self._definition.maps
210+
211+
@maps.setter
212+
def maps(self, maps: Set[str]):
213+
self._definition.maps = maps
214+
215+
@property
216+
def fields(self) -> Dict[str, IndexFieldOptions]:
217+
return self._definition.fields
218+
219+
@fields.setter
220+
def fields(self, fields: Dict[str, IndexFieldOptions]):
221+
self._definition.fields = fields
222+
223+
@property
224+
def reduce(self) -> str:
225+
return self._definition.reduce
226+
227+
@reduce.setter
228+
def reduce(self, reduce: str):
229+
self._definition.reduce = reduce
230+
231+
def is_map_reduce(self) -> bool:
232+
return self.reduce is not None
233+
234+
@property
235+
def output_reduce_to_collection(self) -> str:
236+
return self._definition.output_reduce_to_collection
237+
238+
@output_reduce_to_collection.setter
239+
def output_reduce_to_collection(self, output_reduce_to_collection: str):
240+
self._definition.output_reduce_to_collection = output_reduce_to_collection
241+
242+
@property
243+
def pattern_references_collection_name(self) -> str:
244+
return self._definition.pattern_references_collection_name
245+
246+
@pattern_references_collection_name.setter
247+
def pattern_references_collection_name(self, pattern_references_collection_name: str):
248+
self._definition.pattern_references_collection_name = pattern_references_collection_name
249+
250+
@property
251+
def pattern_for_output_reduce_to_collection_references(self) -> str:
252+
return self._definition.pattern_for_output_reduce_to_collection_references
253+
254+
@pattern_for_output_reduce_to_collection_references.setter
255+
def pattern_for_output_reduce_to_collection_references(self, pattern_for_output_reduce_to_collection_references):
256+
self._definition.pattern_for_output_reduce_to_collection_references = (
257+
pattern_for_output_reduce_to_collection_references
258+
)
259+
260+
def create_index_definition(self) -> TimeSeriesIndexDefinition:
261+
self._definition.name = self.index_name
262+
self._definition.type = IndexType.JAVA_SCRIPT_MAP_REDUCE if self.is_map_reduce() else IndexType.JAVA_SCRIPT_MAP
263+
self._definition.additional_sources = self.additional_sources or {}
264+
self._definition.additional_assemblies = self.additional_assemblies or set()
265+
self._definition.configuration = self.configuration
266+
self._definition.lock_mode = self.lock_mode
267+
self._definition.priority = self.priority
268+
self._definition.state = self.state
269+
self._definition.deployment_mode = self.deployment_mode
270+
return self._definition

ravendb/tests/jvm_migrated_tests/client_tests/indexing_tests/time_series_tests/test_basic_time_series_indexes_java_script.py

Lines changed: 253 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
from datetime import datetime
1+
from datetime import datetime, timedelta
22

33
from ravendb import GetTermsOperation
44
from ravendb.documents.indexes.index_creation import AbstractJavaScriptIndexCreationTask
5-
from ravendb.infrastructure.orders import Company
5+
from ravendb.documents.indexes.time_series import AbstractJavaScriptTimeSeriesIndexCreationTask
6+
from ravendb.infrastructure.entities import User
7+
from ravendb.infrastructure.orders import Company, Employee, Address
68
from ravendb.tests.test_base import TestBase
79
from ravendb.tools.raven_test_helper import RavenTestHelper
810

@@ -58,3 +60,252 @@ def test_time_series_names_for(self):
5860
terms = self.store.maintenance.send(GetTermsOperation(index.index_name, "names_IsArray", None))
5961
self.assertEqual(1, len(terms))
6062
self.assertIn("true", terms)
63+
64+
def test_basic_map_index_with_load(self):
65+
now1 = datetime.utcnow()
66+
now2 = now1 + timedelta(seconds=1)
67+
68+
with self.store.open_session() as session:
69+
employee = Employee(first_name="John")
70+
session.store(employee, "employees/1")
71+
company = Company()
72+
session.store(company, "companies/1")
73+
74+
session.time_series_for_entity(company, "HeartRate").append_single(now1, 7.0, employee.Id)
75+
76+
company2 = Company()
77+
session.store(company2, "companies/11")
78+
79+
session.time_series_for_entity(company2, "HeartRate").append_single(now1, 11.0, employee.Id)
80+
81+
session.save_changes()
82+
83+
time_series_index = MyTsIndex_Load()
84+
index_name = time_series_index.index_name
85+
index_definition = time_series_index.create_index_definition()
86+
87+
time_series_index.execute(self.store)
88+
89+
self.wait_for_indexing(self.store)
90+
91+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "employee", None))
92+
self.assertEqual(1, len(terms))
93+
self.assertIn("john", terms)
94+
95+
with self.store.open_session() as session:
96+
employee = session.load("employees/1", Employee)
97+
employee.first_name = "Bob"
98+
session.save_changes()
99+
100+
self.wait_for_indexing(self.store)
101+
102+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "employee", None))
103+
self.assertEqual(1, len(terms))
104+
self.assertIn("bob", terms)
105+
106+
with self.store.open_session() as session:
107+
session.delete("employees/1")
108+
session.save_changes()
109+
110+
self.wait_for_indexing(self.store)
111+
112+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "employee", None))
113+
self.assertEqual(0, len(terms))
114+
115+
def test_basic_map_reduce_index_with_load(self):
116+
today = RavenTestHelper.utc_today()
117+
118+
with self.store.open_session() as session:
119+
address = Address()
120+
address.city = "NY"
121+
122+
session.store(address, "addresses/1")
123+
124+
user = User(address_id=address.Id)
125+
session.store(user, "users/1")
126+
127+
for i in range(10):
128+
session.time_series_for_entity(user, "heartRate").append_single(
129+
today + timedelta(hours=i), 180 + i, address.Id
130+
)
131+
132+
session.save_changes()
133+
134+
time_series_index = AverageHeartRateDaily_ByDateAndCity()
135+
index_name = time_series_index.index_name
136+
index_definition = time_series_index.create_index_definition()
137+
138+
time_series_index.execute(self.store)
139+
140+
self.wait_for_indexing(self.store)
141+
142+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "heart_beat", None))
143+
self.assertEqual(1, len(terms))
144+
self.assertIn("184.5", terms)
145+
146+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "date", None))
147+
self.assertEqual(1, len(terms))
148+
149+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "city", None))
150+
self.assertEqual(1, len(terms))
151+
self.assertIn("ny", terms)
152+
153+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "count", None))
154+
self.assertEqual(1, len(terms))
155+
self.assertEqual("10", terms[0])
156+
157+
with self.store.open_session() as session:
158+
address = session.load("addresses/1", Address)
159+
address.city = "LA"
160+
session.save_changes()
161+
162+
self.wait_for_indexing(self.store)
163+
164+
terms = self.store.maintenance.send(GetTermsOperation(index_name, "city", None))
165+
self.assertEqual(1, len(terms))
166+
self.assertIn("la", terms)
167+
168+
def test_can_map_all_time_series_from_collection(self):
169+
now1 = datetime.utcnow()
170+
now2 = now1 + timedelta(seconds=1)
171+
172+
with self.store.open_session() as session:
173+
company = Company()
174+
session.store(company, "companies/1")
175+
session.time_series_for_entity(company, "heartRate").append_single(now1, 7.0, "tag1")
176+
session.time_series_for_entity(company, "likes").append_single(now1, 3.0, "tag2")
177+
178+
session.save_changes()
179+
180+
MyTsIndex_AllTimeSeries().execute(self.store)
181+
self.wait_for_indexing(self.store)
182+
183+
terms = self.store.maintenance.send(GetTermsOperation("MyTsIndex/AllTimeSeries", "heart_beat", None))
184+
self.assertEqual(2, len(terms))
185+
self.assertIn("7", terms)
186+
self.assertIn("3", terms)
187+
188+
def test_basic_multi_map_index(self):
189+
now = RavenTestHelper.utc_today()
190+
191+
time_series_index = MyMultiMapTsIndex()
192+
time_series_index.execute(self.store)
193+
with self.store.open_session() as session:
194+
company = Company()
195+
session.store(company)
196+
197+
session.time_series_for_entity(company, "heartRate").append_single(now, 2.5, "tag1")
198+
session.time_series_for_entity(company, "heartRate2").append_single(now, 3.5, "tag2")
199+
200+
user = User()
201+
session.store(user)
202+
session.time_series_for_entity(user, "heartRate").append_single(now, 4.5, "tag3")
203+
204+
session.save_changes()
205+
206+
self.wait_for_indexing(self.store)
207+
208+
with self.store.open_session() as session:
209+
results = list(session.query_index_type(MyMultiMapTsIndex, MyMultiMapTsIndex.Result))
210+
self.assertEqual(3, len(results))
211+
212+
213+
class MyTsIndex_Load(AbstractJavaScriptTimeSeriesIndexCreationTask):
214+
def __init__(self):
215+
super().__init__()
216+
self.maps = [
217+
"timeSeries.map('Companies', 'HeartRate', function (ts) {\n"
218+
+ "return ts.Entries.map(entry => ({\n"
219+
+ " heart_beat: entry.Value,\n"
220+
+ " date: new Date(entry.Timestamp.getFullYear(), entry.Timestamp.getMonth(), entry.Timestamp.getDate()),\n"
221+
+ " user: ts.DocumentId,\n"
222+
+ " employee: load(entry.Tag, 'Employees').first_name\n"
223+
+ " }));\n"
224+
+ "})"
225+
]
226+
227+
228+
class MyTsIndex_AllTimeSeries(AbstractJavaScriptTimeSeriesIndexCreationTask):
229+
def __init__(self):
230+
super().__init__()
231+
self.maps = [
232+
"timeSeries.map('Companies', function (ts) {\n"
233+
+ " return ts.Entries.map(entry => ({\n"
234+
+ " heart_beat: entry.Values[0],\n"
235+
+ " date: new Date(entry.Timestamp.getFullYear(), entry.Timestamp.getMonth(), entry.Timestamp.getDate()),\n"
236+
+ " user: ts.documentId\n"
237+
+ " }));\n"
238+
+ " })"
239+
]
240+
241+
242+
class AverageHeartRateDaily_ByDateAndCity(AbstractJavaScriptTimeSeriesIndexCreationTask):
243+
class Result:
244+
def __init__(self, heart_beat: float = None, date: datetime = None, city: str = None, count: int = None):
245+
self.heart_beat = heart_beat
246+
self.date = date
247+
self.city = city
248+
self.count = count
249+
250+
def __init__(self):
251+
super(AverageHeartRateDaily_ByDateAndCity, self).__init__()
252+
self.maps = [
253+
"timeSeries.map('Users', 'heartRate', function (ts) {\n"
254+
+ "return ts.Entries.map(entry => ({\n"
255+
+ " heart_beat: entry.Value,\n"
256+
+ " date: new Date(entry.Timestamp.getFullYear(), entry.Timestamp.getMonth(), entry.Timestamp.getDate()),\n"
257+
+ " city: load(entry.Tag, 'Addresses').city,\n"
258+
+ " count: 1\n"
259+
+ " }));\n"
260+
+ "})"
261+
]
262+
263+
self.reduce = (
264+
"groupBy(r => ({ date: r.date, city: r.city }))\n"
265+
" .aggregate(g => ({\n"
266+
" heart_beat: g.values.reduce((total, val) => val.heart_beat + total, 0) / g.values.reduce((total, val) => val.count + total, 0),\n"
267+
" date: g.key.date,\n"
268+
" city: g.key.city\n"
269+
" count: g.values.reduce((total, val) => val.count + total, 0)\n"
270+
" }))"
271+
)
272+
273+
274+
class MyMultiMapTsIndex(AbstractJavaScriptTimeSeriesIndexCreationTask):
275+
class Result:
276+
def __init__(self, heart_beat: float = None, date: datetime = None, user: str = None):
277+
self.heart_beat = heart_beat
278+
self.date = date
279+
self.user = user
280+
281+
def __init__(self):
282+
super(MyMultiMapTsIndex, self).__init__()
283+
self.maps = set()
284+
self.maps.add(
285+
"timeSeries.map('Companies', 'HeartRate', function (ts) {\n"
286+
+ "return ts.Entries.map(entry => ({\n"
287+
+ " heart_beat: entry.Values[0],\n"
288+
+ " date: new Date(entry.Timestamp.getFullYear(), entry.Timestamp.getMonth(), entry.Timestamp.getDate()),\n"
289+
+ " user: ts.DocumentId\n"
290+
+ " }));\n"
291+
+ "})"
292+
)
293+
294+
self.maps.add(
295+
"timeSeries.map('Companies', 'HeartRate2', function (ts) {\n"
296+
+ "return ts.Entries.map(entry => ({\n"
297+
+ " heart_beat: entry.Values[0],\n"
298+
+ " date: new Date(entry.Timestamp.getFullYear(), entry.Timestamp.getMonth(), entry.Timestamp.getDate()),\n"
299+
+ " user: ts.DocumentId\n"
300+
+ " }));\n"
301+
+ "})"
302+
)
303+
self.maps.add(
304+
"timeSeries.map('Users', 'HeartRate', function (ts) {\n"
305+
+ "return ts.Entries.map(entry => ({\n"
306+
+ " heart_beat: entry.Values[0],\n"
307+
+ " date: new Date(entry.Timestamp.getFullYear(), entry.Timestamp.getMonth(), entry.Timestamp.getDate()),\n"
308+
+ " user: ts.DocumentId\n"
309+
+ " }));\n"
310+
+ "})"
311+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from datetime import datetime
2+
3+
from ravendb import PutIndexesOperation, GetTermsOperation
4+
from ravendb.documents.indexes.time_series import TimeSeriesIndexDefinition
5+
from ravendb.infrastructure.orders import Company
6+
from ravendb.tests.test_base import TestBase
7+
8+
9+
class TestBasicTimeSeriesIndexes_MixedSyntax(TestBase):
10+
def setUp(self):
11+
super(TestBasicTimeSeriesIndexes_MixedSyntax, self).setUp()
12+
13+
def test_basic_map_index(self):
14+
now1 = datetime.utcnow()
15+
16+
with self.store.open_session() as session:
17+
company = Company()
18+
session.store(company, "companies/1")
19+
session.time_series_for_entity(company, "HeartRate").append_single(now1, 7, "tag")
20+
session.save_changes()
21+
22+
time_series_index_definition = TimeSeriesIndexDefinition()
23+
time_series_index_definition.name = "MyTsIndex"
24+
time_series_index_definition.maps = [
25+
"from ts in timeSeries.Companies.HeartRate.Where(x => true) "
26+
+ "from entry in ts.Entries "
27+
+ "select new { "
28+
+ " heartBeat = entry.Values[0], "
29+
+ " date = entry.Timestamp.Date, "
30+
+ " user = ts.DocumentId "
31+
+ "}"
32+
]
33+
34+
self.store.maintenance.send(PutIndexesOperation(time_series_index_definition))
35+
self.wait_for_indexing(self.store)
36+
37+
terms = self.store.maintenance.send(GetTermsOperation("MyTsIndex", "heartBeat", None))
38+
self.assertEqual(1, len(terms))
39+
self.assertIn("7", terms)

0 commit comments

Comments
 (0)