CE-1405 / CE-1479 - add queryInput.fieldNamesToInclude

This commit is contained in:
2024-08-15 08:53:19 -05:00
parent 51eb7d89be
commit 9a65ea81b2
10 changed files with 466 additions and 63 deletions

View File

@ -41,6 +41,7 @@ import com.kingsrook.qqq.backend.core.model.actions.tables.query.JoinsContext;
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.expressions.AbstractFilterExpression;
import com.kingsrook.qqq.backend.core.model.data.QRecord;
import com.kingsrook.qqq.backend.core.model.metadata.QInstance;
@ -176,18 +177,28 @@ public class AbstractMongoDBAction
/*******************************************************************************
** Convert a mongodb document to a QRecord.
*******************************************************************************/
protected QRecord documentToRecord(QTableMetaData table, Document document)
protected QRecord documentToRecord(QueryInput queryInput, Document document)
{
QRecord record = new QRecord();
QTableMetaData table = queryInput.getTable();
QRecord record = new QRecord();
record.setTableName(table.getName());
/////////////////////////////////////////////
// build the set of field names to include //
/////////////////////////////////////////////
Set<String> fieldNamesToInclude = queryInput.getFieldNamesToInclude();
List<QFieldMetaData> selectedFields = table.getFields().values()
.stream().filter(field -> fieldNamesToInclude == null || fieldNamesToInclude.contains(field.getName()))
.toList();
//////////////////////////////////////////////////////////////////////////////////////////////
// first iterate over the table's fields, looking for them (at their backend name (path, //
// if it has dots) inside the document note that we'll remove values from the document //
// as we go - then after this loop, will handle all remaining values as unstructured fields //
//////////////////////////////////////////////////////////////////////////////////////////////
Map<String, Serializable> values = record.getValues();
for(QFieldMetaData field : table.getFields().values())
for(QFieldMetaData field : selectedFields)
{
String fieldName = field.getName();
String fieldBackendName = getFieldBackendName(field);

View File

@ -41,6 +41,7 @@ import com.kingsrook.qqq.backend.module.mongodb.model.metadata.MongoDBBackendMet
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.Projections;
import org.bson.Document;
import org.bson.conversions.Bson;
@ -96,6 +97,15 @@ public class MongoDBQueryAction extends AbstractMongoDBAction implements QueryIn
////////////////////////////////////////////////////////////
FindIterable<Document> cursor = collection.find(mongoClientContainer.getMongoSession(), searchQuery);
///////////////////////////////////////////////////////////////////////////////////////////////
// if input specifies a set of field names to include, then add a 'projection' to the cursor //
///////////////////////////////////////////////////////////////////////////////////////////////
if(queryInput.getFieldNamesToInclude() != null)
{
List<String> backendFieldNames = queryInput.getFieldNamesToInclude().stream().map(f -> getFieldBackendName(table.getField(f))).toList();
cursor.projection(Projections.include(backendFieldNames));
}
///////////////////////////////////
// add a sort operator if needed //
///////////////////////////////////
@ -138,7 +148,7 @@ public class MongoDBQueryAction extends AbstractMongoDBAction implements QueryIn
actionTimeoutHelper.cancel();
setQueryStatFirstResultTime();
QRecord record = documentToRecord(table, document);
QRecord record = documentToRecord(queryInput, document);
queryOutput.addRecord(record);
if(queryInput.getAsyncJobCallback().wasCancelRequested())

View File

@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import com.kingsrook.qqq.backend.core.actions.tables.InsertAction;
import com.kingsrook.qqq.backend.core.actions.tables.QueryAction;
import com.kingsrook.qqq.backend.core.context.QContext;
@ -54,6 +55,8 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
/*******************************************************************************
@ -994,4 +997,46 @@ class MongoDBQueryActionTest extends BaseTest
.allMatch(r -> r.getValueInteger("storeKey").equals(1));
}
/*******************************************************************************
**
*******************************************************************************/
@Test
void testFieldNamesToInclude() throws QException
{
QQueryFilter filter = new QQueryFilter().withCriteria("firstName", QCriteriaOperator.EQUALS, "Darin");
QueryInput queryInput = new QueryInput(TestUtils.TABLE_NAME_PERSON).withFilter(filter);
QRecord record = new QueryAction().execute(queryInput.withFieldNamesToInclude(null)).getRecords().get(0);
assertTrue(record.getValues().containsKey("id"));
assertTrue(record.getValues().containsKey("firstName"));
assertTrue(record.getValues().containsKey("createDate"));
//////////////////////////////////////////////////////////////////////////////
// note, we get an extra "metaData" field (??), which, i guess is expected? //
//////////////////////////////////////////////////////////////////////////////
assertEquals(QContext.getQInstance().getTable(TestUtils.TABLE_NAME_PERSON).getFields().size() + 1, record.getValues().size());
record = new QueryAction().execute(queryInput.withFieldNamesToInclude(Set.of("id", "firstName"))).getRecords().get(0);
assertTrue(record.getValues().containsKey("id"));
assertTrue(record.getValues().containsKey("firstName"));
assertFalse(record.getValues().containsKey("createDate"));
assertEquals(2, record.getValues().size());
//////////////////////////////////////////////////////////////////////////////////////////////
// here, we'd have put _id (which mongo always returns) as "id", since caller requested it. //
//////////////////////////////////////////////////////////////////////////////////////////////
assertFalse(record.getValues().containsKey("_id"));
record = new QueryAction().execute(queryInput.withFieldNamesToInclude(Set.of("homeTown"))).getRecords().get(0);
assertFalse(record.getValues().containsKey("id"));
assertFalse(record.getValues().containsKey("firstName"));
assertFalse(record.getValues().containsKey("createDate"));
assertTrue(record.getValues().containsKey("homeTown"));
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// here, mongo always gives back _id (but, we won't have re-mapped it to "id", since caller didn't request it), so, do expect 2 fields here //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
assertEquals(2, record.getValues().size());
assertTrue(record.getValues().containsKey("_id"));
}
}