Search code examples
neo4jspring-data-neo4j-4neo4j-spatial

Integration testing with SDN4 repositories using neo4j-spatial cypher queries


I'm starting to use neo4j-spatial in some of my code. I thought I would be able to integration test neo4j-spatial code by including the spatial server lib as a maven dependency in my project. This has not worked for me though. I can't find any documentation anywhere on this.

How can I get my integration tests to work?

Any tips anyone? :)

Just to give an idea of what I'm doing, I've pasted a segment of my controller, service and repository code below, with the final code posting being my tests that don't work against an embedded TestServer.

Repository

package nz.co.domain.core.repository.location;

import org.springframework.data.neo4j.annotation.Query;
import org.springframework.data.neo4j.repository.GraphRepository;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;

import nz.co.domain.model.pojos.Location;

@Repository
public interface LocationRepository extends GraphRepository<Location> {

    @Query("match (n:Location {domainId : {domainId}}) with n call spatial.addNode({layerName}, n) yield node return node;")
    public Location indexLocation(@Param("domainId") String locationId, @Param("layerName") String layerName);

    @Query("call spatial.withinDistance({layerName},{longitude: {longitude},latitude: {latitude}}, {rangeInKms});")
    public Iterable<Location> findLocationsWithinRange(@Param("longitude") String longitude, @Param("latitude") String latitude, @Param("rangeInKms") String rangeInKms, @Param("layerName") String layerName);

    @Query("call spatial.addPointLayer({layerName});")
    public void createLayer(@Param("layerName") String layerName);

    @Query("match ()-[:LAYER]->(n) where n.layer = {layerName} return count(n) > 0;")
    public boolean hasLayer(@Param("layerName") String layerName);

    public Location findByDomainSpecificId(String domainSpecificId);

    public Location findByGooglePlaceId(String googlePlaceId);

}

Service

package nz.co.domain.core.location;

import javax.inject.Inject;

import org.springframework.stereotype.Service;

import nz.co.domain.core.repository.location.LocationRepository;
import nz.co.domain.model.UniqueIDGenerator;
import nz.co.domain.model.pojos.Location;

@Service
public class LocationService {

    @Inject
    private LocationRepository locationRepository;

    public void createLayer(String layerName) { 
        locationRepository.createLayer(layerName);
    }

    public Location createLocation(double latitude, double longitude, String googlePlaceId, String layerName) { 
        Location location = new Location(UniqueIDGenerator.randomID(), googlePlaceId, latitude, longitude);
        boolean hasLayer = locationRepository.hasLayer(layerName);
        if (!hasLayer) { 
            locationRepository.createLayer(layerName);
        }

        Location preExistingLocation = locationRepository.findByGooglePlaceId(googlePlaceId);

        if (preExistingLocation == null) { 
            location = locationRepository.save(location);
            location = locationRepository.indexLocation(location.getDomainId(), layerName);
        } else { 
            location = preExistingLocation;
        }

        return location;
    }

    public Iterable<Location> findLocationsWithinRange(String latitude, String longitude, String rangeInKms, String layerName) { 
        return locationRepository.findLocationsWithinRange(longitude, latitude, rangeInKms, layerName);
    }

    public Location loadLocationByGooglePlaceId(String googlePlaceId) {
        return locationRepository.findByGooglePlaceId(googlePlaceId);
    }

    public Location loadLocationByDomainId(String domainId) {
        return locationRepository.findByDomainId(domainId);
    }

}

Controller

...

/**
     * Add location. 
     * @param profiletypes
     * @param profileId
     * @return
     */
    @RequestMapping(value = "/{profileType}/{profileId}/location", method = RequestMethod.POST, produces = "application/hal+json")
    public HttpEntity<LocationResource> addLocation(@PathVariable String profileType,
            @PathVariable String profileId, @RequestBody CreateLocationRequest locationRequest, @ApiIgnore LocationResourceAssembler locationResourceAssembler) {

        Profile profile = profileService.loadProfileByDomainId(profileId);

        Location location = locationService.createLocation(locationRequest.getLatitude(), locationRequest.getLongitude(), locationRequest.getGooglePlaceId(), profileType + "-layer");

        profile.setLocation(location);
        profileService.save(profile);

        location = locationService.loadLocationByGooglePlaceId(location.getGooglePlaceId());

        LocationResource resource = locationResourceAssembler.toResource(location);
        return new ResponseEntity<>(resource, HttpStatus.CREATED) ;
    }

...

Service test

(It is my tests here that I can't get working as part of a standard build against an embedded TestServer)

package nz.co.domain.core.location;

import javax.inject.Inject;

import org.junit.Ignore;
import org.junit.Test;

import nz.co.domain.core.AbstractTest;
import nz.co.domain.model.pojos.Location;

public class LocationServiceTest extends AbstractTest {

    @Inject
    private LocationService locationService;

    @Test
    public void indexLocationTest() { 

        // The Rogue and Vagabond
        Location rogueAndVagabond = locationService.createLocation(51.469150, 7.23212, "ChIJmwfKGdivOG0R9eTCVFOngnU", "test-layer");

        /* more test code here */

        // Te Papa Museum
        Location tePapaMuseum = locationService.createLocation(-41.289964, 174.778354, "ChIJfxn9AdGvOG0RpLRGGO3tRX8", "test-layer");

        /* more test code here */

        // Porirua Club
        Location poriruaClub = locationService.createLocation(-41.136048, 174.836409, "ChIJ9wl16m1TP20R3G3npuEokak", "test-layer");

        /* more test code here */

        Iterable<Location> findLocationsWithinRange = locationService.findLocationsWithinRange("-41.289964", "longitude", "5", "test-layer");

       /* more test code here */

    }
}

Solution

  • Spatial functionality is not provided in SDN 4 yet. If you integrate the neo4j-spatial lib then the only option you have at the moment is to use it directly- there will be no repository support etc. However, spatial integration work is currently in progress, so some basic functionality should be introduced in the next release.