http://git-wip-us.apache.org/repos/asf/incubator-ignite/blob/1b728431/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
----------------------------------------------------------------------
diff --cc 
modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
index 1369378,0000000..d8586a5
mode 100644,000000..100644
--- 
a/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
+++ 
b/modules/geospatial/src/main/java/org/apache/ignite/internal/processors/query/h2/opt/GridH2SpatialIndex.java
@@@ -1,326 -1,0 +1,326 @@@
 +/*
 + * Licensed to the Apache Software Foundation (ASF) under one or more
 + * contributor license agreements.  See the NOTICE file distributed with
 + * this work for additional information regarding copyright ownership.
 + * The ASF 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.apache.ignite.internal.processors.query.h2.opt;
 +
 +import com.vividsolutions.jts.geom.*;
 +import org.h2.engine.*;
- import org.h2.index.*;
 +import org.h2.index.Cursor;
++import org.h2.index.*;
 +import org.h2.message.*;
 +import org.h2.mvstore.*;
 +import org.h2.mvstore.rtree.*;
 +import org.h2.result.*;
 +import org.h2.table.*;
 +import org.h2.value.*;
 +
 +import java.util.*;
 +import java.util.concurrent.locks.*;
 +
 +/**
 + * Spatial index.
 + */
 +public class GridH2SpatialIndex extends GridH2IndexBase implements 
