Skip to content

Commit b6a319d

Browse files
committed
Add examples for aggregators, processors, and filters. Added generics to processors to resolve use cases in examples.
1 parent 95a5296 commit b6a319d

15 files changed

+353
-150
lines changed

examples/aggregators.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Copyright (c) 2023, Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at
3+
# https://oss.oracle.com/licenses/upl.
4+
5+
import asyncio
6+
from dataclasses import dataclass
7+
from decimal import Decimal
8+
from typing import List
9+
10+
from coherence import Aggregators, Filters, NamedMap, Session
11+
12+
13+
@dataclass
14+
class Hobbit:
15+
"""
16+
A simple class representing a Hobbit.
17+
"""
18+
19+
id: int
20+
name: str
21+
age: int
22+
hobbies: str
23+
24+
25+
async def do_run() -> None:
26+
"""
27+
Demonstrates various Aggregator operations against a NamedMap.
28+
29+
:return: None
30+
"""
31+
person_data = {
32+
1: Hobbit(1, "Bilbo", 111, "Burglaring"),
33+
2: Hobbit(2, "Frodo", 50, "Bearing"),
34+
3: Hobbit(3, "Sam", 38, "Side Kick"),
35+
4: Hobbit(3, "Meriadoc", 36, "Side Kick"),
36+
5: Hobbit(3, "Peregrin", 28, "Side Kick"),
37+
}
38+
39+
session: Session = Session()
40+
try:
41+
namedMap: NamedMap[int, Hobbit] = await session.get_map("aggregation-test")
42+
43+
await namedMap.clear()
44+
45+
await namedMap.put_all(person_data)
46+
47+
distinct_hobbies: List[str] = await namedMap.aggregate(Aggregators.distinct("hobbies"))
48+
print("Distinct hobbies :", distinct_hobbies)
49+
50+
count: int = await namedMap.aggregate(Aggregators.count())
51+
print("Number of Hobbits :", count)
52+
53+
over_forty: int = await namedMap.aggregate(Aggregators.count(), filter=Filters.greater("age", 40))
54+
print("Number of Hobbits older than 40 :", over_forty)
55+
56+
avg_under_forty: Decimal = await namedMap.aggregate(Aggregators.average("age"), filter=Filters.less("age", 40))
57+
print("Average age of Hobbits under 40 :", int(avg_under_forty))
58+
59+
print("The oldest Hobbit for each hobby ...")
60+
results: dict[str, int] = await namedMap.aggregate(Aggregators.group_by("hobbies", Aggregators.max("age")))
61+
for hobby, age in results.items():
62+
print("Hobby: ", hobby, "Max age: ", age)
63+
finally:
64+
await session.close()
65+
66+
67+
asyncio.run(do_run())

examples/basic/crud.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,17 @@ async def do_run() -> None:
2121
print("Put key 1; value one")
2222
await namedMap.put(1, "one")
2323

24-
print("Value for key 1 is : ", await namedMap.get(1))
24+
print("Value for key 1 is :", await namedMap.get(1))
2525

26-
print("NamedMap size is : ", await namedMap.size())
26+
print("NamedMap size is :", await namedMap.size())
2727

2828
print("Updating value of key 1 to ONE from ", await namedMap.put(1, "ONE"))
2929

30-
print("Value for key 1 is : ", await namedMap.get(1))
30+
print("Value for key 1 is :", await namedMap.get(1))
3131

32-
print("Removing key 1, current value : ", await namedMap.remove(1))
32+
print("Removing key 1, current value :", await namedMap.remove(1))
3333

34-
print("NamedMap size is : ", await namedMap.size())
34+
print("NamedMap size is :", await namedMap.size())
3535
finally:
3636
await session.close()
3737

examples/basic/python_object_keys.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ async def do_run() -> None:
4141
print(f"Add new account {new_account} with key {new_account_key}")
4242
await namedMap.put(new_account_key, new_account)
4343

44-
print("NamedMap size is : ", await namedMap.size())
44+
print("NamedMap size is :", await namedMap.size())
4545

46-
print("Account from get() : ", await namedMap.get(new_account_key))
46+
print("Account from get() :", await namedMap.get(new_account_key))
4747

4848
print("Update account balance using processor ...")
4949
await namedMap.invoke(new_account_key, Processors.update("balance", new_account.balance + 1000))
5050

51-
print("Updated account is : ", await namedMap.get(new_account_key))
51+
print("Updated account is :", await namedMap.get(new_account_key))
5252

5353
await namedMap.remove(new_account_key)
5454

55-
print("NamedMap size is : ", await namedMap.size())
55+
print("NamedMap size is :", await namedMap.size())
5656
finally:
5757
await session.close()
5858

examples/basic/python_object_values.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010

