1 package de.dlr.shepard.common.search.query; 2 3 import com.fasterxml.jackson.core.JsonProcessingException; 4 import com.fasterxml.jackson.databind.JsonNode; 5 import com.fasterxml.jackson.databind.ObjectMapper; 6 import de.dlr.shepard.common.exceptions.ShepardParserException; 7 import de.dlr.shepard.common.util.SearchConstants; 8 import java.util.Iterator; 9 import java.util.List; 10 import java.util.NoSuchElementException; 11 12 @SuppressWarnings("PMD.AvoidDuplicateLiterals") 13 public final class MongodbQueryBuilder { 14 15 private static final List<String> booleanOperators = List.of( 16 SearchConstants.JSON_AND, 17 SearchConstants.JSON_OR, 18 SearchConstants.JSON_NOT 19 ); 20 private static final List<String> opAttributes = List.of( 21 SearchConstants.OP_PROPERTY, 22 SearchConstants.OP_VALUE, 23 SearchConstants.OP_OPERATOR 24 ); 25 26 private MongodbQueryBuilder() {} 27 28 public static String getMongoDBQueryString(String query) { 29 ObjectMapper objectMapper = new ObjectMapper(); 30 JsonNode jsonNode = null; 31 try { 32 jsonNode = objectMapper.readValue(query, JsonNode.class); 33 } catch (JsonProcessingException e) { 34 throw new ShepardParserException("error while reading JSON\n" + e.getMessage()); 35 } 36 return getMongoDBQueryString(jsonNode); 37 } 38 39 private static String getMongoDBQueryString(JsonNode rootNode) { 40 String operator = ""; 41 try { 42 operator = rootNode.fieldNames().next(); 43 } catch (NoSuchElementException e) { 44 throw new ShepardParserException("error in parsing" + e.getMessage()); 45 } 46 47 if (opAttributes.contains(operator)) { 48 return primitiveClause(rootNode); 49 } else if (booleanOperators.contains(operator)) { 50 return complexClause(rootNode, operator); 51 } else { 52 throw new ShepardParserException("unknown operator: " + operator); 53 } 54 } 55 56 private static String operatorString(JsonNode node) { 57 String operator = node.textValue(); 58 return switch (operator) { 59 case SearchConstants.JSON_GT -> "$gt"; 60 case SearchConstants.JSON_LT -> "$lt"; 61 case SearchConstants.JSON_GE -> "$gte"; 62 case SearchConstants.JSON_LE -> "$lte"; 63 case SearchConstants.JSON_EQ -> "$eq"; 64 case SearchConstants.JSON_IN -> "$in"; 65 case SearchConstants.JSON_NE -> "$ne"; 66 default -> throw new ShepardParserException("unknown comparison operator " + operator); 67 }; 68 } 69 70 private static String complexClause(JsonNode node, String operator) { 71 if (operator.equals(SearchConstants.JSON_NOT)) { 72 return negatedClause(node.get(SearchConstants.JSON_NOT)); 73 } else { 74 return multaryClause(node, operator); 75 } 76 } 77 78 private static String negatedClause(JsonNode node) { 79 String operator = node.fieldNames().next(); 80 if (opAttributes.contains(operator)) { 81 return negatedPrimitiveClause(node); 82 } else if (booleanOperators.contains(operator)) { 83 return negatedComplexClause(node, operator); 84 } else { 85 throw new ShepardParserException("unknown operator: " + operator); 86 } 87 } 88 89 private static String negatedComplexClause(JsonNode node, String operator) { 90 if (operator.equals(SearchConstants.JSON_NOT)) return getMongoDBQueryString(node.get(SearchConstants.JSON_NOT)); 91 else return negatedMultaryClause(node, operator); 92 } 93 94 private static String negatedPrimitiveClause(JsonNode node) { 95 String ret = ""; 96 ret = ret + node.get(SearchConstants.OP_PROPERTY).textValue() + ": {"; 97 ret = ret + "$not: {"; 98 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + ": "; 99 ret = ret + node.get(SearchConstants.OP_VALUE) + "}}"; 100 return ret; 101 } 102 103 private static String primitiveClause(JsonNode node) { 104 String ret = ""; 105 String property = node.get(SearchConstants.OP_PROPERTY).textValue(); 106 ret = ret + property + ": {"; 107 ret = ret + operatorString(node.get(SearchConstants.OP_OPERATOR)) + ": "; 108 ret = ret + node.get(SearchConstants.OP_VALUE); 109 ret = ret + "}"; 110 return ret; 111 } 112 113 private static String multaryClause(JsonNode node, String operator) { 114 String ret = ""; 115 Iterator<JsonNode> argumentsArray = node.get(operator).elements(); 116 ret = ret + booleanOperator(operator) + " [{"; 117 String firstArgument = getMongoDBQueryString(argumentsArray.next()); 118 ret = ret + firstArgument; 119 while (argumentsArray.hasNext()) { 120 ret = ret + "}, {" + getMongoDBQueryString(argumentsArray.next()); 121 } 122 ret = ret + "}]"; 123 return ret; 124 } 125 126 private static String negatedMultaryClause(JsonNode node, String operator) { 127 String ret = ""; 128 Iterator<JsonNode> argumentsArray = node.get(operator).elements(); 129 ret = ret + negatedBooleanOperator(operator) + " [{"; 130 String firstArgument = negatedClause(argumentsArray.next()); 131 ret = ret + firstArgument; 132 while (argumentsArray.hasNext()) { 133 ret = ret + "}, {" + negatedClause(argumentsArray.next()); 134 } 135 ret = ret + "}]"; 136 return ret; 137 } 138 139 private static String booleanOperator(String operator) { 140 return switch (operator) { 141 case SearchConstants.JSON_AND -> "$and:"; 142 case SearchConstants.JSON_OR -> "$or:"; 143 default -> throw new ShepardParserException("unknown operator: " + operator); 144 }; 145 } 146 147 private static String negatedBooleanOperator(String operator) { 148 return switch (operator) { 149 case SearchConstants.JSON_AND -> "$or:"; 150 case SearchConstants.JSON_OR -> "$and:"; 151 default -> throw new ShepardParserException("unknown operator: " + operator); 152 }; 153 } 154 }