Skip to content

Search by distance

With the ability to link data objects with geo points, you can search for data objects by distance. This type of search returns a paged set of data objects which satisfy the whereClause condition and located within the specified distance. Distance-based search uses a special function in whereClause of the search request. The syntax of the function is:

distance( 
   center point latitude, 
   center point longitude, 
   columnname which contains geo point.latitude,
   columnname which contains geo point.longitude )<operator> units-function(value)

where:

Argument                Description
<operator> Possible values are <, >, =, >=, <=
units-function Defines the units of measure for the distance. Possible values are:
ft( X ) - the distance value X is expressed in feet
km( X ) - the distance value X is expressed in kilometers
mi( X ) - the distance value X is expressed in miles
yd( X ) -  the distance value X is expressed in yards

For example, the following whereClause expression searches for data objects located within 200 miles from the point at 30.26715, -97.74306. Each data object must have the coordinates property of type GeoPoint.

distance( 30.26715, -97.74306, coordinates.latitude, coordinates.longitude ) < mi(200)

The following example demonstrates a search-by-distance query. The example uses three data objects stored in the Friend table: Bob, Jane, and Fred who respectively live in Austin, Houston, San Antonio. The search query in the example finds all friends who live within the specified distance. Before running the search query, create the objects in the data storage with the corresponding geo points.

The application must be setup with demo data in order to run the example and see the distance operator in action. See the setup code in the expandable block below:

Setting up the app with demo data.

@interface Friend: NSObject

@property (strong, nonatomic) NSString *objectId;
@property (strong, nonatomic) NSString *name;
@property (strong, nonatomic) NSString *phoneNumber;
@property (strong, nonatomic) GeoPoint *coordinates;

@end
@objcMembers class Friend: NSObject {
    var objectId: String?
    var name: String?
    var phoneNumber: String?
    var coordinates: GeoPoint?
}

######
The dictionary-based approach does not require a class definition for data objects saved in Backendless.
The dictionary-based approach does not require a class definition for data objects saved in Backendless.
Run the following query/code to store a data object representing Bob with a link to his home in Austin, TX:

Friend *bob = [Friend new];
bob.name = @"Bob";
bob.phoneNumber = @"512-555-1212";

