All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.jnosql.communication.semistructured.CriteriaCondition Maven / Gradle / Ivy

/*
 *  Copyright (c) 2024 Contributors to the Eclipse Foundation
 *   All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 * and Apache License v2.0 which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
 * and the Apache License v2.0 is available at http://www.opensource.org/licenses/apache2.0.php.
 * You may elect to redistribute this code under either of these licenses.
 *
 */
package org.eclipse.jnosql.communication.semistructured;


import org.eclipse.jnosql.communication.Condition;
import org.eclipse.jnosql.communication.TypeReference;
import org.eclipse.jnosql.communication.Value;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.StreamSupport;


/**
 * Represents a condition in a query, including an element and a condition type, forming a predicate.
 * This class is used in conjunction with {@link DatabaseManager#select(SelectQuery)}.
 * It encapsulates a condition for filtering data in a semi-structured NoSQL database.
 *
 * @see Condition
 * @see DatabaseManager#select(SelectQuery)
 */
public final class CriteriaCondition {

    private final Element element;
    private final Condition condition;
    private final boolean readOnly;

    private CriteriaCondition(Element element, Condition condition) {
        this.element = element;
        this.condition = condition;
        this.readOnly = false;
    }

    private CriteriaCondition(Element element, Condition condition, boolean readOnly) {
        this.element = element;
        this.condition = condition;
        this.readOnly = readOnly;
    }

    /**
     * Retrieves the element used in the condition.
     *
     * @return the element
     */
    public Element element() {
        return element;
    }

    /**
     * Retrieves the condition type used in the condition.
     *
     * @return the condition
     * @see Condition
     */
    public Condition condition() {
        return condition;
    }

    /**
     * Creates a new {@link CriteriaCondition} with the {@link Condition#AND} conjunction.
     *
     * @param condition the condition to be combined with
     * @return the combined condition
     * @throws NullPointerException when the condition is null
     */
    public CriteriaCondition and(CriteriaCondition condition) {
        validateReadOnly();
        Objects.requireNonNull(condition, "Conditions are required");
        if (Condition.AND.equals(this.condition)) {
            Element newElement = getConditions(condition, Condition.AND);
            return new CriteriaCondition(newElement, Condition.AND);
        }
        return CriteriaCondition.and(this, condition);
    }

    /**
     * Creates a new {@link CriteriaCondition} by negating the current one.
     *
     * @return the negated condition
     * @see Condition#NOT
     */
    public CriteriaCondition negate() {
        validateReadOnly();
        if (Condition.NOT.equals(this.condition)) {
            return this.element.get(CriteriaCondition.class);
        } else {
            Element newElement = Element.of(Condition.NOT.getNameField(), this);
            return new CriteriaCondition(newElement, Condition.NOT);
        }

    }

    /**
     * Creates a new {@link CriteriaCondition} with the {@link Condition#OR} conjunction.
     *
     * @param condition the condition to be combined with
     * @return the combined condition
     * @throws NullPointerException when the condition is null
     */
    public CriteriaCondition or(CriteriaCondition condition) {
        validateReadOnly();
        Objects.requireNonNull(condition, "Condition is required");
        if (Condition.OR.equals(this.condition)) {
            Element newElement = getConditions(condition, Condition.OR);
            return new CriteriaCondition(newElement, Condition.OR);
        }
        return CriteriaCondition.or(this, condition);
    }

    private Element getConditions(CriteriaCondition criteriaCondition, Condition condition) {
        List conditions = new ArrayList<>(element.get(new TypeReference>() {
        }));
        conditions.add(criteriaCondition);
        return Element.of(condition.getNameField(), conditions);
    }

    private void validateReadOnly() {
        if (readOnly) {
            throw new IllegalStateException("You cannot change the status after building the query");
        }
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        CriteriaCondition that = (CriteriaCondition) o;
        return Objects.equals(element, that.element)
                && condition == that.condition;
    }

    @Override
    public int hashCode() {
        return Objects.hash(element, condition, readOnly);
    }

    @Override
    public String toString() {
        return "CriteriaCondition{" +
                "element=" + element +
                ", condition=" + condition +
                '}';
    }

    /**
     * Creates a new read-only {@link CriteriaCondition} based on the provided condition.
     *
     * @param condition the condition to be marked as read-only
     * @return a new read-only condition
     * @throws NullPointerException when the condition is null
     */
    public static CriteriaCondition readOnly(CriteriaCondition condition) {
        Objects.requireNonNull(condition, "Condition is required");
        return new CriteriaCondition(condition.element(), condition.condition(), true);
    }

    static CriteriaCondition of(Element element, Condition condition) {
        return new CriteriaCondition(Objects.requireNonNull(element, "Column is required"), condition);
    }


