diff options
Diffstat (limited to 'src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search')
6 files changed, 415 insertions, 0 deletions
diff --git a/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/IllegalJsonValueException.java b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/IllegalJsonValueException.java new file mode 100644 index 0000000..d2c1c2a --- /dev/null +++ b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/IllegalJsonValueException.java @@ -0,0 +1,28 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.simulators.nfsimulator.vesclient.template.search; + +public class IllegalJsonValueException extends IllegalArgumentException { + + IllegalJsonValueException(String message) { + super(message); + } +} diff --git a/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/JsonUtils.java b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/JsonUtils.java new file mode 100644 index 0000000..3d7c60d --- /dev/null +++ b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/JsonUtils.java @@ -0,0 +1,104 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.simulators.nfsimulator.vesclient.template.search; + +import com.google.common.base.Strings; +import com.google.gson.Gson; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import org.bson.Document; + +/** + * This util flattens nested json and produces json with keys transformed to form of json path + * where default separator between parent object key and object key is ':' + * For easing searching of boolean values, they are converted to its string representation + */ +public class JsonUtils { + + private static final String DEFAULT_PARENT_KEY_TO_OBJECT_KEY_SEPARATOR = ":"; + private static final String SEED_PREFIX = ""; + private static final Gson GSON = new Gson(); + + public JsonObject flatten(JsonObject original) { + return flattenWithPrefixedKeys(DEFAULT_PARENT_KEY_TO_OBJECT_KEY_SEPARATOR, original.deepCopy(), SEED_PREFIX, new JsonObject()); + } + + public JsonObject flatten(String parentKeyToKeySeparator, JsonObject original) { + return flattenWithPrefixedKeys(parentKeyToKeySeparator, original.deepCopy(), SEED_PREFIX, new JsonObject()); + } + + public Document flatten(Document original) { + return flatten(DEFAULT_PARENT_KEY_TO_OBJECT_KEY_SEPARATOR, original); + } + + public Document flatten(String parentKeyToKeySeparator, Document original) { + JsonObject originalJsonObject = GSON.fromJson(original.toJson(), JsonObject.class); + JsonObject flattenedJson = flatten(parentKeyToKeySeparator, originalJsonObject); + return Document.parse(flattenedJson.toString()); + } + + private JsonObject flattenWithPrefixedKeys(String parentKeyToKeySeparator, JsonElement topLevelElem, String prefix, JsonObject acc) { + if (topLevelElem.isJsonPrimitive()) { + handleJsonPrimitive(topLevelElem, prefix, acc); + } else if (topLevelElem.isJsonArray()) { + handleJsonArray(parentKeyToKeySeparator, topLevelElem, prefix, acc); + } else if (topLevelElem.isJsonObject()) { + handleJsonObject(parentKeyToKeySeparator, topLevelElem, prefix, acc); + } else { + acc.add(prefix, topLevelElem.getAsJsonNull()); + } + return acc.deepCopy(); + } + + private void handleJsonObject(String parentKeyToKeySeparator, JsonElement topLevelElem, String prefix, JsonObject acc) { + boolean isEmpty = true; + JsonObject thisToplevelObj = topLevelElem.getAsJsonObject(); + for (String key : thisToplevelObj.keySet()) { + isEmpty = false; + String keyPrefix = String.format("%s%s%s", prefix, parentKeyToKeySeparator, key); + flattenWithPrefixedKeys(parentKeyToKeySeparator, thisToplevelObj.get(key), keyPrefix, acc); + } + if (isEmpty && !Strings.isNullOrEmpty(prefix)) { + acc.add(prefix, new JsonObject()); + } + } + + private void handleJsonArray(String parentKeyToKeySeparator, JsonElement topLevelElem, String prefix, JsonObject acc) { + JsonArray asJsonArray = topLevelElem.getAsJsonArray(); + if (asJsonArray.size() == 0) { + acc.add(prefix, new JsonArray()); + } + for (int i = 0; i < asJsonArray.size(); i++) { + flattenWithPrefixedKeys(parentKeyToKeySeparator, asJsonArray.get(i), String.format("%s[%s]", prefix, i), acc); + } + } + + private void handleJsonPrimitive(JsonElement topLevelElem, String prefix, JsonObject acc) { + JsonPrimitive jsonPrimitive = topLevelElem.getAsJsonPrimitive(); + if (jsonPrimitive.isBoolean()) { + acc.add(prefix, new JsonPrimitive(jsonPrimitive.getAsString())); + } else { + acc.add(prefix, topLevelElem.getAsJsonPrimitive()); + } + } +} diff --git a/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/TemplateSearchHelper.java b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/TemplateSearchHelper.java new file mode 100644 index 0000000..432b2d6 --- /dev/null +++ b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/TemplateSearchHelper.java @@ -0,0 +1,95 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.simulators.nfsimulator.vesclient.template.search; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import org.onap.integration.simulators.nfsimulator.vesclient.template.search.handler.PrimitiveValueCriteriaBuilder; +import org.onap.integration.simulators.nfsimulator.vesclient.template.search.viewmodel.FlatTemplateContent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.data.mongodb.core.query.Criteria; +import org.springframework.data.mongodb.core.query.Query; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@Component +public class TemplateSearchHelper { + private static final String PARENT_TO_CHILD_KEY_SEPARATOR = ":"; //compliant with flat json stored in db + private static final String FLATTENED_JSON_KEY_REGEX = PARENT_TO_CHILD_KEY_SEPARATOR + "%s(?:(\\[[\\d]+\\]))?$"; + private static final String FLATTENED_TEMPLATES_VIEW = "flatTemplatesView"; + + private MongoTemplate mongoTemplate; + private PrimitiveValueCriteriaBuilder criteriaBuilder; + + @Autowired + public TemplateSearchHelper(MongoTemplate mongoTemplate) { + this.mongoTemplate = mongoTemplate; + this.criteriaBuilder = new PrimitiveValueCriteriaBuilder(); + } + + public List<String> getIdsOfDocumentMatchingCriteria(JsonObject jsonCriteria) { + if (isNullValuePresentInCriteria(jsonCriteria)) { + throw new IllegalJsonValueException("Null values in search criteria are not supported."); + } + Criteria mongoDialectCriteria = composeCriteria(jsonCriteria); + Query query = new Query(mongoDialectCriteria); + List<FlatTemplateContent> flatTemplateContents = mongoTemplate.find(query, FlatTemplateContent.class, FLATTENED_TEMPLATES_VIEW); + return flatTemplateContents + .stream() + .map(FlatTemplateContent::getId) + .collect(Collectors.toList()); + } + + + private Criteria composeCriteria(JsonObject criteria) { + Criteria[] criteriaArr = criteria.entrySet() + .stream() + .map(this::mapEntryCriteriaWithRegex) + .toArray(Criteria[]::new); + return criteriaArr.length > 0 ? new Criteria().andOperator(criteriaArr) : new Criteria(); + } + + private Criteria mapEntryCriteriaWithRegex(Map.Entry<String, JsonElement> entry) { + Pattern primitiveOrArrayElemKeyRegex = getCaseInsensitive(String.format(FLATTENED_JSON_KEY_REGEX, entry.getKey())); + Criteria criteriaForJsonKey = Criteria.where("k").regex(primitiveOrArrayElemKeyRegex); + Criteria criteriaWithValue = criteriaBuilder.applyValueCriteriaBasedOnPrimitiveType(criteriaForJsonKey.and("v"), entry.getValue().getAsJsonPrimitive()); + return Criteria.where("keyValues").elemMatch(criteriaWithValue); + + } + + private boolean isNullValuePresentInCriteria(JsonObject jsonObject) { + return jsonObject.entrySet() + .stream() + .map(Map.Entry::getValue) + .anyMatch(JsonElement::isJsonNull); + } + + static Pattern getCaseInsensitive(String base) { + return Pattern.compile(base, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + } +} + + diff --git a/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/handler/PrimitiveValueCriteriaBuilder.java b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/handler/PrimitiveValueCriteriaBuilder.java new file mode 100644 index 0000000..7e5ae02 --- /dev/null +++ b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/handler/PrimitiveValueCriteriaBuilder.java @@ -0,0 +1,103 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.simulators.nfsimulator.vesclient.template.search.handler; + +import com.google.common.collect.Lists; +import com.google.gson.JsonPrimitive; +import org.springframework.data.mongodb.core.query.Criteria; + +import java.util.List; +import java.util.regex.Pattern; + +/** + * This class is a helper class for constructing apropriate criteria for query send to mongodb based on type of value. + * Query is build to search mongodb for templates that contains key-value pairs that satisfy given criteria. + * Value is oftype JsonPrimitive, based on its primitive java type following criteria are build to get proper document: + * -for string - there is a regex expression that ignores every meta character inside passed argument and searches for exact literal match ignoring case; + * -for number - all numbers are treated as double (mongodb number type equivalent) + * -for boolean - exact match, used string representation of boolean in search + **/ + +public class PrimitiveValueCriteriaBuilder { + + private final List<ValueTypeHandler> typeHandlers; + + public PrimitiveValueCriteriaBuilder() { + typeHandlers = Lists.newArrayList(new StringValueHandler(), new NumberValueHandler(), new BoolValueHandler()); + } + + public Criteria applyValueCriteriaBasedOnPrimitiveType(Criteria baseCriteria, JsonPrimitive jsonPrimitive) { + ValueTypeHandler typeHandler = typeHandlers.stream() + .filter(el -> el.isProperTypeHandler(jsonPrimitive)) + .findFirst() + .orElseThrow(() -> + new IllegalArgumentException(String.format( + "Expected json primitive, but given value: %s is of type: %s and could not be decoded", + jsonPrimitive, jsonPrimitive.getClass().toString()))); + return typeHandler.chainCriteriaForValue(baseCriteria, jsonPrimitive); + } + + private interface ValueTypeHandler { + boolean isProperTypeHandler(JsonPrimitive value); + + Criteria chainCriteriaForValue(Criteria criteria, JsonPrimitive value); + } + + private class BoolValueHandler implements ValueTypeHandler { + public boolean isProperTypeHandler(JsonPrimitive value) { + return value.isBoolean(); + } + + public Criteria chainCriteriaForValue(Criteria criteria, JsonPrimitive value) { + return criteria.is(value.getAsString()); + } + + } + + private class NumberValueHandler implements ValueTypeHandler { + public boolean isProperTypeHandler(JsonPrimitive value) { + return value.isNumber(); + } + + public Criteria chainCriteriaForValue(Criteria baseCriteria, JsonPrimitive value) { + return baseCriteria.is(value.getAsDouble()); + } + } + + private class StringValueHandler implements ValueTypeHandler { + public boolean isProperTypeHandler(JsonPrimitive value) { + return value.isString(); + } + + public Criteria chainCriteriaForValue(Criteria baseCriteria, JsonPrimitive value) { + return baseCriteria.regex(makeRegexCaseInsensitive(value.getAsString())); + } + + private Pattern makeRegexCaseInsensitive(String base) { + String metaCharEscaped = convertToIgnoreMetaChars(base); + return Pattern.compile("^" + metaCharEscaped + "$", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE); + } + + private String convertToIgnoreMetaChars(String valueWithMetaChars) { + return Pattern.quote(valueWithMetaChars); + } + } +} diff --git a/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/viewmodel/FlatTemplateContent.java b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/viewmodel/FlatTemplateContent.java new file mode 100644 index 0000000..9c9a39d --- /dev/null +++ b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/viewmodel/FlatTemplateContent.java @@ -0,0 +1,45 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.simulators.nfsimulator.vesclient.template.search.viewmodel; + + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; +import org.onap.integration.simulators.nfsimulator.vesclient.db.Row; + +import java.util.List; + +@Getter +@NoArgsConstructor +@ToString +public class FlatTemplateContent extends Row { + + private List<KeyValuePair> keyValues; + + + public FlatTemplateContent(String name, List<KeyValuePair> keyValues) { + this.setId(name); + this.keyValues = keyValues; + } +} + + diff --git a/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/viewmodel/KeyValuePair.java b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/viewmodel/KeyValuePair.java new file mode 100644 index 0000000..9c9ca72 --- /dev/null +++ b/src/main/java/org/onap/integration/simulators/nfsimulator/vesclient/template/search/viewmodel/KeyValuePair.java @@ -0,0 +1,40 @@ +/*- + * ============LICENSE_START======================================================= + * Simulator + * ================================================================================ + * Copyright (C) 2019 Nokia. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ============LICENSE_END========================================================= + */ + +package org.onap.integration.simulators.nfsimulator.vesclient.template.search.viewmodel; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.ToString; + +@Getter +@ToString +@NoArgsConstructor +@AllArgsConstructor +/** + * POJO for mongo structure after $objectToArray mapping where object consists of fields: k and v + */ +public class KeyValuePair { + + private String key; + private String value; + +} |