/modules/plugin/mongodb/src/main/java/org/geotools/data/mongodb/complex/MongoNestedAttributeExpressionFactory.java

https://github.com/geotools/geotools · Java · 160 lines · 127 code · 12 blank · 21 comment · 19 complexity · 98f6881e0462eaa4a87af845a186514b MD5 · raw file

  1. /*
  2. * GeoTools - The Open Source Java GIS Toolkit
  3. * http://geotools.org
  4. *
  5. * (C) 2017, Open Source Geospatial Foundation (OSGeo)
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation;
  10. * version 2.1 of the License.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. */
  17. package org.geotools.data.mongodb.complex;
  18. import java.io.IOException;
  19. import java.util.Collections;
  20. import java.util.List;
  21. import javax.xml.namespace.QName;
  22. import org.geotools.data.complex.AttributeMapping;
  23. import org.geotools.data.complex.FeatureTypeMapping;
  24. import org.geotools.data.complex.NestedAttributeMapping;
  25. import org.geotools.data.complex.spi.CustomAttributeExpressionFactory;
  26. import org.geotools.data.complex.util.XPathUtil;
  27. import org.geotools.data.mongodb.MongoFeatureStore;
  28. import org.geotools.feature.type.Types;
  29. import org.geotools.filter.ConstantExpression;
  30. import org.opengis.feature.type.Name;
  31. import org.opengis.filter.expression.Expression;
  32. /** Custom nested attribute expressions builder for MongoDB. */
  33. public class MongoNestedAttributeExpressionFactory implements CustomAttributeExpressionFactory {
  34. @Override
  35. public Expression createNestedAttributeExpression(
  36. FeatureTypeMapping mappings,
  37. XPathUtil.StepList xpath,
  38. NestedAttributeMapping nestedMapping) {
  39. return getSourceExpression(mappings, xpath, nestedMapping);
  40. }
  41. private Expression getSourceExpression(
  42. FeatureTypeMapping mappings,
  43. XPathUtil.StepList xpath,
  44. NestedAttributeMapping nestedMapping) {
  45. if (!(mappings.getSource() instanceof MongoFeatureStore)) {
  46. // nothing to do here
  47. return null;
  48. }
  49. if (!nestedMapping
  50. .getTargetXPath()
  51. .equalsIgnoreIndex(xpath.subList(0, nestedMapping.getTargetXPath().size()))) {
  52. return Expression.NIL;
  53. }
  54. int steps = xpath.size();
  55. XPathUtil.StepList finalXpath = xpath.subList(nestedMapping.getTargetXPath().size(), steps);
  56. AttributeMapping attributeMapping = nestedMapping;
  57. String jsonPath = addPath(attributeMapping, "");
  58. int end = finalXpath.size();
  59. int start = 0;
  60. while (end > start) {
  61. try {
  62. // let's check if the feature type name was provided
  63. FeatureTypeMapping featureTypeMapping =
  64. ((NestedAttributeMapping) attributeMapping).getFeatureTypeMapping(null);
  65. Name featureTypeName = featureTypeMapping.getTargetFeature().getName();
  66. QName name = finalXpath.get(start).getName();
  67. if (Types.equals(featureTypeName, name)) {
  68. // feature type name provided, let's skip it
  69. start++;
  70. if (start >= end) {
  71. // looks like only the feature type name was provided, we are done
  72. break;
  73. }
  74. }
  75. SearchResult result = search(finalXpath.subList(start, end), attributeMapping);
  76. if (!result.found) {
  77. break;
  78. }
  79. attributeMapping = result.attributeMapping;
  80. jsonPath = addPath(attributeMapping, jsonPath);
  81. start += result.index;
  82. } catch (Exception exception) {
  83. throw new RuntimeException("Error getting feature type mapping.");
  84. }
  85. }
  86. if (attributeMapping == null) {
  87. return Expression.NIL;
  88. }
  89. Expression sourceExpression = attributeMapping.getSourceExpression();
  90. if (sourceExpression instanceof JsonSelectFunction) {
  91. JsonSelectAllFunction jsonSelect = new JsonSelectAllFunction();
  92. jsonPath = addPath(jsonPath, ((JsonSelectFunction) sourceExpression).getJsonPath());
  93. List<Expression> parameters =
  94. Collections.singletonList(ConstantExpression.constant(jsonPath));
  95. jsonSelect.setParameters(parameters);
  96. return jsonSelect;
  97. }
  98. return sourceExpression;
  99. }
  100. private String addPath(AttributeMapping attribute, String currentPath) {
  101. if (attribute instanceof MongoNestedMapping) {
  102. Expression sourceExpression = attribute.getSourceExpression();
  103. if (sourceExpression instanceof CollectionLinkFunction) {
  104. String collection = ((CollectionLinkFunction) sourceExpression).getPath();
  105. return addPath(currentPath, collection);
  106. }
  107. }
  108. return currentPath;
  109. }
  110. private String addPath(String currentPath, String newPath) {
  111. return currentPath == null || currentPath.isEmpty() ? newPath : currentPath + "." + newPath;
  112. }
  113. private static final class SearchResult {
  114. static final SearchResult NOT_FOUND = new SearchResult(false, -1, null);
  115. final boolean found;
  116. final int index;
  117. final AttributeMapping attributeMapping;
  118. SearchResult(boolean found, int index, AttributeMapping attributeMapping) {
  119. this.found = found;
  120. this.index = index;
  121. this.attributeMapping = attributeMapping;
  122. }
  123. }
  124. private SearchResult search(XPathUtil.StepList xpath, AttributeMapping attributeMapping)
  125. throws Exception {
  126. for (int i = xpath.size(); i > 0; i--) {
  127. AttributeMapping foundAttributeMapping = match(attributeMapping, xpath.subList(0, i));
  128. if (foundAttributeMapping != null) {
  129. return new SearchResult(true, i, foundAttributeMapping);
  130. }
  131. }
  132. return SearchResult.NOT_FOUND;
  133. }
  134. private AttributeMapping match(AttributeMapping attributeMapping, XPathUtil.StepList xpath)
  135. throws IOException {
  136. if (attributeMapping instanceof NestedAttributeMapping) {
  137. FeatureTypeMapping mappings =
  138. ((NestedAttributeMapping) attributeMapping).getFeatureTypeMapping(null);
  139. List<AttributeMapping> attributesMappings = mappings.getAttributeMappings();
  140. for (AttributeMapping candidateAttributeMapping : attributesMappings) {
  141. if (xpath.equalsIgnoreIndex(candidateAttributeMapping.getTargetXPath())) {
  142. return candidateAttributeMapping;
  143. }
  144. }
  145. }
  146. return null;
  147. }
  148. }