Skip to content

Commit 719ced2

Browse files
committed
Refractor the Integration tests to abstract the geoshape and geo-point index preparation code.
Signed-off-by: Navneet Verma <navneev@amazon.com>
1 parent d1c3763 commit 719ced2

File tree

5 files changed

+306
-277
lines changed

5 files changed

+306
-277
lines changed

modules/geo/src/internalClusterTest/java/org/opensearch/geo/search/aggregations/bucket/AbstractBucketAggregationIntegTest.java

Lines changed: 186 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,30 @@
88

99
package org.opensearch.geo.search.aggregations.bucket;
1010

11+
import com.carrotsearch.hppc.ObjectIntHashMap;
12+
import com.carrotsearch.hppc.ObjectIntMap;
1113
import org.opensearch.Version;
1214
import org.opensearch.action.index.IndexRequestBuilder;
15+
import org.opensearch.cluster.metadata.IndexMetadata;
16+
import org.opensearch.common.geo.GeoPoint;
17+
import org.opensearch.common.geo.GeoShapeDocValue;
18+
import org.opensearch.common.settings.Settings;
1319
import org.opensearch.common.xcontent.XContentBuilder;
1420
import org.opensearch.geo.GeoModulePluginIntegTestCase;
21+
import org.opensearch.geo.tests.common.RandomGeoGenerator;
22+
import org.opensearch.geo.tests.common.RandomGeoGeometryGenerator;
1523
import org.opensearch.geometry.Geometry;
1624
import org.opensearch.geometry.Rectangle;
1725
import org.opensearch.test.VersionUtils;
1826

27+
import java.util.ArrayList;
28+
import java.util.HashSet;
1929
import java.util.List;
30+
import java.util.Random;
31+
import java.util.Set;
2032

2133
import static org.opensearch.common.xcontent.XContentFactory.jsonBuilder;
34+
import static org.opensearch.test.hamcrest.OpenSearchAssertions.assertAcked;
2235

