Skip to the content.

Geo Distance Sort

Geo distance sorting allows you to sort documents based on their distance from a specified geographical point.

Basic Geo Distance Sort

To sort by distance from a specific location:

<?php
use Zvonchuk\Elastic\Search\Sort\SortBuilders;

// Sort by distance from New York City
$sort = SortBuilders::geoDistanceSort('location', 40.7128, -74.0060);

Specifying Sort Order

By default, results are sorted by ascending distance (closest first), but you can change this:

<?php
use Zvonchuk\Elastic\Search\Sort\SortBuilders;
use Zvonchuk\Elastic\Search\Sort\SortBuilder;

// Closest first (default)
$closestFirstSort = SortBuilders::geoDistanceSort('location', 40.7128, -74.0060)
    ->order(SortBuilder::ASC);

// Farthest first
$farthestFirstSort = SortBuilders::geoDistanceSort('location', 40.7128, -74.0060)
    ->order(SortBuilder::DESC);

Specifying Distance Unit

You can specify the unit in which distances are calculated:

<?php
use Zvonchuk\Elastic\Search\Sort\SortBuilders;
use Zvonchuk\Elastic\Search\Sort\GeoSort;

// Sort by distance in kilometers
$sortKm = SortBuilders::geoDistanceSort('location', 40.7128, -74.0060)
    ->unit(GeoSort::KILOMETERS);

// Sort by distance in miles
$sortMiles = SortBuilders::geoDistanceSort('location', 40.7128, -74.0060)
    ->unit(GeoSort::MILES);

Available units include:

Example: Finding Nearby Restaurants

Here’s a complete example of finding restaurants sorted by distance:

<?php
use Zvonchuk\Elastic\Client;
use Zvonchuk\Elastic\Core\SearchRequest;
use Zvonchuk\Elastic\Search\Builder\SearchSourceBuilder;
use Zvonchuk\Elastic\Query\QueryBuilders;
use Zvonchuk\Elastic\Search\Sort\SortBuilders;
use Zvonchuk\Elastic\Search\Sort\GeoSort;

$client = Client::getInstance(['localhost:9200']);

// User's location (New York City)
$userLat = 40.7128;
$userLon = -74.0060;

// Create a search for restaurants
$searchSource = new SearchSourceBuilder();
$searchSource->query(
    QueryBuilders::boolQuery()
        ->must(QueryBuilders::termQuery('type', 'restaurant'))
        ->filter(
            QueryBuilders::GeoDistanceQuery('location')
                ->point($userLat, $userLon)
                ->distance('5km')
        )
);

// Sort by distance from user's location
$searchSource->sort(
    SortBuilders::geoDistanceSort('location', $userLat, $userLon)
        ->unit(GeoSort::KILOMETERS)
);

// Add rating as a secondary sort
$searchSource->sort(
    SortBuilders::fieldSort('rating')->order('desc')
);

// Execute the search
$request = new SearchRequest('places');
$request->source($searchSource);
$response = $client->search($request);

// Process results
echo "Restaurants within 5km of your location, sorted by distance:\n";
foreach ($response->getHits() as $hit) {
    $restaurant = $hit['_source'];
    $distance = isset($hit['sort'][0]) ? round($hit['sort'][0], 2) . 'km' : 'unknown';
    
    echo "- {$restaurant['name']}\n";
    echo "  Distance: $distance\n";
    echo "  Rating: {$restaurant['rating']}/5\n";
    echo "  Address: {$restaurant['address']}\n\n";
}

Combining Geo Distance Sort with Other Sorts

You can combine geo distance sorting with other sort types:

<?php
use Zvonchuk\Elastic\Search\Builder\SearchSourceBuilder;
use Zvonchuk\Elastic\Search\Sort\SortBuilders;
use Zvonchuk\Elastic\Search\Sort\GeoSort;

$searchSource = new SearchSourceBuilder();

// First sort by premium status
$searchSource->sort(
    SortBuilders::fieldSort('is_premium')->order('desc')
);

// Then sort by distance
$searchSource->sort(
    SortBuilders::geoDistanceSort('location', 40.7128, -74.0060)
        ->unit(GeoSort::KILOMETERS)
);

// Then by rating
$searchSource->sort(
    SortBuilders::fieldSort('rating')->order('desc')
);

Example: Find and Group Locations by Distance

Here’s an example that finds locations and groups them by distance ranges:

<?php
use Zvonchuk\Elastic\Client;
use Zvonchuk\Elastic\Core\SearchRequest;
use Zvonchuk\Elastic\Search\Builder\SearchSourceBuilder;
use Zvonchuk\Elastic\Query\QueryBuilders;
use Zvonchuk\Elastic\Search\Sort\SortBuilders;
use Zvonchuk\Elastic\Search\Sort\GeoSort;

$client = Client::getInstance(['localhost:9200']);

// User's location
$userLat = 40.7128;
$userLon = -74.0060;

// Create a search for places within 10km
$searchSource = new SearchSourceBuilder();
$searchSource->query(
    QueryBuilders::GeoDistanceQuery('location')
        ->point($userLat, $userLon)
        ->distance('10km')
);

// Sort by distance
$searchSource->sort(
    SortBuilders::geoDistanceSort('location', $userLat, $userLon)
        ->unit(GeoSort::KILOMETERS)
);

// Execute the search
$request = new SearchRequest('places');
$request->source($searchSource);
$response = $client->search($request);

// Group results by distance
$nearby = []; // 0-1km
$walkable = []; // 1-3km
$drivable = []; // 3-10km

foreach ($response->getHits() as $hit) {
    $place = $hit['_source'];
    $distance = $hit['sort'][0]; // Distance in km
    
    if ($distance <= 1) {
        $nearby[] = [
            'name' => $place['name'],
            'distance' => round($distance, 2) . 'km'
        ];
    } elseif ($distance <= 3) {
        $walkable[] = [
            'name' => $place['name'],
            'distance' => round($distance, 2) . 'km'
        ];
    } else {
        $drivable[] = [
            'name' => $place['name'],
            'distance' => round($distance, 2) . 'km'
        ];
    }
}

// Display grouped results
echo "Nearby Places (0-1km):\n";
foreach ($nearby as $place) {
    echo "- {$place['name']} ({$place['distance']})\n";
}

echo "\nWalkable Places (1-3km):\n";
foreach ($walkable as $place) {
    echo "- {$place['name']} ({$place['distance']})\n";
}

echo "\nDrivable Places (3-10km):\n";
foreach ($drivable as $place) {
    echo "- {$place['name']} ({$place['distance']})\n";
}