Skip to content

Commit 94922f1

Browse files
committed
Ditto 1521: Initial submission of scoping search fields for things.
1 parent f2904ef commit 94922f1

File tree

17 files changed

+1220
-75
lines changed

17 files changed

+1220
-75
lines changed

.run/SearchService.run.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
<option name="Make" enabled="true" />
1616
</method>
1717
</configuration>
18-
</component>
18+
</component>

internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/DittoCachingSignalEnrichmentFacade.java

+15-15
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,15 @@
5252
* Instantiated once per cluster node so that it builds up a cache across all signal enrichments on a local cluster
5353
* node.
5454
*/
55-
public final class DittoCachingSignalEnrichmentFacade implements CachingSignalEnrichmentFacade {
55+
public class DittoCachingSignalEnrichmentFacade implements CachingSignalEnrichmentFacade {
5656

57-
private static final ThreadSafeDittoLogger LOGGER = DittoLoggerFactory
58-
.getThreadSafeLogger(DittoCachingSignalEnrichmentFacade.class);
57+
private static final ThreadSafeDittoLogger LOGGER = DittoLoggerFactory.getThreadSafeLogger(DittoCachingSignalEnrichmentFacade.class);
5958
private static final String CACHE_NAME_SUFFIX = "_signal_enrichment_cache";
6059

61-
private final Cache<SignalEnrichmentCacheKey, JsonObject> extraFieldsCache;
60+
protected final Cache<SignalEnrichmentCacheKey, JsonObject> extraFieldsCache;
6261

63-
private DittoCachingSignalEnrichmentFacade(final SignalEnrichmentFacade cacheLoaderFacade,
62+
protected DittoCachingSignalEnrichmentFacade(
63+
final SignalEnrichmentFacade cacheLoaderFacade,
6464
final CacheConfig cacheConfig,
6565
final Executor cacheLoaderExecutor,
6666
final String cacheNamePrefix) {
@@ -157,9 +157,9 @@ public CompletionStage<JsonObject> retrievePartialThing(final EntityId thingId,
157157
.thenApply(jsonObject -> applyJsonFieldSelector(jsonObject, jsonFieldSelector));
158158
}
159159

160-
private CompletionStage<JsonObject> doRetrievePartialThing(final EntityId thingId,
161-
final DittoHeaders dittoHeaders,
162-
final CachingParameters cachingParameters) {
160+
protected CompletionStage<JsonObject> doRetrievePartialThing(final EntityId thingId,
161+
final DittoHeaders dittoHeaders,
162+
final CachingParameters cachingParameters) {
163163

164164
final var fieldSelector = cachingParameters.fieldSelector;
165165
final JsonFieldSelector enhancedFieldSelector = enhanceFieldSelectorWithRevision(fieldSelector);
@@ -278,8 +278,8 @@ private static DittoHeaders getLastDittoHeaders(final List<? extends Signal<?>>
278278
}
279279
}
280280

281-
private CompletableFuture<JsonObject> doCacheLookup(final SignalEnrichmentCacheKey cacheKey,
282-
final DittoHeaders dittoHeaders) {
281+
protected CompletableFuture<JsonObject> doCacheLookup(final SignalEnrichmentCacheKey cacheKey,
282+
final DittoHeaders dittoHeaders) {
283283
LOGGER.withCorrelationId(dittoHeaders).debug("Looking up cache entry for <{}>", cacheKey);
284284

285285
return extraFieldsCache.get(cacheKey)
@@ -446,17 +446,17 @@ private JsonObject enhanceJsonObject(final JsonObject jsonObject, final List<Thi
446446
return applyJsonFieldSelector(jsonObjectBuilder.build(), enhancedFieldSelector);
447447
}
448448

449-
private static final class CachingParameters {
449+
protected static final class CachingParameters {
450450

451451
@Nullable private final JsonFieldSelector fieldSelector;
452452
private final List<ThingEvent<?>> concernedEvents;
453453
private final boolean invalidateCacheOnPolicyChange;
454454
private final long minAcceptableSeqNr;
455455

456-
private CachingParameters(@Nullable final JsonFieldSelector fieldSelector,
457-
final List<ThingEvent<?>> concernedEvents,
458-
final boolean invalidateCacheOnPolicyChange,
459-
final long minAcceptableSeqNr) {
456+
public CachingParameters(@Nullable final JsonFieldSelector fieldSelector,
457+
final List<ThingEvent<?>> concernedEvents,
458+
final boolean invalidateCacheOnPolicyChange,
459+
final long minAcceptableSeqNr) {
460460

461461
this.fieldSelector = fieldSelector;
462462
this.concernedEvents = concernedEvents;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2019 Contributors to the Eclipse Foundation
3+
*
4+
* See the NOTICE file(s) distributed with this work for additional
5+
* information regarding copyright ownership.
6+
*
7+
* This program and the accompanying materials are made available under the
8+
* terms of the Eclipse Public License 2.0 which is available at
9+
* http://www.eclipse.org/legal/epl-2.0
10+
*
11+
* SPDX-License-Identifier: EPL-2.0
12+
*/
13+
package org.eclipse.ditto.internal.models.signalenrichment;
14+
15+
import org.eclipse.ditto.base.model.headers.DittoHeaders;
16+
import org.eclipse.ditto.internal.utils.cache.config.CacheConfig;
17+
import org.eclipse.ditto.json.JsonFieldSelector;
18+
import org.eclipse.ditto.json.JsonObject;
19+
import org.eclipse.ditto.things.model.Thing;
20+
import org.eclipse.ditto.things.model.ThingId;
21+
import org.eclipse.ditto.things.model.signals.events.ThingEvent;
22+
23+
import java.util.List;
24+
import java.util.Map;
25+
import java.util.concurrent.CompletionStage;
26+
import java.util.concurrent.Executor;
27+
28+
import static org.eclipse.ditto.base.model.common.ConditionChecker.checkNotNull;
29+
30+
public class SearchIndexingSignalEnrichmentFacade extends DittoCachingSignalEnrichmentFacade {
31+
32+
private final Map<String, JsonFieldSelector> selectedIndexes;
33+
34+
protected SearchIndexingSignalEnrichmentFacade(
35+
final Map<String, JsonFieldSelector> selectedIndexes,
36+
final SignalEnrichmentFacade cacheLoaderFacade,
37+
final CacheConfig cacheConfig,
38+
final Executor cacheLoaderExecutor,
39+
final String cacheNamePrefix) {
40+
41+
super(cacheLoaderFacade, cacheConfig, cacheLoaderExecutor, cacheNamePrefix);
42+
43+
this.selectedIndexes = selectedIndexes;
44+
}
45+
46+
/**
47+
* Returns a new {@code SearchIndexingSignalEnrichmentFacade} instance.
48+
*
49+
* @param selectedIndexes The selected indexes to be loaded into the search context
50+
* @param cacheLoaderFacade the facade whose argument-result-pairs we are caching.
51+
* @param cacheConfig the cache configuration to use for the cache.
52+
* @param cacheLoaderExecutor the executor to use in order to asynchronously load cache entries.
53+
* @param cacheNamePrefix the prefix to use as cacheName of the cache.
54+
* @throws NullPointerException if any argument is null.
55+
*/
56+
public static SearchIndexingSignalEnrichmentFacade newInstance(
57+
final Map<String, JsonFieldSelector> selectedIndexes,
58+
final SignalEnrichmentFacade cacheLoaderFacade,
59+
final CacheConfig cacheConfig,
60+
final Executor cacheLoaderExecutor,
61+
final String cacheNamePrefix) {
62+
63+
return new SearchIndexingSignalEnrichmentFacade(
64+
checkNotNull(selectedIndexes, "selectedIndexes"),
65+
checkNotNull(cacheLoaderFacade, "cacheLoaderFacade"),
66+
checkNotNull(cacheConfig, "cacheConfig"),
67+
checkNotNull(cacheLoaderExecutor, "cacheLoaderExecutor"),
68+
checkNotNull(cacheNamePrefix, "cacheNamePrefix"));
69+
}
70+
71+
@Override
72+
public CompletionStage<JsonObject> retrieveThing(final ThingId thingId, final List<ThingEvent<?>> events, final long minAcceptableSeqNr) {
73+
74+
final DittoHeaders dittoHeaders = DittoHeaders.empty();
75+
76+
// Retrieve any namespace definition from the configuration. Note that this might return null.
77+
JsonFieldSelector selector = selectedIndexes.get(thingId.getNamespace());
78+
79+
if (minAcceptableSeqNr < 0) {
80+
81+
final var cacheKey =
82+
SignalEnrichmentCacheKey.of(thingId, SignalEnrichmentContext.of(dittoHeaders, selector));
83+
extraFieldsCache.invalidate(cacheKey);
84+
return doCacheLookup(cacheKey, dittoHeaders);
85+
} else {
86+
final var cachingParameters =
87+
new CachingParameters(selector, events, false, minAcceptableSeqNr);
88+
89+
return doRetrievePartialThing(thingId, dittoHeaders, cachingParameters);
90+
}
91+
}
92+
}

internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/SignalEnrichmentCacheKey.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
* Implementation for a {@link CacheKey} used in scope of signal enrichment.
2828
*/
2929
@Immutable
30-
final class SignalEnrichmentCacheKey implements CacheKey<SignalEnrichmentContext> {
30+
public final class SignalEnrichmentCacheKey implements CacheKey<SignalEnrichmentContext> {
3131

3232
static final String DELIMITER = ":";
3333

@@ -47,7 +47,8 @@ private SignalEnrichmentCacheKey(final EntityId id, @Nullable final SignalEnrich
4747
* @return the entity ID with resource type object.
4848
* @throws NullPointerException if {@code id} is {@code null}.
4949
*/
50-
static SignalEnrichmentCacheKey of(final EntityId id, @Nullable final SignalEnrichmentContext cacheLookupContext) {
50+
public static SignalEnrichmentCacheKey of(final EntityId id,
51+
@Nullable final SignalEnrichmentContext cacheLookupContext) {
5152
return new SignalEnrichmentCacheKey(id, cacheLookupContext);
5253
}
5354

internal/models/signalenrichment/src/main/java/org/eclipse/ditto/internal/models/signalenrichment/SignalEnrichmentContext.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* signal enrichment caching.
3030
*/
3131
@Immutable
32-
final class SignalEnrichmentContext implements CacheLookupContext {
32+
public final class SignalEnrichmentContext implements CacheLookupContext {
3333

3434
private final DittoHeaders dittoHeaders;
3535
@Nullable private final JsonFieldSelector jsonFieldSelector;
@@ -48,8 +48,8 @@ private SignalEnrichmentContext(final DittoHeaders dittoHeaders,
4848
* @param jsonFieldSelector the JsonFieldSelector to use in the cache lookup context.
4949
* @return the created context.
5050
*/
51-
static SignalEnrichmentContext of(final DittoHeaders dittoHeaders,
52-
@Nullable final JsonFieldSelector jsonFieldSelector) {
51+
public static SignalEnrichmentContext of(final DittoHeaders dittoHeaders,
52+
@Nullable final JsonFieldSelector jsonFieldSelector) {
5353

5454
return new SignalEnrichmentContext(dittoHeaders, jsonFieldSelector);
5555
}

internal/models/signalenrichment/src/test/java/org/eclipse/ditto/internal/models/signalenrichment/AbstractSignalEnrichmentFacadeTest.java

+19-11
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ abstract class AbstractSignalEnrichmentFacadeTest {
4646
JsonFieldSelector.newInstance("policyId", "attributes/x", "features/y/properties/z", "_metadata");
4747

4848
protected static final String RESULT_POLICY_ID = "policy:id";
49-
protected static final AttributeModified THING_EVENT = AttributeModified.of(ThingId.generateRandom(),
49+
private static final AttributeModified THING_EVENT = AttributeModified.of(ThingId.generateRandom(),
5050
JsonPointer.of("x"),
5151
JsonValue.of(5),
5252
3L,
@@ -65,11 +65,11 @@ public void success() {
6565
final ThingId thingId = ThingId.of("test:thing-id");
6666
final DittoHeaders headers = DittoHeaders.newBuilder().correlationId(UUID.randomUUID().toString()).build();
6767
final CompletionStage<JsonObject> askResult =
68-
underTest.retrievePartialThing(thingId, SELECTOR, headers, THING_EVENT);
68+
underTest.retrievePartialThing(thingId, getJsonFieldSelector(), headers, THING_EVENT);
6969

7070
// WHEN: Command handler receives expected RetrieveThing and responds with RetrieveThingResponse
7171
final RetrieveThing retrieveThing = kit.expectMsgClass(RetrieveThing.class);
72-
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(SELECTOR));
72+
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(getJsonFieldSelector()));
7373
kit.reply(RetrieveThingResponse.of(thingId, getThingResponseThingJson(), headers));
7474

7575
// THEN: The result future completes with the entity of the RetrieveThingResponse
@@ -94,6 +94,14 @@ protected JsonObject getExpectedThingJson() {
9494
return getThingResponseThingJson();
9595
}
9696

97+
protected JsonFieldSelector getJsonFieldSelector() {
98+
return SELECTOR;
99+
}
100+
101+
protected AttributeModified getThingEvent() {
102+
return THING_EVENT;
103+
}
104+
97105
@Test
98106
public void thingNotAccessible() {
99107
DittoTestSystem.run(this, kit -> {
@@ -103,11 +111,11 @@ public void thingNotAccessible() {
103111
final ThingId thingId = ThingId.generateRandom();
104112
final DittoHeaders headers = DittoHeaders.newBuilder().correlationId(UUID.randomUUID().toString()).build();
105113
final CompletionStage<JsonObject> askResult =
106-
underTest.retrievePartialThing(thingId, SELECTOR, headers, THING_EVENT);
114+
underTest.retrievePartialThing(thingId, getJsonFieldSelector(), headers, THING_EVENT);
107115

108116
// WHEN: Command handler receives expected RetrieveThing and responds with ThingNotAccessibleException
109117
final RetrieveThing retrieveThing = kit.expectMsgClass(RetrieveThing.class);
110-
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(SELECTOR));
118+
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(getJsonFieldSelector()));
111119
final ThingNotAccessibleException thingNotAccessibleException =
112120
ThingNotAccessibleException.newBuilder(thingId).dittoHeaders(headers).build();
113121
kit.reply(thingNotAccessibleException);
@@ -127,11 +135,11 @@ public void unexpectedMessage() {
127135
final ThingId thingId = ThingId.generateRandom();
128136
final DittoHeaders headers = DittoHeaders.newBuilder().correlationId(UUID.randomUUID().toString()).build();
129137
final CompletionStage<JsonObject> askResult =
130-
underTest.retrievePartialThing(thingId, SELECTOR, headers, THING_EVENT);
138+
underTest.retrievePartialThing(thingId, getJsonFieldSelector(), headers, THING_EVENT);
131139

132140
// WHEN: Command handler receives expected RetrieveThing and responds with a random object
133141
final RetrieveThing retrieveThing = kit.expectMsgClass(RetrieveThing.class);
134-
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(SELECTOR));
142+
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(getJsonFieldSelector()));
135143
final Object randomObject = new Object();
136144
kit.reply(randomObject);
137145

@@ -152,11 +160,11 @@ public void timeout() {
152160
final ThingId thingId = ThingId.generateRandom();
153161
final DittoHeaders headers = DittoHeaders.newBuilder().correlationId(UUID.randomUUID().toString()).build();
154162
final CompletionStage<JsonObject> askResult =
155-
underTest.retrievePartialThing(thingId, SELECTOR, headers, THING_EVENT);
163+
underTest.retrievePartialThing(thingId, getJsonFieldSelector(), headers, THING_EVENT);
156164

157165
// WHEN: Command handler does not respond
158166
final RetrieveThing retrieveThing = kit.expectMsgClass(RetrieveThing.class);
159-
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(SELECTOR));
167+
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(getJsonFieldSelector()));
160168

161169
// THEN: The result future fails with an AskTimeoutException.
162170
askResult.toCompletableFuture().exceptionally(e -> null).join();
@@ -177,9 +185,9 @@ public void enrichThingDeleted() {
177185

178186
// WHEN: ThingDeleted event is about to be enriched by facade
179187
final CompletionStage<JsonObject> askResult =
180-
underTest.retrievePartialThing(thingId, SELECTOR, headers, thingDeleted);
188+
underTest.retrievePartialThing(thingId, getJsonFieldSelector(), headers, thingDeleted);
181189
final RetrieveThing retrieveThing = kit.expectMsgClass(RetrieveThing.class);
182-
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(SELECTOR));
190+
assertThat(retrieveThing.getSelectedFields()).contains(actualSelectedFields(getJsonFieldSelector()));
183191
kit.reply(RetrieveThingResponse.of(thingId, getThingResponseThingJson(), headers));
184192

185193
// THEN: The result future completes with the entity of the RetrieveThingResponse

0 commit comments

Comments
 (0)