2336
/**
2437
* This is the base class for all the Bucket Aggregation related integration tests. Use this class to add common
@@ -33,33 +46,169 @@ public abstract class AbstractBucketAggregationIntegTest extends GeoModulePlugin
3346

3447
protected static final String GEO_SHAPE_INDEX_NAME = "geoshape_index";
3548

36-
// We don't want to generate this BB at random as it may lead to generation of very large or very small BB.
37-
// Hence, we are making the parameters of this BB static for simplicity.
38-
protected static final Rectangle BOUNDING_RECTANGLE_FOR_GEO_SHAPES_AGG = new Rectangle(-4.1, 20.9, 21.9, -3.1);
49+
protected static Rectangle boundingRectangleForGeoShapesAgg;
3950

40-
protected static final String AGG_NAME = "geohashgrid";
51+
protected static ObjectIntMap<String> expectedDocsCountForGeoShapes;
52+
53+
protected static ObjectIntMap<String> expectedDocCountsForSingleGeoPoint;
54+
55+
protected static ObjectIntMap<String> multiValuedExpectedDocCountsGeoPoint;
4156

4257
protected static final String GEO_SHAPE_FIELD_NAME = "location_geo_shape";
4358

4459
protected static final String GEO_POINT_FIELD_NAME = "location";
4560

4661
protected static final String KEYWORD_FIELD_NAME = "city";
4762

63+
protected static String smallestGeoHash = null;
64+
4865
protected final Version version = VersionUtils.randomIndexCompatibleVersion(random());
4966

5067
@Override
5168
protected boolean forbidPrivateIndexSettings() {
5269
return false;
5370
}
5471

72+
/**
73+
* Prepares a GeoShape index for testing the GeoShape bucket aggregations. Different bucket aggregations can use
74+
* different techniques for creating buckets. Override the method
75+
* {@link AbstractBucketAggregationIntegTest#generateBucketsForGeometry} in the test class for creating the
76+
* buckets which will then be used for verifications.
77+
*
78+
* @param random {@link Random}
79+
* @throws Exception thrown during index creation.
80+
*/
81+
protected void prepareGeoShapeIndexForAggregations(final Random random) throws Exception {
82+
expectedDocsCountForGeoShapes = new ObjectIntHashMap<>();
83+
final Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build();
84+
final List<IndexRequestBuilder> geoshapes = new ArrayList<>();
85+
assertAcked(prepareCreate(GEO_SHAPE_INDEX_NAME).setSettings(settings).setMapping(GEO_SHAPE_FIELD_NAME, "type" + "=geo_shape"));
86+
boolean isShapeIntersectingBB = false;
87+
for (int i = 0; i < NUM_DOCS;) {
88+
final Geometry geometry = RandomGeoGeometryGenerator.randomGeometry(random);
89+
final GeoShapeDocValue geometryDocValue = GeoShapeDocValue.createGeometryDocValue(geometry);
90+
// make sure that there is 1 shape is intersecting with the bounding box
91+
if (!isShapeIntersectingBB) {
92+
isShapeIntersectingBB = geometryDocValue.isIntersectingRectangle(boundingRectangleForGeoShapesAgg);
93+
if (!isShapeIntersectingBB && i == NUM_DOCS - 1) {
94+
continue;
95+
}
96+
}
97+
i++;
98+
final Set<String> values = generateBucketsForGeometry(geometry, geometryDocValue);
99+
geoshapes.add(indexGeoShape(GEO_SHAPE_INDEX_NAME, geometry));
100+
for (final String hash : values) {
101+
expectedDocsCountForGeoShapes.put(hash, expectedDocsCountForGeoShapes.getOrDefault(hash, 0) + 1);
102+
}
103+
}
104+
indexRandom(true, geoshapes);
105+
ensureGreen(GEO_SHAPE_INDEX_NAME);
106+
}
107+
108+
/**
109+
* Returns a set of buckets for the shape at different precision level. Override this method for different bucket
110+
* aggregations.
111+
*
112+
* @param geometry {@link Geometry}
113+
* @param geoShapeDocValue {@link GeoShapeDocValue}
114+
* @return A {@link Set} of {@link String} which represents the buckets.
115+
*/
116+
protected abstract Set<String> generateBucketsForGeometry(final Geometry geometry, final GeoShapeDocValue geoShapeDocValue);
117+
118+
/**
119+
* Prepares a GeoPoint index for testing the GeoPoint bucket aggregations. Different bucket aggregations can use
120+
* different techniques for creating buckets. Override the method
121+
* {@link AbstractBucketAggregationIntegTest#generateBucketsForGeoPoint} in the test class for creating the
122+
* buckets which will then be used for verifications.
123+
*
124+
* @param random {@link Random}
125+
* @throws Exception thrown during index creation.
126+
*/
127+
protected void prepareSingleValueGeoPointIndex(final Random random) throws Exception {
128+
expectedDocCountsForSingleGeoPoint = new ObjectIntHashMap<>();
129+
createIndex("idx_unmapped");
130+
final Settings settings = Settings.builder()
131+
.put(IndexMetadata.SETTING_VERSION_CREATED, version)
132+
.put("index.number_of_shards", 4)
133+
.put("index.number_of_replicas", 0)
134+
.build();
135+
assertAcked(
136+
prepareCreate("idx").setSettings(settings)
137+
.setMapping(GEO_POINT_FIELD_NAME, "type=geo_point", KEYWORD_FIELD_NAME, "type=keyword")
138+
);
139+
final List<IndexRequestBuilder> cities = new ArrayList<>();
140+
for (int i = 0; i < NUM_DOCS; i++) {
141+
// generate random point
142+
final GeoPoint geoPoint = RandomGeoGenerator.randomPoint(random);
143+
cities.add(indexGeoPoint("idx", geoPoint.toString(), geoPoint.getLat() + ", " + geoPoint.getLon()));
144+
final Set<String> buckets = generateBucketsForGeoPoint(geoPoint);
145+
for (final String bucket : buckets) {
146+
expectedDocCountsForSingleGeoPoint.put(bucket, expectedDocCountsForSingleGeoPoint.getOrDefault(bucket, 0) + 1);
147+
}
148+
}
149+
indexRandom(true, cities);
150+
ensureGreen("idx_unmapped", "idx");
151+
}
152+
153+
protected void prepareMultiValuedGeoPointIndex(final Random random) throws Exception {
154+
multiValuedExpectedDocCountsGeoPoint = new ObjectIntHashMap<>();
155+
final Settings settings = Settings.builder().put(IndexMetadata.SETTING_VERSION_CREATED, version).build();
156+
final List<IndexRequestBuilder> cities = new ArrayList<>();
157+
assertAcked(
158+
prepareCreate("multi_valued_idx").setSettings(settings)
159+
.setMapping(GEO_POINT_FIELD_NAME, "type=geo_point", KEYWORD_FIELD_NAME, "type=keyword")
160+
);
161+
for (int i = 0; i < NUM_DOCS; i++) {
162+
final int numPoints = random.nextInt(4);
163+
final List<String> points = new ArrayList<>();
164+
final Set<String> buckets = new HashSet<>();
165+
for (int j = 0; j < numPoints; ++j) {
166+
// generate random point
167+
final GeoPoint geoPoint = RandomGeoGenerator.randomPoint(random);
168+
points.add(geoPoint.getLat() + "," + geoPoint.getLon());
169+
buckets.addAll(generateBucketsForGeoPoint(geoPoint));
170+
}
171+
cities.add(indexGeoPoints("multi_valued_idx", Integer.toString(i), points));
172+
for (final String bucket : buckets) {
173+
multiValuedExpectedDocCountsGeoPoint.put(bucket, multiValuedExpectedDocCountsGeoPoint.getOrDefault(bucket, 0) + 1);
174+
}
175+
}
176+
indexRandom(true, cities);
177+
ensureGreen("multi_valued_idx");
178+
}
179+
180+
/**
181+
* Returns a set of buckets for the GeoPoint at different precision level. Override this method for different bucket
182+
* aggregations.
183+
*
184+
* @param geoPoint {@link GeoPoint}
185+
* @return A {@link Set} of {@link String} which represents the buckets.
186+
*/
187+
protected abstract Set<String> generateBucketsForGeoPoint(final GeoPoint geoPoint);
188+
189+
/**
190+
* Indexes a GeoShape in the provided index.
191+
* @param index {@link String} index name
192+
* @param geometry {@link Geometry} the Geometry to be indexed
193+
* @return {@link IndexRequestBuilder}
194+
* @throws Exception thrown during creation of {@link IndexRequestBuilder}
195+
*/
55196
protected IndexRequestBuilder indexGeoShape(final String index, final Geometry geometry) throws Exception {
56197
XContentBuilder source = jsonBuilder().startObject();
57198
source = source.field(GEO_SHAPE_FIELD_NAME, WKT.toWKT(geometry));
58199
source = source.endObject();
59200
return client().prepareIndex(index).setSource(source);
60201
}
61202

62-
protected IndexRequestBuilder indexCity(final String index, final String name, final List<String> latLon) throws Exception {
203+
/**
204+
* Indexes a {@link List} of {@link GeoPoint}s in the provided Index name.
205+
* @param index {@link String} index name
206+
* @param name {@link String} value for the string field in index
207+
* @param latLon {@link List} of {@link String} representing the String representation of GeoPoint
208+
* @return {@link IndexRequestBuilder}
209+
* @throws Exception thrown during indexing.
210+
*/
211+
protected IndexRequestBuilder indexGeoPoints(final String index, final String name, final List<String> latLon) throws Exception {
63212
XContentBuilder source = jsonBuilder().startObject().field(KEYWORD_FIELD_NAME, name);
64213
if (latLon != null) {
65214
source = source.field(GEO_POINT_FIELD_NAME, latLon);
@@ -68,8 +217,38 @@ protected IndexRequestBuilder indexCity(final String index, final String name, f
68217
return client().prepareIndex(index).setSource(source);
69218
}
70219

71-
protected IndexRequestBuilder indexCity(final String index, final String name, final String latLon) throws Exception {
72-
return indexCity(index, name, List.of(latLon));
220+
/**
221+
* Indexes a {@link GeoPoint} in the provided Index name.
222+
* @param index {@link String} index name
223+
* @param name {@link String} value for the string field in index
224+
* @param latLon {@link String} representing the String representation of GeoPoint
225+
* @return {@link IndexRequestBuilder}
226+
* @throws Exception thrown during indexing.
227+
*/
228+
protected IndexRequestBuilder indexGeoPoint(final String index, final String name, final String latLon) throws Exception {
229+
return indexGeoPoints(index, name, List.of(latLon));
230+
}
231+
232+
/**
233+
* Generates a Bounding Box of a fixed radius that can be used for shapes aggregations to reduce the size of
234+
* aggregation results.
235+
* @param random {@link Random}
236+
* @return {@link Rectangle}
237+
*/
238+
protected Rectangle getGridAggregationBoundingBox(final Random random) {
239+
final double radius = getRadiusOfBoundingBox();
240+
assertTrue("The radius of Bounding Box is less than or equal to 0", radius > 0);
241+
return RandomGeoGeometryGenerator.randomRectangle(random, radius);
242+
}
243+
244+
/**
245+
* Returns a radius for the Bounding box. Test classes can override this method to change the radius of BBox for
246+
* the test cases. If we increase this value, it will lead to creation of a lot of buckets that can lead of
247+
* IndexOutOfBoundsExceptions.
248+
* @return double
249+
*/
250+
protected double getRadiusOfBoundingBox() {
251+
return 5.0;
73252
}
74253

75254
}

0 commit comments

Comments
 (0)