mirror of
https://github.com/Kingsrook/qqq.git
synced 2025-07-19 05:30:43 +00:00
Refactor caching out of GetAction - namely, to support initial use-cases in QueryAction.
This commit is contained in:
@ -22,29 +22,15 @@
|
||||
package com.kingsrook.qqq.backend.core.actions.tables;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
@ -85,166 +71,4 @@ class GetActionTest extends BaseTest
|
||||
assertNotNull(result.getRecord());
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCache() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(TestUtils.TABLE_NAME_PERSON_MEMORY), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "George").withValue("lastName", "Washington").withValue("noOfShoes", 5),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "John").withValue("lastName", "Adams"),
|
||||
new QRecord().withValue("id", 3).withValue("firstName", "Thomas").withValue("lastName", "Jefferson"),
|
||||
new QRecord().withValue("id", 4).withValue("firstName", "Thomas 503").withValue("lastName", "Jefferson"),
|
||||
new QRecord().withValue("id", 5).withValue("firstName", "Thomas 999").withValue("lastName", "Jefferson")
|
||||
));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// get from the table which caches it - confirm they are (magically) found //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(5, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to get from the table which caches it - but should not find because use case should filter out because of matching 503 //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "Thomas 503", "lastName", "Jefferson"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
getInput.setUniqueKey(Map.of("firstName", "Thomas 999", "lastName", "Jefferson"));
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// request a row that doesn't exist in cache or source, should miss both //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "John", "lastName", "McCain"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// update the record in the source table - then re-get from cache table - shouldn't see new value. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfShoes", 6)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(5, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// delete the cached record; re-get, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
deleteInput.setQueryFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "George")));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(6, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// update the source record; see that it isn't updated in cache. //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfShoes", 7)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(6, getOutput.getRecord().getValue("noOfShoes"));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
updateInput.setRecords(List.of(getOutput.getRecord().withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertEquals(7, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// should only be 1 cache record at this point //
|
||||
/////////////////////////////////////////////////
|
||||
assertEquals(1, TestUtils.queryTable(QContext.getQInstance(), TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE).size());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// delete the source record - it will still be in the cache though. //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY);
|
||||
deleteInput.setPrimaryKeys(List.of(1));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and now it should go away //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
updateInput.setRecords(List.of(getOutput.getRecord().withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
getInput = new GetInput();
|
||||
getInput.setTableName(TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,276 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.tables.helpers;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.GetAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.get.GetOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for GetActionCacheHelper
|
||||
*******************************************************************************/
|
||||
class GetActionCacheHelperTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCache() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
String sourceTableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
||||
String cacheTableName = TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE;
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(sourceTableName), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "George").withValue("lastName", "Washington").withValue("noOfShoes", 5),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "John").withValue("lastName", "Adams"),
|
||||
new QRecord().withValue("id", 3).withValue("firstName", "Thomas").withValue("lastName", "Jefferson"),
|
||||
new QRecord().withValue("id", 4).withValue("firstName", "James").withValue("lastName", "Garfield").withValue("noOfShoes", 503),
|
||||
new QRecord().withValue("id", 5).withValue("firstName", "Abraham").withValue("lastName", "Lincoln").withValue("noOfShoes", 999),
|
||||
new QRecord().withValue("id", 6).withValue("firstName", "Bill").withValue("lastName", "Clinton")
|
||||
));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// get from the table which caches it - confirm they are (magically) found //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(5, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to get records through the cache table, which meet the conditions that cause them to not be cached. //
|
||||
// so we should get results from the Get request - but - then let's go directly to the backend to confirm the records are not cached. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "James", "lastName", "Garfield"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
|
||||
getInput.setUniqueKey(Map.of("firstName", "Abraham", "lastName", "Lincoln"));
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.IN, "Abraham", "James"))));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// fetch a record through the cache, so it gets cached. //
|
||||
// then update the source record so that it meets the condition that doesn't allow it to be cached. //
|
||||
// then expire the cached record. //
|
||||
// then re-fetch through cache - which should see the expiration, re-fetch from source, and delete from cache. //
|
||||
// assert record is no longer in cache. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "Bill", "lastName", "Clinton"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
|
||||
assertEquals(1, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Clinton"))));
|
||||
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 6).withValue("noOfShoes", 503)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(getOutput.getRecord().withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "Bill", "lastName", "Clinton"));
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertEquals(503, getOutput.getRecord().getValue("noOfShoes"));
|
||||
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Clinton"))));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// request a row that doesn't exist in cache or source, should miss both //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "John", "lastName", "McCain"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// update the record in the source table - then re-get from cache table - shouldn't see new value. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfShoes", 6)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(5, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// delete the cached record; re-get, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(cacheTableName);
|
||||
deleteInput.setQueryFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "George")));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(6, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// update the source record; see that it isn't updated in cache. //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfShoes", 7)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
assertNotNull(getOutput.getRecord().getValue("cachedDate"));
|
||||
assertEquals(6, getOutput.getRecord().getValue("noOfShoes"));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(getOutput.getRecord().withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertEquals(7, getOutput.getRecord().getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// should only be 1 cache record at this point //
|
||||
/////////////////////////////////////////////////
|
||||
assertEquals(1, TestUtils.queryTable(QContext.getQInstance(), cacheTableName).size());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// delete the source record - it will still be in the cache though. //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(sourceTableName);
|
||||
deleteInput.setPrimaryKeys(List.of(1));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
GetInput getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
GetOutput getOutput = new GetAction().execute(getInput);
|
||||
assertNotNull(getOutput.getRecord());
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and now it should go away //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(getOutput.getRecord().withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
getInput = new GetInput();
|
||||
getInput.setTableName(cacheTableName);
|
||||
getInput.setUniqueKey(Map.of("firstName", "George", "lastName", "Washington"));
|
||||
getOutput = new GetAction().execute(getInput);
|
||||
assertNull(getOutput.getRecord());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static int countCachedRecordsDirectlyInBackend(String tableName, QQueryFilter filter) throws QException
|
||||
{
|
||||
List<QRecord> cachedRecords = MemoryRecordStore.getInstance().query(new QueryInput()
|
||||
.withTableName(tableName)
|
||||
.withFilter(filter));
|
||||
return cachedRecords.size();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,850 @@
|
||||
/*
|
||||
* QQQ - Low-code Application Framework for Engineers.
|
||||
* Copyright (C) 2021-2023. Kingsrook, LLC
|
||||
* 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States
|
||||
* contact@kingsrook.com
|
||||
* https://github.com/Kingsrook/
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.kingsrook.qqq.backend.core.actions.tables.helpers;
|
||||
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
import com.kingsrook.qqq.backend.core.BaseTest;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.DeleteAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
|
||||
import com.kingsrook.qqq.backend.core.actions.tables.UpdateAction;
|
||||
import com.kingsrook.qqq.backend.core.context.QContext;
|
||||
import com.kingsrook.qqq.backend.core.exceptions.QException;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.delete.DeleteInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QCriteriaOperator;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QFilterCriteria;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QQueryFilter;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryInput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.query.QueryOutput;
|
||||
import com.kingsrook.qqq.backend.core.model.actions.tables.update.UpdateInput;
|
||||
import com.kingsrook.qqq.backend.core.model.data.QRecord;
|
||||
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
|
||||
import com.kingsrook.qqq.backend.core.modules.backend.implementations.memory.MemoryRecordStore;
|
||||
import com.kingsrook.qqq.backend.core.utils.TestUtils;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Unit test for QueryActionCacheHelper
|
||||
*******************************************************************************/
|
||||
class QueryActionCacheHelperTest extends BaseTest
|
||||
{
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCacheSingleFieldUniqueKeySingleRecordUseCases() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
String sourceTableName = TestUtils.TABLE_NAME_SHAPE;
|
||||
String cacheTableName = TestUtils.TABLE_NAME_SHAPE_CACHE;
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance, qInstance.getTable(sourceTableName), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("name", "Triangle").withValue("noOfSides", 3),
|
||||
new QRecord().withValue("id", 2).withValue("name", "Square").withValue("noOfSides", 4),
|
||||
new QRecord().withValue("id", 3).withValue("name", "Pentagon").withValue("noOfSides", 5),
|
||||
new QRecord().withValue("id", 4).withValue("name", "ServerErrorGon").withValue("noOfSides", 503),
|
||||
new QRecord().withValue("id", 5).withValue("name", "ManyGon").withValue("noOfSides", 999)
|
||||
));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// get from the table which caches it - confirm they are (magically) found //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertNotEquals(0, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(3, queryOutput.getRecords().get(0).getValue("noOfSides"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to get from the table which caches it - it should be found, but not cached //
|
||||
// because use case should filter out because of matching 503 //
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "ServerErrorGon")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "ManyGon")));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// request a row that doesn't exist in cache or source, should miss both //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Line"))); // lines aren't shapes :)
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// update the record in the source table - then re-get from cache table - shouldn't see new value. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfSides", 6)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertNotEquals(0, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(3, queryOutput.getRecords().get(0).getValue("noOfSides"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// delete the cached record; re-get, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(cacheTableName);
|
||||
deleteInput.setQueryFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertNotEquals(0, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(6, queryOutput.getRecords().get(0).getValue("noOfSides"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// update the source record; see that it isn't updated in cache. //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfSides", 7)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertNotEquals(0, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(6, queryOutput.getRecords().get(0).getValue("noOfSides"));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(queryOutput.getRecords().get(0).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(7, queryOutput.getRecords().get(0).getValue("noOfSides"));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// should only be 1 cache record at this point //
|
||||
/////////////////////////////////////////////////
|
||||
assertEquals(1, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// delete the source record - it will still be in the cache though. //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(sourceTableName);
|
||||
deleteInput.setPrimaryKeys(List.of(1));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertEquals(1, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and now it should go away //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(queryOutput.getRecords().get(0).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCacheSingleFieldUniqueKeyMultiRecordUseCases() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
String sourceTableName = TestUtils.TABLE_NAME_SHAPE;
|
||||
String cacheTableName = TestUtils.TABLE_NAME_SHAPE_CACHE;
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance.getTable(sourceTableName), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("name", "Triangle").withValue("noOfSides", 3),
|
||||
new QRecord().withValue("id", 2).withValue("name", "Square").withValue("noOfSides", 4),
|
||||
new QRecord().withValue("id", 3).withValue("name", "Pentagon").withValue("noOfSides", 5),
|
||||
new QRecord().withValue("id", 4).withValue("name", "ServerErrorGon").withValue("noOfSides", 503),
|
||||
new QRecord().withValue("id", 5).withValue("name", "ManyGon").withValue("noOfSides", 999)
|
||||
));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// get from the table which caches it - confirm they are (magically) found //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.IN, "Triangle", "Square", "Pentagon")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(3, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to get records through the cache table, which meet the conditions that cause them to not be cached. //
|
||||
// so we should get results from the Query request - but - then let's go directly to the backend to confirm the records are not cached. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
assertEquals(3, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "ServerErrorGon")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.IN, "ManyGon", "ServerErrorGon")));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(2, queryOutput.getRecords().size());
|
||||
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.IN, "ManyGon", "Square")));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(2, queryOutput.getRecords().size());
|
||||
|
||||
assertEquals(3, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// request a row that doesn't exist in cache or source, should miss both //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Line"))); // lines aren't shapes :)
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// update one source record; delete another - query and should still find the previously cached //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfSides", 6)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(sourceTableName);
|
||||
deleteInput.setPrimaryKeys(List.of(2)); // delete Square
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.IN, "Triangle", "Square", "Pentagon", "ServerErrorGon")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(4, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(3, queryOutput.getRecords().get(0).getValue("noOfSides"));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and we should see the updated value (and the deleted one) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(Stream.of(1, 2, 3).map(id -> new QRecord().withValue("id", id).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))).toList());
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(3, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(6, queryOutput.getRecords().stream().filter(r -> r.getValueString("name").equals("Triangle")).findFirst().get().getValue("noOfSides"));
|
||||
|
||||
assertEquals(2, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCacheMultiFieldUniqueKeySingleRecordUseCases() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
String sourceTableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
||||
String cacheTableName = TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE;
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance.getTable(sourceTableName), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "George").withValue("lastName", "Washington").withValue("noOfShoes", 5),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "John").withValue("lastName", "Adams"),
|
||||
new QRecord().withValue("id", 3).withValue("firstName", "Thomas").withValue("lastName", "Jefferson"),
|
||||
new QRecord().withValue("id", 4).withValue("firstName", "James").withValue("lastName", "Garfield").withValue("noOfShoes", 503),
|
||||
new QRecord().withValue("id", 5).withValue("firstName", "Abraham").withValue("lastName", "Lincoln").withValue("noOfShoes", 999),
|
||||
new QRecord().withValue("id", 6).withValue("firstName", "Bill").withValue("lastName", "Clinton")
|
||||
));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// get from the table which caches it - confirm they are (magically) found //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("George", "Washington"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(5, queryOutput.getRecords().get(0).getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to get records through the cache table, which meet the conditions that cause them to not be cached. //
|
||||
// so we should get results from the Get request - but - then let's go directly to the backend to confirm the records are not cached. //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("James", "Garfield"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
|
||||
queryInput.setFilter(getFilterForPerson("Abraham", "Lincoln"));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.IN, "Abraham", "James"))));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// fetch a record through the cache, so it gets cached. //
|
||||
// then update the source record so that it meets the condition that doesn't allow it to be cached. //
|
||||
// then expire the cached record. //
|
||||
// then re-fetch through cache - which should see the expiration, re-fetch from source, and delete from cache. //
|
||||
// assert record is no longer in cache. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("Bill", "Clinton"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
|
||||
assertEquals(1, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Clinton"))));
|
||||
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 6).withValue("noOfShoes", 503)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(queryOutput.getRecords().get(0).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(503, queryOutput.getRecords().get(0).getValue("noOfShoes"));
|
||||
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Clinton"))));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// request a row that doesn't exist in cache or source, should miss both //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("John", "McCain"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// update the record in the source table - then re-get from cache table - shouldn't see new value. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfShoes", 6)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("George", "Washington"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(5, queryOutput.getRecords().get(0).getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// delete the cached record; re-get, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(cacheTableName);
|
||||
deleteInput.setQueryFilter(new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, "George")));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("George", "Washington"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(6, queryOutput.getRecords().get(0).getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////
|
||||
// update the source record; see that it isn't updated in cache. //
|
||||
///////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfShoes", 7)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("George", "Washington"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(6, queryOutput.getRecords().get(0).getValue("noOfShoes"));
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and we should see the updated value //
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(queryOutput.getRecords().get(0).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(7, queryOutput.getRecords().get(0).getValue("noOfShoes"));
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
// should only be 1 cache record at this point //
|
||||
/////////////////////////////////////////////////
|
||||
assertEquals(1, TestUtils.queryTable(QContext.getQInstance(), cacheTableName).size());
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// delete the source record - it will still be in the cache though. //
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(sourceTableName);
|
||||
deleteInput.setPrimaryKeys(List.of(1));
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("George", "Washington"));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and now it should go away //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(List.of(queryOutput.getRecords().get(0).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPerson("George", "Washington"));
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCacheMultiFieldUniqueKeyMultiRecordUseCases() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
String sourceTableName = TestUtils.TABLE_NAME_PERSON_MEMORY;
|
||||
String cacheTableName = TestUtils.TABLE_NAME_PERSON_MEMORY_CACHE;
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance.getTable(sourceTableName), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("firstName", "George").withValue("lastName", "Washington").withValue("noOfShoes", 5),
|
||||
new QRecord().withValue("id", 2).withValue("firstName", "John").withValue("lastName", "Adams"),
|
||||
new QRecord().withValue("id", 3).withValue("firstName", "Thomas").withValue("lastName", "Jefferson"),
|
||||
new QRecord().withValue("id", 4).withValue("firstName", "James").withValue("lastName", "Garfield").withValue("noOfShoes", 503),
|
||||
new QRecord().withValue("id", 5).withValue("firstName", "Abraham").withValue("lastName", "Lincoln").withValue("noOfShoes", 999),
|
||||
new QRecord().withValue("id", 6).withValue("firstName", "Bill").withValue("lastName", "Clinton")
|
||||
));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// get from the table which caches it - confirm they are (magically) found //
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPersons(getFilterForPerson("George", "Washington"), getFilterForPerson("John", "Adams"), getFilterForPerson("Thomas", "Jefferson")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(3, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// try to get records through the cache table, which meet the conditions that cause them to not be cached. //
|
||||
// so we should get results from the Query request - but - then let's go directly to the backend to confirm the records are not cached. //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
assertEquals(3, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
|
||||
queryInput.setFilter(getFilterForPersons(getFilterForPerson("James", "Garfield"), getFilterForPerson("Abraham", "Lincoln"), getFilterForPerson("Thomas", "Jefferson")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(3, queryOutput.getRecords().size());
|
||||
|
||||
assertEquals(3, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// request a row that doesn't exist in cache or source, should miss both //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPersons(getFilterForPerson("John", "McCain"), getFilterForPerson("John", "Kerry")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// fetch a record through the cache, so it gets cached. //
|
||||
// then update the source record so that it meets the condition that doesn't allow it to be cached. //
|
||||
// and delete another one. //
|
||||
// then expire the cached records. //
|
||||
// then re-fetch through cache - which should see the expiration, re-fetch from source, and delete from cache. //
|
||||
// assert record is no longer in cache. //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(getFilterForPersons(getFilterForPerson("George", "Washington"), getFilterForPerson("Bill", "Clinton")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(2, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
|
||||
assertEquals(1, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Clinton"))));
|
||||
assertEquals(1, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Washington"))));
|
||||
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 6).withValue("noOfShoes", 503)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(sourceTableName);
|
||||
deleteInput.setPrimaryKeys(List.of(1)); // delete Washington
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(Stream.of(1, 2, 3, 4).map(id -> new QRecord().withValue("id", id).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))).toList());
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(1, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(503, queryOutput.getRecords().stream().filter(r -> r.getValueString("lastName").equals("Clinton")).findFirst().get().getValue("noOfShoes"));
|
||||
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Clinton"))));
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter(new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, "Washington"))));
|
||||
}
|
||||
|
||||
/*
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// update one source record; delete another - query and should still find the previously cached //
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
UpdateInput updateInput = new UpdateInput();
|
||||
updateInput.setTableName(sourceTableName);
|
||||
updateInput.setRecords(List.of(new QRecord().withValue("id", 1).withValue("noOfSides", 6)));
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
DeleteInput deleteInput = new DeleteInput();
|
||||
deleteInput.setTableName(sourceTableName);
|
||||
deleteInput.setPrimaryKeys(List.of(2)); // delete Square
|
||||
new DeleteAction().execute(deleteInput);
|
||||
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.IN, "Triangle", "Square", "Pentagon", "ServerErrorGon")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(4, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(3, queryOutput.getRecords().get(0).getValue("noOfSides"));
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// then artificially move back the cachedDate in the cache table. //
|
||||
// then re-get from cache table, and we should see the updated value (and the deleted one) //
|
||||
/////////////////////////////////////////////////////////////////////////////////////////////
|
||||
updateInput = new UpdateInput();
|
||||
updateInput.setTableName(cacheTableName);
|
||||
updateInput.setRecords(Stream.of(1, 2, 3).map(id -> new QRecord().withValue("id", id).withValue("cachedDate", Instant.parse("2001-01-01T00:00:00Z"))).toList());
|
||||
new UpdateAction().execute(updateInput);
|
||||
|
||||
queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(3, queryOutput.getRecords().size());
|
||||
assertNotNull(queryOutput.getRecords().get(0).getValue("cachedDate"));
|
||||
assertEquals(6, queryOutput.getRecords().stream().filter(r -> r.getValueString("name").equals("Triangle")).findFirst().get().getValue("noOfSides"));
|
||||
|
||||
assertEquals(2, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
@Test
|
||||
void testUniqueKeyCacheNonCachingUseCases() throws QException
|
||||
{
|
||||
QInstance qInstance = QContext.getQInstance();
|
||||
|
||||
String sourceTableName = TestUtils.TABLE_NAME_SHAPE;
|
||||
String cacheTableName = TestUtils.TABLE_NAME_SHAPE_CACHE;
|
||||
|
||||
/////////////////////////////////////
|
||||
// insert rows in the source table //
|
||||
/////////////////////////////////////
|
||||
TestUtils.insertRecords(qInstance.getTable(sourceTableName), List.of(
|
||||
new QRecord().withValue("id", 1).withValue("name", "Triangle").withValue("noOfSides", 3),
|
||||
new QRecord().withValue("id", 2).withValue("name", "Square").withValue("noOfSides", 4),
|
||||
new QRecord().withValue("id", 3).withValue("name", "Pentagon").withValue("noOfSides", 5),
|
||||
new QRecord().withValue("id", 4).withValue("name", "ServerErrorGon").withValue("noOfSides", 503),
|
||||
new QRecord().withValue("id", 5).withValue("name", "ManyGon").withValue("noOfSides", 999)));
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// do queries on the cache table that we aren't allowed to do caching with - confirm that cache remains empty //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
///////////////
|
||||
// no filter //
|
||||
///////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
{
|
||||
//////////////////////////////
|
||||
// unique key not in filter //
|
||||
//////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("noOfSides", QCriteriaOperator.LESS_THAN_OR_EQUALS, 5)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
{
|
||||
////////////////////////////////////////////////
|
||||
// unsupported operator in filter on UK field //
|
||||
////////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.STARTS_WITH, "T")));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// an AND sub-filter //
|
||||
// (technically we could do this, since only 1 sub-filter, but we don't) //
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter().withSubFilters(List.of(
|
||||
new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle"))
|
||||
)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
{
|
||||
//////////////////////////////////////////////
|
||||
// an OR sub-filter, but unsupported fields //
|
||||
//////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR).withSubFilters(List.of(
|
||||
new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")),
|
||||
new QQueryFilter(new QFilterCriteria("id", QCriteriaOperator.EQUALS, 3))
|
||||
)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
{
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// an OR sub-filter, but with unsupported operator (IN - supported w/o subqueries, but not like this) //
|
||||
// (technically we could do this, since only 1 sub-filter, but we don't) //
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR).withSubFilters(List.of(
|
||||
new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.IN, "Triangle", "Square"))
|
||||
)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(0, queryOutput.getRecords().size());
|
||||
assertEquals(0, countCachedRecordsDirectlyInBackend(cacheTableName, new QQueryFilter()));
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// finally - queries that DO hit cache (so note, cache will stop being empty after here) //
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
{
|
||||
///////////////////////////////////////////////////////////
|
||||
// an OR sub-filter, with supported ops, and UKey fields //
|
||||
///////////////////////////////////////////////////////////
|
||||
QueryInput queryInput = new QueryInput();
|
||||
queryInput.setTableName(cacheTableName);
|
||||
queryInput.setFilter(new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR).withSubFilters(List.of(
|
||||
new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Triangle")),
|
||||
new QQueryFilter(new QFilterCriteria("name", QCriteriaOperator.EQUALS, "Square"))
|
||||
)));
|
||||
QueryOutput queryOutput = new QueryAction().execute(queryInput);
|
||||
assertEquals(2, queryOutput.getRecords().size());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static QQueryFilter getFilterForPerson(String firstName, String lastName)
|
||||
{
|
||||
return new QQueryFilter(new QFilterCriteria("firstName", QCriteriaOperator.EQUALS, firstName), new QFilterCriteria("lastName", QCriteriaOperator.EQUALS, lastName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private QQueryFilter getFilterForPersons(QQueryFilter... subFilters)
|
||||
{
|
||||
QQueryFilter filter = new QQueryFilter().withBooleanOperator(QQueryFilter.BooleanOperator.OR);
|
||||
for(QQueryFilter subFilter : subFilters)
|
||||
{
|
||||
filter.addSubFilter(subFilter);
|
||||
}
|
||||
return (filter);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
**
|
||||
*******************************************************************************/
|
||||
private static int countCachedRecordsDirectlyInBackend(String tableName, QQueryFilter filter) throws QException
|
||||
{
|
||||
List<QRecord> cachedRecords = MemoryRecordStore.getInstance().query(new QueryInput()
|
||||
.withTableName(tableName)
|
||||
.withFilter(filter));
|
||||
return cachedRecords.size();
|
||||
}
|
||||
|
||||
}
|
@ -129,6 +129,7 @@ public class TestUtils
|
||||
|
||||
public static final String TABLE_NAME_PERSON = "person";
|
||||
public static final String TABLE_NAME_SHAPE = "shape";
|
||||
public static final String TABLE_NAME_SHAPE_CACHE = "shapeCache";
|
||||
public static final String TABLE_NAME_ORDER = "order";
|
||||
public static final String TABLE_NAME_LINE_ITEM = "orderLine";
|
||||
public static final String TABLE_NAME_LINE_ITEM_EXTRINSIC = "orderLineExtrinsic";
|
||||
@ -185,6 +186,7 @@ public class TestUtils
|
||||
qInstance.addTable(definePersonMemoryCacheTable());
|
||||
qInstance.addTable(defineTableIdAndNameOnly());
|
||||
qInstance.addTable(defineTableShape());
|
||||
qInstance.addTable(defineShapeCacheTable());
|
||||
qInstance.addTable(defineTableBasepull());
|
||||
qInstance.addTable(defineTableOrder());
|
||||
qInstance.addTable(defineTableLineItem());
|
||||
@ -338,8 +340,7 @@ public class TestUtils
|
||||
private static QAutomationProviderMetaData definePollingAutomationProvider()
|
||||
{
|
||||
return (new PollingAutomationProviderMetaData()
|
||||
.withName(POLLING_AUTOMATION)
|
||||
);
|
||||
.withName(POLLING_AUTOMATION));
|
||||
}
|
||||
|
||||
|
||||
@ -847,8 +848,43 @@ public class TestUtils
|
||||
.withCacheSourceMisses(false)
|
||||
.withExcludeRecordsMatching(List.of(
|
||||
new QQueryFilter(
|
||||
new QFilterCriteria("firstName", QCriteriaOperator.CONTAINS, "503"),
|
||||
new QFilterCriteria("firstName", QCriteriaOperator.CONTAINS, "999")
|
||||
new QFilterCriteria("noOfShoes", QCriteriaOperator.EQUALS, "503"),
|
||||
new QFilterCriteria("noOfShoes", QCriteriaOperator.EQUALS, "999")
|
||||
).withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
)
|
||||
))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*******************************************************************************
|
||||
** Define another version of the 'shape' table, also in-memory, and as a
|
||||
** cache on the other in-memory one...
|
||||
*******************************************************************************/
|
||||
public static QTableMetaData defineShapeCacheTable()
|
||||
{
|
||||
UniqueKey uniqueKey = new UniqueKey("name");
|
||||
return (new QTableMetaData()
|
||||
.withName(TABLE_NAME_SHAPE_CACHE)
|
||||
.withBackendName(MEMORY_BACKEND_NAME)
|
||||
.withPrimaryKeyField("id")
|
||||
.withUniqueKey(uniqueKey)
|
||||
.withFields(TestUtils.defineTableShape().getFields()))
|
||||
.withField(new QFieldMetaData("cachedDate", QFieldType.DATE_TIME))
|
||||
.withCacheOf(new CacheOf()
|
||||
.withSourceTable(TABLE_NAME_SHAPE)
|
||||
.withCachedDateFieldName("cachedDate")
|
||||
.withExpirationSeconds(60)
|
||||
.withUseCase(new CacheUseCase()
|
||||
.withType(CacheUseCase.Type.UNIQUE_KEY_TO_UNIQUE_KEY)
|
||||
.withSourceUniqueKey(uniqueKey)
|
||||
.withCacheUniqueKey(uniqueKey)
|
||||
.withCacheSourceMisses(false)
|
||||
.withExcludeRecordsMatching(List.of(
|
||||
new QQueryFilter(
|
||||
new QFilterCriteria("noOfSides", QCriteriaOperator.EQUALS, 503),
|
||||
new QFilterCriteria("noOfSides", QCriteriaOperator.EQUALS, 999)
|
||||
).withBooleanOperator(QQueryFilter.BooleanOperator.OR)
|
||||
)
|
||||
))
|
||||
|
Reference in New Issue
Block a user