Search in sources :

Example 11 with CQL2PgJSON

use of org.folio.cql2pgjson.CQL2PgJSON in project raml-module-builder by folio-org.

the class PgUtil method get.

/**
 * Get records by CQL.
 * @param table  the table that contains the records
 * @param clazz  the class of the record type T
 * @param collectionClazz  the class of the collection type C containing records of type T
 * @param cql  the CQL query for filtering and sorting the records
 * @param hasTotalRecords how to calculate totalRecords
 * @param offset number of records to skip, use 0 or negative number for not skipping
 * @param limit maximum number of records to return, use a negative number for no limit
 * @param okapiHeaders  http headers provided by okapi
 * @param vertxContext  the current context
 * @param responseDelegateClass  the ResponseDelegate class generated as defined by the RAML file,
 *    must have these methods: respond200(C), respond400WithTextPlain(Object), respond500WithTextPlain(Object).
 * @return future  where to return the result created by the responseDelegateClass
 */
// Method has >7 parameters
@SuppressWarnings({ "squid:S107" })
public static <T, C> Future<Response> get(String table, Class<T> clazz, Class<C> collectionClazz, String cql, String hasTotalRecords, int offset, int limit, Map<String, String> okapiHeaders, Context vertxContext, Class<? extends ResponseDelegate> responseDelegateClass) {
    try {
        CQL2PgJSON cql2pgJson = new CQL2PgJSON(table + "." + JSON_COLUMN);
        CQLWrapper cqlWrapper = new CQLWrapper(cql2pgJson, cql, limit, offset, hasTotalRecords);
        PreparedCQL preparedCql = new PreparedCQL(table, cqlWrapper, okapiHeaders);
        return get(preparedCql, clazz, collectionClazz, okapiHeaders, vertxContext, responseDelegateClass);
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        final Method respond500;
        try {
            respond500 = responseDelegateClass.getMethod(RESPOND_500_WITH_TEXT_PLAIN, Object.class);
        } catch (Exception e2) {
            logger.error(e2.getMessage(), e2);
            return response(e2.getMessage(), null, null);
        }
        // invalid CQL is handled by get(...), here we get Exception about invalid table
        return response(e.getMessage(), respond500, respond500);
    }
}
Also used : CQL2PgJSON(org.folio.cql2pgjson.CQL2PgJSON) Method(java.lang.reflect.Method) CQLWrapper(org.folio.rest.persist.cql.CQLWrapper) FieldException(org.folio.cql2pgjson.exception.FieldException) InvocationTargetException(java.lang.reflect.InvocationTargetException) QueryValidationException(org.folio.cql2pgjson.exception.QueryValidationException) CQLParseException(org.z3950.zing.cql.CQLParseException) CQLFeatureUnsupportedException(org.folio.cql2pgjson.exception.CQLFeatureUnsupportedException) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException) IOException(java.io.IOException)

Example 12 with CQL2PgJSON

use of org.folio.cql2pgjson.CQL2PgJSON in project raml-module-builder by folio-org.

the class PgUtil method streamGet.

/**
 * Streaming GET with query. This produces a HTTP with JSON content with
 * properties {@code totalRecords}, {@code resultInfo} and custom element.
 * The custom element is array type which POJO that is of type clazz.
 * The JSON schema looks as follows:
 *
 * <pre>{@code
 * "properties": {
 *   "element": {
 *     "description": "the custom element array wrapper",
 *     "type": "array",
 *     "items": {
 *       "description": "The clazz",
 *       "type": "object",
 *       "$ref": "clazz.schema"
 *     }
 *   },
 *   "totalRecords": {
 *     "type": "integer"
 *   },
 *   "resultInfo": {
 *     "$ref": "raml-util/schemas/resultInfo.schema",
 *     "readonly": true
 *   }
 * },
 * "required": [
 *   "instances"
 * ]
 *</pre>
 * @param <T> Class for each item returned
 * @param table SQL table
 * @param clazz The item class
 * @param cql CQL query
 * @param hasTotalRecords "auto" for estimating totalRecords, "none" to suppress totalRecords estimation
 * @param offset offset >= 0; < 0 for no offset
 * @param limit  limit >= 0 ; <0 for no limit
 * @param facets facets (null or empty for no facets)
 * @param element wrapper JSON element for list of items (eg books / users)
 * @param routingContext routing context from which a HTTP response is made
 * @param okapiHeaders
 * @param vertxContext
 */
