Search in sources :

Example 1 with Operator

use of org.molgenis.data.QueryRule.Operator in project molgenis by molgenis.

the class PostgreSqlQueryGenerator method getSqlWhere.

static <E extends Entity> String getSqlWhere(EntityType entityType, Query<E> q, List<Object> parameters, AtomicInteger mrefFilterIndex) {
    StringBuilder result = new StringBuilder();
    for (QueryRule r : q.getRules()) {
        Attribute attr = null;
        if (r.getField() != null) {
            attr = entityType.getAttribute(r.getField());
            if (attr == null) {
                throw new MolgenisDataException(format("Unknown attribute [%s]", r.getField()));
            }
            if (isPersistedInOtherTable(attr)) {
                mrefFilterIndex.incrementAndGet();
            }
        }
        StringBuilder predicate = new StringBuilder();
        Operator operator = r.getOperator();
        switch(operator) {
            case AND:
                result.append(" AND ");
                break;
            case NESTED:
                QueryImpl<Entity> nestedQ = new QueryImpl<>(r.getNestedRules());
                result.append('(').append(getSqlWhere(entityType, nestedQ, parameters, mrefFilterIndex)).append(')');
                break;
            case OR:
                result.append(" OR ");
                break;
            case LIKE:
                requireNonNull(attr, format(UNSPECIFIED_ATTRIBUTE_MSG, LIKE));
                String columnName;
                if (isPersistedInOtherTable(attr)) {
                    columnName = getFilterColumnName(attr, mrefFilterIndex.get());
                } else {
                    columnName = "this." + getColumnName(attr);
                }
                if (isStringType(attr) || isTextType(attr)) {
                    result.append(' ').append(columnName);
                } else {
                    result.append(" CAST(").append(columnName).append(" as TEXT)");
                }
                result.append(" LIKE ?");
                parameters.add("%" + PostgreSqlUtils.getPostgreSqlQueryValue(r.getValue(), attr) + '%');
                break;
            case IN:
                {
                    requireNonNull(attr, format(UNSPECIFIED_ATTRIBUTE_MSG, IN));
                    Object inValue = r.getValue();
                    if (inValue == null) {
                        throw new MolgenisDataException("Missing value for IN query");
                    }
                    if (!(inValue instanceof Iterable<?>)) {
                        throw new MolgenisDataException(format("IN value is of type [%s] instead of [Iterable]", inValue.getClass().getSimpleName()));
                    }
                    StringBuilder in = new StringBuilder();
                    Attribute inAttr = attr;
                    Stream<Object> postgreSqlIds = stream(((Iterable<?>) inValue).spliterator(), false).map(idValue -> PostgreSqlUtils.getPostgreSqlQueryValue(idValue, inAttr));
                    for (Iterator<Object> it = postgreSqlIds.iterator(); it.hasNext(); ) {
                        Object postgreSqlId = it.next();
                        in.append('?');
                        if (it.hasNext()) {
                            in.append(',');
                        }
                        parameters.add(postgreSqlId);
                    }
                    if (isPersistedInOtherTable(attr)) {
                        result.append(getFilterColumnName(attr, mrefFilterIndex.get()));
                    } else {
                        result.append("this");
                    }
                    Attribute equalsAttr;
                    if (attr.isMappedBy()) {
                        equalsAttr = attr.getRefEntity().getIdAttribute();
                    } else {
                        equalsAttr = entityType.getAttribute(r.getField());
                    }
                    result.append('.').append(getColumnName(equalsAttr));
                    result.append(" IN (").append(in).append(')');
                    break;
                }
            case NOT:
                result.append(" NOT ");
                break;
            case RANGE:
                requireNonNull(attr, format(UNSPECIFIED_ATTRIBUTE_MSG, RANGE));
                Object range = r.getValue();
                if (range == null) {
                    throw new MolgenisDataException("Missing value for RANGE query");
                }
                if (!(range instanceof Iterable<?>)) {
                    throw new MolgenisDataException(format("RANGE value is of type [%s] instead of [Iterable]", range.getClass().getSimpleName()));
                }
                Iterator<?> rangeValues = ((Iterable<?>) range).iterator();
                // from
                parameters.add(rangeValues.next());
                // to
                parameters.add(rangeValues.next());
                StringBuilder column = new StringBuilder();
                if (isPersistedInOtherTable(attr)) {
                    column.append(getFilterColumnName(attr, mrefFilterIndex.get()));
                } else {
                    column.append("this");
                }
                column.append('.').append(getColumnName(entityType.getAttribute(r.getField())));
                predicate.append(column).append(" >= ? AND ").append(column).append(" <= ?");
                result.append(predicate);
                break;
            case EQUALS:
                if (attr == null) {
                    throw new MolgenisDataException("Missing attribute field in EQUALS query rule");
                }
                if (isPersistedInOtherTable(attr)) {
                    predicate.append(getFilterColumnName(attr, mrefFilterIndex.get()));
                } else {
                    predicate.append("this");
                }
                Attribute equalsAttr;
                if (attr.isMappedBy()) {
                    equalsAttr = attr.getRefEntity().getIdAttribute();
                } else {
                    equalsAttr = entityType.getAttribute(r.getField());
                }
                predicate.append('.').append(getColumnName(equalsAttr));
                if (r.getValue() == null) {
                    // expression = null is not valid, use IS NULL
                    predicate.append(" IS NULL ");
                } else {
                    Object postgreSqlVal = PostgreSqlUtils.getPostgreSqlQueryValue(r.getValue(), attr);
                    // It does however return those rows when queried with "... NOT abstract IS TRUE"
                    if (attr.getDataType() == BOOL) {
                        Boolean bool = (Boolean) postgreSqlVal;
                        // noinspection ConstantConditions (getPostgreSqlQueryValue() != null if r.getValue() != null)
                        if (bool)
                            predicate.append(" IS TRUE");
                        else
                            predicate.append(" IS FALSE");
                    } else {
                        predicate.append(" =");
                        predicate.append(" ? ");
                        parameters.add(postgreSqlVal);
                    }
                }
                if (result.length() > 0 && !result.toString().endsWith(" OR ") && !result.toString().endsWith(" AND ") && !result.toString().endsWith(" NOT ")) {
                    result.append(" AND ");
                }
                result.append(predicate);
                break;
            case GREATER:
            case GREATER_EQUAL:
            case LESS:
            case LESS_EQUAL:
                requireNonNull(attr, format(UNSPECIFIED_ATTRIBUTE_MSG, format("%s, %s, %s or %s", GREATER, GREATER_EQUAL, LESS, LESS_EQUAL)));
                if (isPersistedInOtherTable(attr)) {
                    predicate.append(getFilterColumnName(attr, mrefFilterIndex.get()));
                } else {
                    predicate.append("this");
                }
                predicate.append('.').append(getColumnName(entityType.getAttribute(r.getField())));
                switch(operator) {
                    case GREATER:
                        predicate.append(" >");
                        break;
                    case GREATER_EQUAL:
                        predicate.append(" >=");
                        break;
                    case LESS:
                        predicate.append(" <");
                        break;
                    case LESS_EQUAL:
                        predicate.append(" <=");
                        break;
                    // $CASES-OMITTED$
                    default:
                        throw new RuntimeException(format("Unexpected query operator [%s]", operator));
                }
                predicate.append(" ? ");
                parameters.add(PostgreSqlUtils.getPostgreSqlQueryValue(r.getValue(), attr));
                if (result.length() > 0 && !result.toString().endsWith(" OR ") && !result.toString().endsWith(" AND ") && !result.toString().endsWith(" NOT ")) {
                    result.append(" AND ");
                }
                result.append(predicate);
                break;
            case DIS_MAX:
            case FUZZY_MATCH:
            case FUZZY_MATCH_NGRAM:
            case SEARCH:
            case SHOULD:
                // PostgreSQL does not support semantic searching and sorting matching rows on relevance.
                throw new UnsupportedOperationException(format("Query operator [%s] not supported by PostgreSQL repository", operator.toString()));
            default:
                throw new UnexpectedEnumException(operator);
        }
    }
    return result.toString().trim();
}
Also used : Operator(org.molgenis.data.QueryRule.Operator) IntStream.range(java.util.stream.IntStream.range) Operator(org.molgenis.data.QueryRule.Operator) LoggerFactory(org.slf4j.LoggerFactory) QueryImpl(org.molgenis.data.support.QueryImpl) StringUtils(org.apache.commons.lang3.StringUtils) Attribute(org.molgenis.data.meta.model.Attribute) MessageFormat(java.text.MessageFormat) Lists(com.google.common.collect.Lists) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) AttributeUtils(org.molgenis.data.support.AttributeUtils) Objects.requireNonNull(java.util.Objects.requireNonNull) PostgreSqlNameGenerator(org.molgenis.data.postgresql.PostgreSqlNameGenerator) AttributeType(org.molgenis.data.meta.AttributeType) Logger(org.slf4j.Logger) PostgreSqlQueryUtils(org.molgenis.data.postgresql.PostgreSqlQueryUtils) org.molgenis.data(org.molgenis.data) Iterator(java.util.Iterator) Collection(java.util.Collection) EntityTypeUtils(org.molgenis.data.support.EntityTypeUtils) Instant(java.time.Instant) EntityType(org.molgenis.data.meta.model.EntityType) String.format(java.lang.String.format) Collectors.joining(java.util.stream.Collectors.joining) UnexpectedEnumException(org.molgenis.util.UnexpectedEnumException) INCLUDE_DEFAULT_CONSTRAINT(org.molgenis.data.postgresql.PostgreSqlQueryGenerator.ColumnMode.INCLUDE_DEFAULT_CONSTRAINT) List(java.util.List) Collectors.toList(java.util.stream.Collectors.toList) ChronoUnit(java.time.temporal.ChronoUnit) Stream(java.util.stream.Stream) StreamSupport.stream(java.util.stream.StreamSupport.stream) LocalDate(java.time.LocalDate) UTC(java.time.ZoneOffset.UTC) Attribute(org.molgenis.data.meta.model.Attribute) UnexpectedEnumException(org.molgenis.util.UnexpectedEnumException) QueryImpl(org.molgenis.data.support.QueryImpl) Iterator(java.util.Iterator) Stream(java.util.stream.Stream)

