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

Neo4j SDN 4 node_auto_index


I'm moving from SDN 3 to SDN 4 and from Neo4j 2.3 to 3.0.1

I have a following Search Cypher query:

@Query(value = "START d=node:node_auto_index({autoIndexQuery}) MATCH (d:Decision) RETURN d")
Page<Decision> searchDecisions(@Param("autoIndexQuery") String autoIndexQuery, Pageable page);

Param autoIndexQuery in my test equals to following Lucene query:

(name:rdbma~0.33)^3 OR description:rdbma OR (name:mosyl~0.33)^3 OR description:mosyl

Right now

decisionRepository.searchDecisions(autoIndexQuery, new PageRequest(page, size));

returns null

but works fine on SDN 3 and Neo4j 2.3 and returns a Decision nodes.

This is my Neo4jTestConfig:

@Configuration
@EnableNeo4jRepositories(basePackages = "com.example")
@EnableTransactionManagement
public class Neo4jTestConfig extends Neo4jConfiguration implements BeanFactoryAware {

    private static final String NEO4J_EMBEDDED_DATABASE_PATH_PROPERTY = "neo4j.embedded.database.path";

    @Resource
    private Environment environment;

    private BeanFactory beanFactory;

    @SuppressWarnings("deprecation")
    @Bean(destroyMethod = "shutdown")
    public GraphDatabaseService graphDatabaseService() {

        // @formatter:off
        GraphDatabaseService graphDb = new GraphDatabaseFactory()
                .newEmbeddedDatabaseBuilder(new File(environment.getProperty(NEO4J_EMBEDDED_DATABASE_PATH_PROPERTY)))       
                .setConfig(GraphDatabaseSettings.node_keys_indexable, "name,description")
                .setConfig(GraphDatabaseSettings.node_auto_indexing, "true").
                newGraphDatabase();         
        // @formatter:on

        return graphDb;
    }

    /**
     * Hook into the application lifecycle and register listeners that perform
     * behaviour across types of entities during this life cycle
     * 
     */
    @EventListener
    public void handleBeforeSaveEvent(BeforeSaveEvent event) {
        Object entity = event.getEntity();
        if (entity instanceof BaseEntity) {
            BaseEntity baseEntity = (BaseEntity) entity;
            if (baseEntity.getCreateDate() == null) {
                baseEntity.setCreateDate(new Date());
            } else {
                baseEntity.setUpdateDate(new Date());
            }
        }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public BeanFactory getBeanFactory() {
        return beanFactory;
    }

    @Bean
    public org.neo4j.ogm.config.Configuration getConfiguration() {
        // @formatter:off
        org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
        config.driverConfiguration()
            .setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver");
        // @formatter:on
        return config;
    }

    @Override
    public SessionFactory getSessionFactory() {
        return new SessionFactory(getConfiguration(), "com.example");
    }

}

What can be wrong with my configuration and how to get it working on SDN 4?

UPDATED

Also, I have found the following answer Can't configure node_auto_index with InProcessServer() SDN 4 but can't find ServerControls and TestServerBuilders in my classpath.

This is my database index configuration:

@Transactional
public class SearchDaoImpl implements SearchDao {

    @Autowired
    private DecisionRepository decisionRepository;

    @PostConstruct
    public void init() {
        EmbeddedDriver embeddedDriver = (EmbeddedDriver) Components.driver();
        GraphDatabaseService databaseService = embeddedDriver.getGraphDatabaseService();
        try (Transaction t = databaseService.beginTx()) {
            Index<Node> autoIndex = databaseService.index().forNodes("node_auto_index");
            databaseService.index().setConfiguration(autoIndex, "type", "fulltext");
            databaseService.index().setConfiguration(autoIndex, "to_lower_case", "true");
            databaseService.index().setConfiguration(autoIndex, "analyzer", StandardAnalyzerV36.class.getName());
            t.success();
        }
    }
...
}

Right now I don't know where in my code is a correct place for adding

Components.setDriver(new EmbeddedDriver(graphDatabaseService()));

as suggested in the answers below

UPDATED 2

@SuppressWarnings("deprecation")
@Bean(destroyMethod = "shutdown")
public GraphDatabaseService graphDatabaseService() {

    // @formatter:off
    GraphDatabaseService graphDatabaseService = new GraphDatabaseFactory()
            .newEmbeddedDatabaseBuilder(new File(environment.getProperty(NEO4J_EMBEDDED_DATABASE_PATH_PROPERTY)))       
            .setConfig(GraphDatabaseSettings.node_keys_indexable, "name,description")
            .setConfig(GraphDatabaseSettings.node_auto_indexing, "true").
            newGraphDatabase();         
    // @formatter:on        

    Components.setDriver(new EmbeddedDriver(graphDatabaseService));

    return graphDatabaseService;
}

@Override
public SessionFactory getSessionFactory() {
    return new SessionFactory("com.example");
}

The following configuration fails with:

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getSession' defined in com.techbook.domain.configuration.Neo4jTestConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.neo4j.ogm.session.Session]: Factory method 'getSession' threw exception; nested exception is org.neo4j.ogm.exception.ServiceNotFoundException: Driver: null
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)

Solution

  • By setting the driver class name property, the EmbeddedDriver will create a private instance of the GraphDatabaseService, which is not what you want because you are initialising it yourself. Instead, you need to tell the Components class to explicitly use the driver you are providing:

    Components.setDriver(new EmbeddedDriver(graphDatabaseService()));

    This should wire up the components as you expect.