com.browseengine.bobo.geosearch.solo.search.impl.GeoOnlySearcherTest.java Source code

Java tutorial

Introduction

Here is the source code for com.browseengine.bobo.geosearch.solo.search.impl.GeoOnlySearcherTest.java

Source

/**
 * This software is licensed to you under the Apache License, Version 2.0 (the
 * "Apache License").
 *
 * LinkedIn's contributions are made under the Apache License. If you contribute
 * to the Software, the contributions will be deemed to have been made under the
 * Apache License, unless you expressly indicate otherwise. Please do not make any
 * contributions that would be inconsistent with the Apache License.
 *
 * You may obtain a copy of the Apache License at http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, this software
 * distributed under the Apache License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the Apache
 * License for the specific language governing permissions and limitations for the
 * software governed under the Apache License.
 *
 *  2012 LinkedIn Corp. All Rights Reserved.  
 */

package com.browseengine.bobo.geosearch.solo.search.impl;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;

import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.annotation.IfProfileValue;

import com.browseengine.bobo.geosearch.IGeoConverter;
import com.browseengine.bobo.geosearch.bo.CartesianCoordinateUUID;
import com.browseengine.bobo.geosearch.bo.GeoSearchConfig;
import com.browseengine.bobo.geosearch.impl.GeoConverter;
import com.browseengine.bobo.geosearch.index.impl.GeoSegmentReader;
import com.browseengine.bobo.geosearch.query.GeoQuery;
import com.browseengine.bobo.geosearch.score.impl.Conversions;
import com.browseengine.bobo.geosearch.solo.bo.IDGeoRecord;

/**
 * 
 * @author gcooney
 *
 */
@IfProfileValue(name = "test-suite", values = { "unit", "all" })
public class GeoOnlySearcherTest {
    Mockery context = new Mockery() {
        {
            setImposteriser(ClassImposteriser.INSTANCE);
        }
    };

    GeoOnlySearcher searcher;
    private GeoSearchConfig config;
    private Directory directory;
    private String indexName;

    private GeoSegmentReader<IDGeoRecord> mockGeoSegmentReader;
    private IGeoConverter geoConverter;

    @SuppressWarnings("unchecked")
    @Before
    public void setUp() throws IOException {
        indexName = UUID.randomUUID().toString();
        config = new GeoSearchConfig();
        directory = new RAMDirectory();

        mockGeoSegmentReader = context.mock(GeoSegmentReader.class);

        geoConverter = new GeoConverter();

        searcher = new GeoOnlySearcher(config, directory, indexName) {
            @Override
            GeoSegmentReader<IDGeoRecord> getGeoSegmentReader() {
                return mockGeoSegmentReader;
            }
        };
    }

