views:

340

answers:

4

I'm trying to integrate hibernate search in my project. My models are indexed, but for some reason my search queries do not return any results. I've been trying to solve this issue for a few hours now, but nothing I do seems to work.

Domain object:

@Entity
@Table(name = "roles")
@Indexed
public class Role implements GrantedAuthority {
private static final long serialVersionUID = 8227887773948216849L;

    @Id @GeneratedValue
    @DocumentId
    private Long ID;

    @Column(name = "authority", nullable = false)
    @Field(index = Index.TOKENIZED, store = Store.YES)
    private String authority;

    @ManyToMany
    @JoinTable(name = "user_roles", joinColumns = { @JoinColumn(name = "role_id") }, inverseJoinColumns = { @JoinColumn(name = "username") })
    @ContainedIn
    private List<User> users;

    ...

}

DAO:

public abstract class GenericPersistenceDao<T> implements IGenericDao<T> {

@PersistenceContext
private EntityManager entityManager;

...

    @Override
    public FullTextEntityManager getSearchManager() {
        return Search.getFullTextEntityManager(entityManager);
    }

}

Service:

@Service(value = "roleService")
public class RoleServiceImpl implements RoleService {

    @Autowired
    private RoleDao roleDAO;

    ...

    @Override
    @SuppressWarnings("unchecked")
    public List<Role> searchRoles(String keyword) throws ParseException {
        FullTextEntityManager manager = roleDAO.getSearchManager();
        TermQuery tquery = new TermQuery(new Term("authority", keyword));
        FullTextQuery query = manager.createFullTextQuery(tquery, Role.class);
        return query.getResultList();
    }

}

Test:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "classpath:applicationContext.xml" })
@Transactional
public class TestRoleService extends Assert {

    @Autowired
    private RoleService roleService;

    @Test
    public void testSearchRoles() {
       roleService.saveRole(/* role with authority="test" */);
       List<Role> roles = roleService.searchRoles("test");
       assertEquals(1, roles.size()); // returns 0
    }

}

Configurations

<persistence-unit name="hibernatePersistence" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
      <property name="hibernate.search.default.directory_provider" value="org.hibernate.search.store.FSDirectoryProvider" />
      <property name="hibernate.search.default.indexBase" value="indexes" />
        </properties>
</persistence-unit>

<!-- Entity manager -->
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
    <property name="persistenceUnitName" value="hibernatePersistence" />
</bean>

<!-- Transaction manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<!-- Enable the configuration of transaction behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager" />

<context:component-scan base-package="org.myproject" />

The database is in fact filled with a role matching that authority field value. The entity manager is valid as all my regular CRUD tests succeed. Meaning the error is entirely hibernate search(3.1.1.GA) related, but where does it go wrong?

+1  A: 

Finally managed getting it to work.. apparently the objects are not automatically indexed.. or not committed at least. My implementation now looks as follows:

public List<Role> searchRoles(String keyword) {
        // Index domain object (works)
        EntityManager manager = factory.createEntityManager();
        FullTextEntityManager ftManager = Search.getFullTextEntityManager(manager);
        ftManager.getTransaction().begin();

        List<Role> roles = ftManager.createQuery("select e from " + Role.class.getName() + " e").getResultList();
        for (Role role : roles) {
            ftManager.index(role);
        }
        ftManager.getTransaction().commit();

        // Retrieve element from search (works)
        TermQuery tquery = new TermQuery(new Term("authority", keyword));
        FullTextQuery query = ftManager.createFullTextQuery(tquery, Role.class);
        return query.getResultList();
}

By performing the index and getTransactionCommit functions the indexes are correctly stored in my indexes folder. This implementation, however, is pretty unnatural as I make an alternative entity manager for text searching. Is there a "cleaner" way to index( and commit ) records using the @Transactional annotations???

Jeroen
+1  A: 

Hello,

In theory it all works but there might be a few problems:

  • did you initially index your existing objects? While Hibernate Search index all new changes, it is not aware of the pre-existing objects and thus you need to initially index them (using ftem#index())
  • by default, HSearch hooks into either Hibernate or JTA transactions to listen to before and after transaction events. Maybe your Spring tx configuration bypasses that and thus HSearch is not triggered and thus cannot index. The best approach really is to use a real JTA transaction manager and avoid these facades.
  • if you're talking about the initial indexing (using index()), you can alternatively use #flushToIndexes() to force the indexing even if the tx is not committed.
  • last but not least, your last piece of code will likely throw an OutOfMemoryException because you load all the objects in memory before indexing them. Check the Hibernate Search reference documentation on how to properly index loads of objects in batch. Hibernate Search in Action by Manning (I'm the author) also goes deeper into all of this.
Emmanuel Bernard
+1  A: 

Eventually my problem was solved by attaching the following property: hibernate.search.worker.batch_size=1

Not only can I now properly query, but the indexes are also automatically updated whenever I persist my domain object. The only problem I'm having now is that the data inserted via my import.sql are not automatically indexed. Is there some kind of "magic" hibernate.search property available for this problem or should I index them manually?

Jeroen
A: 

Hi Jeroen,

I do have the same problem with you. How do you solve it so that the index automatically stored?

Ben