Package com.foundationdb.server.test.it.qp

Source Code of com.foundationdb.server.test.it.qp.BoxTableIndexScanIT

/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.foundationdb.server.test.it.qp;

import com.foundationdb.qp.expression.IndexBound;
import com.foundationdb.qp.expression.IndexKeyRange;
import com.foundationdb.qp.operator.API;
import com.foundationdb.qp.operator.Cursor;
import com.foundationdb.qp.operator.Operator;
import com.foundationdb.qp.row.Row;
import com.foundationdb.qp.rowtype.IndexRowType;
import com.foundationdb.qp.rowtype.RowType;
import com.foundationdb.qp.rowtype.Schema;
import com.foundationdb.qp.rowtype.TableRowType;
import com.foundationdb.server.api.dml.SetColumnSelector;
import com.foundationdb.server.error.OutOfRangeException;
import com.foundationdb.server.spatial.Spatial;
import com.geophile.z.Space;
import com.geophile.z.spatialobject.d2.Box;
import org.junit.Ignore;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import static com.foundationdb.qp.operator.API.cursor;
import static com.foundationdb.qp.operator.API.indexScan_Default;
import static java.lang.Math.abs;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

@Ignore
public class BoxTableIndexScanIT extends OperatorITBase
{
    @Override
    protected void setupCreateSchema()
    {
        box = createTable(
            "schema", "box",
            "id int not null",
            "before int not null", // id mod 3
            "after int not null", // id mod 5
            "lat decimal(11, 7)",
            "lon decimal(11, 7)",
            "box_blob blob",
            "primary key(id)");
        createSpatialTableIndex("schema", "box", "idx_box_blob", 0, 2, "box_blob");
    }

    @Override
    protected void setupPostCreateSchema()
    {
        schema = new Schema(ais());
        boxRowType = schema.tableRowType(table(box));
        boxOrdinal = boxRowType.table().getOrdinal();
        latLonIndexRowType = indexType(box, "box_blob");
        space = Spatial.createLatLonSpace();
        adapter = newStoreAdapter(schema);
        queryContext = queryContext(adapter);
        queryBindings = queryContext.createBindings();
    }

    protected int lookaheadQuantum() {
        return 1;
    }