1111
@dataclass
12-
class Person:
12+
class Hobbit:
1313
"""
1414
A simple class representing a person.
1515
"""
@@ -28,26 +28,26 @@ async def do_run() -> None:
2828
"""
2929
session: Session = Session()
3030
try:
31-
namedMap: NamedMap[int, Person] = await session.get_map("people")
31+
namedMap: NamedMap[int, Hobbit] = await session.get_map("hobbits")
3232

3333
await namedMap.clear()
3434

35-
person: Person = Person(1, "Bilbo", 111)
36-
print("Add new person : ", person)
37-
await namedMap.put(person.id, person)
35+
hobbit: Hobbit = Hobbit(1, "Bilbo", 111)
36+
print("Add new hobbit :", hobbit)
37+
await namedMap.put(hobbit.id, hobbit)
3838

39-
print("NamedMap size is : ", await namedMap.size())
39+
print("NamedMap size is :", await namedMap.size())
4040

41-
print("Person from get() : ", await namedMap.get(person.id))
41+
print("Hobbit from get() :", await namedMap.get(hobbit.id))
4242

43-
print("Update person using processor ...")
44-
await namedMap.invoke(person.id, Processors.update("age", 112))
43+
print("Update Hobbit using processor ...")
44+
await namedMap.invoke(hobbit.id, Processors.update("age", 112))
4545

46-
print("Updated person is : ", await namedMap.get(person.id))
46+
print("Updated Hobbit is :", await namedMap.get(hobbit.id))
4747

48-
await namedMap.remove(person.id)
48+
await namedMap.remove(hobbit.id)
4949

50-
print("NamedMap size is : ", await namedMap.size())
50+
print("NamedMap size is :", await namedMap.size())
5151
finally:
5252
await session.close()
5353

examples/filters.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright (c) 2023, Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at
3+
# https://oss.oracle.com/licenses/upl.
4+
5+
import asyncio
6+
from dataclasses import dataclass
7+
from typing import List
8+
9+
from coherence import Filters, NamedMap, Session
10+
from coherence.filter import Filter
11+
12+
13+
@dataclass
14+
class Hobbit:
15+
"""
16+
A simple class representing a Hobbit.
17+
"""
18+
19+
id: int
20+
name: str
21+
age: int
22+
home: str
23+
24+
25+
async def do_run() -> None:
26+
"""
27+
Demonstrates various Filter operations against a NamedMap.
28+
29+
:return: None
30+
"""
31+
session: Session = Session()
32+
try:
33+
homes: List[str] = ["Hobbiton", "Buckland", "Frogmorton", "Stock"]
34+
namedMap: NamedMap[int, Hobbit] = await session.get_map("hobbits")
35+
36+
await namedMap.clear()
37+
38+
num_hobbits: int = 20
39+
print("Adding", num_hobbits, "random Hobbits ...")
40+
for i in range(num_hobbits):
41+
await namedMap.put(i, Hobbit(i, "Hobbit-" + str(i), 15 + i, homes[i % 4]))
42+
43+
print("NamedMap size is :", await namedMap.size())
44+
45+
print("Retrieve the Hobbits between the ages of 17 and 21 ...")
46+
async for entry in namedMap.entries(Filters.between("age", 17, 21)):
47+
print("Key :", entry.key, ", Value :", entry.value)
48+
49+
print("Retrieve the Hobbits between the ages of 17 and 30 and live in Hobbiton ...")
50+
query_filter: Filter = Filters.between("age", 17, 30).And(Filters.equals("home", "Hobbiton"))
51+
async for entry in namedMap.entries(query_filter):
52+
print("Key :", entry.key, ", Value :", entry.value)
53+
54+
print("Retrieve the Hobbits between the ages of 17 and 25 who live in Hobbiton or Frogmorton")
55+
query_filter = Filters.between("age", 17, 25).And(Filters.is_in("home", {"Hobbiton", "Frogmorton"}))
56+
async for entry in namedMap.entries(query_filter):
57+
print("Key :", entry.key, ", Value :", entry.value)
58+
59+
finally:
60+
await session.close()
61+
62+
63+
asyncio.run(do_run())

examples/processors.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Copyright (c) 2023, Oracle and/or its affiliates.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at
3+
# https://oss.oracle.com/licenses/upl.
4+
5+
import asyncio
6+
from dataclasses import dataclass
7+
from typing import List
8+
9+
from coherence import NamedMap, Processors, Session
10+
11+
12+
@dataclass
13+
class Hobbit:
14+
"""
15+
A simple class representing a Hobbit.
16+
"""
17+
18+
id: int
19+
name: str
20+
age: int
21+
22+
23+
async def do_run() -> None:
24+
"""
25+
Demonstrates various EntryProcessor operations against a NamedMap.
26+
27+
:return: None
28+
"""
29+
session: Session = Session()
30+
try:
31+
namedMap: NamedMap[int, Hobbit] = await session.get_map("hobbits")
32+
33+
await namedMap.clear()
34+
35+
hobbit: Hobbit = Hobbit(1, "Bilbo", 111)
36+
print("Add new hobbit :", hobbit)
37+
await namedMap.put(hobbit.id, hobbit)
38+
39+
print("NamedMap size is :", await namedMap.size())
40+
41+
print("Hobbit from get() :", await namedMap.get(hobbit.id))
42+
43+
print("Update Hobbit using processor ...")
44+
await namedMap.invoke(hobbit.id, Processors.update("age", 112))
45+
46+
print("Updated Hobbit is :", await namedMap.get(hobbit.id))
47+
48+
hobbit2: Hobbit = Hobbit(2, "Frodo", 50)
49+
50+
print("Add new hobbit :", hobbit2)
51+
await namedMap.put(hobbit2.id, hobbit2)
52+
53+
print("NamedMap size is :", await namedMap.size())
54+
55+
print("Sending all Hobbits ten years into the future!")
56+
keys: List[int] = []
57+
async for entry in namedMap.invoke_all(Processors.increment("age", 10)):
58+
keys.append(entry.key)
59+
print("Updated age of Hobbit with id ", entry.key, "to", entry.value)
60+
61+
print("Displaying all updated Hobbits ...")
62+
async for result in namedMap.get_all(set(keys)):
63+
print(result.value)
64+
65+
await namedMap.remove(hobbit.id)
66+
await namedMap.remove(hobbit2.id)
67+
finally:
68+
await session.close()
69+
70+
71+
asyncio.run(do_run())

src/coherence/aggregator.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from abc import ABC
88
from decimal import Decimal
99
from enum import Enum, IntEnum
10-
from typing import Any, Dict, Generic, List, Optional, Tuple, TypeAlias, TypeVar
10+
from typing import Any, Dict, Generic, List, Optional, TypeAlias, TypeVar
1111

1212
from .comparator import Comparator, InverseComparator, SafeComparator
1313
from .extractor import ExtractorExpression, IdentityExtractor, ValueExtractor, extract
@@ -22,7 +22,6 @@
2222
V = TypeVar("V")
2323

2424
ReducerResult: TypeAlias = Dict[K, Any | List[Any]]
25-
AggregationResult: TypeAlias = List[Tuple[G, T]]
2625

2726

2827
class EntryAggregator(ABC, Generic[R]):
@@ -648,7 +647,7 @@ def group_by(
648647
extractor_or_property: ExtractorExpression[T, E],
649648
aggregator: EntryAggregator[Any],
650649
filter: Optional[Filter] = None,
651-
) -> EntryAggregator[AggregationResult[G, T]]:
650+
) -> EntryAggregator[Dict[G, T]]:
652651
"""
653652
Return a :class:`coherence.aggregator.GroupAggregator` based on a specified property or method name(s) and an
654653
:class:`coherence.aggregator.EntryAggregator`.