// Method has >7 parameters
@SuppressWarnings("squid:S107")
public static <T> void streamGet(String table, Class<T> clazz, String cql, String hasTotalRecords, int offset, int limit, List<String> facets, String element, int queryTimeout, RoutingContext routingContext, Map<String, String> okapiHeaders, Context vertxContext) {
    HttpServerResponse response = routingContext.response();
    try {
        List<FacetField> facetList = FacetManager.convertFacetStrings2FacetFields(facets, JSON_COLUMN);
        CQLWrapper wrapper = new CQLWrapper(new CQL2PgJSON(table + "." + JSON_COLUMN), cql, limit, offset, hasTotalRecords);
        streamGet(table, clazz, wrapper, facetList, element, queryTimeout, routingContext, okapiHeaders, vertxContext);
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        response.setStatusCode(500);
        response.putHeader(HttpHeaders.CONTENT_TYPE, "text/plain");
        response.end(e.toString());
    }
}
Also used : CQL2PgJSON(org.folio.cql2pgjson.CQL2PgJSON) HttpServerResponse(io.vertx.core.http.HttpServerResponse) FacetField(org.folio.rest.persist.facets.FacetField) CQLWrapper(org.folio.rest.persist.cql.CQLWrapper) FieldException(org.folio.cql2pgjson.exception.FieldException) InvocationTargetException(java.lang.reflect.InvocationTargetException) QueryValidationException(org.folio.cql2pgjson.exception.QueryValidationException) CQLParseException(org.z3950.zing.cql.CQLParseException) CQLFeatureUnsupportedException(org.folio.cql2pgjson.exception.CQLFeatureUnsupportedException) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException) IOException(java.io.IOException)

Example 13 with CQL2PgJSON

use of org.folio.cql2pgjson.CQL2PgJSON in project raml-module-builder by folio-org.

the class PgUtil method getWithOptimizedSql.

/**
 * Run the cql query using optimized SQL (if possible) or standard SQL.
 * <p>
 * PostgreSQL has no statistics about a field within a JSONB resulting in bad performance.
 * <p>
 * This method requires that the sortField has a b-tree index (non-unique) and caseSensitive=false
 * and removeAccents=true, and that the cql query is supported by a full text index.
 * <p>
 * This method starts a full table scan until getOptimizedSqlSize() records have been scanned.
 * Then it assumes that there are only a few result records and uses the full text match.
 * If the requested number of records have been found it stops immediately.
 *  @param table
 * @param clazz
 * @param cql
 * @param queryTimeout query timeout in milliseconds, or 0 for no timeout
 * @param okapiHeaders
 * @param vertxContext
 * @param responseDelegateClass
 * @return
 */