    @Test
    public void testLoad()
    {
        loadDB();
        {
            // Check (lat, lon) index
            Operator plan = indexScan_Default(latLonIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{z, id};
            }
            compareRows(rows(latLonIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (before, lat, lon) index
            Operator plan = indexScan_Default(beforeLatLonIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{before(id), z, id};
            }
            compareRows(rows(beforeLatLonIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (lat, lon, after) index
            Operator plan = indexScan_Default(latLonAfterIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{z, after(id), id};
            }
            compareRows(rows(latLonAfterIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (before, lat, lon, after) index
            Operator plan = indexScan_Default(beforeLatLonAfterIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{before(id), z, after(id), id};
            }
            compareRows(rows(beforeLatLonAfterIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
    }

    @Test
    public void testLoadAndRemove()
    {
        loadDB();
        {
            // Delete rows with odd ids
            for (Integer id : zToId.values()) {
                if ((id % 2) == 1) {
                    deleteRow(box, id, before(id), after(id), lats.get(id), lons.get(id));
                }
            }
        }
        {
            // Check (lat, lon) index
            Operator plan = indexScan_Default(latLonIndexRowType);
            int rowsRemaining = zToId.size() / 2;
            if ((zToId.size() % 2) == 1) {
                rowsRemaining += 1;
            }
            long[][] expected = new long[rowsRemaining][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                // Only even ids should remain
                if ((id % 2) == 0) {
                    expected[r++] = new long[]{z, id};
                }
            }
            compareRows(rows(latLonIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (before, lat, lon) index
            Operator plan = indexScan_Default(beforeLatLonIndexRowType);
            int rowsRemaining = zToId.size() / 2;
            if ((zToId.size() % 2) == 1) {
                rowsRemaining += 1;
            }
            long[][] expected = new long[rowsRemaining][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                // Only even ids should remain
                if ((id % 2) == 0) {
                    expected[r++] = new long[]{before(id), z, id};
                }
            }
            compareRows(rows(beforeLatLonIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (lat, lon, after) index
            Operator plan = indexScan_Default(latLonAfterIndexRowType);
            int rowsRemaining = zToId.size() / 2;
            if ((zToId.size() % 2) == 1) {
                rowsRemaining += 1;
            }
            long[][] expected = new long[rowsRemaining][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                // Only even ids should remain
                if ((id % 2) == 0) {
                    expected[r++] = new long[]{z, after(id), id};
                }
            }
            compareRows(rows(latLonAfterIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (before, lat, lon, after) index
            Operator plan = indexScan_Default(beforeLatLonAfterIndexRowType);
            int rowsRemaining = zToId.size() / 2;
            if ((zToId.size() % 2) == 1) {
                rowsRemaining += 1;
            }
            long[][] expected = new long[rowsRemaining][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                // Only even ids should remain
                if ((id % 2) == 0) {
                    expected[r++] = new long[]{before(id), z, after(id), id};
                }
            }
            compareRows(rows(beforeLatLonAfterIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
    }

    @Test
    public void testLoadAndUpdate()
    {
        loadDB();
        int n = lats.size();
        zToId.clear();
        {
            // Increment y values
            for (int id = 0; id < n; id++) {
                BigDecimal lat = lats.get(id);
                BigDecimal lon = lons.get(id);
                Row before = row(box, id, before(id), after(id), lat, lon);
                Row after = row(box, id, before(id), after(id), lat, lon.add(BigDecimal.ONE));
                long z = Spatial.shuffle(space, lat.doubleValue(), lon.doubleValue() + 1);
                zToId.put(z, id);
                updateRow(before, after);
            }
        }
        {
            // Check (lat, lon) index
            Operator plan = indexScan_Default(latLonIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{z, id};
            }
            compareRows(rows(latLonIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (before, lat, lon) index
            Operator plan = indexScan_Default(beforeLatLonIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{before(id), z, id};
            }
            compareRows(rows(beforeLatLonIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (lat, lon, after) index
            Operator plan = indexScan_Default(latLonAfterIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{z, after(id), id};
            }
            compareRows(rows(latLonAfterIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
        {
            // Check (before, lat, lon, after) index
            Operator plan = indexScan_Default(beforeLatLonAfterIndexRowType);
            long[][] expected = new long[zToId.size()][];
            int r = 0;
            for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                long z = entry.getKey();
                int id = entry.getValue();
                expected[r++] = new long[]{before(id), z, after(id), id};
            }
            compareRows(rows(beforeLatLonAfterIndexRowType.physicalRowType(), sort(expected)), cursor(plan, queryContext, queryBindings));
        }
    }

    @Test
    public void testSpatialQueryLatLon()
    {
        loadDB();
        final int N = 100;
        BigDecimal latLo;
        BigDecimal latHi;
        BigDecimal lonLo;
        BigDecimal lonHi;
        for (int i = 0; i < N; i++) {
            latLo = randomLat();
            latHi = randomLat();
            if (latLo.compareTo(latHi) > 0) {
                BigDecimal swap = latLo;
                latLo = latHi;
                latHi = swap;
            }
            lonLo = randomLon();
            lonHi = randomLon();
            if (lonLo.compareTo(lonHi) > 0) {
                BigDecimal swap = lonLo;
                lonLo = lonHi;
                lonHi = swap;
            }
            // Get the right answer
            Set<Integer> expected = new HashSet<>();
            for (int id = 0; id < lats.size(); id++) {
                BigDecimal lat = lats.get(id);
                BigDecimal lon = lons.get(id);
                if (latLo.compareTo(lat) <= 0 &&
                    lat.compareTo(latHi) <= 0 &&
                    lonLo.compareTo(lon) <= 0 &&
                    lon.compareTo(lonHi) <= 0) {
                    expected.add(id);
                }
            }
            // Get the query result using the (lat, lon) index
            Set<Integer> actual = new HashSet<>();
            IndexBound lowerLeft = new IndexBound(row(latLonIndexRowType, latLo, lonLo),
                                                  new SetColumnSelector(0, 1));
            IndexBound upperRight = new IndexBound(row(latLonIndexRowType, latHi, lonHi),
                                                   new SetColumnSelector(0, 1));
            IndexKeyRange box = IndexKeyRange.spatial(latLonIndexRowType, lowerLeft, upperRight);
            Operator plan = indexScan_Default(latLonIndexRowType, box, lookaheadQuantum());
            Cursor cursor = API.cursor(plan, queryContext, queryBindings);
            cursor.openTopLevel();
            Row row;
            while ((row = cursor.next()) != null) {
                assertSame(latLonIndexRowType.physicalRowType(), row.rowType());
                long z = getLong(row, 0);
                Integer expectedId = zToId.get(z);
                assertNotNull(expectedId);
                int id = getLong(row, 1).intValue();
                assertEquals(expectedId.intValue(), id);
                assertEquals(expectedHKey(id), row.hKey().toString());
                actual.add(id);
            }
            // There should be no false negatives
            assertTrue(actual.containsAll(expected));
        }
    }

    @Test
    public void testSpatialQueryWithWraparound()
    {
        loadDB();
        final int N = 100;
        BigDecimal latLo;
        BigDecimal latHi;
        BigDecimal lonLo;
        BigDecimal lonHi;
        for (int i = 0; i < N; i++) {
            latLo = randomLat();
            latHi = randomLat();
            if (latLo.compareTo(latHi) > 0) {
                BigDecimal swap = latLo;
                latLo = latHi;
                latHi = swap;
            }
            lonLo = randomLon();
            lonHi = randomLon();
            if (lonLo.compareTo(lonHi) < 0) {
                // Guarantee wraparound
                BigDecimal swap = lonLo;
                lonLo = lonHi;
                lonHi = swap;
            }
            // Get the right answer
            Set<Integer> expected = new HashSet<>();
            for (int id = 0; id < lats.size(); id++) {
                BigDecimal lat = lats.get(id);
                BigDecimal lon = lons.get(id);
                if (latLo.compareTo(lat) <= 0 &&
                    lat.compareTo(latHi) <= 0 &&
                    lonLo.compareTo(lon) <= 0 &&
                    lon.compareTo(lonHi) <= 0) {
                    expected.add(id);
                }
            }
            // Get the query result
            Set<Integer> actual = new HashSet<>();
            IndexBound lowerLeft = new IndexBound(row(latLonIndexRowType, latLo, lonLo),
                                                  new SetColumnSelector(0, 1));
            IndexBound upperRight = new IndexBound(row(latLonIndexRowType, latHi, lonHi),
                                                   new SetColumnSelector(0, 1));
            IndexKeyRange box = IndexKeyRange.spatial(latLonIndexRowType, lowerLeft, upperRight);
            Operator plan = indexScan_Default(latLonIndexRowType, box, lookaheadQuantum());
            Cursor cursor = API.cursor(plan, queryContext, queryBindings);
            cursor.openTopLevel();
            Row row;
            while ((row = cursor.next()) != null) {
                assertSame(latLonIndexRowType.physicalRowType(), row.rowType());
                long z = getLong(row, 0);
                Integer expectedId = zToId.get(z);
                assertNotNull(expectedId);
                int id = getLong(row, 1).intValue();
                assertEquals(expectedId.intValue(), id);
                assertEquals(expectedHKey(id), row.hKey().toString());
                actual.add(id);
            }
            // There should be no false negatives
            assertTrue(actual.containsAll(expected));
        }
    }

    @Test
    public void testHybridQueryLatLon()
    {
        loadDB();
        final int N = 100;
        BigDecimal latLo;
        BigDecimal latHi;
        BigDecimal lonLo;
        BigDecimal lonHi;
        for (int i = 0; i < N; i++) {
            latLo = randomLat();
            latHi = randomLat();
            if (latLo.compareTo(latHi) > 0) {
                BigDecimal swap = latLo;
                latLo = latHi;
                latHi = swap;
            }
            lonLo = randomLon();
            lonHi = randomLon();
            if (lonLo.compareTo(lonHi) > 0) {
                BigDecimal swap = lonLo;
                lonLo = lonHi;
                lonHi = swap;
            }
            // before = id mod 3, so try before = 0, 1, 2
            for (int before = 0; before <= 2; before++) {
                // Get the right answer
                Set<Integer> expected = new HashSet<>();
                for (int id = 0; id < lats.size(); id++) {
                    BigDecimal lat = lats.get(id);
                    BigDecimal lon = lons.get(id);
                    if (before(id) == before &&
                        latLo.compareTo(lat) <= 0 &&
                        lat.compareTo(latHi) <= 0 &&
                        lonLo.compareTo(lon) <= 0 &&
                        lon.compareTo(lonHi) <= 0) {
                        expected.add(id);
                    }
                }
                // Get the query result using the (before, lat, lon) index
                Set<Integer> actual = new HashSet<>();
                IndexBound lowerLeft = new IndexBound(row(beforeLatLonIndexRowType, before, latLo, lonLo),
                                                      new SetColumnSelector(0, 1, 2));
                IndexBound upperRight = new IndexBound(row(beforeLatLonIndexRowType, before, latHi, lonHi),
                                                       new SetColumnSelector(0, 1, 2));
                IndexKeyRange box = IndexKeyRange.spatial(beforeLatLonIndexRowType, lowerLeft, upperRight);
                Operator plan = indexScan_Default(beforeLatLonIndexRowType, box, lookaheadQuantum());
                Cursor cursor = API.cursor(plan, queryContext, queryBindings);
                cursor.openTopLevel();
                Row row;
                while ((row = cursor.next()) != null) {
                    assertSame(beforeLatLonIndexRowType.physicalRowType(), row.rowType());
                    int rowBefore = getLong(row, 0).intValue();
                    long z = getLong(row, 1);
                    Integer expectedId = zToId.get(z);
                    assertNotNull(expectedId);
                    int rowId = getLong(row, 2).intValue();
                    assertEquals(before, rowBefore);
                    assertEquals(expectedId.intValue(), rowId);
                    assertEquals(expectedHKey(rowId), row.hKey().toString());
                    actual.add(rowId);
                }
                // There should be no false negatives
                assertTrue(actual.containsAll(expected));
            }
        }
    }

    @Test
    public void testNearPoint()
    {
        loadDB();
        final int N = 100;
        for (int i = 0; i < N; i++) {
            BigDecimal queryLat = randomLat();
            BigDecimal queryLon = randomLon();
            long zStart = Spatial.shuffle(space, queryLat.doubleValue(), queryLon.doubleValue());
            IndexBound zStartBound = new IndexBound(row(latLonIndexRowType, queryLat, queryLon),
                                                    new SetColumnSelector(0, 1));
            IndexKeyRange zStartRange = IndexKeyRange.around(latLonIndexRowType, zStartBound);
            Operator plan = indexScan_Default(latLonIndexRowType, zStartRange, lookaheadQuantum());
            Cursor cursor = API.cursor(plan, queryContext, queryBindings);
            cursor.openTopLevel();
            Row row;
            long previousDistance = Long.MIN_VALUE;
            int count = 0;
            while ((row = cursor.next()) != null) {
                assertSame(latLonIndexRowType.physicalRowType(), row.rowType());
                long zActual = getLong(row, 0);
                int id = getLong(row, 1).intValue();
                BigDecimal lat = lats.get(id);
                BigDecimal lon = lons.get(id);
                long zExpected = Spatial.shuffle(space, lat.doubleValue(), lon.doubleValue());
                assertEquals(zExpected, zActual);
                Integer expectedId = zToId.get(zActual);
                assertNotNull(expectedId);
                assertEquals(expectedId.intValue(), id);
                assertEquals(expectedHKey(id), row.hKey().toString());
                long distance = abs(zExpected - zStart);
                assertTrue(distance >= previousDistance);
                previousDistance = distance;
                count++;
            }
            assertEquals(zToId.size(), count);
        }
    }

    @Test
    public void testHybridNearPoint()
    {
        loadDB();
        final int N = 100;
        for (int i = 0; i < N; i++) {
            BigDecimal queryLat = randomLat();
            BigDecimal queryLon = randomLon();
            long zStart = Spatial.shuffle(space, queryLat.doubleValue(), queryLon.doubleValue());
            for (int before = 0; before <= 2; before++) {
                // Expected
                SortedMap<Long, Integer> distanceToId = new TreeMap<>();
                for (Map.Entry<Long, Integer> entry : zToId.entrySet()) {
                    long z = entry.getKey();
                    int id = entry.getValue();
                    if (before(id) == before) {
                        long distance = abs(z - zStart);
                        Integer replaced = distanceToId.put(distance, id);
                        // TODO: Duplicate distances are possible
                        assertNull(replaced);
                    }
                }
                Collection<Integer> expectedIdByDistance = distanceToId.values();
                // Actual
                IndexBound zStartBound =
                    new IndexBound(row(beforeLatLonIndexRowType, before,  queryLat, queryLon),
                                   new SetColumnSelector(0, 1, 2));
                IndexKeyRange zStartRange = IndexKeyRange.around(beforeLatLonIndexRowType, zStartBound);
                Operator plan = indexScan_Default(beforeLatLonIndexRowType, zStartRange, lookaheadQuantum());
                Cursor cursor = API.cursor(plan, queryContext, queryBindings);
                cursor.openTopLevel();
                Row row;
                long previousDistance = Long.MIN_VALUE;
                Collection<Integer> actualIdByDistance = new ArrayList<>();
                while ((row = cursor.next()) != null) {
                    assertSame(beforeLatLonIndexRowType.physicalRowType(), row.rowType());
                    int beforeActual = getLong(row, 0).intValue();
                    assertEquals(before, beforeActual);
                    long zActual = getLong(row, 1);
                    int id = getLong(row, 2).intValue();
                    BigDecimal lat = lats.get(id);
                    BigDecimal lon = lons.get(id);
                    long zExpected = Spatial.shuffle(space, lat.doubleValue(), lon.doubleValue());
                    assertEquals(zExpected, zActual);
                    Integer expectedId = zToId.get(zActual);
                    assertNotNull(expectedId);
                    assertEquals(expectedId.intValue(), id);
                    assertEquals(expectedHKey(id), row.hKey().toString());
                    long distance = abs(zExpected - zStart);
                    assertTrue(distance >= previousDistance);
                    previousDistance = distance;
                    actualIdByDistance.add(id);
                }
                assertEquals(new ArrayList<>(expectedIdByDistance),
                             new ArrayList<>(actualIdByDistance));
            }
        }
    }

    @Test
    public void testLongitudeBounds()
    {
        goodBox(0, 0, 0, 179);
        goodBox(0, 0, 0, 180);
        goodBox(0, 0, 0, 181);
        goodBox(0, 0, 0, 359);
        goodBox(0, 0, 0, 360);
        goodBox(0, 0, 0, 361);
        goodBox(0, 0, 0, 539);
        goodBox(0, 0, 0, 540);
        badBox(0, 0, 0, 541);

        goodBox(0, 0, 179, 0);
        goodBox(0, 0, 180, 0);
        goodBox(0, 0, 181, 0);
        goodBox(0, 0, 359, 0);
        goodBox(0, 0, 360, 0);
        goodBox(0, 0, 361, 0);
        goodBox(0, 0, 539, 0);
        goodBox(0, 0, 540, 0);
        badBox(0, 0, 541, 0);

        goodBox(0, 0, 0, -179);
        goodBox(0, 0, 0, -180);
        goodBox(0, 0, 0, -181);
        goodBox(0, 0, 0, -359);
        goodBox(0, 0, 0, -360);
        goodBox(0, 0, 0, -361);
        goodBox(0, 0, 0, -539);
        goodBox(0, 0, 0, -540);
        badBox(0, 0, 0, -541);

        goodBox(0, 0, -179, 0);
        goodBox(0, 0, -180, 0);
        goodBox(0, 0, -181, 0);
        goodBox(0, 0, -359, 0);
        goodBox(0, 0, -360, 0);
        goodBox(0, 0, -361, 0);
        goodBox(0, 0, -539, 0);
        goodBox(0, 0, -540, 0);
        badBox(0, 0, -541, 0);
    }

    @Test
    public void testLatitudeBounds()
    {
        goodBox(0, 89, 0, 0);
        goodBox(0, 90, 0, 0);
        goodBox(0, 91, 0, 0);
        goodBox(0, 181, 0, 0);
        goodBox(0, 361, 0, 0);
        goodBox(0, 449, 0, 0);
        goodBox(0, 450, 0, 0);
        badBox(0, 451, 0, 0);

        goodBox(89, 0, 0, 0);
        goodBox(90, 0, 0, 0);
        goodBox(91, 0, 0, 0);
        goodBox(181, 0, 0, 0);
        goodBox(361, 0, 0, 0);
        goodBox(449, 0, 0, 0);
        goodBox(450, 0, 0, 0);
        badBox(451, 0, 0, 0);

        goodBox(0, -89, 0, 0);
        goodBox(0, -90, 0, 0);
        goodBox(0, -91, 0, 0);
        goodBox(0, -181, 0, 0);
        goodBox(0, -361, 0, 0);
        goodBox(0, -449, 0, 0);
        goodBox(0, -450, 0, 0);
        badBox(0, -451, 0, 0);

        goodBox(-89, 0, 0, 0);
        goodBox(-90, 0, 0, 0);
        goodBox(-91, 0, 0, 0);
        goodBox(-181, 0, 0, 0);
        goodBox(-361, 0, 0, 0);
        goodBox(-449, 0, 0, 0);
        goodBox(-450, 0, 0, 0);
        badBox(-451, 0, 0, 0);
    }

    @Test
    public void testExceedingMaxLatitude()
    {
        loadDB();
        BigDecimal latLo = new BigDecimal(70);
        BigDecimal latHi = new BigDecimal(120);
        BigDecimal lonLo = new BigDecimal(40);
        BigDecimal lonHi = new BigDecimal(90);
        // Get the right answer
        Set<Integer> expected = new HashSet<>();
        for (int id = 0; id < lats.size(); id++) {
            BigDecimal lat = lats.get(id);
            BigDecimal lon = lons.get(id);
            if (latLo.compareTo(lat) <= 0 &&
                lat.compareTo(latHi) <= 0 &&
                lonLo.compareTo(lon) <= 0 &&
                lon.compareTo(lonHi) <= 0) {
                expected.add(id);
            }
        }
        // Get the query result
        Set<Integer> actual = new HashSet<>();
        IndexBound lowerLeft = new IndexBound(row(latLonIndexRowType, latLo, lonLo),
                                              new SetColumnSelector(0, 1));
        IndexBound upperRight = new IndexBound(row(latLonIndexRowType, latHi, lonHi),
                                               new SetColumnSelector(0, 1));
        IndexKeyRange box = IndexKeyRange.spatial(latLonIndexRowType, lowerLeft, upperRight);
        Operator plan = indexScan_Default(latLonIndexRowType, box, lookaheadQuantum());
        Cursor cursor = API.cursor(plan, queryContext, queryBindings);
        cursor.openTopLevel();
        Row row;
        while ((row = cursor.next()) != null) {
            assertSame(latLonIndexRowType.physicalRowType(), row.rowType());
            long z = getLong(row, 0);
            Integer expectedId = zToId.get(z);
            assertNotNull(expectedId);
            int id = getLong(row, 1).intValue();
            assertEquals(expectedId.intValue(), id);
            assertEquals(expectedHKey(id), row.hKey().toString());
            actual.add(id);
        }
        // There should be no false negatives
        assertTrue(actual.containsAll(expected));
    }

    @Test
    public void testExceedingMaxLongitude()
    {
        loadDB();
        BigDecimal latLo = new BigDecimal(-15);
        BigDecimal latHi = new BigDecimal(15);
        BigDecimal lonLo = new BigDecimal(160);
        BigDecimal lonHi = new BigDecimal(190);
        // Get the right answer
        Set<Integer> expected = new HashSet<>();
        for (int id = 0; id < lats.size(); id++) {
            BigDecimal lat = lats.get(id);
            BigDecimal lon = lons.get(id);
            if (latLo.compareTo(lat) <= 0 &&
                lat.compareTo(latHi) <= 0 &&
                lonLo.compareTo(lon) <= 0 &&
                lon.compareTo(lonHi) <= 0) {
                expected.add(id);
            }
        }
        // Get the query result
        Set<Integer> actual = new HashSet<>();
        IndexBound lowerLeft = new IndexBound(row(latLonIndexRowType, latLo, lonLo),
                                              new SetColumnSelector(0, 1));
        IndexBound upperRight = new IndexBound(row(latLonIndexRowType, latHi, lonHi),
                                               new SetColumnSelector(0, 1));
        IndexKeyRange box = IndexKeyRange.spatial(latLonIndexRowType, lowerLeft, upperRight);
        Operator plan = indexScan_Default(latLonIndexRowType, box, lookaheadQuantum());
        Cursor cursor = API.cursor(plan, queryContext, queryBindings);
        cursor.openTopLevel();
        Row row;
        while ((row = cursor.next()) != null) {
            assertSame(latLonIndexRowType.physicalRowType(), row.rowType());
            long z = getLong(row, 0);
            Integer expectedId = zToId.get(z);
            assertNotNull(expectedId);
            int id = getLong(row, 1).intValue();
            assertEquals(expectedId.intValue(), id);
            assertEquals(expectedHKey(id), row.hKey().toString());
            actual.add(id);
        }
        // There should be no false negatives
        assertTrue(actual.containsAll(expected));
    }

    private void loadDB()
    {
        int id = 0;
        for (long y = LAT_LO; y <= LAT_HI; y += DLAT) {
            for (long x = LON_LO; x < LON_HI; x += DLON) {
                BigDecimal lat = new BigDecimal(y);
                BigDecimal lon = new BigDecimal(x);
                writeRow(box, id, before(id), after(id), lat, lon);
                long z = Spatial.shuffle(space, lat.doubleValue(), lon.doubleValue());
                zToId.put(z, id);
                lats.add(lat);
                lons.add(lon);
                zs.add(z);
                id++;
            }
        }
    }

    private BigDecimal randomLat()
    {
        return new BigDecimal(random.nextDouble() * LAT_RANGE + LAT_LO);
    }

    private BigDecimal randomLon()
    {
        return new BigDecimal(random.nextDouble() * LON_RANGE + LON_LO);
    }

    private long before(long id)
    {
        return id % 3;
    }

    private long after(long id)
    {
        return id % 5;
    }

    private Row[] rows(RowType rowType, long[][] x)
    {
        Row[] rows = new Row[x.length];
        for (int i = 0; i < x.length; i++) {
            long[] a = x[i];
            Object[] oa = new Object[a.length];
            for (int j = 0; j < a.length; j++) {
                oa[j] = a[j];
            }
            rows[i] = row(rowType, oa);
        }
        return rows;
    }

    private String expectedHKey(int id)
    {
        return String.format("{%s,(long)%s}", boxOrdinal, id);
    }

    private long[][] sort(long[][] a)
    {
        Arrays.sort(a,
                    new Comparator<long[]>()
                    {
                        @Override
                        public int compare(long[] x, long[] y)
                        {
                            for (int i = 0; i < x.length; i++) {
                                if (x[i] < y[i]) {
                                    return -1;
                                }
                                if (x[i] > y[i]) {
                                    return 1;
                                }
                            }
                            return 0;
                        }
                    });
        return a;
    }

    private void goodBox(int latLo, int latHi, int lonLo, int lonHi)
    {
        new Box(latLo, latHi, lonLo, lonHi);
    }

    private void badBox(int latLo, int latHi, int lonLo, int lonHi)
    {
        try {
            goodBox(latLo, latHi, lonLo, lonHi);
            fail();
        } catch (OutOfRangeException e) {
        }
    }

    private BigDecimal decimal(int x)
    {
        return new BigDecimal(x);
    }

    private static final int LAT_LO = -90;
    private static final int LAT_HI = 90;
    private static final int LON_LO = -180;
    private static final int LON_HI = 180;
    private static final int LAT_RANGE = LAT_HI - LAT_LO;
    private static final int LON_RANGE = LON_HI - LON_LO;
    private static final int DLAT = 10;
    private static final int DLON = 10;

    private int box;
    private TableRowType boxRowType;
    private int boxOrdinal;
    private IndexRowType latLonIndexRowType;
    private IndexRowType beforeLatLonIndexRowType;
    private IndexRowType latLonAfterIndexRowType;
    private IndexRowType beforeLatLonAfterIndexRowType;
    private Space space;
    private Map<Long, Integer> zToId = new TreeMap<>();
    List<BigDecimal> lats = new ArrayList<>(); // indexed by id
    List<BigDecimal> lons = new ArrayList<>(); // indexed by id
    List<Long> zs = new ArrayList<>(); // indexed by id
    Random random = new Random(123456);
}
TOP

Related Classes of com.foundationdb.server.test.it.qp.BoxTableIndexScanIT

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.