    /**
     * Creates a new {@link CriteriaCondition} with a {@link Condition#EQUALS} condition.
     * This indicates that a select operation will scan data with the same element name and an equal value to the provided value.
     *
     * @param element the element representing the data to match
     * @return a {@link CriteriaCondition} with {@link Condition#EQUALS}
     * @throws NullPointerException when the element is null
     */
    public static CriteriaCondition eq(Element element) {
        return new CriteriaCondition(element, Condition.EQUALS);
    }

    /**
     * An alias method to {@link CriteriaCondition#eq(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the value of the element
     * @return a {@link CriteriaCondition} with {@link Condition#EQUALS}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition eq(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return eq(Element.of(name, value));
    }

    /**
     * Creates a new {@link CriteriaCondition} with a {@link Condition#GREATER_THAN} condition.
     * This indicates that a select operation will scan data with the same element name and a value greater than the provided value.
     *
     * @param element the element representing the data to match
     * @return a {@link CriteriaCondition} with {@link Condition#GREATER_THAN}
     * @throws NullPointerException when the element is null
     */
    public static CriteriaCondition gt(Element element) {
        return new CriteriaCondition(element, Condition.GREATER_THAN);
    }

    /**
     * An alias method to {@link CriteriaCondition#gt(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the value of the element
     * @return a {@link CriteriaCondition} with {@link Condition#GREATER_THAN}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition gt(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return gt(Element.of(name, value));
    }

    /**
     * Creates a new {@link CriteriaCondition} with a {@link Condition#GREATER_EQUALS_THAN} condition.
     * This indicates that a select operation will scan data with the same element name and a value greater than or equal to the provided value.
     *
     * @param element the element representing the data to match
     * @return a {@link CriteriaCondition} with {@link Condition#GREATER_EQUALS_THAN}
     * @throws NullPointerException when the element is null
     */
    public static CriteriaCondition gte(Element element) {
        return new CriteriaCondition(element, Condition.GREATER_EQUALS_THAN);
    }

    /**
     * An alias method to {@link CriteriaCondition#gte(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the value of the element
     * @return a {@link CriteriaCondition} with {@link Condition#GREATER_EQUALS_THAN}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition gte(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return gte(Element.of(name, value));
    }

    /**
     * Creates a new {@link CriteriaCondition} with a {@link Condition#LESSER_THAN} condition.
     * This indicates that a select operation will scan data with the same element name and a value lesser than the provided value.
     *
     * @param element the element representing the data to match
     * @return a {@link CriteriaCondition} with {@link Condition#LESSER_THAN}
     * @throws NullPointerException when the element is null
     */
    public static CriteriaCondition lt(Element element) {
        return new CriteriaCondition(element, Condition.LESSER_THAN);
    }

    /**
     * An alias method to {@link CriteriaCondition#lt(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the value of the element
     * @return a {@link CriteriaCondition} with {@link Condition#LESSER_THAN}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition lt(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return lt(Element.of(name, value));
    }

    /**
     * Creates a new {@link CriteriaCondition} with a {@link Condition#LESSER_EQUALS_THAN} condition.
     * This indicates that a select operation will scan data with the same element name and a value lesser than or equal to the provided value.
     *
     * @param element the element representing the data to match
     * @return a {@link CriteriaCondition} with {@link Condition#LESSER_EQUALS_THAN}
     * @throws NullPointerException when the element is null
     */
    public static CriteriaCondition lte(Element element) {
        return new CriteriaCondition(element, Condition.LESSER_EQUALS_THAN);
    }

    /**
     * An alias method to {@link CriteriaCondition#lte(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the value of the element
     * @return a {@link CriteriaCondition} with {@link Condition#LESSER_EQUALS_THAN}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition lte(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return lte(Element.of(name, value));
    }

    /**
     * Creates a {@link CriteriaCondition} with a {@link Condition#IN}, indicating that a select will scan a semistructured NoSQL
     * database with the same name and the value within the provided collection.
     *
     * @param element an element instance
     * @return a {@link CriteriaCondition} with {@link Condition#IN}
     * @throws NullPointerException     when the element is null
     * @throws IllegalArgumentException when the {@link Element#get()} is not an iterable implementation
     */
    public static CriteriaCondition in(Element element) {
        Objects.requireNonNull(element, "element is required");
        checkInClause(element.value());
        return new CriteriaCondition(element, Condition.IN);
    }

    /**
     * An alias method to {@link CriteriaCondition#in(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the element information
     * @return a {@link CriteriaCondition} with {@link Condition#IN}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition in(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return in(Element.of(name, value));
    }

    /**
     * Creates a {@link CriteriaCondition} with a {@link Condition#LIKE}, indicating that a select will scan a
     * semistructured NoSQL database with the same name and the value is like the one provided in this element.
     *
     * @param element an element instance
     * @return a {@link CriteriaCondition} with {@link Condition#LIKE}
     * @throws NullPointerException when the element is null
     */
    public static CriteriaCondition like(Element element) {
        return new CriteriaCondition(element, Condition.LIKE);
    }