    @Test
    public void testSearch_top1() throws IOException {
        final int start = 0;
        final int count = 1;
        final int hitCount = 1;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 10;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_20inRange() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 20;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_start10_20inRange() throws IOException {
        final int start = 10;
        final int count = 10;
        final int hitCount = 20;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_start30_20inRange() throws IOException {
        final int start = 30;
        final int count = 10;
        final int hitCount = 20;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top25_1000inRange() throws IOException {
        final int start = 0;
        final int count = 25;
        final int hitCount = 1000;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 20f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_start15_20inRange() throws IOException {
        final int start = 15;
        final int count = 10;
        final int hitCount = 20;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_90long() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 10;
        final double centroidLat = 0;
        final double centroidLong = 90;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_90long45lat() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 10;
        final double centroidLat = 45;
        final double centroidLong = 90;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_180long_range100() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 10;
        final double centroidLat = 0;
        final double centroidLong = 180;
        final float rangeInMiles = 100f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_90lat_range100() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 10;
        final double centroidLat = 90;
        final double centroidLong = 0;
        final float rangeInMiles = 100f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_neg90lat_range100() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 10;
        final double centroidLat = -90;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, start, count);
    }

    @Test
    public void testSearch_top10_allmisses() throws IOException {
        final int start = 0;
        final int count = 10;
        final int hitCount = 0;
        final int missCount = 1000;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, missCount, start, count);
    }

    @Test
    public void testSearch_top10_10justmisses() throws IOException {
        final int start = 0;
        final int count = 20;
        final int hitCount = 10;
        final int missCount = 10;
        final double centroidLat = 0;
        final double centroidLong = 0;
        final float rangeInMiles = 10f;

        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, missCount, start, count);
    }

    private void searchAndVerify_allHitsInRange(double centroidLat, double centroidLong, Float rangeInMiles,
            int hitCount, int start, int count) throws IOException {
        int missCount = 0;
        searchAndVerify_allHitsInRange(centroidLat, centroidLong, rangeInMiles, hitCount, missCount, start, count);
    }

    private void searchAndVerify_allHitsInRange(double centroidLat, double centroidLong, Float rangeInMiles,
            int hitCount, int missCount, int start, int count) throws IOException {
        Float rangeInKilometers = Conversions.mi2km(rangeInMiles);
        final GeoQuery query = new GeoQuery(centroidLat, centroidLong, rangeInKilometers);

        final IDGeoRecord maxRecord = buildMaxRecord(centroidLong, centroidLat, rangeInMiles,
                GeoOnlySearcher.EMPTY_UUID);
        final IDGeoRecord minRecord = buildMinRecord(centroidLong, centroidLat, rangeInMiles,
                GeoOnlySearcher.EMPTY_UUID);
        final List<IDGeoRecord> expectedHits = buildHitSet(centroidLong, centroidLat, rangeInMiles, hitCount);
        final List<IDGeoRecord> expectedMisses = buildJustMissSet(centroidLong, centroidLat, rangeInMiles,
                missCount);
        final List<IDGeoRecord> treeHits = new ArrayList<IDGeoRecord>(expectedHits);
        treeHits.addAll(expectedMisses);
        context.checking(new Expectations() {
            {
                one(mockGeoSegmentReader).getIterator(minRecord, maxRecord);
                will(returnValue(treeHits.iterator()));
            }
        });

        Iterator<IDGeoRecord> hitIter = expectedHits.iterator();
        for (int i = 0; i < start && hitIter.hasNext(); i++) {
            hitIter.next();
        }

        GeoOnlyHits hits = searcher.search(query, start, count);
        assertEquals(hitCount, hits.totalHits());
        int expectedResultsCount = Math.min(Math.max(0, hitCount - start), count);
        assertEquals(expectedResultsCount, hits.getHits().size());
        for (int index = 0; index < count && hitIter.hasNext(); index++) {
            IDGeoRecord expectedHit = hitIter.next();
            GeoOnlyHit actualHit = hits.getHits().get(index);
            assertNotNull("Expected hit at index " + index + " is null", expectedHit);
            assertNotNull("Hit at index " + index + " is null", actualHit);
            assertEquals("Hit at index " + index + " has wrong id", new String(expectedHit.id),
                    new String(actualHit.uuid));
        }
    }

    private IDGeoRecord buildMinRecord(double centroidLong, double centroidLat, Float rangeInMiles, byte[] uuid) {
        CartesianCoordinateUUID coordinate = buildMinCoordinate(centroidLong, centroidLat, rangeInMiles, uuid);

        return geoConverter.toIDGeoRecord(coordinate);
    }

    private IDGeoRecord buildMaxRecord(double centroidLong, double centroidLat, Float rangeInMiles, byte[] uuid) {
        CartesianCoordinateUUID coordinate = buildMaxCoordinate(centroidLong, centroidLat, rangeInMiles, uuid);

        return geoConverter.toIDGeoRecord(coordinate);
    }

    private List<IDGeoRecord> buildHitSet(double centroidLong, double centroidLat, float rangeInMiles,
            int hitCount) {
        List<IDGeoRecord> hits = new ArrayList<IDGeoRecord>();

        CartesianCoordinateUUID centroidCoordinate = buildMaxCoordinate(centroidLong, centroidLat, 0, new byte[0]);
        int deltax = calculateMaxDelta(centroidCoordinate.x, rangeInMiles);
        int deltay = calculateMaxDelta(centroidCoordinate.y, rangeInMiles);
        int deltaz = calculateMaxDelta(centroidCoordinate.z, rangeInMiles);

        for (int i = 0; i < hitCount; i++) {
            IDGeoRecord geoRecord;
            byte[] uuid = new byte[] { (byte) (i + "a".getBytes()[0]) };
            if (i % 2 == 0) {
                CartesianCoordinateUUID coordinate = new CartesianCoordinateUUID(
                        centroidCoordinate.x + ((deltax * i) / hitCount),
                        centroidCoordinate.y + ((deltay * i) / hitCount),
                        centroidCoordinate.z + ((deltaz * i) / hitCount), uuid);

                geoRecord = geoConverter.toIDGeoRecord(coordinate);
            } else {
                CartesianCoordinateUUID coordinate = new CartesianCoordinateUUID(
                        centroidCoordinate.x - ((deltax * i) / hitCount),
                        centroidCoordinate.y - ((deltay * i) / hitCount),
                        centroidCoordinate.z - ((deltaz * i) / hitCount), uuid);

                geoRecord = geoConverter.toIDGeoRecord(coordinate);
            }
            hits.add(geoRecord);
        }

        return hits;
    }

    private List<IDGeoRecord> buildJustMissSet(double centroidLong, double centroidLat, float rangeInMiles,
            int missCount) {
        List<IDGeoRecord> hits = new ArrayList<IDGeoRecord>();

        CartesianCoordinateUUID centroidCoordinate = buildMaxCoordinate(centroidLong, centroidLat, 0, new byte[0]);
        int deltax = calculateMaxDelta(centroidCoordinate.x, rangeInMiles);
        int deltay = calculateMaxDelta(centroidCoordinate.y, rangeInMiles);
        int deltaz = calculateMaxDelta(centroidCoordinate.z, rangeInMiles);

        for (int i = 0; i < missCount; i++) {
            IDGeoRecord geoRecord;
            byte[] uuid = new byte[] { (byte) (i + "a".getBytes()[0]) };
            if (i % 2 == 0) {
                CartesianCoordinateUUID coordinate = new CartesianCoordinateUUID(
                        centroidCoordinate.x + deltax + (i + 1) * 2, centroidCoordinate.y + deltay + (i + 1) * 2,
                        centroidCoordinate.z + deltaz + (i + 1) * 2, uuid);

                geoRecord = geoConverter.toIDGeoRecord(coordinate);
            } else {
                CartesianCoordinateUUID coordinate = new CartesianCoordinateUUID(
                        centroidCoordinate.x - deltax - (i + 1) * 2, centroidCoordinate.y - deltay - (i + 1) * 2,
                        centroidCoordinate.z - deltaz - (i + 1) * 2, uuid);

                geoRecord = geoConverter.toIDGeoRecord(coordinate);
            }
            hits.add(geoRecord);
        }

        return hits;
    }

    private int calculateMaxDelta(int originalPoint, float rangeInMiles) {
        float rangeInKm = Conversions.mi2km(rangeInMiles);
        int rangeInUnits = Conversions.radiusMetersToIntegerUnits(rangeInKm * 1000);
        int max = calculateMaximumCoordinate(originalPoint, rangeInUnits);
        int min = calculateMinimumCoordinate(originalPoint, rangeInUnits);
        return Math.min(max - originalPoint, originalPoint - min);
    }

    private CartesianCoordinateUUID buildMinCoordinate(double centroidLong, double centroidLat, float rangeInMiles,
            byte[] uuid) {
        float rangeInkm = Conversions.mi2km(rangeInMiles);
        int rangeInUnits = Conversions.radiusMetersToIntegerUnits(rangeInkm * 1000);
        CartesianCoordinateUUID centroidCoordinate = geoConverter.toCartesianCoordinate(centroidLat, centroidLong,
                uuid);

        int minX = calculateMinimumCoordinate(centroidCoordinate.x, rangeInUnits);
        int minY = calculateMinimumCoordinate(centroidCoordinate.y, rangeInUnits);
        int minZ = calculateMinimumCoordinate(centroidCoordinate.z, rangeInUnits);
        CartesianCoordinateUUID minCoordinate = new CartesianCoordinateUUID(minX, minY, minZ, uuid);

        return minCoordinate;
    }

    private CartesianCoordinateUUID buildMaxCoordinate(double centroidLong, double centroidLat, float rangeInMiles,
            byte[] uuid) {
        float rangeInkm = Conversions.mi2km(rangeInMiles);
        int rangeInUnits = Conversions.radiusMetersToIntegerUnits(rangeInkm * 1000);
        CartesianCoordinateUUID centroidCoordinate = geoConverter.toCartesianCoordinate(centroidLat, centroidLong,
                GeoOnlySearcher.EMPTY_UUID);

        int maxX = calculateMaximumCoordinate(centroidCoordinate.x, rangeInUnits);
        int maxY = calculateMaximumCoordinate(centroidCoordinate.y, rangeInUnits);
        int maxZ = calculateMaximumCoordinate(centroidCoordinate.z, rangeInUnits);
        CartesianCoordinateUUID maxCoordinate = new CartesianCoordinateUUID(maxX, maxY, maxZ, uuid);

        return maxCoordinate;
    }

    private int calculateMinimumCoordinate(int originalPoint, int delta) {
        if (originalPoint > 0 || originalPoint > Integer.MIN_VALUE + delta) {
            return originalPoint - delta;
        } else {
            return Integer.MIN_VALUE;
        }
    }

    private int calculateMaximumCoordinate(int originalPoint, int delta) {
        if (originalPoint < 0 || originalPoint < Integer.MAX_VALUE - delta) {
            return originalPoint + delta;
        } else {
            return Integer.MAX_VALUE;
        }
    }

}