DataStoreFactory *dataStore = [Backendless.shared.data of:[Friend class]];
[dataStore saveWithEntity:bob responseHandler:^(Friend *savedBob) {
    GeoPoint *bobGeoPoint = [[GeoPoint alloc] initWithLatitude:30.26715 longitude:-97.74306 categories:@[@"Home"] metadata:@{@"description": @"Bob's home"}];
    [Backendless.shared.geo savePointWithGeoPoint:bobGeoPoint responseHandler:^(GeoPoint *savedBobGeoPoint) {
        [dataStore setRelationWithColumnName:@"coordinates:GeoPoint:1" parentObjectId:savedBob.objectId childrenObjectIds:@[savedBobGeoPoint.objectId] responseHandler:^(NSNumber *relations) {
            NSLog(@"Bob: relation to geo has been set");
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let bob = Friend()
bob.name = "Bob"
bob.phoneNumber = "512-555-1212"

let dataStore = Backendless.shared.data.of(Friend.self)
dataStore.save(entity: bob, responseHandler: { savedBob in
    let bobGeoPoint = GeoPoint(latitude: 30.26715, longitude: -97.74306, categories: ["Home"], metadata: ["description": "Bob's home"])
    Backendless.shared.geo.savePoint(geoPoint: bobGeoPoint, responseHandler: { savedBobGeoPoint in
        if let bobObjectId = (savedBob as! Friend).objectId {
            dataStore.setRelation(columnName: "coordinates:GeoPoint:1", parentObjectId: bobObjectId, childrenObjectIds: [savedBobGeoPoint.objectId], responseHandler: { relations in
                print("Bob: relation to geo has been set")
            }, errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
            })
        }
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

NSDictionary *bob = @{@"name": @"Bob", @"phoneNumber": @"512-555-1212"};
MapDrivenDataStore *dataStore = [Backendless.shared.data ofTable:@"Friend"];
[dataStore saveWithEntity:bob responseHandler:^(NSDictionary *savedBob) {
    GeoPoint *bobGeoPoint = [[GeoPoint alloc] initWithLatitude:30.26715 longitude:-97.74306 categories:@[@"Home"] metadata:@{@"description": @"Bob's home"}];
    [Backendless.shared.geo savePointWithGeoPoint:bobGeoPoint responseHandler:^(GeoPoint *savedBobGeoPoint) {
        [dataStore setRelationWithColumnName:@"coordinates:GeoPoint:1" parentObjectId:[savedBob valueForKey:@"objectId"] childrenObjectIds:@[savedBobGeoPoint.objectId] responseHandler:^(NSNumber *relations) {
            NSLog(@"Bob: relation to geo has been set");
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let bob = ["name": "Bob", "phoneNumber": "512-555-1212"]
let dataStore = Backendless.shared.data.ofTable("Friend")
dataStore.save(entity: bob, responseHandler: { savedBob in
    let bobGeoPoint = GeoPoint(latitude: 30.26715, longitude: -97.74306, categories: ["Home"], metadata: ["description": "Bob's home"])
    Backendless.shared.geo.savePoint(geoPoint: bobGeoPoint, responseHandler: { savedBobGeoPoint in
        if let bobObjectId = savedBob["objectId"] as? String {
            dataStore.setRelation(columnName: "coordinates:GeoPoint:1", parentObjectId: bobObjectId, childrenObjectIds: [savedBobGeoPoint.objectId], responseHandler: { relations in
                print("Bob: relation to geo has been set")
            }, errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
            })
        }
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

Run the following query/code to store a data object representing Jane with a link to her home in Houston, TX:

Friend *jane = [Friend new];
jane.name = @"Jane";
jane.phoneNumber = @"281-555-1212";

DataStoreFactory *dataStore = [Backendless.shared.data of:[Friend class]];
[dataStore saveWithEntity:jane responseHandler:^(Friend *savedJane) {
    GeoPoint *janeGeoPoint = [[GeoPoint alloc] initWithLatitude:29.76328 longitude:-95.3632 categories:@[@"Home"] metadata:@{@"description": @"Jane's home"}];
    [Backendless.shared.geo savePointWithGeoPoint:janeGeoPoint responseHandler:^(GeoPoint *savedJaneGeoPoint) {
        [dataStore setRelationWithColumnName:@"coordinates:GeoPoint:1" parentObjectId:savedJane.objectId childrenObjectIds:@[savedJaneGeoPoint.objectId] responseHandler:^(NSNumber *relations) {
            NSLog(@"Jane: relation to geo has been set");
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let jane = Friend()
jane.name = "Jane"
jane.phoneNumber = "281-555-1212"

let dataStore = Backendless.shared.data.of(Friend.self)
dataStore.save(entity: jane, responseHandler: { savedJane in
    let janeGeoPoint = GeoPoint(latitude: 29.76328, longitude: -95.3632, categories: ["Home"], metadata: ["description": "Jane's home"])
    Backendless.shared.geo.savePoint(geoPoint: janeGeoPoint, responseHandler: { savedJaneGeoPoint in
        if let janeObjectId = (savedJane as! Friend).objectId {
            dataStore.setRelation(columnName: "coordinates:GeoPoint:1", parentObjectId: janeObjectId, childrenObjectIds: [savedJaneGeoPoint.objectId], responseHandler: { relations in
                print("Jane: relation to geo has been set")
            }, errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
            })
        }
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

NSDictionary *jane = @{@"name": @"Jane", @"phoneNumber": @"281-555-1212"};
MapDrivenDataStore *dataStore = [Backendless.shared.data ofTable:@"Friend"];
[dataStore saveWithEntity:jane responseHandler:^(NSDictionary *savedJane) {
    GeoPoint *janeGeoPoint = [[GeoPoint alloc] initWithLatitude:29.76328 longitude:-95.3632 categories:@[@"Home"] metadata:@{@"description": @"Jane's home"}];
    [Backendless.shared.geo savePointWithGeoPoint:janeGeoPoint responseHandler:^(GeoPoint *savedJaneGeoPoint) {
        [dataStore setRelationWithColumnName:@"coordinates:GeoPoint:1" parentObjectId:[savedJane valueForKey:@"objectId"] childrenObjectIds:@[savedJaneGeoPoint.objectId] responseHandler:^(NSNumber *relations) {
            NSLog(@"Jane: relation to geo has been set");
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let jane = ["name": "Jane", "phoneNumber": "281-555-1212"]
let dataStore = Backendless.shared.data.ofTable("Friend")
dataStore.save(entity: jane, responseHandler: { savedJane in
    let janeGeoPoint = GeoPoint(latitude: 29.76328, longitude: -95.3632, categories: ["Home"], metadata: ["description": "Jane's home"])
    Backendless.shared.geo.savePoint(geoPoint: janeGeoPoint, responseHandler: { savedJaneGeoPoint in
        if let janeObjectId = savedJane["objectId"] as? String {
            dataStore.setRelation(columnName: "coordinates:GeoPoint:1", parentObjectId: janeObjectId, childrenObjectIds: [savedJaneGeoPoint.objectId], responseHandler: { relations in
                print("Jane: relation to geo has been set")
            }, errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
            })
        }
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

Run the following query/code to store a data object representing Fred with a link to his home in San Antonio, TX:

Friend *fred = [Friend new];
fred.name = @"Fred";
fred.phoneNumber = @"210-555-1212";

DataStoreFactory *dataStore = [Backendless.shared.data of:[Friend class]];
[dataStore saveWithEntity:fred responseHandler:^(Friend *savedFred) {
    GeoPoint *fredGeoPoint = [[GeoPoint alloc] initWithLatitude:29.42412 longitude:-98.49363 categories:@[@"Home"] metadata:@{@"description": @"Fred's home"}];
    [Backendless.shared.geo savePointWithGeoPoint:fredGeoPoint responseHandler:^(GeoPoint *savedFredGeoPoint) {
        [dataStore setRelationWithColumnName:@"coordinates:GeoPoint:1" parentObjectId:savedFred.objectId childrenObjectIds:@[savedFredGeoPoint.objectId] responseHandler:^(NSNumber *relations) {
            NSLog(@"Fred: relation to geo has been set");
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let fred = Friend()
fred.name = "Fred"
fred.phoneNumber = "210-555-1212"

let dataStore = Backendless.shared.data.of(Friend.self)
dataStore.save(entity: fred, responseHandler: { savedFred in
    let fredGeoPoint = GeoPoint(latitude: 29.42412, longitude: -98.49363, categories: ["Home"], metadata: ["description": "Fred's home"])
    Backendless.shared.geo.savePoint(geoPoint: fredGeoPoint, responseHandler: { savedFredGeoPoint in
        if let fredObjectId = (savedFred as! Friend).objectId {
            dataStore.setRelation(columnName: "coordinates:GeoPoint:1", parentObjectId: fredObjectId, childrenObjectIds: [savedFredGeoPoint.objectId], responseHandler: { relations in
                print("Fred: relation to geo has been set")
            }, errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
            })
        }
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

NSDictionary *fred = @{@"name": @"Fred", @"phoneNumber": @"210-555-1212"};
MapDrivenDataStore *dataStore = [Backendless.shared.data ofTable:@"Friend"];
[dataStore saveWithEntity:fred responseHandler:^(NSDictionary *savedFred) {
    GeoPoint *fredGeoPoint = [[GeoPoint alloc] initWithLatitude:29.42412 longitude:-98.49363 categories:@[@"Home"] metadata:@{@"description": @"Fred's home"}];
    [Backendless.shared.geo savePointWithGeoPoint:fredGeoPoint responseHandler:^(GeoPoint *savedFredGeoPoint) {
        [dataStore setRelationWithColumnName:@"coordinates:GeoPoint:1" parentObjectId:[savedFred valueForKey:@"objectId"] childrenObjectIds:@[savedFredGeoPoint.objectId] responseHandler:^(NSNumber *relations) {
            NSLog(@"Fred: relation to geo has been set");
        } errorHandler:^(Fault *fault) {
            NSLog(@"Error: %@", fault.message);
        }];
    } errorHandler:^(Fault *fault) {
        NSLog(@"Error: %@", fault.message);
    }];
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let fred = ["name": "Fred", "phoneNumber": "210-555-1212"]
let dataStore = Backendless.shared.data.ofTable("Friend")
dataStore.save(entity: fred, responseHandler: { savedFred in
    let fredGeoPoint = GeoPoint(latitude: 29.42412, longitude: -98.49363, categories: ["Home"], metadata: ["description": "Fred's home"])
    Backendless.shared.geo.savePoint(geoPoint: fredGeoPoint, responseHandler: { savedFredGeoPoint in
        if let fredObjectId = savedFred["objectId"] as? String {
            dataStore.setRelation(columnName: "coordinates:GeoPoint:1", parentObjectId: fredObjectId, childrenObjectIds: [savedFredGeoPoint.objectId], responseHandler: { relations in
                print("Fred: relation to geo has been set")
            }, errorHandler: { fault in
                print("Error: \(fault.message ?? "")")
            })
        }
    }, errorHandler: { fault in
        print("Error: \(fault.message ?? "")")
    })
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

Once the data is in the database and the geo location storage, you can verify it in Backendless Console by opening the Geolocation screen and selecting the Home geocategory:

distance-search

Suppose you need to get all objects located within 300 miles radius from Beaumont, TX, which has the GPS coordinates of 30.084, -94.145. The following code/query performs that distance-based search:

DataQueryBuilder *queryBuilder = [DataQueryBuilder new];
[queryBuilder setRelationsDepthWithRelationsDepth:1];
[queryBuilder setWhereClauseWithWhereClause:@"distance(30.084, -94.145, Coordinates.latitude, Coordinates.longitude ) < mi(300)"];

DataStoreFactory *dataStore = [Backendless.shared.data of:[Friend class]];
[dataStore findWithQueryBuilder:queryBuilder responseHandler:^(NSArray *friends) {
    for (Friend *friend in friends) {
        NSLog(@"%@ lives at %f, %f tagged as '%@'", friend.name, friend.coordinates.latitude, friend.coordinates.longitude, [friend.coordinates.metadata valueForKey:@"description"]);
    }
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let queryBuilder = DataQueryBuilder()
queryBuilder.setRelationsDepth(relationsDepth: 1)
queryBuilder.setWhereClause(whereClause: "distance(30.084, -94.145, Coordinates.latitude, Coordinates.longitude ) < mi(300)")

let dataStore = Backendless.shared.data.of(Friend.self)
dataStore.find(queryBuilder: queryBuilder, responseHandler: { friends in
    if let friends = friends as? [Friend] {
        for friend in friends {
            if let name = friend.name,
                let latitude = friend.coordinates?.latitude,
                let longitude = friend.coordinates?.longitude,
                let meta_description = friend.coordinates?.metadata?["description"] {
                print("\(name) lives at \(latitude), \(longitude) tagged as '\(meta_description)'")
            }
        }
    }
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

DataQueryBuilder *queryBuilder = [DataQueryBuilder new];
[queryBuilder setRelationsDepthWithRelationsDepth:1];
[queryBuilder setWhereClauseWithWhereClause:@"distance(30.084, -94.145, Coordinates.latitude, Coordinates.longitude ) < mi(300)"];

MapDrivenDataStore *dataStore = [Backendless.shared.data ofTable:@"Friend"];
[dataStore findWithQueryBuilder:queryBuilder responseHandler:^(NSArray *friends) {
    for (NSDictionary *friend in friends) {
        NSString *name = [friend valueForKey:@"name"];
        double latitude = [[[friend valueForKey:@"coordinates"] valueForKey:@"latitude"] doubleValue];
        double longitude = [[[friend valueForKey:@"coordinates"] valueForKey:@"longitude"] doubleValue];
        NSString *meta_description = [[[friend valueForKey:@"coordinates"]valueForKey:@"metadata"]valueForKey:@"description"];
        NSLog(@"%@ lives at %f, %f tagged as '%@'", name, latitude, longitude, meta_description);
    }
} errorHandler:^(Fault *fault) {
    NSLog(@"Error: %@", fault.message);
}];
let queryBuilder = DataQueryBuilder()
queryBuilder.setRelationsDepth(relationsDepth: 1)
queryBuilder.setWhereClause(whereClause: "distance(30.084, -94.145, Coordinates.latitude, Coordinates.longitude ) < mi(300)")

let dataStore = Backendless.shared.data.ofTable("Friend")
dataStore.find(queryBuilder: queryBuilder, responseHandler: { friends in
    for friend in friends {
        if let name = friend["name"],
            let latitude = (friend["coordinates"] as! [String: Any])["latitude"],
            let longitude = (friend["coordinates"] as! [String: Any])["longitude"],
            let meta_description = ((friend["coordinates"] as! [String: Any])["metadata"] as! [String: Any])["description"] {
            print("\(name) lives at \(latitude), \(longitude) tagged as '\(meta_description)'")
        }
    }
}, errorHandler: { fault in
    print("Error: \(fault.message ?? "")")
})

The search returns all data objects within the specified distance. Each data object has the Coordinates property containing the coordinates of a geo point associated with this data object.