    /**
     * An alias method to {@link CriteriaCondition#like(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the element information
     * @return a {@link CriteriaCondition} with {@link Condition#LIKE}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition like(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return like(Element.of(name, value));
    }

    /**
     * Creates a new {@link CriteriaCondition} with a {@link Condition#BETWEEN} condition.
     * This indicates that a select operation will scan data with the same element name and a value between the two provided values.
     * The element must contain an iterable with exactly two elements.
     *
     * @param element the element representing the data to match
     * @return a {@link CriteriaCondition} with {@link Condition#BETWEEN}
     * @throws NullPointerException when the element is null
     * @throws IllegalArgumentException when the element is not an iterable with two elements
     */
    public static CriteriaCondition between(Element element) {
        Objects.requireNonNull(element, "element is required");
        checkBetweenClause(element.get());
        return new CriteriaCondition(element, Condition.BETWEEN);
    }

    /**
     * An alias method to {@link CriteriaCondition#between(Element)}, which first creates an {@link Element}
     * instance and then applies the condition.
     *
     * @param name  the name of the element
     * @param value the value of the element
     * @return a {@link CriteriaCondition} with {@link Condition#BETWEEN}
     * @throws NullPointerException when either the name or the value is null
     */
    public static CriteriaCondition between(String name, Object value) {
        Objects.requireNonNull(name, "Name is required");
        Objects.requireNonNull(value, "Value is required");
        return between(Element.of(name, value));
    }

    /**
     * Returns a condition that is the negation of the supplied condition.
     * This is accomplished by returning the result of calling {@code target.negate()}.
     *
     * @param condition the condition
     * @return a condition that negates the results of the supplied condition
     * @throws NullPointerException when the condition is null
     */
    public static CriteriaCondition not(CriteriaCondition condition) {
        Objects.requireNonNull(condition, "Condition is required");
        return condition.negate();
    }

    /**
     * Returns a new {@link CriteriaCondition} aggregating all the conditions as just one condition with "AND".
     * The {@link Element} will store the {@link Condition#getNameField()} as the key and the value will be
     * the {@link java.util.List} of all conditions.
     * 

Example:

*
{@code
     * Element age = Element.of("age", 26);
     * Element name = Element.of("name", "otavio");
     * CriteriaCondition condition = CriteriaCondition.eq(name).and(CriteriaCondition.gte(age));
     * }
* The {@link CriteriaCondition#element()} will have "_AND" as the key and the list of conditions as the value. * * @param conditions the conditions to be aggregated * @return the new {@link CriteriaCondition} instance * @throws NullPointerException when the conditions are null */ public static CriteriaCondition and(CriteriaCondition... conditions) { Objects.requireNonNull(conditions, "Condition is required"); Element element = Element.of(Condition.AND.getNameField(), Arrays.asList(conditions)); return CriteriaCondition.of(element, Condition.AND); } /** * Returns a new {@link CriteriaCondition} aggregating all the conditions as just one condition with "OR". * The {@link Element} will store the {@link Condition#getNameField()} as the key and the value will be * the {@link java.util.List} of all conditions. *

Example:

*
{@code
     * Element age = Element.of("age", 26);
     * Element name = Element.of("name", "otavio");
     * CriteriaCondition condition = CriteriaCondition.eq(name).or(CriteriaCondition.gte(age));
     * }
* The {@link CriteriaCondition#element()} will have "_OR" as the key and the list of conditions as the value. * * @param conditions the conditions to be aggregated * @return the new {@link CriteriaCondition} instance * @throws NullPointerException when the condition is null */ public static CriteriaCondition or(CriteriaCondition... conditions) { Objects.requireNonNull(conditions, "Condition is required"); Element element = Element.of(Condition.OR.getNameField(), Arrays.asList(conditions)); return CriteriaCondition.of(element, Condition.OR); } private static void checkInClause(Value value) { if (!value.isInstanceOf(Iterable.class)) { throw new IllegalArgumentException("On CriteriaCondition#in, you must use an iterable" + " instead of class: " + value.getClass().getName()); } } private static void checkBetweenClause(Object value) { if (Iterable.class.isInstance(value)) { long count = StreamSupport.stream(Iterable.class.cast(value).spliterator(), false).count(); if (count != 2) { throw new IllegalArgumentException("On CriteriaCondition#between, you must use an iterable" + " with two elements"); } } else { throw new IllegalArgumentException("On CriteriaCondition#between, you must use an iterable" + " with two elements instead of class: " + value.getClass().getName()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy