Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Transform] adds geotile_grid support in group_by #56514

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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.
*/

package org.elasticsearch.client.transform.transforms.pivot;

import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.geo.GeoBoundingBox;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;

import java.io.IOException;
import java.util.Objects;

import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg;

/*
* A geotile_grid aggregation source for group_by
*/
public class GeoTileGroupSource extends SingleGroupSource implements ToXContentObject {
private static final String NAME = "transform_geo_tile_group";

private static final ParseField PRECISION = new ParseField("precision");
private static final ConstructingObjectParser<GeoTileGroupSource, Void> PARSER = new ConstructingObjectParser<>(NAME, true, (args) -> {
String field = (String) args[0];
Integer precision = (Integer) args[1];
GeoBoundingBox boundingBox = (GeoBoundingBox) args[2];

return new GeoTileGroupSource(field, precision, boundingBox);
});

static {
PARSER.declareString(optionalConstructorArg(), FIELD);
PARSER.declareInt(optionalConstructorArg(), PRECISION);
PARSER.declareField(
optionalConstructorArg(),
(p, context) -> GeoBoundingBox.parseBoundingBox(p),
GeoBoundingBox.BOUNDS_FIELD,
ObjectParser.ValueType.OBJECT
);
}
private final Integer precision;
private final GeoBoundingBox geoBoundingBox;

public GeoTileGroupSource(final String field, final Integer precision, final GeoBoundingBox boundingBox) {
super(field, null);
if (precision != null) {
GeoTileUtils.checkPrecisionRange(precision);
}
this.precision = precision;
this.geoBoundingBox = boundingBox;
}

@Override
public Type getType() {
return Type.GEOTILE_GRID;
}

public Integer getPrecision() {
return precision;
}

public GeoBoundingBox getGeoBoundingBox() {
return geoBoundingBox;
}

public static GeoTileGroupSource fromXContent(final XContentParser parser) {
return PARSER.apply(parser, null);
}

@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
super.innerXContent(builder, params);
if (precision != null) {
builder.field(PRECISION.getPreferredName(), precision);
}
if (geoBoundingBox != null) {
geoBoundingBox.toXContent(builder, params);
}
builder.endObject();
return builder;
}

@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}

if (other == null || getClass() != other.getClass()) {
return false;
}

final GeoTileGroupSource that = (GeoTileGroupSource) other;

return Objects.equals(this.field, that.field)
&& Objects.equals(this.precision, that.precision)
&& Objects.equals(this.geoBoundingBox, that.geoBoundingBox);
}

@Override
public int hashCode() {
return Objects.hash(field, precision, geoBoundingBox);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ public static GroupConfig fromXContent(final XContentParser parser) throws IOExc
case "date_histogram":
groupSource = DateHistogramGroupSource.fromXContent(parser);
break;
case "geotile_grid":
groupSource = GeoTileGroupSource.fromXContent(parser);
break;
default:
// not a valid group source. Consume up to the dest field end object
consumeUntilEndObject(parser, 2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public abstract class SingleGroupSource implements ToXContentObject {
public enum Type {
TERMS,
HISTOGRAM,
DATE_HISTOGRAM;
DATE_HISTOGRAM,
GEOTILE_GRID;

public String value() {
return name().toLowerCase(Locale.ROOT);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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.
*/

package org.elasticsearch.client.transform.transforms.pivot;

import org.elasticsearch.common.geo.GeoBoundingBox;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
import org.elasticsearch.test.AbstractXContentTestCase;

import java.io.IOException;
import java.util.function.Predicate;

public class GeoTileGroupSourceTests extends AbstractXContentTestCase<GeoTileGroupSource> {
benwtrent marked this conversation as resolved.
Show resolved Hide resolved

public static GeoTileGroupSource randomGeoTileGroupSource() {
Rectangle rectangle = GeometryTestUtils.randomRectangle();
return new GeoTileGroupSource(
randomBoolean() ? null : randomAlphaOfLength(10),
randomBoolean() ? null : randomIntBetween(1, GeoTileUtils.MAX_ZOOM),
randomBoolean() ? null : new GeoBoundingBox(
new GeoPoint(rectangle.getMaxLat(), rectangle.getMinLon()),
new GeoPoint(rectangle.getMinLat(), rectangle.getMaxLon())
)
);
}

@Override
protected GeoTileGroupSource createTestInstance() {
return randomGeoTileGroupSource();
}

@Override
protected GeoTileGroupSource doParseInstance(XContentParser parser) throws IOException {
return GeoTileGroupSource.fromXContent(parser);
}

@Override
protected boolean supportsUnknownFields() {
return true;
}

@Override
protected Predicate<String> getRandomFieldsExcludeFilter() {
// allow unknown fields in the root of the object only
return field -> !field.isEmpty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,11 @@ public static GroupConfig randomGroupConfig() {
groupBy = HistogramGroupSourceTests.randomHistogramGroupSource();
break;
case DATE_HISTOGRAM:
default:
groupBy = DateHistogramGroupSourceTests.randomDateHistogramGroupSource();
break;
case GEOTILE_GRID:
default:
groupBy = GeoTileGroupSourceTests.randomGeoTileGroupSource();
}
groups.put(targetFieldName, groupBy);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you 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.
*/

package org.elasticsearch.client.transform.transforms.pivot.hlrc;

import org.elasticsearch.client.AbstractResponseTestCase;
import org.elasticsearch.common.geo.GeoBoundingBox;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.geo.GeometryTestUtils;
import org.elasticsearch.geometry.Rectangle;
import org.elasticsearch.search.aggregations.bucket.geogrid.GeoTileUtils;
import org.elasticsearch.xpack.core.transform.transforms.pivot.GeoTileGroupSource;


import static org.hamcrest.Matchers.equalTo;

public class GeoTileGroupSourceTests extends AbstractResponseTestCase<
GeoTileGroupSource,
org.elasticsearch.client.transform.transforms.pivot.GeoTileGroupSource> {

public static GeoTileGroupSource randomGeoTileGroupSource() {
Rectangle rectangle = GeometryTestUtils.randomRectangle();
return new GeoTileGroupSource(
randomBoolean() ? null : randomAlphaOfLength(10),
randomBoolean() ? null : randomIntBetween(1, GeoTileUtils.MAX_ZOOM),
randomBoolean() ? null : new GeoBoundingBox(
new GeoPoint(rectangle.getMaxLat(), rectangle.getMinLon()),
new GeoPoint(rectangle.getMinLat(), rectangle.getMaxLon())
)
);
}

@Override
protected GeoTileGroupSource createServerTestInstance(XContentType xContentType) {
return randomGeoTileGroupSource();
}

@Override
protected org.elasticsearch.client.transform.transforms.pivot.GeoTileGroupSource doParseToClientInstance(XContentParser parser) {
return org.elasticsearch.client.transform.transforms.pivot.GeoTileGroupSource.fromXContent(parser);
}

@Override
protected void assertInstances(
GeoTileGroupSource serverTestInstance,
org.elasticsearch.client.transform.transforms.pivot.GeoTileGroupSource clientInstance
) {
assertThat(serverTestInstance.getField(), equalTo(clientInstance.getField()));
assertNull(clientInstance.getScript());
assertThat(serverTestInstance.getPrecision(), equalTo(clientInstance.getPrecision()));
assertThat(serverTestInstance.getGeoBoundingBox(), equalTo(clientInstance.getGeoBoundingBox()));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,14 @@ public static Rectangle toBoundingBox(long hash) {
return toBoundingBox(hashAsInts[1], hashAsInts[2], hashAsInts[0]);
}

/**
* Decode a string bucket key in "zoom/x/y" format to a bounding box of the tile corners
*/
public static Rectangle toBoundingBox(String hash) {
int[] hashAsInts = parseHash(hash);
return toBoundingBox(hashAsInts[1], hashAsInts[2], hashAsInts[0]);
}

public static Rectangle toBoundingBox(int xTile, int yTile, int precision) {
final double tiles = validateZXY(precision, xTile, yTile);
final double minN = Math.PI - (2.0 * Math.PI * (yTile + 1)) / tiles;
Expand Down
Loading