src/coherence/client.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ async def size(self) -> int:
353353
"""
354354

355355
@abc.abstractmethod
356-
async def invoke(self, key: K, processor: EntryProcessor) -> R:
356+
async def invoke(self, key: K, processor: EntryProcessor[R]) -> R:
357357
"""
358358
Invoke the passed EntryProcessor against the Entry specified by the
359359
passed key, returning the result of the invocation.
@@ -365,7 +365,7 @@ async def invoke(self, key: K, processor: EntryProcessor) -> R:
365365

366366
@abc.abstractmethod
367367
def invoke_all(
368-
self, processor: EntryProcessor, keys: Optional[set[K]] = None, filter: Optional[Filter] = None
368+
self, processor: EntryProcessor[R], keys: Optional[set[K]] = None, filter: Optional[Filter] = None
369369
) -> AsyncIterator[MapEntry[K, R]]:
370370
"""
371371
Invoke the passed EntryProcessor against the set of entries that are selected by the given Filter,
@@ -628,14 +628,14 @@ async def size(self) -> int:
628628
return self._request_factory.get_serializer().deserialize(v.value)
629629

630630
@_pre_call_cache
631-
async def invoke(self, key: K, processor: EntryProcessor) -> R:
631+
async def invoke(self, key: K, processor: EntryProcessor[R]) -> R:
632632
r = self._request_factory.invoke_request(key, processor)
633633
v = await self._client_stub.invoke(r)
634634
return self._request_factory.get_serializer().deserialize(v.value)
635635

636636
@_pre_call_cache
637637
def invoke_all(
638-
self, processor: EntryProcessor, keys: Optional[set[K]] = None, filter: Optional[Filter] = None
638+
self, processor: EntryProcessor[R], keys: Optional[set[K]] = None, filter: Optional[Filter] = None
639639
) -> AsyncIterator[MapEntry[K, R]]:
640640
r = self._request_factory.invoke_all_request(processor, keys, filter)
641641
stream = self._client_stub.invokeAll(r)
@@ -1421,9 +1421,7 @@ async def __load_next_page(self) -> None:
14211421
14221422
:return: None
14231423
"""
1424-
print("### DEBUG: __load_next_page() called!")
14251424
request: PageRequest = self._client._request_factory.page_request(self._cookie)
1426-
print("### DEBUG: __load_next_page() called!")
14271425
self._stream = self._get_stream(request)
14281426
self._new_page = True
14291427

0 commit comments

Comments
 (0)