public static <T, C> Future<Response> getWithOptimizedSql(String table, Class<T> clazz, Class<C> collectionClazz, String sortField, String cql, int offset, int limit, int queryTimeout, Map<String, String> okapiHeaders, Context vertxContext, Class<? extends ResponseDelegate> responseDelegateClass) {
    final Method respond500;
    try {
        respond500 = responseDelegateClass.getMethod(RESPOND_500_WITH_TEXT_PLAIN, Object.class);
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return response(e.getMessage(), null, null);
    }
    final Method respond200;
    final Method respond400;
    try {
        respond200 = responseDelegateClass.getMethod(RESPOND_200_WITH_APPLICATION_JSON, collectionClazz);
        respond400 = responseDelegateClass.getMethod(RESPOND_400_WITH_TEXT_PLAIN, Object.class);
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return response(e.getMessage(), respond500, respond500);
    }
    try {
        CQL2PgJSON cql2pgJson = new CQL2PgJSON(table + "." + JSON_COLUMN);
        CQLWrapper cqlWrapper = new CQLWrapper(cql2pgJson, cql, limit, offset);
        PreparedCQL preparedCql = new PreparedCQL(table, cqlWrapper, okapiHeaders);
        String sql = generateOptimizedSql(sortField, preparedCql, offset, limit);
        if (sql == null) {
            // the cql is not suitable for optimization, generate simple sql
            return get(preparedCql, clazz, collectionClazz, okapiHeaders, vertxContext, responseDelegateClass);
        }
        logger.info("Optimized SQL generated. Source CQL: " + cql);
        Promise<Response> promise = Promise.promise();
        PostgresClient postgresClient = postgresClient(vertxContext, okapiHeaders);
        postgresClient.select(sql, queryTimeout, reply -> {
            try {
                if (reply.failed()) {
                    Throwable cause = reply.cause();
                    logger.error("Optimized SQL failed: " + cause.getMessage() + ": " + sql, cause);
                    response(cause.getMessage(), respond500, respond500).onComplete(promise);
                    return;
                }
                C collection = collection(clazz, collectionClazz, reply.result(), offset, limit);
                response(collection, respond200, respond500).onComplete(promise);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                response(e.getMessage(), respond500, respond500).onComplete(promise);
            }
        });
        return promise.future();
    } catch (FieldException | QueryValidationException e) {
        logger.error(e.getMessage(), e);
        return response(e.getMessage(), respond400, respond500);
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return response(e.getMessage(), respond500, respond500);
    }
}
Also used : Method(java.lang.reflect.Method) FieldException(org.folio.cql2pgjson.exception.FieldException) InvocationTargetException(java.lang.reflect.InvocationTargetException) QueryValidationException(org.folio.cql2pgjson.exception.QueryValidationException) CQLParseException(org.z3950.zing.cql.CQLParseException) CQLFeatureUnsupportedException(org.folio.cql2pgjson.exception.CQLFeatureUnsupportedException) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException) IOException(java.io.IOException) Response(javax.ws.rs.core.Response) HttpServerResponse(io.vertx.core.http.HttpServerResponse) CQL2PgJSON(org.folio.cql2pgjson.CQL2PgJSON) FieldException(org.folio.cql2pgjson.exception.FieldException) QueryValidationException(org.folio.cql2pgjson.exception.QueryValidationException) CQLWrapper(org.folio.rest.persist.cql.CQLWrapper)

Example 14 with CQL2PgJSON

use of org.folio.cql2pgjson.CQL2PgJSON in project raml-module-builder by folio-org.

the class PgUtil method delete.

/**
 * Delete records by CQL.
 * @param table  the table that contains the records
 * @param cql  the CQL query for filtering the records
 * @param okapiHeaders  http headers provided by okapi
 * @param vertxContext  the current context
 * @param responseDelegateClass  the ResponseDelegate class generated as defined by the RAML file,
 *    must have these methods:  respond204(), respond400WithTextPlain(Object), respond500WithTextPlain(Object).
 * @return future where to return the result created by the responseDelegateClass
 */
