Skip to content

Commit 7488b21

Browse files
authored
Merge pull request #121 from SpineEventEngine/aggregate-storage-unique-index
Return unique aggregate IDs from the `DsAggregateStorage.index()`
2 parents 4881cac + 228369a commit 7488b21

File tree

7 files changed

+76
-61
lines changed

7 files changed

+76
-61
lines changed

datastore/src/main/java/io/spine/server/storage/datastore/DatastoreWrapper.java

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -255,27 +255,29 @@ public Entity read(Key key) {
255255
* <p>The Datastore may return a partial result set, so an execution of this method may
256256
* result in several Datastore queries.
257257
*
258-
* <p>The limit included in the {@link StructuredQuery}, will be a maximum count of entities
258+
* <p>The limit included in the {@link StructuredQuery}, will be a maximum count of objects
259259
* in the returned iterator.
260260
*
261261
* <p>The returned {@link DsQueryIterator} allows to {@linkplain DsQueryIterator#nextPageQuery()
262-
* create a query} to the next page of entities reusing an existing cursor.
262+
* create a query} to the next page of results reusing an existing cursor.
263263
*
264264
* <p>The resulting {@code Iterator} is evaluated lazily. A call to
265265
* {@link Iterator#remove() Iterator.remove()} causes an {@link UnsupportedOperationException}.
266266
*
267267
* @param query
268268
* {@link Query} to execute upon the Datastore
269+
* @param <R>
270+
* the type of queried objects
269271
* @return results fo the query as a lazily evaluated {@link Iterator}
270272
* @see DatastoreReader#run(Query)
271273
*/
272-
public DsQueryIterator read(StructuredQuery<Entity> query) {
274+
public <R> DsQueryIterator<R> read(StructuredQuery<R> query) {
273275
Namespace namespace = currentNamespace();
274-
StructuredQuery<Entity> queryWithNamespace =
276+
StructuredQuery<R> queryWithNamespace =
275277
query.toBuilder()
276278
.setNamespace(namespace.getValue())
277279
.build();
278-
DsQueryIterator result = new DsQueryIterator(queryWithNamespace, actor);
280+
DsQueryIterator<R> result = new DsQueryIterator<>(queryWithNamespace, actor);
279281
return result;
280282
}
281283

@@ -292,11 +294,13 @@ public DsQueryIterator read(StructuredQuery<Entity> query) {
292294
* {@link Query} to execute upon the Datastore
293295
* @param pageSize
294296
* a non-zero number of elements to be returned per a single read from Datastore
297+
* @param <R>
298+
* the type of queried objects
295299
* @return results fo the query as a lazily evaluated {@link Iterator}
296300
* @throws IllegalArgumentException
297301
* if the provided {@linkplain StructuredQuery#getLimit() query includes a limit}
298302
*/
299-
Iterator<Entity> readAll(StructuredQuery<Entity> query, int pageSize) {
303+
<R> Iterator<R> readAll(StructuredQuery<R> query, int pageSize) {
300304
return readAllPageByPage(query, pageSize);
301305
}
302306

@@ -311,11 +315,13 @@ Iterator<Entity> readAll(StructuredQuery<Entity> query, int pageSize) {
311315
*
312316
* @param query
313317
* {@link Query} to execute upon the Datastore
318+
* @param <R>
319+
* the type of queried objects
314320
* @return results fo the query as a lazily evaluated {@link Iterator}
315321
* @throws IllegalArgumentException
316322
* if the provided {@linkplain StructuredQuery#getLimit() query includes a limit}
317323
*/
318-
Iterator<Entity> readAll(StructuredQuery<Entity> query) {
324+
<R> Iterator<R> readAll(StructuredQuery<R> query) {
319325
return readAllPageByPage(query, null);
320326
}
321327

@@ -333,26 +339,29 @@ Iterator<Entity> readAll(StructuredQuery<Entity> query) {
333339
* @param pageSize
334340
* a non-zero number of elements to be returned per a single read from Datastore;
335341
* if {@code null} the page size will be dictated by the Datastore
342+
* @param <R>
343+
* the type of queried objects
336344
* @return results fo the query as a lazily evaluated {@link Iterator}
337345
* @throws IllegalArgumentException
338346
* if the provided {@linkplain StructuredQuery#getLimit() query includes a limit} or
339347
* the provided {@code batchSize} is 0
340348
*/
341-
private Iterator<Entity> readAllPageByPage(StructuredQuery<Entity> query,
342-
@Nullable Integer pageSize) {
349+
@SuppressWarnings("unchecked") // Checked logically.
350+
private <R> Iterator<R>
351+
readAllPageByPage(StructuredQuery<R> query, @Nullable Integer pageSize) {
343352
checkArgument(query.getLimit() == null,
344353
"Cannot limit a number of entities for \"read all\" operation.");
345354
checkArgument(pageSize == null || pageSize != 0,
346355
"The size of a single read operation cannot be 0.");
347356

348-
StructuredQuery<Entity> limitedQuery = limit(query, pageSize);
349-
return stream(new DsQueryPageIterator(limitedQuery, this))
357+
StructuredQuery<R> limitedQuery = limit(query, pageSize);
358+
return stream(new DsQueryPageIterator<>(limitedQuery, this))
350359
.flatMap(Streams::stream)
351360
.iterator();
352361
}
353362

354-
private static StructuredQuery<Entity> limit(StructuredQuery<Entity> query,
355-
@Nullable Integer batchSize) {
363+
private static <R> StructuredQuery<R> limit(StructuredQuery<R> query,
364+
@Nullable Integer batchSize) {
356365
return batchSize == null
357366
? query
358367
: query.toBuilder()

datastore/src/main/java/io/spine/server/storage/datastore/DsAggregateStorage.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.google.cloud.datastore.Entity;
2424
import com.google.cloud.datastore.EntityQuery;
2525
import com.google.cloud.datastore.Key;
26+
import com.google.cloud.datastore.ProjectionEntity;
2627
import com.google.cloud.datastore.Query;
2728
import com.google.cloud.datastore.StructuredQuery;
2829
import com.google.common.annotations.VisibleForTesting;
@@ -306,10 +307,12 @@ public void writeLifecycleFlags(I id, LifecycleFlags flags) {
306307
public Iterator<I> index() {
307308
checkNotClosed();
308309

309-
StructuredQuery<Entity> allQuery = Query.newEntityQueryBuilder()
310-
.setKind(stateTypeName.value())
311-
.build();
312-
Iterator<I> index = stream(datastore.readAll(allQuery))
310+
StructuredQuery<ProjectionEntity> query = Query.newProjectionEntityQueryBuilder()
311+
.setKind(stateTypeName.value())
312+
.setProjection(aggregate_id.name())
313+
.setDistinctOn(aggregate_id.name())
314+
.build();
315+
Iterator<I> index = stream(datastore.readAll(query))
313316
.map(new IndexTransformer<>(idClass))
314317
.iterator();
315318
return index;
@@ -333,7 +336,7 @@ private Key toLifecycleRecordKey(I id) {
333336
* @param <I>
334337
* the generic ID type
335338
*/
336-
private static class IndexTransformer<I> implements Function<Entity, I> {
339+
private static class IndexTransformer<I> implements Function<ProjectionEntity, I> {
337340

338341
private final Class<I> idClass;
339342

@@ -342,7 +345,7 @@ private IndexTransformer(Class<I> idClass) {
342345
}
343346

344347
@Override
345-
public I apply(@Nullable Entity entity) {
348+
public I apply(@Nullable ProjectionEntity entity) {
346349
checkNotNull(entity);
347350
String stringId = entity.getString(aggregate_id.toString());
348351
return Stringifiers.fromString(stringId, idClass);

datastore/src/main/java/io/spine/server/storage/datastore/DsQueryIterator.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222

2323
import com.google.cloud.datastore.Cursor;
2424
import com.google.cloud.datastore.DatastoreReaderWriter;
25-
import com.google.cloud.datastore.Entity;
2625
import com.google.cloud.datastore.QueryResults;
2726
import com.google.cloud.datastore.StructuredQuery;
2827
import com.google.common.collect.UnmodifiableIterator;
@@ -44,18 +43,21 @@
4443
* <p>A call to {@link #next() next()} may not cause a Datastore query.
4544
*
4645
* <p>The {@link #remove() remove()} method throws an {@link UnsupportedOperationException}.
46+
*
47+
* @param <R>
48+
* the type of queried objects
4749
*/
48-
final class DsQueryIterator extends UnmodifiableIterator<Entity> {
50+
final class DsQueryIterator<R> extends UnmodifiableIterator<R> {
4951

50-
private final StructuredQuery<Entity> query;
51-
private final QueryResults<Entity> currentPage;
52+
private final StructuredQuery<R> query;
53+
private final QueryResults<R> currentPage;
5254

5355
private final Integer limit;
5456
private int readCount = 0;
5557

5658
private boolean terminated;
5759

58-
DsQueryIterator(StructuredQuery<Entity> query, DatastoreReaderWriter datastore) {
60+
DsQueryIterator(StructuredQuery<R> query, DatastoreReaderWriter datastore) {
5961
super();
6062
this.query = query;
6163
this.limit = query.getLimit();
@@ -92,21 +94,21 @@ private void terminate() {
9294
* <p>The query is built utilizing the {@linkplain Cursor Datastore Cursor} from the current
9395
* query results.
9496
*/
95-
StructuredQuery<Entity> nextPageQuery() {
97+
StructuredQuery<R> nextPageQuery() {
9698
Cursor cursorAfter = currentPage.getCursorAfter();
97-
StructuredQuery<Entity> queryForMoreResults =
99+
StructuredQuery<R> queryForMoreResults =
98100
query.toBuilder()
99101
.setStartCursor(cursorAfter)
100102
.build();
101103
return queryForMoreResults;
102104
}
103105

104106
@Override
105-
public Entity next() {
107+
public R next() {
106108
if (!hasNext()) {
107109
throw new NoSuchElementException("The query results Iterator is empty.");
108110
}
109-
Entity result = currentPage.next();
111+
R result = currentPage.next();
110112
readCount++;
111113
return result;
112114
}

datastore/src/main/java/io/spine/server/storage/datastore/DsQueryPageIterator.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
package io.spine.server.storage.datastore;
2222

23-
import com.google.cloud.datastore.Entity;
2423
import com.google.cloud.datastore.StructuredQuery;
2524
import org.checkerframework.checker.nullness.qual.Nullable;
2625

@@ -39,15 +38,18 @@
3938
*
4039
* <p>If the limit is not specified, then the page size is determined by the Datastore
4140
* query restrictions.
41+
*
42+
* @param <R>
43+
* the type of queried objects
4244
*/
43-
final class DsQueryPageIterator implements Iterator<DsQueryIterator> {
45+
final class DsQueryPageIterator<R> implements Iterator<DsQueryIterator> {
4446

4547
private final DatastoreWrapper datastore;
4648

47-
private DsQueryIterator currentPage;
48-
private @Nullable DsQueryIterator nextPage;
49+
private DsQueryIterator<R> currentPage;
50+
private @Nullable DsQueryIterator<R> nextPage;
4951

50-
DsQueryPageIterator(StructuredQuery<Entity> query, DatastoreWrapper datastore) {
52+
DsQueryPageIterator(StructuredQuery<R> query, DatastoreWrapper datastore) {
5153
this.datastore = datastore;
5254
this.currentPage = datastore.read(query);
5355
}
@@ -61,7 +63,7 @@ public boolean hasNext() {
6163
}
6264

6365
@Override
64-
public DsQueryIterator next() {
66+
public DsQueryIterator<R> next() {
6567
if (nextPage == null) {
6668
currentPage = loadNextPage();
6769
} else {
@@ -74,8 +76,8 @@ public DsQueryIterator next() {
7476
return currentPage;
7577
}
7678

77-
private DsQueryIterator loadNextPage() {
78-
StructuredQuery<Entity> nextPageQuery = currentPage.nextPageQuery();
79+
private DsQueryIterator<R> loadNextPage() {
80+
StructuredQuery<R> nextPageQuery = currentPage.nextPageQuery();
7981
return datastore.read(nextPageQuery);
8082
}
8183
}

datastore/src/main/java/io/spine/server/storage/datastore/Indexes.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,9 @@
2020

2121
package io.spine.server.storage.datastore;
2222

23-
import com.google.cloud.datastore.Entity;
24-
import com.google.cloud.datastore.EntityQuery;
2523
import com.google.cloud.datastore.Key;
2624
import com.google.cloud.datastore.Query;
25+
import com.google.cloud.datastore.StructuredQuery;
2726
import com.google.common.collect.Streams;
2827
import io.spine.string.Stringifiers;
2928
import org.checkerframework.checker.nullness.qual.Nullable;
@@ -63,19 +62,19 @@ static <I> Iterator<I> indexIterator(DatastoreWrapper datastore, Kind kind, Clas
6362
checkNotNull(kind);
6463
checkNotNull(idType);
6564

66-
EntityQuery.Builder query = Query.newEntityQueryBuilder()
67-
.setKind(kind.getValue());
68-
Iterator<Entity> allEntities = datastore.read(query.build());
65+
StructuredQuery<Key> query = Query.newKeyQueryBuilder()
66+
.setKind(kind.getValue())
67+
.build();
68+
Iterator<Key> allEntities = datastore.read(query);
6969
Iterator<I> idIterator = Streams.stream(allEntities)
7070
.map(idExtractor(idType))
7171
.iterator();
7272
return idIterator;
7373
}
7474

75-
private static <I> Function<Entity, @Nullable I> idExtractor(Class<I> idType) {
76-
return input -> {
77-
checkNotNull(input);
78-
Key key = input.getKey();
75+
private static <I> Function<Key, @Nullable I> idExtractor(Class<I> idType) {
76+
return key -> {
77+
checkNotNull(key);
7978
String stringId = key.getName();
8079
I id = Stringifiers.fromString(stringId, idType);
8180
return id;

license-report.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -578,10 +578,10 @@
578578
* **POM Project URL:** [https://javacc.dev.java.net/](https://javacc.dev.java.net/)
579579
* **POM License: Berkeley Software Distribution (BSD) License** - [http://www.opensource.org/licenses/bsd-license.html](http://www.opensource.org/licenses/bsd-license.html)
580580

581-
1. **Group:** net.sourceforge.pmd **Name:** pmd-core **Version:** 6.13.0
581+
1. **Group:** net.sourceforge.pmd **Name:** pmd-core **Version:** 6.16.0
582582
* **POM License: BSD-style** - [http://pmd.sourceforge.net/license.html](http://pmd.sourceforge.net/license.html)
583583

584-
1. **Group:** net.sourceforge.pmd **Name:** pmd-java **Version:** 6.13.0
584+
1. **Group:** net.sourceforge.pmd **Name:** pmd-java **Version:** 6.16.0
585585
* **POM License: BSD-style** - [http://pmd.sourceforge.net/license.html](http://pmd.sourceforge.net/license.html)
586586

587587
1. **Group:** net.sourceforge.saxon **Name:** saxon **Version:** 9.1.0.8
@@ -698,9 +698,9 @@
698698
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
699699
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
700700

701-
1. **Group:** org.junit.jupiter **Name:** junit-jupiter-engine **Version:** 5.4.2
701+
1. **Group:** org.junit.jupiter **Name:** junit-jupiter-engine **Version:** 5.5.1
702702
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
703-
* **POM License: Eclipse Public License v2.0** - [http://www.eclipse.org/legal/epl-v20.html](http://www.eclipse.org/legal/epl-v20.html)
703+
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
704704

705705
1. **Group:** org.junit.jupiter **Name:** junit-jupiter-params **Version:** 5.5.1
706706
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
@@ -710,9 +710,9 @@
710710
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
711711
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
712712

713-
1. **Group:** org.junit.platform **Name:** junit-platform-engine **Version:** 1.4.2
713+
1. **Group:** org.junit.platform **Name:** junit-platform-engine **Version:** 1.5.1
714714
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
715-
* **POM License: Eclipse Public License v2.0** - [http://www.eclipse.org/legal/epl-v20.html](http://www.eclipse.org/legal/epl-v20.html)
715+
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
716716

717717
1. **Group:** org.mockito **Name:** mockito-core **Version:** 2.12.0
718718
* **POM Project URL:** [https://github.com/mockito/mockito](https://github.com/mockito/mockito)
@@ -780,7 +780,7 @@
780780
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
781781

782782

783-
This report was generated on **Thu Jul 25 16:54:10 EEST 2019** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
783+
This report was generated on **Fri Aug 02 19:17:57 EEST 2019** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
784784

785785

786786

@@ -1347,10 +1347,10 @@ This report was generated on **Thu Jul 25 16:54:10 EEST 2019** using [Gradle-Lic
13471347
* **POM Project URL:** [https://javacc.dev.java.net/](https://javacc.dev.java.net/)
13481348
* **POM License: Berkeley Software Distribution (BSD) License** - [http://www.opensource.org/licenses/bsd-license.html](http://www.opensource.org/licenses/bsd-license.html)
13491349

1350-
1. **Group:** net.sourceforge.pmd **Name:** pmd-core **Version:** 6.13.0
1350+
1. **Group:** net.sourceforge.pmd **Name:** pmd-core **Version:** 6.16.0
13511351
* **POM License: BSD-style** - [http://pmd.sourceforge.net/license.html](http://pmd.sourceforge.net/license.html)
13521352

1353-
1. **Group:** net.sourceforge.pmd **Name:** pmd-java **Version:** 6.13.0
1353+
1. **Group:** net.sourceforge.pmd **Name:** pmd-java **Version:** 6.16.0
13541354
* **POM License: BSD-style** - [http://pmd.sourceforge.net/license.html](http://pmd.sourceforge.net/license.html)
13551355

13561356
1. **Group:** net.sourceforge.saxon **Name:** saxon **Version:** 9.1.0.8
@@ -1467,9 +1467,9 @@ This report was generated on **Thu Jul 25 16:54:10 EEST 2019** using [Gradle-Lic
14671467
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
14681468
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
14691469

1470-
1. **Group:** org.junit.jupiter **Name:** junit-jupiter-engine **Version:** 5.4.2
1470+
1. **Group:** org.junit.jupiter **Name:** junit-jupiter-engine **Version:** 5.5.1
14711471
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
1472-
* **POM License: Eclipse Public License v2.0** - [http://www.eclipse.org/legal/epl-v20.html](http://www.eclipse.org/legal/epl-v20.html)
1472+
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
14731473

14741474
1. **Group:** org.junit.jupiter **Name:** junit-jupiter-params **Version:** 5.5.1
14751475
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
@@ -1479,9 +1479,9 @@ This report was generated on **Thu Jul 25 16:54:10 EEST 2019** using [Gradle-Lic
14791479
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
14801480
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
14811481

1482-
1. **Group:** org.junit.platform **Name:** junit-platform-engine **Version:** 1.4.2
1482+
1. **Group:** org.junit.platform **Name:** junit-platform-engine **Version:** 1.5.1
14831483
* **POM Project URL:** [https://junit.org/junit5/](https://junit.org/junit5/)
1484-
* **POM License: Eclipse Public License v2.0** - [http://www.eclipse.org/legal/epl-v20.html](http://www.eclipse.org/legal/epl-v20.html)
1484+
* **POM License: Eclipse Public License v2.0** - [https://www.eclipse.org/legal/epl-v20.html](https://www.eclipse.org/legal/epl-v20.html)
14851485

14861486
1. **Group:** org.mockito **Name:** mockito-core **Version:** 2.12.0
14871487
* **POM Project URL:** [https://github.com/mockito/mockito](https://github.com/mockito/mockito)
@@ -1549,4 +1549,4 @@ This report was generated on **Thu Jul 25 16:54:10 EEST 2019** using [Gradle-Lic
15491549
The dependencies distributed under several licenses, are used according their commercial-use-friendly license.
15501550

15511551

1552-
This report was generated on **Thu Jul 25 16:54:11 EEST 2019** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).
1552+
This report was generated on **Fri Aug 02 19:18:10 EEST 2019** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE).

0 commit comments

Comments
 (0)