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

com.github.mhewedy.expressions.Expressions Maven / Gradle / Ivy

The newest version!
package com.github.mhewedy.expressions;

import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.github.mhewedy.expressions.Expression.*;
import static com.github.mhewedy.expressions.Operator.$and;
import static com.github.mhewedy.expressions.Operator.$or;


/**
 * Represents Group of expression using mongodb query api.
 * 

* Example: *

 * {
 *     "status": "A",
 *     "$or": [{ "qty": { "$lt": 30 } }, { "item": { "$in": ["A", "D"] } }]
 * }
 * 
*

* Support a list of Operators defined in {@link Operator}. * * @see ExpressionsRepository#findAll(Expressions) * @see ExpressionsRepository#findAll(Expressions, Sort) * @see ExpressionsRepository#findAll(Expressions, Pageable) * @see Mongo Query Documents */ public class Expressions extends HashMap { /** * Create a new $or expression on the root, * then adds to it the current expressions attached at the root * and the expression passes as parameter. *

* Example: * Suppose we have the following expression as json:

 {"firstName": "ali"} 
*

* Then we added the following: *

     *  expressions.or(Expression.and(
     *          Expression.of("lastName", Operator.$eq, "ibrahim"),
     *          Expression.of("age", Operator.$gte, 10)
     *  ));
     * 
* Then the output could be represented as: * *
     * {
     *     "$or": [
     *          {"firstName": "ali"},
     *          {
     *              "$and": [
     *                  {"lastName": "ibrahim"},
     *                  {"age": {"$gte": 10}},
     *              ]
     *          }
     *     ]
     * }
     * 
* Or in sql as: *
     *  where firstName = "ali" or lastName = "ibrahim" and age >= 10
     * 
*/ public Expressions or(Expression expression) { Map tmp = new HashMap<>(this); this.clear(); List> list = new ArrayList<>(); this.put($or.name(), list); if (!tmp.isEmpty()) list.add(tmp); Map map = new HashMap<>(); list.add(map); add(expression, map); return this; } /** * Add the parameter expression to the list of expression in the expressions object. *

* Example: * Suppose we have the following expression as json: *

 {"firstName": "ali"} 
*

* Then we added the following: *

     *  expressions.and(Expression.of("birthDate", Operator.$gt, "1980-10-10"));
     *  expressions.and(Expression.or(
     *      Expression.of("lastName", Operator.$eq, "ibrahim"),
     *      Expression.of("age", Operator.$in, 10, 30)
     *  ));
     *
     * 
* Then the output could be represented as: * *
     * {
     *     "firstName": "ali",
     *     "birthDate": {"$gt": "1980-10-10"},
     *     "$or": [
     *          {"lastName": "ibrahim"},
     *          {"age": {"$in": [10, 30]}}
     *     ]
     * }
     * 
* Or in sql as: *
     * where firstName = "ali" and birthDate > "1980-10-10"
     *     and (lastName = "ibrahim" or age in (10, 30) )
     * 
*/ public Expressions and(Expression expression) { Map tmp = new HashMap<>(this); this.clear(); List> list = new ArrayList<>(); this.put($and.name(), list); if (!tmp.isEmpty()) list.add(tmp); Map map = new HashMap<>(); list.add(map); add(expression, map); return this; } public Specification getSpecification() { return new ExpressionsRepositoryImpl.ExpressionsSpecification<>(this); } static Expressions of(Expression expression) { Expressions expressions = new Expressions(); expressions.and(expression); return expressions; } /** * The bridge between {@link Expression} and the internal representation of mongodb query lang * represented by {@link Expressions} class. */ private static void add(Expression expression, Map map) { if (expression instanceof SingularExpression) { SingularExpression se = (SingularExpression) expression; if (se.operator == Operator.$eq) { map.put(se.field, se.value); } else { map.put(se.field, map(se.operator.name(), se.value)); } } else if (expression instanceof ListExpression) { ListExpression le = (ListExpression) expression; if (le.operator == Operator.$eq) { map.put(le.field, le.values); } else { map.put(le.field, map(le.operator.name(), le.values)); } } else if (expression instanceof OrExpression) { OrExpression oe = (OrExpression) expression; List> list = new ArrayList<>(); map.put($or.name(), list); oe.expressions.forEach(it -> { Map m = new HashMap<>(); list.add(m); add(it, m); }); } else if (expression instanceof AndExpression) { AndExpression ae = (AndExpression) expression; List> list = new ArrayList<>(); map.put($and.name(), list); ae.expressions.forEach(it -> { Map m = new HashMap<>(); list.add(m); add(it, m); }); } } List getExpressions() { return getExpressions(this); } /** * Returns this object as list of {@link Expression} to be passed to * Spring Data Specification builder {@link ExpressionsPredicateBuilder} */ @SuppressWarnings({"unchecked"}) private static List getExpressions(Map map) { List expressions = new ArrayList<>(); for (Entry entry : map.entrySet()) { String key = entry.getKey(); Object value = entry.getValue(); if ($or.name().equalsIgnoreCase(key)) { List> valueList = (List>) value; OrExpression orExpression = new OrExpression(); expressions.add(orExpression); for (Map valueMap : valueList) { orExpression.expressions.add(getExpressions(valueMap).get(0)); } } else if ($and.name().equalsIgnoreCase(key)) { List> valueList = (List>) value; AndExpression andExpression = new AndExpression(); expressions.add(andExpression); for (Map valueMap : valueList) { andExpression.expressions.add(getExpressions(valueMap).get(0)); } } else { if (value instanceof Map) { // value in the form of {"$operator": "value"} Map valueMap = ((Map) value); Entry first = valueMap.entrySet().iterator().next(); Operator operator = Operator.valueOf(first.getKey()); if (operator.isList) { expressions.add(new ListExpression(key, operator, first.getValue())); } else { expressions.add(new SingularExpression(key, operator, first.getValue())); } } else { // operator is "$eq" expressions.add(new SingularExpression(key, Operator.$eq, value)); } } } return expressions; } private static Map map(String key, Object value) { Map m = new HashMap<>(); m.put(key, value); return m; } /** * Extracts all field names and their corresponding values from the current * {@code Expressions} object, including nested fields within `$and` and `$or` * compound operators. *

* This method traverses the structure of the {@code Expressions} object recursively. * If a compound operator (`$and` or `$or`) is encountered, it extracts fields and * values from all nested expressions. *

*

* Example: * Given the following {@code Expressions} structure: *

     * {
     *     "firstName": "John",
     *     "$or": [
     *         {"lastName": "Doe"},
     *         {"age": {"$gt": 30}}
     *     ]
     * }
     * 
* The resulting map of fields will be: *
     * {
     *     "firstName": "John",
     *     "lastName": "Doe",
     *     "age": 30
     * }
     * 
* * @return a map containing field names as keys and their corresponding values, including nested fields. */ public Map extractFields() { return extractFields(getExpressions(this)); } private static Map extractFields(List expressionList) { var map = new HashMap(); for (Expression expression : expressionList) { if (expression instanceof SingularExpression singularExpression) { map.put(singularExpression.field, singularExpression.value); } else if (expression instanceof ListExpression listExpression) { map.put(listExpression.field, listExpression.values); } else if (expression instanceof AndExpression andExpression) { map.putAll(extractFields(andExpression.expressions)); } else if (expression instanceof OrExpression andExpression) { map.putAll(extractFields(andExpression.expressions)); } } return map; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy