提交 a741e6d5 authored 作者: Evgenij Ryazanov's avatar Evgenij Ryazanov

Optimize ST_EXTENT

上级 de7f02e0
......@@ -19,6 +19,7 @@ import org.h2.expression.ExpressionVisitor;
import org.h2.index.Cursor;
import org.h2.index.Index;
import org.h2.message.DbException;
import org.h2.mvstore.db.MVSpatialIndex;
import org.h2.result.SearchRow;
import org.h2.result.SortOrder;
import org.h2.table.Column;
......@@ -378,7 +379,7 @@ public class Aggregate extends Expression {
case MEDIAN:
return AggregateDataMedian.getResultFromIndex(session, on, dataType);
case ST_EXTENT:
return AggregateDataST_Extent.getResultFromIndex(session, on);
return ((MVSpatialIndex) AggregateDataST_Extent.getGeometryColumnIndex(on)).getBounds(session);
default:
DbException.throwInternalError("type=" + type);
}
......
......@@ -8,12 +8,10 @@ package org.h2.expression.aggregate;
import java.util.ArrayList;
import org.h2.engine.Database;
import org.h2.engine.Session;
import org.h2.expression.Expression;
import org.h2.expression.ExpressionColumn;
import org.h2.index.Index;
import org.h2.mvstore.db.MVSpatialIndex;
import org.h2.mvstore.rtree.SpatialKey;
import org.h2.table.Column;
import org.h2.table.TableFilter;
import org.h2.value.Value;
......@@ -59,32 +57,6 @@ class AggregateDataST_Extent extends AggregateData {
return null;
}
/**
* Get the result from the index.
*
* @param session
* the session
* @param on
* the expression
* @return the result
*/
static Value getResultFromIndex(Session session, Expression on) {
MVSpatialIndex index = (MVSpatialIndex) getGeometryColumnIndex(on);
MVSpatialIndex.MVStoreCursor cursor = (MVSpatialIndex.MVStoreCursor) index.find(session, null, null);
Envelope envelope = new Envelope();
while (cursor.next()) {
SpatialKey key = cursor.getKey();
if (!key.isNull()) {
envelope.expandToInclude(key.min(0), key.min(1));
envelope.expandToInclude(key.max(0), key.max(1));
}
}
if (envelope.isNull()) {
return ValueNull.INSTANCE;
}
return ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(envelope));
}
@Override
void add(Database database, int dataType, boolean distinct, Value v) {
if (v == ValueNull.INSTANCE) {
......
......@@ -17,6 +17,7 @@ import org.h2.index.IndexType;
import org.h2.index.SpatialIndex;
import org.h2.index.SpatialTreeIndex;
import org.h2.message.DbException;
import org.h2.mvstore.Page;
import org.h2.mvstore.rtree.MVRTreeMap;
import org.h2.mvstore.rtree.MVRTreeMap.RTreeCursor;
import org.h2.mvstore.rtree.SpatialKey;
......@@ -33,6 +34,7 @@ import org.h2.value.ValueGeometry;
import org.h2.value.ValueLong;
import org.h2.value.ValueNull;
import org.locationtech.jts.geom.Envelope;
import org.locationtech.jts.geom.GeometryFactory;
/**
* This is an index based on a MVRTreeMap.
......@@ -217,6 +219,21 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
return new MVStoreCursor(session, it, mvTable);
}
/**
* Returns the minimum bounding box that encloses all keys.
*
* @param session the session
* @return the minimum bounding box that encloses all keys, or null
*/
public Value getBounds(Session session) {
FindBoundsCursor cursor = new FindBoundsCursor(spatialMap.getRootPage(), new SpatialKey(0), session,
getMap(session), columnIds[0]);
while (cursor.hasNext()) {
cursor.next();
}
return cursor.getBounds();
}
private SpatialKey getKey(SearchRow row) {
Value v = row.getValue(columnIds[0]);
if (v == ValueNull.INSTANCE) {
......@@ -381,5 +398,89 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex, MVIndex {
}
}
/**
* A cursor for getBounds() method.
*/
private final class FindBoundsCursor extends RTreeCursor {
private final Session session;
private final TransactionMap<SpatialKey, Value> map;
private final int columnId;
private boolean hasBounds;
private float bminxf, bmaxxf, bminyf, bmaxyf;
private double bminxd, bmaxxd, bminyd, bmaxyd;
FindBoundsCursor(Page root, SpatialKey filter, Session session, TransactionMap<SpatialKey, Value> map,
int columnId) {
super(root, filter);
this.session = session;
this.map = map;
this.columnId = columnId;
}
@Override
protected boolean check(boolean leaf, SpatialKey key, SpatialKey test) {
float minxf = key.min(0), maxxf = key.max(0), minyf = key.min(1), maxyf = key.max(1);
if (leaf) {
if (hasBounds) {
if ((minxf <= bminxf || maxxf >= bmaxxf || minyf <= bminyf || maxyf >= bmaxyf)
&& map.containsKey(key)) {
Envelope env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
.getEnvelopeNoCopy();
double minxd = env.getMinX(), maxxd = env.getMaxX(), minyd = env.getMinY(),
maxyd = env.getMaxY();
if (minxd < bminxd) {
bminxf = minxf;
bminxd = minxd;
}
if (maxxd > bmaxxd) {
bmaxxf = maxxf;
bmaxxd = maxxd;
}
if (minyd < bminyd) {
bminyf = minyf;
bminyd = minyd;
}
if (maxyd > bmaxyd) {
bmaxyf = maxyf;
bmaxyd = maxyd;
}
}
} else if (map.containsKey(key)) {
hasBounds = true;
Envelope env = ((ValueGeometry) mvTable.getRow(session, key.getId()).getValue(columnId))
.getEnvelopeNoCopy();
bminxf = minxf;
bminxd = env.getMinX();
bmaxxf = maxxf;
bmaxxd = env.getMaxX();
bminyf = minyf;
bminyd = env.getMinY();
bmaxyf = maxyf;
bmaxyd = env.getMaxY();
}
} else if (hasBounds) {
if (minxf <= bminxf || maxxf >= bmaxxf || minyf <= bminyf || maxyf >= bmaxyf) {
return true;
}
} else {
return true;
}
return false;
}
Value getBounds() {
return hasBounds ? ValueGeometry.getFromGeometry(new GeometryFactory().toGeometry(
new Envelope(bminxd, bmaxxd, bminyd, bmaxyd))) : ValueNull.INSTANCE;
}
}
}
......@@ -28,22 +28,75 @@ SELECT ST_EXTENT(V), ST_EXTENT(V) FILTER (WHERE V <> 'POINT(3 1)') FILTERED1,
CREATE SPATIAL INDEX IDX ON TEST(V);
> ok
-- Without index
SELECT ST_EXTENT(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((1 1, 1 2, 3 2, 3 1, 1 1))
-- With index
SELECT ST_EXTENT(V) FROM TEST;
>> POLYGON ((1 1, 1 2, 3 2, 3 1, 1 1))
-- Without index
SELECT ST_EXTENT(V) FILTER (WHERE V <> 'POINT(3 1)') FILTERED FROM TEST;
>> LINESTRING (1 1, 1 2)
-- Without index
SELECT ST_EXTENT(V) FROM TEST WHERE V <> 'POINT(3 1)';
>> LINESTRING (1 1, 1 2)
INSERT INTO TEST VALUES ('POINT(-1.0000000001 1)');
> update count: 1
-- Without index
SELECT ST_EXTENT(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((-1.0000000001 1, -1.0000000001 2, 3 2, 3 1, -1.0000000001 1))
-- With index
SELECT ST_EXTENT(V) FROM TEST;
>> POLYGON ((-1.0000000001 1, -1.0000000001 2, 3 2, 3 1, -1.0000000001 1))
TRUNCATE TABLE TEST;
> ok
-- Without index
SELECT ST_EXTENT(N) FROM (SELECT V AS N FROM TEST);
>> null
-- With index
SELECT ST_EXTENT(V) FROM TEST;
>> null
SELECT RAND(1000) * 0;
>> 0.0
INSERT INTO TEST SELECT CAST('POINT(' || CAST(RAND() * 100000 AS INT) || ' ' || CAST(RAND() * 100000 AS INT) || ')' AS GEOMETRY) FROM SYSTEM_RANGE(1, 1000);
> update count: 1000
-- Without index
SELECT ST_EXTENT(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((68 78, 68 99951, 99903 99951, 99903 78, 68 78))
-- With index
SELECT ST_EXTENT(V) FROM TEST;
>> POLYGON ((68 78, 68 99951, 99903 99951, 99903 78, 68 78))
TRUNCATE TABLE TEST;
> ok
SELECT RAND(1000) * 0;
>> 0.0
INSERT INTO TEST SELECT CAST('POINT(' || (CAST(RAND() * 100000 AS INT) * 0.000000001 + 1) || ' '
|| (CAST(RAND() * 100000 AS INT) * 0.000000001 + 1) || ')' AS GEOMETRY) FROM SYSTEM_RANGE(1, 1000);
> update count: 1000
-- Without index
SELECT ST_EXTENT(N) FROM (SELECT V AS N FROM TEST);
>> POLYGON ((1.000000068 1.000000078, 1.000000068 1.000099951, 1.000099903 1.000099951, 1.000099903 1.000000078, 1.000000068 1.000000078))
-- With index
SELECT ST_EXTENT(V) FROM TEST;
>> POLYGON ((1.000000068 1.000000078, 1.000000068 1.000099951, 1.000099903 1.000099951, 1.000099903 1.000000078, 1.000000068 1.000000078))
DROP TABLE TEST;
> ok
......@@ -790,3 +790,5 @@ tied ties
launched unavailable smallmoney erroneously multiplier newid pan streamline unmap preview unexpectedly presumably
converging smth rng curs casts unmapping unmapper
immediate hhmmss scheduled hhmm prematurely postponed arranges subexpression subexpressions encloses plane
minxf maxxf minyf maxyf bminxf bmaxxf bminyf bmaxyf
minxd maxxd minyd maxyd bminxd bmaxxd bminyd bmaxyd
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论