package com.constellio.model.services.records.cache; import static com.constellio.model.entities.schemas.Schemas.IDENTIFIER; import static com.constellio.model.entities.schemas.Schemas.TITLE; import static com.constellio.model.services.search.query.ReturnedMetadatasFilter.idVersionSchema; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.from; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.fromAllSchemasIn; import static com.constellio.sdk.tests.TestUtils.mockManualMetadata; import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import org.assertj.core.api.ListAssert; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.wrappers.User; import com.constellio.model.entities.schemas.DataStoreField; import com.constellio.model.entities.schemas.Metadata; import com.constellio.model.entities.schemas.MetadataSchemaType; import com.constellio.model.entities.schemas.MetadataValueType; import com.constellio.model.entities.schemas.Schemas; import com.constellio.model.services.factories.ModelLayerFactory; import com.constellio.model.services.records.cache.RecordsCacheImpl.RecordHolder; import com.constellio.model.services.records.cache.RecordsCacheImplRuntimeException.RecordsCacheImplRuntimeException_InvalidSchemaTypeCode; import com.constellio.model.services.search.StatusFilter; import com.constellio.model.services.search.entities.SearchBoost; import com.constellio.model.services.search.query.ResultsProjection; import com.constellio.model.services.search.query.logical.LogicalSearchQuery; import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition; import com.constellio.sdk.tests.ConstellioTest; import com.constellio.sdk.tests.annotations.SlowTest; public class RecordsCacheImplTest extends ConstellioTest { @Mock User user; @Mock SearchBoost searchBoost; @Mock ResultsProjection resultsProjection; @Mock ModelLayerFactory modelLayerFactory; boolean givenDisabledRecordDuplications = false; Metadata zeTypeCodeMetadata, anotherTypeCodeMetadata, anotherTypeLegacyIdMetadata; List<Metadata> withoutIndexByMetadata = new ArrayList<>(); String zeType = "zeType"; String anotherType = "anotherType"; RecordsCacheImpl cache; @Before public void setUp() throws Exception { cache = new RecordsCacheImpl(zeCollection, modelLayerFactory); zeTypeCodeMetadata = mockManualMetadata("zeType_default_code", MetadataValueType.STRING); anotherTypeCodeMetadata = mockManualMetadata("anotherType_default_code", MetadataValueType.STRING); anotherTypeLegacyIdMetadata = mockManualMetadata("anotherType_default_legacyId", MetadataValueType.STRING); } @Test public void whenInsertingAPrevisoulyAddedRecordInAFullVolatileCacheThenInserted() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); Record record1 = cache.insert(newRecord(zeType, 1)); Record record2 = cache.insert(newRecord(zeType, 2)); Record record3 = cache.insert(newRecord(zeType, 3)); Record record4 = cache.insert(newRecord(zeType, 4)); Record record5 = cache.insert(newRecord(zeType, 5)); cache.insert(record1); cache.insert(record2); cache.insert(record3); cache.insert(record4); cache.insert(record5); assertThatRecords("3", "4", "5").areInCache(); assertThatRecords("1", "2").areNotInCache(); cache.insert(record1); assertThatRecords("1", "4", "5").areInCache(); assertThatRecords("2", "3").areNotInCache(); assertThat(cache.get("1")).isNotNull(); } @Test public void whenConfigureCachedTypeThenConfigsSavedInCache() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 100, withoutIndexByMetadata)); assertThat(cache.getConfiguredCaches()).containsOnly( CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata), CacheConfig.volatileCache(anotherType, 100, withoutIndexByMetadata) ); } @Test(expected = RecordsCacheImplRuntimeException_InvalidSchemaTypeCode.class) public void whenConfigureCacheUsingAnInvalidTypeThenIllegalArgumentException() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially("zeType_default", withoutIndexByMetadata)); } @Test public void whenConfigureCacheThatIsAlreadyConfiguredThenReconfigure() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(zeType, 100, withoutIndexByMetadata)); assertThat(cache.getCacheConfigOf(zeType).isVolatile()).isTrue(); } @Test(expected = IllegalArgumentException.class) public void whenConfigureCacheUsingNullThenIllegalArgumentException() throws Exception { cache.configureCache(null); } @Test public void givenpermanentCacheNotLoadedInitiallyWhenInsertRecordsThenKeepAllOfThem() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); Record record1 = cache.insert(newRecord(zeType, 1)); Record record2 = cache.insert(newRecord(zeType, 2)); Record record3 = cache.insert(newRecord(zeType, 3)); assertThatRecords("1", "2", "3").areInCache(); assertThat(cache.get("1").getId()).isEqualTo("1"); assertThat(cache.get("1")).isNotSameAs(record1); assertThat(cache.get("2").getId()).isEqualTo("2"); assertThat(cache.get("2")).isNotSameAs(record2); assertThat(cache.get("3").getId()).isEqualTo("3"); assertThat(cache.get("3")).isNotSameAs(record3); } @Test public void whenInsertRecordsOfNonCachedTpesThenNotKeptInCache() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); Record record1 = cache.insert(newRecord(zeType, 1)); Record record2 = cache.insert(newRecord(anotherType, 2)); Record record3 = cache.insert(newRecord(anotherType, 3)); assertThatRecord("1").isInCache(); assertThatRecords("2", "3").areNotInCache(); } @Test public void whenInsertRecordsOfVolatileCachedTypesThenKeptInCache() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); Record record1 = cache.insert(newRecord(zeType, 1)); Record record2 = cache.insert(newRecord(zeType, 2)); Record record3 = cache.insert(newRecord(zeType, 3)); assertThatRecords("1", "2", "3").areInCache(); } @Test public void givenVolatileCacheIsFullWhenInsertingANewItemThenRemoveOlder() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 3)); cache.insert(newRecord(zeType, 4)); cache.insert(newRecord(zeType, 5)); assertThatRecords("3", "4", "5").areInCache(); assertThatRecords("1", "2").areNotInCache(); cache.insert(newRecord(zeType, 6)); assertThatRecords("4", "5", "6").areInCache(); assertThatRecords("1", "2", "3").areNotInCache(); cache.insert(newRecord(zeType, 1)); assertThatRecords("1", "5", "6").areInCache(); assertThatRecords("2", "3", "4").areNotInCache(); } @Test public void givenVolatileCacheWhenARecordIsHitThenMovedOnTop() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 3)); cache.get("1"); cache.insert(newRecord(zeType, 4)); assertThatRecords("1", "3", "4").areInCache(); assertThatRecord("2").isNotInCache(); cache.insert(newRecord(zeType, 5)); assertThatRecords("1", "4", "5").areInCache(); assertThatRecords("2", "3").areNotInCache(); cache.insert(newRecord(zeType, 6)); assertThatRecords("4", "5", "6").areInCache(); assertThatRecords("1", "2", "3").areNotInCache(); cache.insert(newRecord(zeType, 1)); assertThatRecords("1", "5", "6").areInCache(); assertThatRecords("2", "3", "4").areNotInCache(); } @Test @SlowTest public void givenVolatileCacheWhenTopRecordIsHitThenNotConsideredHasAHit() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 3)); for (int i = 0; i < 20000; i++) { cache.get("1"); cache.get("2"); } cache.insert(newRecord(zeType, 4)); assertThatRecords("1", "2", "4").areInCache(); assertThatRecord("3").isNotInCache(); cache.insert(newRecord(zeType, 5)); assertThatRecords("2", "5").areInCache(); assertThatRecords("1", "3").areNotInCache(); cache.insert(newRecord(zeType, 6)); assertThatRecords("4", "5", "6").areInCache(); assertThatRecords("1", "2", "3").areNotInCache(); assertThat(cache.volatileCaches.get(zeType).holders.size()).isEqualTo(3); } @Test public void givenpermanentCacheNotLoadedInitiallyWhenInsertASameRecordMultipleTimesThenOnlyAddedOnce() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 2, 2)); cache.insert(newRecord(zeType, 2, 3)); cache.insert(newRecord(zeType, 3)); cache.insert(newRecord(zeType, 3, 2)); assertThatRecords("1", "2", "3").areInCache(); assertThat(cache.permanentCaches.get(zeType).holders).hasSize(3); assertThat(cache.get("1").getVersion()).isEqualTo(1L); assertThat(cache.get("2").getVersion()).isEqualTo(3L); assertThat(cache.get("3").getVersion()).isEqualTo(2L); } @Test public void givenVolatileCacheWhenInsertASameRecordMultipleTimesThenOnlyAddedOnce() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 2, 2)); cache.insert(newRecord(zeType, 2, 3)); cache.insert(newRecord(zeType, 3)); cache.insert(newRecord(zeType, 3, 2)); assertThatRecords("1", "2", "3").areInCache(); assertThat(cache.volatileCaches.get(zeType).holders).hasSize(3); assertThat(cache.get("1").getVersion()).isEqualTo(1L); assertThat(cache.get("2").getVersion()).isEqualTo(3L); assertThat(cache.get("3").getVersion()).isEqualTo(2L); } @Test public void givenVolatileCacheWithRecordsWhenInvalidateAllThenAllRemovedAndCacheEmpty() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 4, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 4, withoutIndexByMetadata)); cache.insert(newRecord(anotherType, 10)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 3)); cache.get("2"); cache.insert(newRecord(zeType, 3, 2)); assertThatRecords("1", "2", "3", "10").areInCache(); cache.invalidateRecordsOfType(zeType); assertThatRecords("1", "2", "3").areNotInCache(); assertThatRecord("10").isInCache(); assertThat(cache.volatileCaches.get(zeType).recordsInCache).isEqualTo(0); assertThat(cache.volatileCaches.get(zeType).holders).isEmpty(); } @Test public void givenpermanentCacheNotLoadedInitiallyWithRecordsWhenInvalidateAllThenAllRemovedAndCacheEmpty() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); cache.insert(newRecord(anotherType, 10)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 3)); cache.get("2"); cache.insert(newRecord(zeType, 3, 2)); assertThatRecords("1", "2", "3", "10").areInCache(); cache.invalidateRecordsOfType(zeType); assertThatRecords("1", "2", "3").areNotInCache(); assertThatRecord("10").isInCache(); assertThat(cache.permanentCaches.get(zeType).holders).hasSize(3); } @Test public void whenInsertingMultipleRecordsThenRegistered() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 3, withoutIndexByMetadata)); cache.insert(asList( newRecord(anotherType, 1), newRecord(anotherType, 2), newRecord(zeType, 3), newRecord(zeType, 4) )); assertThatRecords("1", "2", "3", "4").areInCache(); cache.insert(asList( newRecord(anotherType, 5), newRecord(anotherType, 6), newRecord(zeType, 7), newRecord(zeType, 8) )); assertThatRecords("2", "3", "4", "5", "6", "7", "8").areInCache(); assertThatRecord("1").isNotInCache(); } @Test public void givenVolatileCacheIsNotBigEnoughWhenInsertingThenInsertHasMuchRecordsHasPossible() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 3, withoutIndexByMetadata)); cache.insert(asList( newRecord(anotherType, 1), newRecord(anotherType, 2), newRecord(anotherType, 3), newRecord(anotherType, 4), newRecord(zeType, 5), newRecord(zeType, 6), newRecord(zeType, 7), newRecord(zeType, 8) )); assertThatRecords("2", "3", "4", "5", "6", "7", "8").areInCache(); assertThatRecord("1").isNotInCache(); } @Test public void givenVolatileCacheWhenInsertingNullThenDoNothing() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert((Record) null); cache.insert((List<Record>) null); assertThatRecord("1").isInCache(); } @Test public void givenVolatileCacheWhenInsertingEmptyListThenDoNothing() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(new ArrayList<Record>()); assertThatRecord("1").isInCache(); } @Test public void givenVolatileCacheWhenInsertingDirtyRecordThenNotInserted() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); Record dirtyRecord = newRecord(zeType, 2); when(dirtyRecord.isDirty()).thenReturn(true); cache.insert(newRecord(zeType, 1)); cache.insert(dirtyRecord); assertThatRecord("1").isInCache(); assertThatRecord("2").isNotInCache(); } @Test public void givenVolatileCacheWhenInsertingNotFullyLoadedRecordThenNotInsertedAndInvalidateExisting() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); Record notFullyLoadedRecord = newRecord(zeType, 2); when(notFullyLoadedRecord.isFullyLoaded()).thenReturn(false); cache.insert(newRecord(zeType, 1)); cache.insert(notFullyLoadedRecord); assertThatRecord("1").isInCache(); assertThatRecord("2").isNotInCache(); Record zeTypeRecordUpdate = newRecord(zeType, 1); when(zeTypeRecordUpdate.isFullyLoaded()).thenReturn(false); cache.insert(zeTypeRecordUpdate); assertThatRecords("1", "2").areNotInCache(); } @Test public void givenVolatileCacheWhenInsertingLogicallyDeletedRecordThenNotInserted() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); Record logicallyDeletedRecord = newRecord(zeType, 2); when(logicallyDeletedRecord.get(Schemas.LOGICALLY_DELETED_STATUS)).thenReturn(true); Record restoredRecord = newRecord(zeType, 3); when(restoredRecord.get(Schemas.LOGICALLY_DELETED_STATUS)).thenReturn(false); cache.insert(newRecord(zeType, 1)); cache.insert(logicallyDeletedRecord); cache.insert(restoredRecord); assertThatRecords("1", "3").areInCache(); assertThatRecord("2").isNotInCache(); } @Test public void givenVolatileCacheWhenInsertingUnsavedRecordThenNotInserted() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); Record dirtyRecord = newRecord(zeType, 2); when(dirtyRecord.isSaved()).thenReturn(false); cache.insert(newRecord(zeType, 1)); cache.insert(dirtyRecord); assertThatRecord("1").isInCache(); assertThatRecord("2").isNotInCache(); } @Test public void givenPermanenCacheWhenInsertingNullThenDoNothing() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert((Record) null); cache.insert((List<Record>) null); assertThatRecord("1").isInCache(); } @Test public void givenPermanenCacheWhenInsertingEmptyListThenDoNothing() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(new ArrayList<Record>()); assertThatRecord("1").isInCache(); } @Test public void givenpermanentCacheNotLoadedInitiallyWhenInsertingLogicallyDeletedRecordThenNotInserted() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); Record logicallyDeletedRecord = newRecord(zeType, 2); when(logicallyDeletedRecord.get(Schemas.LOGICALLY_DELETED_STATUS)).thenReturn(true); Record restoredRecord = newRecord(zeType, 3); when(restoredRecord.get(Schemas.LOGICALLY_DELETED_STATUS)).thenReturn(false); cache.insert(newRecord(zeType, 1)); cache.insert(logicallyDeletedRecord); cache.insert(restoredRecord); assertThatRecords("1", "3").areInCache(); assertThatRecord("2").isNotInCache(); } @Test public void givenPermanenCacheWhenInsertingDirtyRecordThenNotInserted() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); Record dirtyRecord = newRecord(zeType, 2); when(dirtyRecord.isDirty()).thenReturn(true); cache.insert(newRecord(zeType, 1)); cache.insert(dirtyRecord); assertThatRecord("1").isInCache(); assertThatRecord("2").isNotInCache(); } @Test public void givenpermanentCacheNotLoadedInitiallyWhenInsertingNotFullyLoadedRecordThenNotInserted() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); Record notFullyLoadedRecord = newRecord(zeType, 2); when(notFullyLoadedRecord.isFullyLoaded()).thenReturn(false); cache.insert(newRecord(zeType, 1)); cache.insert(notFullyLoadedRecord); assertThatRecord("1").isInCache(); assertThatRecord("2").isNotInCache(); Record zeTypeRecordUpdate = newRecord(zeType, 1); when(zeTypeRecordUpdate.isFullyLoaded()).thenReturn(false); cache.insert(zeTypeRecordUpdate); assertThatRecords("1", "2").areNotInCache(); } @Test public void givenpermanentCacheNotLoadedInitiallyWhenInsertingUnsavedRecordThenNotInserted() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); Record dirtyRecord = newRecord(zeType, 2); when(dirtyRecord.isSaved()).thenReturn(false); cache.insert(newRecord(zeType, 1)); cache.insert(dirtyRecord); assertThatRecord("1").isInCache(); assertThatRecord("2").isNotInCache(); } @Test public void wheInvalidatingAnUnfoundRecordThenNothingChange() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.invalidate("2"); assertThatRecord("1").isInCache(); assertThatRecord("2").isNotInCache(); } @Test public void givenpermanentCacheNotLoadedInitiallyWhenInvalidatingARecordThenDiminishCounter() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 3)); cache.invalidate("2"); assertThatRecords("1", "3").areInCache(); assertThatRecord("2").isNotInCache(); } @Test public void givenVolatileCacheWhenInvalidatingARecordThenInvalidate() throws Exception { cache.configureCache(CacheConfig.volatileCache(zeType, 3, withoutIndexByMetadata)); cache.insert(newRecord(zeType, 1)); cache.insert(newRecord(zeType, 2)); cache.insert(newRecord(zeType, 3)); cache.invalidate("2"); assertThatRecords("1", "3").areInCache(); assertThatRecord("2").isNotInCache(); assertThat(cache.volatileCaches.get(zeType).recordsInCache).isEqualTo(3); //At this step, the cache elements counter has an invalid value cache.insert(newRecord(zeType, 4)); cache.insert(newRecord(zeType, 5)); assertThatRecords("3", "4", "5").areInCache(); assertThatRecords("1", "2").isNotInCache(); assertThat(cache.volatileCaches.get(zeType).recordsInCache).isEqualTo(3); //The counter has return to normal } @Test public void whenInvalidatingMultipleRecordsThenAllInvalidated() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 3, withoutIndexByMetadata)); cache.insert(asList( newRecord(anotherType, 1), newRecord(anotherType, 2), newRecord(anotherType, 3), newRecord(zeType, 4), newRecord(zeType, 5), newRecord(zeType, 6) )); cache.invalidate(asList("1", "3", "5")); assertThatRecords("2", "4", "6").areInCache(); assertThatRecords("1", "3", "5").areNotInCache(); } @Test public void whenInvalidatingPassingNullThenDoesNothing() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 3, withoutIndexByMetadata)); cache.insert(asList( newRecord(anotherType, 1), newRecord(anotherType, 2), newRecord(anotherType, 3), newRecord(zeType, 4), newRecord(zeType, 5), newRecord(zeType, 6) )); cache.invalidate((String) null); cache.invalidate((List<String>) null); assertThatRecords("1", "2", "3", "4", "5", "6").areInCache(); } @Test public void whenGetCachedSearchResultForQueryThenOnlyGetCachedSearchResultsForSameQueryOfpermanentCacheNotLoadedInitially() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 3, withoutIndexByMetadata)); Record anotherTypeRecord1 = newRecord(anotherType, 1); Record anotherTypeRecord2 = newRecord(anotherType, 2); Record anotherTypeRecord3 = newRecord(anotherType, 3); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); cache.insert( asList(anotherTypeRecord1, anotherTypeRecord2, anotherTypeRecord3, zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(anotherType()).returnAll()), asList(anotherTypeRecord1, anotherTypeRecord2)); cache.insertQueryResults(query(from(zeType()).returnAll()), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).where(TITLE).isEqualTo("value1")), asList(zeTypeRecord4, zeTypeRecord6)); cache.insertQueryResults(query(fromAllSchemasIn(zeCollection).returnAll()), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); //List<Record> records = cache.getQueryResults(from(zeType()).returnAll())); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); assertThatQueryResults(from(zeType()).where(TITLE.getAnalyzedField("fr")).isEqualTo("value1")).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).where(TITLE).isEqualTo("value2")), new ArrayList<Record>()); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isEmpty(); } @Test public void whenGetCachedSearchResultForQueryThenOnlyCacheQueriesWithoutUnsupportedFiltersAndFeatures() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.volatileCache(anotherType, 3, withoutIndexByMetadata)); Record anotherTypeRecord1 = newRecord(anotherType, 1); Record anotherTypeRecord2 = newRecord(anotherType, 2); Record anotherTypeRecord3 = newRecord(anotherType, 3); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); LogicalSearchCondition condition = from(zeType()).returnAll(); cache.insert( asList(anotherTypeRecord1, anotherTypeRecord2, anotherTypeRecord3, zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setNumberOfRows(1), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setStartRow(4), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setPreferAnalyzedFields(true), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setHighlighting(true), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setResultsProjection(resultsProjection), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setQueryBoosts(asList(searchBoost)), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setFieldBoosts(asList(searchBoost)), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).addFieldFacet("field_s"), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).addQueryFacet("queries", "field_s:*"), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); DataStoreField field = mock(DataStoreField.class); when(field.getDataStoreCode()).thenReturn("field_s"); cache.insertQueryResults(query(condition).computeStatsOnField(field), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).filteredWithUser(user), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).filteredWithUserDelete(user), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).filteredWithUserWrite(user), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition).setReturnedMetadatas(idVersionSchema()), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); LogicalSearchQuery query = query(condition); query.getFacetFilters().selectedFieldFacetValue("field_s", "value"); cache.insertQueryResults(query, asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(0); cache.insertQueryResults(query(condition), asList(zeTypeRecord4)); assertThat(cache.permanentCaches.get(zeType).queryResults).hasSize(1); } @Test public void whenCacheQueryResultsThenBasedOnSort() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); cache.insert(asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()).sortAsc(IDENTIFIER), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).returnAll()).sortDesc(IDENTIFIER), asList(zeTypeRecord6, zeTypeRecord5, zeTypeRecord4)); cache.insertQueryResults(query(from(zeType()).returnAll()).sortAsc(TITLE).sortAsc(IDENTIFIER), asList(zeTypeRecord5, zeTypeRecord4, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).returnAll()).sortAsc(TITLE).sortDesc(IDENTIFIER), asList(zeTypeRecord5, zeTypeRecord6, zeTypeRecord4)); assertThatQueryResults(query(from(zeType()).returnAll()).sortAsc(IDENTIFIER)) .containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(query(from(zeType()).returnAll()).sortDesc(IDENTIFIER)) .containsExactly(zeTypeRecord6, zeTypeRecord5, zeTypeRecord4); assertThatQueryResults(query(from(zeType()).returnAll()).sortAsc(TITLE).sortAsc(IDENTIFIER)) .containsExactly(zeTypeRecord5, zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(query(from(zeType()).returnAll()).sortAsc(TITLE).sortDesc(IDENTIFIER)) .containsExactly(zeTypeRecord5, zeTypeRecord6, zeTypeRecord4); } @Test public void whenCacheQueryResultsThenBasedOnFreeTextSearch() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); cache.insert(asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("a"), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("b"), asList(zeTypeRecord6, zeTypeRecord4)); cache.insertQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("c"), asList(zeTypeRecord5, zeTypeRecord4)); cache.insertQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("d"), asList(zeTypeRecord4)); assertThatQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("a")) .containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("b")) .containsExactly(zeTypeRecord6, zeTypeRecord4); assertThatQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("c")) .containsExactly(zeTypeRecord5, zeTypeRecord4); assertThatQueryResults(query(from(zeType()).returnAll()).setFreeTextQuery("d")) .containsExactly(zeTypeRecord4); } @Test public void whenCacheQueryResultsThenBasedOnStatusFilter() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); cache.insert(asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()).filteredByStatus(StatusFilter.ACTIVES), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).returnAll()).filteredByStatus(StatusFilter.ALL), asList(zeTypeRecord6, zeTypeRecord4)); cache.insertQueryResults(query(from(zeType()).returnAll()).filteredByStatus(StatusFilter.DELETED), asList(zeTypeRecord5, zeTypeRecord4)); assertThatQueryResults(query(from(zeType()).returnAll()).filteredByStatus(StatusFilter.ACTIVES)) .containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(query(from(zeType()).returnAll()).filteredByStatus(StatusFilter.ALL)) .containsExactly(zeTypeRecord6, zeTypeRecord4); assertThatQueryResults(query(from(zeType()).returnAll()).filteredByStatus(StatusFilter.DELETED)) .containsExactly(zeTypeRecord5, zeTypeRecord4); assertThatQueryResults(query(from(zeType()).returnAll())) .containsExactly(zeTypeRecord6, zeTypeRecord4); } @Test public void givenCachedSearchResultsOfAQueryWhenInsertARecordWithDifferentVersionThenAllQueriesInvalidated() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record anotherTypeRecord1 = newRecord(anotherType, 1); Record anotherTypeRecord2 = newRecord(anotherType, 2); Record anotherTypeRecord3 = newRecord(anotherType, 3); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); Record zeTypeRecord5_v2 = newRecord(zeType, 5, 2); Record anotherTypeRecord2_v2 = newRecord(anotherType, 2, 2); cache.insert( asList(anotherTypeRecord1, anotherTypeRecord2, anotherTypeRecord3, zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).where(TITLE).isEqualTo("value1")), asList(zeTypeRecord4, zeTypeRecord6)); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Inserting a record from another schema - no impacts cache.insert(anotherTypeRecord2); cache.insert(anotherTypeRecord2_v2); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Inserting a record from the schema with same version - no impact cache.insert(zeTypeRecord5); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Inserting a record from the schema with different version - all queries invalidated cache.insert(zeTypeRecord5_v2); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); } @Test public void givenCachedSearchResultsOfAQueryWhenInvalidateARecordThenAllQueriesInvalidated() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record anotherTypeRecord1 = newRecord(anotherType, 1); Record anotherTypeRecord2 = newRecord(anotherType, 2); Record anotherTypeRecord3 = newRecord(anotherType, 3); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); Record zeTypeRecord5_v2 = newRecord(zeType, 5, 2); Record anotherTypeRecord2_v2 = newRecord(anotherType, 2, 2); cache.insert( asList(anotherTypeRecord1, anotherTypeRecord2, anotherTypeRecord3, zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).where(TITLE).isEqualTo("value1")), asList(zeTypeRecord4, zeTypeRecord6)); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Invalidate a record from another schema - queries of ze schema not invalidated cache.invalidate(anotherTypeRecord2.getId()); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Invalidate a record from ze schema - queries of ze schema invalidated cache.invalidate(zeTypeRecord4.getId()); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); } @Test public void givenCachedSearchResultsOfAQueryWhenInvalidateAllRecordsOfTypeThenAllQueriesInvalidated() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record anotherTypeRecord1 = newRecord(anotherType, 1); Record anotherTypeRecord2 = newRecord(anotherType, 2); Record anotherTypeRecord3 = newRecord(anotherType, 3); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); cache.insert( asList(anotherTypeRecord1, anotherTypeRecord2, anotherTypeRecord3, zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).where(TITLE).isEqualTo("value1")), asList(zeTypeRecord4, zeTypeRecord6)); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Invalidate a record from another schema - queries of ze schema not invalidated cache.invalidateRecordsOfType(anotherType); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Invalidate a record from ze schema - queries of ze schema invalidated cache.invalidateRecordsOfType(zeType); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); } @Test public void givenCachedSearchResultsOfAQueryWhenInvalidateAllThenAllQueriesInvalidated() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record anotherTypeRecord1 = newRecord(anotherType, 1); Record anotherTypeRecord2 = newRecord(anotherType, 2); Record anotherTypeRecord3 = newRecord(anotherType, 3); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); cache.insert( asList(anotherTypeRecord1, anotherTypeRecord2, anotherTypeRecord3, zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).where(TITLE).isEqualTo("value1")), asList(zeTypeRecord4, zeTypeRecord6)); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Invalidate a record from another schema - queries of ze schema not invalidated cache.invalidateAll(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); } @Test public void givenCachedSearchResultsOfAQueryWhenInsertANewRecordThenAllQueriesInvalidated() throws Exception { cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, withoutIndexByMetadata)); cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(anotherType, withoutIndexByMetadata)); Record anotherTypeRecord1 = newRecord(anotherType, 1); Record anotherTypeRecord2 = newRecord(anotherType, 2); Record anotherTypeRecord3 = newRecord(anotherType, 3); Record zeTypeRecord4 = newRecord(zeType, 4); Record zeTypeRecord5 = newRecord(zeType, 5); Record zeTypeRecord6 = newRecord(zeType, 6); cache.insert( asList(anotherTypeRecord1, anotherTypeRecord2, anotherTypeRecord3, zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); assertThatQueryResults(from(anotherType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(fromAllSchemasIn(zeCollection).returnAll()).isNull(); cache.insertQueryResults(query(from(zeType()).returnAll()), asList(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6)); cache.insertQueryResults(query(from(zeType()).where(TITLE).isEqualTo("value1")), asList(zeTypeRecord4, zeTypeRecord6)); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Invalidate a record from another schema - queries of ze schema not invalidated cache.insert(newRecord(anotherType, 7)); assertThatQueryResults(from(zeType()).returnAll()).containsExactly(zeTypeRecord4, zeTypeRecord5, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).containsExactly(zeTypeRecord4, zeTypeRecord6); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); //Invalidate a record from ze schema - queries of ze schema invalidated cache.insert(newRecord(zeType, 8)); assertThatQueryResults(from(zeType()).returnAll()).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value1")).isNull(); assertThatQueryResults(from(zeType()).where(TITLE).isEqualTo("value2")).isNull(); } @Test public void givenCacheWithMetadataIndexesThenCanFindRecordsWithThem() throws Exception { givenDisabledRecordDuplications = true; cache.configureCache(CacheConfig.permanentCacheNotLoadedInitially(zeType, asList(zeTypeCodeMetadata))); cache.configureCache( CacheConfig.volatileCache(anotherType, 3, asList(anotherTypeCodeMetadata, anotherTypeLegacyIdMetadata))); Record zeType1 = newRecord(zeType, 1); when(zeType1.get(zeTypeCodeMetadata)).thenReturn("leNumero1"); //Supertimor Record zeType18 = newRecord(zeType, 2); when(zeType18.get(zeTypeCodeMetadata)).thenReturn("code18"); Record zeType42 = newRecord(zeType, 3); when(zeType42.get(zeTypeCodeMetadata)).thenReturn("ze42"); Record anotherType1 = newRecord(anotherType, 4); when(anotherType1.get(anotherTypeCodeMetadata)).thenReturn("leNumero1"); //Supertimor when(anotherType1.get(anotherTypeLegacyIdMetadata)).thenReturn("123"); Record anotherType18 = newRecord(anotherType, 5); when(anotherType18.get(anotherTypeCodeMetadata)).thenReturn("code18"); when(anotherType18.get(anotherTypeLegacyIdMetadata)).thenReturn("456"); Record anotherType42 = newRecord(anotherType, 6); when(anotherType42.get(anotherTypeCodeMetadata)).thenReturn("ze42"); when(anotherType42.get(anotherTypeLegacyIdMetadata)).thenReturn("789"); cache.insert(asList(zeType1, zeType18, zeType42, anotherType1, anotherType18, anotherType42)); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "leNumero1"))).isEqualTo("1"); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "code18"))).isEqualTo("2"); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "ze42"))).isEqualTo("3"); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "666"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "leNumero1"))).isEqualTo("4"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "code18"))).isEqualTo("5"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "ze42"))).isEqualTo("6"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "666"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "123"))).isEqualTo("4"); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "456"))).isEqualTo("5"); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "789"))).isEqualTo("6"); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "666"))).isNull(); zeType18 = newRecord(zeType, 2); when(zeType18.get(zeTypeCodeMetadata)).thenReturn("666"); cache.insert(asList(zeType18)); anotherType42 = newRecord(anotherType, 6); when(anotherType42.get(anotherTypeCodeMetadata)).thenReturn("ze42"); when(anotherType42.get(anotherTypeLegacyIdMetadata)).thenReturn("666"); cache.insert(asList(anotherType42)); anotherType1 = newRecord(anotherType, 4); when(anotherType1.get(anotherTypeCodeMetadata)).thenReturn(null); when(anotherType1.get(anotherTypeLegacyIdMetadata)).thenReturn("123"); cache.insert(asList(anotherType1)); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "leNumero1"))).isEqualTo("1"); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "code18"))).isNull(); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "ze42"))).isEqualTo("3"); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "666"))).isEqualTo("2"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "leNumero1"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "code18"))).isEqualTo("5"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "ze42"))).isEqualTo("6"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "666"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "123"))).isEqualTo("4"); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "456"))).isEqualTo("5"); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "789"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "666"))).isEqualTo("6"); cache.invalidate("1"); cache.invalidate("5"); zeType1 = newRecord(zeType, 1); when(zeType1.get(zeTypeCodeMetadata)).thenReturn("supertimor"); //leNumero1 anotherType18 = newRecord(anotherType, 5); when(anotherType18.get(anotherTypeCodeMetadata)).thenReturn("code18pouces"); cache.insert(asList(zeType1)); cache.insert(asList(anotherType18)); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "leNumero1"))).isNull(); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "code18"))).isNull(); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "ze42"))).isEqualTo("3"); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "666"))).isEqualTo("2"); assertThat(idOf(cache.getByMetadata(zeTypeCodeMetadata, "supertimor"))).isEqualTo("1"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "leNumero1"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "code18"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "ze42"))).isEqualTo("6"); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "666"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeCodeMetadata, "code18pouces"))).isEqualTo("5"); //Record 4 has been released during record 5 insert assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "123"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "456"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "789"))).isNull(); assertThat(idOf(cache.getByMetadata(anotherTypeLegacyIdMetadata, "666"))).isEqualTo("6"); } private String idOf(Record record) { return record == null ? null : record.getId(); } //-------------------------------------- private OngoingEntryAssertion assertThatRecord(String id) { return new OngoingEntryAssertion(asList(id)); } private OngoingEntryAssertion assertThatRecords(String... ids) { return new OngoingEntryAssertion(asList(ids)); } private class OngoingEntryAssertion { private List<String> ids; private OngoingEntryAssertion(List<String> ids) { this.ids = ids; } private void isInCache() { areInCache(); } private void areInCache() { for (String id : ids) { boolean isCached = cache.isCached(id); assertThat(isCached).describedAs("Record with id '" + id + "' is expected to be in cache").isTrue(); RecordHolder holder = cache.cacheById.get(id); assertThat(holder).describedAs("Record holder of '" + id + "'").isNotNull(); assertThat(holder.getCopy()).describedAs("Cache record of '" + id + "'").isNotNull(); } } private void isNotInCache() { areNotInCache(); } private void areNotInCache() { for (String id : ids) { boolean isCached = cache.isCached(id); assertThat(isCached).describedAs("Record with id '" + id + "' is expected to not be in cache").isFalse(); } } } private Record newRecord(String schemaType, int id) { return newRecord(schemaType, "" + id); } private Record newRecord(String schemaType, int id, long version) { return newRecord(schemaType, "" + id, version); } private Record newRecord(final String schemaType, final String id) { return newRecord(schemaType, id, 1); } private Record newRecord(final String schemaType, final String id, final long version) { String schema = schemaType + "_default"; final Record record = mock(Record.class, schemaType + "-" + id); when(record.getId()).thenReturn(id); when(record.getSchemaCode()).thenReturn(schema); when(record.getVersion()).thenReturn(version); when(record.isDirty()).thenReturn(false); when(record.isFullyLoaded()).thenReturn(true); when(record.isSaved()).thenReturn(true); when(record.getCopyOfOriginalRecord()).thenAnswer(new Answer<Object>() { @Override public Object answer(InvocationOnMock invocation) throws Throwable { if (givenDisabledRecordDuplications) { return record; } else { boolean dirty = record.isDirty(); boolean fullyLoaded = record.isFullyLoaded(); boolean saved = record.isSaved(); Boolean logicallyDeleted = record.get(Schemas.LOGICALLY_DELETED_STATUS); Record recordCopy = newRecord(schemaType, id, version); when(recordCopy.isDirty()).thenReturn(dirty); when(recordCopy.isFullyLoaded()).thenReturn(fullyLoaded); when(recordCopy.isSaved()).thenReturn(saved); when(recordCopy.get(Schemas.LOGICALLY_DELETED_STATUS)).thenReturn(logicallyDeleted); return recordCopy; } } }); return record; } private MetadataSchemaType zeType() { MetadataSchemaType type = Mockito.mock(MetadataSchemaType.class); when(type.getCode()).thenReturn(zeType); return type; } private MetadataSchemaType anotherType() { MetadataSchemaType type = Mockito.mock(MetadataSchemaType.class); when(type.getCode()).thenReturn(anotherType); return type; } private LogicalSearchQuery query(LogicalSearchCondition condition) { return new LogicalSearchQuery(condition); } private ListAssert<Record> assertThatQueryResults(LogicalSearchCondition condition) { return assertThatQueryResults(query(condition)); } private ListAssert<Record> assertThatQueryResults(LogicalSearchQuery query) { Comparator<Record> idVersionSchemaRecordComparator = new Comparator<Record>() { @Override public int compare(Record o1, Record o2) { String id1 = o1.getId(); String id2 = o2.getId(); String schemaCode1 = o1.getSchemaCode(); String schemaCode2 = o2.getSchemaCode(); long version1 = o1.getVersion(); long version2 = o2.getVersion(); if (id1.equals(id2) && schemaCode1.equals(schemaCode2) && version1 == version2) { return 0; } else { return 1; } } }; return assertThat(cache.getQueryResults(query)).usingElementComparator( idVersionSchemaRecordComparator); } }