// Method has >7 parameters
@SuppressWarnings({ "unchecked", "squid:S107" })
public static Future<Response> delete(String table, String cql, Map<String, String> okapiHeaders, Context vertxContext, Class<? extends ResponseDelegate> responseDelegateClass) {
    final Method respond500;
    try {
        respond500 = responseDelegateClass.getMethod(RESPOND_500_WITH_TEXT_PLAIN, Object.class);
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return response(e.getMessage(), null, null);
    }
    final Method respond400;
    final Method respond204;
    try {
        respond400 = responseDelegateClass.getMethod(RESPOND_400_WITH_TEXT_PLAIN, Object.class);
        respond204 = responseDelegateClass.getMethod(RESPOND_204);
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return response(e.getMessage(), respond500, respond500);
    }
    try {
        CQL2PgJSON cql2pgJson = new CQL2PgJSON(table + "." + JSON_COLUMN);
        CQLWrapper cqlWrapper = new CQLWrapper(cql2pgJson, cql, -1, -1);
        PreparedCQL preparedCql = new PreparedCQL(table, cqlWrapper, okapiHeaders);
        Promise<Response> promise = Promise.promise();
        PostgresClient postgresClient = PgUtil.postgresClient(vertxContext, okapiHeaders);
        postgresClient.delete(preparedCql.getTableName(), preparedCql.getCqlWrapper(), reply -> {
            try {
                if (reply.failed()) {
                    String message = PgExceptionUtil.badRequestMessage(reply.cause());
                    if (message == null) {
                        message = reply.cause().getMessage();
                    }
                    logger.error(message, reply.cause());
                    response(message, respond400, respond500).onComplete(promise);
                    return;
                }
                response(respond204, respond500).onComplete(promise);
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
                response(e.getMessage(), respond500, respond500).onComplete(promise);
            }
        });
        return promise.future();
    } catch (Exception e) {
        logger.error(e.getMessage(), e);
        return response(e.getMessage(), respond400, respond500);
    }
}
Also used : Response(javax.ws.rs.core.Response) HttpServerResponse(io.vertx.core.http.HttpServerResponse) CQL2PgJSON(org.folio.cql2pgjson.CQL2PgJSON) Method(java.lang.reflect.Method) FieldException(org.folio.cql2pgjson.exception.FieldException) InvocationTargetException(java.lang.reflect.InvocationTargetException) QueryValidationException(org.folio.cql2pgjson.exception.QueryValidationException) CQLParseException(org.z3950.zing.cql.CQLParseException) CQLFeatureUnsupportedException(org.folio.cql2pgjson.exception.CQLFeatureUnsupportedException) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException) IOException(java.io.IOException) CQLWrapper(org.folio.rest.persist.cql.CQLWrapper)

Example 15 with CQL2PgJSON

use of org.folio.cql2pgjson.CQL2PgJSON in project raml-module-builder by folio-org.

the class PostgresClientIT method streamGetCursorPage.

