use of org.folio.rest.persist.interfaces.Results in project raml-module-builder by folio-org.
the class PostgresClient method processResult.
private Results processResult(io.vertx.ext.sql.ResultSet rs, Class<?> clazz, boolean count, boolean setId) {
long start = System.nanoTime();
Object[] ret = new Object[2];
List<Object> list = new ArrayList<>();
List<JsonObject> tempList = rs.getRows();
List<String> columnNames = rs.getColumnNames();
int columnNamesCount = columnNames.size();
Map<String, org.folio.rest.jaxrs.model.Facet> rInfo = new HashMap<>();
// this is incorrect in facet queries which add a row per facet value
int rowCount = rs.getNumRows();
boolean countSet = false;
if (rowCount > 0 && count) {
// if facet query, this wont set the count as it doesnt have a count column at this location,
Object firstColFirstVal = rs.getResults().get(0).getValue(0);
if (null != firstColFirstVal && "Integer".equals(firstColFirstVal.getClass().getSimpleName())) {
// regular query with count requested since count is the first column for each record
rowCount = rs.getResults().get(0).getInteger(0);
}
}
/* an exception to having the jsonb column get mapped to the corresponding clazz is a case where the
* clazz has an jsonb field, for example an audit class which contains a field called
* jsonb - meaning it encapsulates the real object for example for auditing purposes
* (contains the jsonb object as well as some other fields). In such a
* case, do not map the clazz to the content of the jsonb - but rather set the jsonb named field of the clazz
* with the jsonb column value */
boolean isAuditFlavored = false;
try {
clazz.getField(DEFAULT_JSONB_FIELD_NAME);
isAuditFlavored = true;
} catch (NoSuchFieldException nse) {
if (log.isDebugEnabled()) {
log.debug("non audit table, no " + DEFAULT_JSONB_FIELD_NAME + " found in json");
}
}
int facetEntriesInResultSet = 0;
for (int i = 0; i < tempList.size(); i++) {
try {
Object jo = tempList.get(i).getValue(DEFAULT_JSONB_FIELD_NAME);
Object id = tempList.get(i).getValue(idField);
Object o = null;
if (!isAuditFlavored && jo != null) {
try {
// is this a facet entry - if so process it, otherwise will throw an exception
// and continue trying to map to the pojos
o = mapper.readValue(jo.toString(), org.folio.rest.jaxrs.model.Facet.class);
org.folio.rest.jaxrs.model.Facet facet = rInfo.get(((org.folio.rest.jaxrs.model.Facet) o).getType());
if (facet == null) {
rInfo.put(((org.folio.rest.jaxrs.model.Facet) o).getType(), (org.folio.rest.jaxrs.model.Facet) o);
} else {
facet.getFacetValues().add(((org.folio.rest.jaxrs.model.Facet) o).getFacetValues().get(0));
}
facetEntriesInResultSet = facetEntriesInResultSet + 1;
continue;
} catch (Exception e) {
try {
o = mapper.readValue(jo.toString(), clazz);
} catch (UnrecognizedPropertyException e1) {
// this is a facet query , and this is the count entry {"count": 11}
rowCount = new JsonObject(tempList.get(i).getString("jsonb")).getInteger("count");
continue;
}
}
} else {
o = clazz.newInstance();
}
/* attempt to populate jsonb object with values from external columns - for example:
* if there is an update_date column in the record - try to populate a field updateDate in the
* jsonb object - this allows to use the DB for things like triggers to populate the update_date
* automatically, but still push them into the jsonb object - the json schema must declare this field
* as well - also support the audit mode descrbed above.
* NOTE that the query must request any field it wants to get populated into the jsonb obj*/
for (int j = 0; j < columnNamesCount; j++) {
/* if(columnNames.get(j).equals("count") && !countSet){
//check if this is reachable
rowCount = tempList.get(i).getLong(columnNames.get(j)).intValue();
}*/
if ((isAuditFlavored || !columnNames.get(j).equals(DEFAULT_JSONB_FIELD_NAME)) && !columnNames.get(j).equals(idField)) {
try {
Method[] m = o.getClass().getMethods();
for (int k = 0; k < m.length; k++) {
if (m[k].getName().equals(columnNametoCamelCaseWithset(columnNames.get(j)))) {
o.getClass().getMethod(columnNametoCamelCaseWithset(columnNames.get(j)), m[k].getParameterTypes()).invoke(o, new Object[] { tempList.get(i).getValue(columnNames.get(j)) });
}
}
} catch (Exception e) {
log.warn("Unable to populate field " + columnNametoCamelCaseWithset(columnNames.get(j)) + " for object of type " + clazz.getName());
}
}
}
if (setId) {
o.getClass().getMethod(columnNametoCamelCaseWithset(idField), new Class[] { String.class }).invoke(o, new String[] { id.toString() });
}
list.add(o);
} catch (Exception e) {
log.error(e.getMessage(), e);
list.add(null);
}
}
ResultInfo rn = new ResultInfo();
rInfo.forEach((k, v) -> {
rn.getFacets().add(v);
});
rn.setTotalRecords(rowCount);
Results r = new Results();
r.setResults(list);
r.setResultInfo(rn);
long end = System.nanoTime();
StatsTracker.addStatElement(STATS_KEY + ".processResult", (end - start));
if (log.isDebugEnabled()) {
log.debug("timer: process results (ns) " + (end - start));
}
return r;
}
use of org.folio.rest.persist.interfaces.Results in project raml-module-builder by folio-org.
the class PostgresClientIT method processQueryWithCountBelowOffset.
// offset >= estimated total https://issues.folio.org/browse/RMB-684
@Test
public void processQueryWithCountBelowOffset(TestContext context) {
postgresClient = createNumbers(context, 1, 2, 3, 4, 5);
postgresClient.startTx(context.asyncAssertSuccess(conn -> {
QueryHelper queryHelper = new QueryHelper("numbers");
queryHelper.selectQuery = "SELECT i FROM numbers ORDER BY i OFFSET 2";
queryHelper.offset = 2;
// estimation=1 is below offset=2
queryHelper.countQuery = "SELECT 1";
Function<TotaledResults, Results<Integer>> resultSetMapper = totaledResults -> {
context.verify(verify -> {
assertThat(totaledResults.estimatedTotal, is(1));
assertThat(totaledResults.set.size(), is(3));
});
return null;
};
postgresClient.processQueryWithCount(conn.conn, queryHelper, "statMethod", resultSetMapper).onComplete(context.asyncAssertSuccess());
}));
}
use of org.folio.rest.persist.interfaces.Results in project raml-module-builder by folio-org.
the class Conn method get.
/**
* Return records selected by {@link CQLWrapper} filter.
*
* @param table - table to query
* @param clazz - class of objects to be returned
* @param fieldName - database column to return, for example @link {@link PostgresClient#DEFAULT_JSONB_FIELD_NAME}
* @param wrapper - filter to select records
* @param returnCount - whether to return totalRecords, the number of matching records when disabling OFFSET and LIMIT
* @param returnIdField - if the id field should also be returned, must be true for facets
* @param facets - fields to calculate counts for
* @param distinctOn - database column to calculate the number of distinct values for, null or empty string for none
*/
public <T> Future<Results<T>> get(String table, Class<T> clazz, String fieldName, CQLWrapper wrapper, boolean returnCount, boolean returnIdField, List<FacetField> facets, String distinctOn) {
try {
QueryHelper queryHelper = postgresClient.buildQueryHelper(table, fieldName, wrapper, returnIdField, facets, distinctOn);
Function<TotaledResults, Results<T>> resultSetMapper = totaledResults -> postgresClient.processResults(totaledResults.set, totaledResults.estimatedTotal, queryHelper.offset, queryHelper.limit, clazz);
if (returnCount) {
return postgresClient.processQueryWithCount(pgConnection, queryHelper, "get", resultSetMapper);
} else {
return Future.future(promise -> postgresClient.processQuery(pgConnection, queryHelper, null, "get", resultSetMapper, promise));
}
} catch (Exception e) {
log.error(e.getMessage(), e);
return Future.failedFuture(e);
}
}
use of org.folio.rest.persist.interfaces.Results in project raml-module-builder by folio-org.
the class PostgresClientIT method assertCQLWrapper.
private void assertCQLWrapper(TestContext context, Function<CQLWrapper, Future<Results<StringPojo>>> function) {
try {
JsonArray ids = new JsonArray().add(randomUuid()).add(randomUuid());
insertXAndSingleQuotePojo(context, ids);
CQLWrapper cqlWrapper = new CQLWrapper(new CQL2PgJSON("jsonb"), "key = x");
function.apply(cqlWrapper).onComplete(context.asyncAssertSuccess(res -> {
assertThat(res.getResults().size(), is(1));
assertThat(res.getResults().get(0).getId(), is(ids.getString(0)));
}));
} catch (FieldException e) {
context.fail(e);
}
}
use of org.folio.rest.persist.interfaces.Results in project raml-module-builder by folio-org.
the class PostgresClient method processResults.
/**
* converts a result set into pojos - handles 3 types of queries:
* 1. a regular query will return N rows, where each row contains Y columns. one of those columns is the jsonb
* column which is mapped into a pojo. each row will also contain the count column (if count was requested for
* the query), other fields , like updated date may also be returned if they were requested in the select.
* 1a. note that there is an attempt to map external (non jsonb) columns to fields in the pojo. for example,
* a column called update_date will attempt to map its value to a field called updateDate in the pojo. however,
* for this to happen, the query must select the update_date -> select id,jsonb,update_date from ....
* 2. a facet query returns 2 columns, a uuid and a jsonb column. the results of the query are returned as
* id and json rows. facets are returned as jsonb values:
* {"facetValues": [{"count": 542,"value": "11 ed."}], "type": "name"}
* (along with a static '00000000-0000-0000-0000-000000000000' uuid)
* the count for a facet query is returned in the following manner:
* {"count": 501312} , with a static uuid as the facets
* 3. audit queries - queries that query an audit table, meaning the clazz parameter passed in has a jsonb member.
*
* @param rs
* @param total
* @param clazz
* @return
*/
<T> Results<T> processResults(RowSet<Row> rs, Integer total, int offset, int limit, Class<T> clazz) {
long start = System.nanoTime();
ResultsHelper<T> resultsHelper = new ResultsHelper<>(rs, total, clazz);
deserializeResults(resultsHelper);
ResultInfo resultInfo = new ResultInfo();
resultsHelper.facets.forEach((k, v) -> resultInfo.getFacets().add(v));
Integer totalRecords = getTotalRecords(resultsHelper.list.size(), resultsHelper.total, offset, limit);
resultInfo.setTotalRecords(totalRecords);
Results<T> results = new Results<>();
results.setResults(resultsHelper.list);
results.setResultInfo(resultInfo);
statsTracker(PROCESS_RESULTS_STAT_METHOD, clazz.getSimpleName(), start);
return results;
}
Aggregations