SpatialIndex {
 +    /** */
 +    private final ReadWriteLock lock = new ReentrantReadWriteLock();
 +
 +    /** */
 +    private volatile long rowCnt;
 +
 +    /** */
 +    private long rowIds;
 +
 +    /** */
 +    private boolean closed;
 +
 +    /** */
 +    private final MVRTreeMap<Long> treeMap;
 +
 +    /** */
 +    private final Map<Long, GridH2Row> idToRow = new HashMap<>();
 +
 +    /** */
 +    private final Map<Value, Long> keyToId = new HashMap<>();
 +
 +    /** */
 +    private final MVStore store;
 +
 +    /**
 +     * @param tbl Table.
 +     * @param idxName Index name.
 +     * @param cols Columns.
 +     * @param keyCol Key column.
 +     * @param valCol Value column.
 +     */
 +    public GridH2SpatialIndex(Table tbl, String idxName, IndexColumn[] cols, 
int keyCol, int valCol) {
 +        super(keyCol, valCol);
 +
 +        if (cols.length > 1)
 +            throw DbException.getUnsupportedException("can only do one 
column");
 +
 +        if ((cols[0].sortType & SortOrder.DESCENDING) != 0)
 +            throw DbException.getUnsupportedException("cannot do descending");
 +
 +        if ((cols[0].sortType & SortOrder.NULLS_FIRST) != 0)
 +            throw DbException.getUnsupportedException("cannot do nulls 
first");
 +
 +        if ((cols[0].sortType & SortOrder.NULLS_LAST) != 0)
 +            throw DbException.getUnsupportedException("cannot do nulls last");
 +
 +        initBaseIndex(tbl, 0, idxName, cols, IndexType.createNonUnique(false, 
false, true));
 +
 +        table = tbl;
 +
 +        if (cols[0].column.getType() != Value.GEOMETRY) {
 +            throw DbException.getUnsupportedException("spatial index on 
non-geometry column, " +
 +                cols[0].column.getCreateSQL());
 +        }
 +
 +        // Index in memory
 +        store = MVStore.open(null);
 +        treeMap = store.openMap("spatialIndex", new 
MVRTreeMap.Builder<Long>());
 +    }
 +
 +    /**
 +     * Check closed.
 +     */
 +    private void checkClosed() {
 +        if (closed)
 +            throw DbException.throwInternalError();
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public GridH2Row put(GridH2Row row) {
 +        Lock l = lock.writeLock();
 +
 +        l.lock();
 +
 +        try {
 +            checkClosed();
 +
 +            Value key = row.getValue(keyCol);
 +
 +            assert key != null;
 +
 +            Long rowId = keyToId.get(key);
 +
 +            if (rowId != null) {
 +                Long oldRowId = 
treeMap.remove(getEnvelope(idToRow.get(rowId), rowId));
 +
 +                assert rowId.equals(oldRowId);
 +            }
 +            else {
 +                rowId = ++rowIds;
 +
 +                keyToId.put(key, rowId);
 +            }
 +
 +            GridH2Row old = idToRow.put(rowId, row);
 +
 +            treeMap.put(getEnvelope(row, rowId), rowId);
 +
 +            if (old == null)
 +                rowCnt++; // No replace.
 +
 +            return old;
 +        }
 +        finally {
 +            l.unlock();
 +        }
 +    }
 +
 +    /**
 +     * @param row Row.
 +     * @param rowId Row id.
 +     * @return Envelope.
 +     */
 +    private SpatialKey getEnvelope(SearchRow row, long rowId) {
 +        Value v = row.getValue(columnIds[0]);
 +        Geometry g = ((ValueGeometry) 
v.convertTo(Value.GEOMETRY)).getGeometry();
 +        Envelope env = g.getEnvelopeInternal();
 +        return new SpatialKey(rowId,
 +            (float) env.getMinX(), (float) env.getMaxX(),
 +            (float) env.getMinY(), (float) env.getMaxY());
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public GridH2Row remove(SearchRow row) {
 +        Lock l = lock.writeLock();
 +
 +        l.lock();
 +
 +        try {
 +            checkClosed();
 +
 +            Value key = row.getValue(keyCol);
 +
 +            assert key != null;
 +
 +            Long rowId = keyToId.remove(key);
 +
 +            assert rowId != null;
 +
 +            GridH2Row oldRow = idToRow.remove(rowId);
 +
 +            assert oldRow != null;
 +
 +            if (!treeMap.remove(getEnvelope(row, rowId), rowId))
 +                throw DbException.throwInternalError("row not found");
 +
 +            rowCnt--;
 +
 +            return oldRow;
 +        }
 +        finally {
 +            l.unlock();
 +        }
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public void close(Session ses) {
 +        Lock l = lock.writeLock();
 +
 +        l.lock();
 +
 +        try {
 +            closed = true;
 +
 +            store.close();
 +        }
 +        finally {
 +            l.unlock();
 +        }
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override protected long getCostRangeIndex(int[] masks, long rowCnt, 
TableFilter filter, SortOrder sortOrder) {
 +        rowCnt += Constants.COST_ROW_OFFSET;
 +        long cost = rowCnt;
 +        long rows = rowCnt;
 +
 +        if (masks == null)
 +            return cost;
 +
 +        for (Column column : columns) {
 +            int idx = column.getColumnId();
 +            int mask = masks[idx];
 +            if ((mask & IndexCondition.SPATIAL_INTERSECTS) != 0) {
 +                cost = 3 + rows / 4;
 +
 +                break;
 +            }
 +        }
 +
 +        return cost;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public double getCost(Session ses, int[] masks, TableFilter 
filter, SortOrder sortOrder) {
 +        return getCostRangeIndex(masks, rowCnt, filter, sortOrder);
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public Cursor find(Session ses, SearchRow first, SearchRow 
last) {
 +        Lock l = lock.readLock();
 +
 +        l.lock();
 +
 +        try {
 +            checkClosed();
 +
 +            return new GridH2Cursor(rowIterator(treeMap.keySet().iterator()));
 +        }
 +        finally {
 +            l.unlock();
 +        }
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public boolean canGetFirstOrLast() {
 +        return true;
 +    }
 +
 +    /**
 +     * @param i Spatial key iterator.
 +     * @return Iterator over rows.
 +     */
 +    private Iterator<GridH2Row> rowIterator(Iterator<SpatialKey> i) {
 +        if (!i.hasNext())
 +            return Collections.emptyIterator();
 +
 +        List<GridH2Row> rows = new ArrayList<>();
 +
 +        do {
 +            GridH2Row row = idToRow.get(i.next().getId());
 +
 +            assert row != null;
 +
 +            rows.add(row);
 +        }
 +        while (i.hasNext());
 +
 +        return filter(rows.iterator());
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public Cursor findFirstOrLast(Session ses, boolean first) {
 +        Lock l = lock.readLock();
 +
 +        l.lock();
 +
 +        try {
 +            checkClosed();
 +
 +            if (!first)
 +                throw DbException.throwInternalError("Spatial Index can only 
be fetch by ascending order");
 +
 +            Iterator<GridH2Row> iter = 
rowIterator(treeMap.keySet().iterator());
 +
 +            return new SingleRowCursor(iter.hasNext() ? iter.next() : null);
 +        }
 +        finally {
 +            l.unlock();
 +        }
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public long getRowCount(Session ses) {
 +        return rowCnt;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public long getRowCountApproximation() {
 +        return rowCnt;
 +    }
 +
 +    /** {@inheritDoc} */
 +    @Override public Cursor findByGeometry(TableFilter filter, SearchRow 
intersection) {
 +        Lock l = lock.readLock();
 +
 +        l.lock();
 +
 +        try {
 +            if (intersection == null)
 +                return find(filter.getSession(), null, null);
 +
 +            return new 
GridH2Cursor(rowIterator(treeMap.findIntersectingKeys(getEnvelope(intersection, 
0))));
 +        }
 +        finally {
 +            l.unlock();
 +        }
 +    }
 +}

Reply via email to