private void streamGetCursorPage(TestContext context, int limit) throws Exception {
    Async async = context.async();
    CQLWrapper cql = new CQLWrapper(new CQL2PgJSON("jsonb"), "id=*", limit, /* offset */
    0);
    postgresClient.streamGet(MOCK_POLINES_TABLE, Object.class, "jsonb", cql, false, null, context.asyncAssertSuccess(r -> {
        AtomicInteger count = new AtomicInteger();
        r.handler(streamHandler -> {
            count.incrementAndGet();
        });
        r.endHandler(x -> {
            context.assertEquals(limit, count.get());
            async.complete();
        });
        r.exceptionHandler(e -> context.fail(e));
    }));
}
Also used : CoreMatchers.is(org.hamcrest.CoreMatchers.is) TestContext(io.vertx.ext.unit.TestContext) RowStream(io.vertx.sqlclient.RowStream) Arrays(java.util.Arrays) PgNotification(io.vertx.pgclient.PgNotification) TransactionRollbackException(io.vertx.sqlclient.TransactionRollbackException) VertxUtils(org.folio.rest.tools.utils.VertxUtils) Tuple(io.vertx.sqlclient.Tuple) UpdateSection(org.folio.rest.persist.Criteria.UpdateSection) CoreMatchers.instanceOf(org.hamcrest.CoreMatchers.instanceOf) RowIterator(io.vertx.sqlclient.RowIterator) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) SqlResult(io.vertx.sqlclient.SqlResult) After(org.junit.After) JsonObject(io.vertx.core.json.JsonObject) Offset(org.folio.rest.persist.Criteria.Offset) Collector(java.util.stream.Collector) Transaction(io.vertx.sqlclient.Transaction) AfterClass(org.junit.AfterClass) CoreMatchers.containsString(org.hamcrest.CoreMatchers.containsString) RowImpl(io.vertx.pgclient.impl.RowImpl) Set(java.util.Set) UUID(java.util.UUID) FieldException(org.folio.cql2pgjson.exception.FieldException) Future(io.vertx.core.Future) StandardCharsets(java.nio.charset.StandardCharsets) UncheckedIOException(java.io.UncheckedIOException) IOUtils(org.apache.commons.io.IOUtils) CQL2PgJSON(org.folio.cql2pgjson.CQL2PgJSON) Base64(java.util.Base64) List(java.util.List) Stream(java.util.stream.Stream) Criterion(org.folio.rest.persist.Criteria.Criterion) Results(org.folio.rest.persist.interfaces.Results) Facet(org.folio.rest.jaxrs.model.Facet) RowDesc(io.vertx.sqlclient.impl.RowDesc) Async(io.vertx.ext.unit.Async) CQLWrapper(org.folio.rest.persist.cql.CQLWrapper) BeforeClass(org.junit.BeforeClass) FacetField(org.folio.rest.persist.facets.FacetField) PostgresTesterContainer(org.folio.postgres.testing.PostgresTesterContainer) Criteria(org.folio.rest.persist.Criteria.Criteria) CoreMatchers.not(org.hamcrest.CoreMatchers.not) RunWith(org.junit.runner.RunWith) Timeout(io.vertx.ext.unit.junit.Timeout) ResultInfo(org.folio.rest.jaxrs.model.ResultInfo) Function(java.util.function.Function) TotaledResults(org.folio.rest.persist.PostgresClient.TotaledResults) ArrayList(java.util.ArrayList) PreparedStatement(io.vertx.sqlclient.PreparedStatement) HashSet(java.util.HashSet) CompositeFuture(io.vertx.core.CompositeFuture) Poline(org.folio.rest.persist.helpers.Poline) PrepareOptions(io.vertx.sqlclient.PrepareOptions) SqlConnection(io.vertx.sqlclient.SqlConnection) Limit(org.folio.rest.persist.Criteria.Limit) QueryHelper(org.folio.rest.persist.PostgresClient.QueryHelper) RowSet(io.vertx.sqlclient.RowSet) MatcherAssert.assertThat(org.hamcrest.MatcherAssert.assertThat) AsyncResult(io.vertx.core.AsyncResult) LinkedList(java.util.LinkedList) DatabaseMetadata(io.vertx.sqlclient.spi.DatabaseMetadata) CoreMatchers.nullValue(org.hamcrest.CoreMatchers.nullValue) Before(org.junit.Before) Files(java.nio.file.Files) Query(io.vertx.sqlclient.Query) Promise(io.vertx.core.Promise) ObjectMapper(com.fasterxml.jackson.databind.ObjectMapper) Vertx(io.vertx.core.Vertx) PgPool(io.vertx.pgclient.PgPool) JsonProcessingException(com.fasterxml.jackson.core.JsonProcessingException) IOException(java.io.IOException) Test(org.junit.Test) VertxUnitRunner(io.vertx.ext.unit.junit.VertxUnitRunner) PreparedQuery(io.vertx.sqlclient.PreparedQuery) JsonArray(io.vertx.core.json.JsonArray) PgConnection(io.vertx.pgclient.PgConnection) Rule(org.junit.Rule) Paths(java.nio.file.Paths) Row(io.vertx.sqlclient.Row) LocalRowSet(org.folio.rest.persist.helpers.LocalRowSet) Handler(io.vertx.core.Handler) SimplePojo(org.folio.rest.persist.helpers.SimplePojo) Collections(java.util.Collections) InputStream(java.io.InputStream) CQL2PgJSON(org.folio.cql2pgjson.CQL2PgJSON) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Async(io.vertx.ext.unit.Async) CQLWrapper(org.folio.rest.persist.cql.CQLWrapper)

Aggregations

CQL2PgJSON (org.folio.cql2pgjson.CQL2PgJSON)25 CQLWrapper (org.folio.rest.persist.cql.CQLWrapper)22 JsonProcessingException (com.fasterxml.jackson.core.JsonProcessingException)16 Test (org.junit.Test)16 AsyncResult (io.vertx.core.AsyncResult)14 Future (io.vertx.core.Future)14 Handler (io.vertx.core.Handler)14 Vertx (io.vertx.core.Vertx)14 JsonArray (io.vertx.core.json.JsonArray)14 JsonObject (io.vertx.core.json.JsonObject)14 Async (io.vertx.ext.unit.Async)14 PgConnection (io.vertx.pgclient.PgConnection)14 Collections (java.util.Collections)14 UUID (java.util.UUID)14 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)14 Function (java.util.function.Function)14 PostgresTesterContainer (org.folio.postgres.testing.PostgresTesterContainer)14 ObjectMapper (com.fasterxml.jackson.databind.ObjectMapper)12 CompositeFuture (io.vertx.core.CompositeFuture)12 Promise (io.vertx.core.Promise)12