Example 2 with Operator

use of org.molgenis.data.QueryRule.Operator in project molgenis by molgenis.

the class QueryValidatorTest method validateValidProvider.

@DataProvider(name = "validateValidProvider")
public static Iterator<Object[]> validateValidProvider() {
    List<Object[]> queries = new ArrayList<>(256);
    EnumSet.of(EQUALS).forEach(operator -> {
        // BOOL
        Entity boolEntityType = createEntityType(BOOL);
        asList(Boolean.TRUE, Boolean.FALSE, null, "true", "false", "True", "False").forEach(value -> queries.add(new Object[] { boolEntityType, new QueryImpl<>().eq("attr", value) }));
        // CATEGORICAL, XREF, CATEGORICAL_MREF, MREF, ONE_TO_MANY
        EnumSet.of(STRING, INT, LONG, EMAIL, HYPERLINK).forEach(refIdAttrType -> EnumSet.of(CATEGORICAL, XREF, CATEGORICAL_MREF, MREF, ONE_TO_MANY).forEach(refAttrType -> {
            Entity refEntityType = createEntityType(refAttrType, refIdAttrType);
            asList("1", 1, 1L, null).forEach(idValue -> queries.add(new Object[] { refEntityType, new QueryImpl<>().eq("attr", idValue) }));
            Entity refEntity = when(mock(Entity.class).getIdValue()).thenReturn("1").getMock();
            queries.add(new Object[] { refEntityType, new QueryImpl<>().eq("attr", refEntity) });
        }));
        // DATE
        Entity dateEntityType = createEntityType(DATE);
        asList(LocalDate.now(), "2016-11-25", null).forEach(value -> queries.add(new Object[] { dateEntityType, new QueryImpl<>().eq("attr", value) }));
        // DATE_TIME
        Entity dateTimeEntityType = createEntityType(DATE_TIME);
        asList(Instant.now(), "1985-08-12T11:12:13+0500", null).forEach(value -> queries.add(new Object[] { dateTimeEntityType, new QueryImpl<>().eq("attr", value) }));
        // DECIMAL
        Entity decimalEntityType = createEntityType(DECIMAL);
        asList(1.23, "1.23", 1, 1L, null).forEach(value -> queries.add(new Object[] { decimalEntityType, new QueryImpl<>().eq("attr", value) }));
        // EMAIL, HTML, HYPERLINK, SCRIPT, STRING, TEXT
        EnumSet.of(EMAIL, HTML, HYPERLINK, SCRIPT, STRING, TEXT).forEach(attrType -> {
            Entity entityType = createEntityType(attrType);
            asList("abc", 1, 1L, 1.23, null).forEach(value -> queries.add(new Object[] { entityType, new QueryImpl<>().eq("attr", value) }));
        });
        // INT, LONG
        EnumSet.of(INT, LONG).forEach(attrType -> {
            Entity entityType = createEntityType(attrType);
            asList(1, 1L, "1", null).forEach(value -> queries.add(new Object[] { entityType, new QueryImpl<>().eq("attr", value) }));
        });
        // FILE
        Entity fileEntityType = createEntityType(FILE, STRING);
        asList("file0", mock(FileMeta.class), null).forEach(idValue -> queries.add(new Object[] { fileEntityType, new QueryImpl<>().eq("attr", idValue) }));
        // ENUM
        Entity enumEntityType = createEntityType(ENUM);
        asList(TestEnum.ENUM0, TestEnum.ENUM1, "ENUM0", "ENUM1", null).forEach(value -> queries.add(new Object[] { enumEntityType, new QueryImpl<>().eq("attr", value) }));
    });
    EnumSet.of(GREATER, GREATER_EQUAL, LESS, LESS_EQUAL).forEach(operator -> {
        Entity entityType = createEntityType(INT);
        QueryImpl<Entity> query = new QueryImpl<>();
        query.addRule(new QueryRule("attr", operator, 1));
        queries.add(new Object[] { entityType, query });
    });
    EnumSet.of(FUZZY_MATCH, FUZZY_MATCH_NGRAM, LIKE).forEach(operator -> {
        Entity entityType = createEntityType(STRING);
        QueryImpl<Entity> query = new QueryImpl<>();
        query.addRule(new QueryRule("attr", operator, "abc"));
        queries.add(new Object[] { entityType, query });
    });
    EnumSet.of(IN, RANGE).forEach(operator -> {
        Entity entityType = createEntityType(INT);
        QueryImpl<Entity> query = new QueryImpl<>();
        query.addRule(new QueryRule("attr", operator, asList(1, 2)));
        queries.add(new Object[] { entityType, query });
    });
    return queries.iterator();
}
Also used : AttributeType(org.molgenis.data.meta.AttributeType) DataProvider(org.testng.annotations.DataProvider) Iterator(java.util.Iterator) BeforeMethod(org.testng.annotations.BeforeMethod) Operator(org.molgenis.data.QueryRule.Operator) Test(org.testng.annotations.Test) QueryImpl(org.molgenis.data.support.QueryImpl) Mockito.when(org.mockito.Mockito.when) Instant(java.time.Instant) Attribute(org.molgenis.data.meta.model.Attribute) EntityType(org.molgenis.data.meta.model.EntityType) ArrayList(java.util.ArrayList) FileMeta(org.molgenis.data.file.model.FileMeta) List(java.util.List) Arrays.asList(java.util.Arrays.asList) LocalDate(java.time.LocalDate) Query(org.molgenis.data.Query) EntityManager(org.molgenis.data.EntityManager) QueryRule(org.molgenis.data.QueryRule) EnumSet(java.util.EnumSet) Entity(org.molgenis.data.Entity) Mockito.mock(org.mockito.Mockito.mock) Entity(org.molgenis.data.Entity) QueryImpl(org.molgenis.data.support.QueryImpl) ArrayList(java.util.ArrayList) QueryRule(org.molgenis.data.QueryRule) DataProvider(org.testng.annotations.DataProvider)

Aggregations

Instant (java.time.Instant)2 LocalDate (java.time.LocalDate)2 Iterator (java.util.Iterator)2 List (java.util.List)2 Operator (org.molgenis.data.QueryRule.Operator)2 AttributeType (org.molgenis.data.meta.AttributeType)2 Attribute (org.molgenis.data.meta.model.Attribute)2 EntityType (org.molgenis.data.meta.model.EntityType)2 QueryImpl (org.molgenis.data.support.QueryImpl)2 Lists (com.google.common.collect.Lists)1 String.format (java.lang.String.format)1 MessageFormat (java.text.MessageFormat)1 UTC (java.time.ZoneOffset.UTC)1 ChronoUnit (java.time.temporal.ChronoUnit)1 ArrayList (java.util.ArrayList)1 Arrays.asList (java.util.Arrays.asList)1 Collection (java.util.Collection)1 EnumSet (java.util.EnumSet)1 Objects.requireNonNull (java.util.Objects.requireNonNull)1 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)1