/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.ogm.service.impl;
import java.util.Map;
import org.antlr.runtime.ANTLRStringStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.antlr.runtime.TokenStream;
import org.antlr.runtime.tree.CommonTree;
import org.antlr.runtime.tree.CommonTreeNodeStream;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.search.FullTextQuery;
import org.hibernate.search.FullTextSession;
import org.hibernate.search.Search;
import org.hibernate.search.engine.spi.SearchFactoryImplementor;
import org.hibernate.search.query.DatabaseRetrievalMethod;
import org.hibernate.search.query.ObjectLookupMethod;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.sql.ast.origin.hql.parse.HQLLexer;
import org.hibernate.sql.ast.origin.hql.parse.HQLParser;
import org.hibernate.sql.ast.origin.hql.resolve.EntityNamesResolver;
import org.hibernate.sql.ast.origin.hql.resolve.LuceneJPQLWalker;
/**
* QueryParserService using the ANTLR3-powered LuceneJPQLWalker.
* Expects the targeted entities and used attributes to be indexed via Hibernate Search,
* transforming HQL and JPQL in Lucene Queries.
*
* @author Sanne Grinovero <sanne@hibernate.org> (C) 2012 Red Hat Inc.
*/
public class LuceneBasedQueryParserService implements QueryParserService {
private final ServiceRegistryImplementor registry;
private volatile SessionFactoryEntityNamesResolver entityNamesResolver;
public LuceneBasedQueryParserService(ServiceRegistryImplementor registry, Map configurationValues) {
this.registry = registry;
//TODO: make it possible to lookup the SearchFactoryImplementor at initialization time
//searchFactoryImplementor = lookupSearchFactory( registry );
}
@Override
public Query getParsedQueryExecutor(Session session, String queryString, Map<String, Object> namedParameters) {
HQLLexer lexed = new HQLLexer( new ANTLRStringStream( queryString ) );
TokenStream tokens = new CommonTokenStream( lexed );
HQLParser parser = new HQLParser( tokens );
try {
//TODO move the following logic into the hibernate-jpql-parser project?
//needs to consider usage of a parsed query plans cache
// parser#statement() is the entry point for evaluation of any kind of statement
HQLParser.statement_return r = parser.statement();
CommonTree tree = (CommonTree) r.getTree();
// To walk the resulting tree we need a treenode stream:
CommonTreeNodeStream treeStream = new CommonTreeNodeStream( tree );
// AST nodes have payloads referring to the tokens from the Lexer:
treeStream.setTokenStream( tokens );
EntityNamesResolver entityNamesResolver = getDefinedEntityNames( session.getSessionFactory() );
FullTextSession fullTextSession = Search.getFullTextSession( session );
SearchFactoryImplementor searchFactory = (SearchFactoryImplementor) fullTextSession.getSearchFactory();
// Finally create the treewalker:
LuceneJPQLWalker walker = new LuceneJPQLWalker( treeStream, searchFactory, entityNamesResolver, namedParameters );
walker.statement();
org.apache.lucene.search.Query luceneQuery = walker.getLuceneQuery();
Class targetEntity = walker.getTargetEntity();
FullTextQuery fullTextQuery = fullTextSession.createFullTextQuery( luceneQuery, targetEntity );
//Following options are mandatory to load matching entities without using a query
//(chicken and egg problem)
fullTextQuery.initializeObjectsWith( ObjectLookupMethod.SKIP, DatabaseRetrievalMethod.FIND_BY_ID );
return fullTextQuery;
}
catch (RecognitionException e) {
throw new HibernateException( "Invalid query syntax", e );
}
}
private EntityNamesResolver getDefinedEntityNames(SessionFactory sessionFactory) {
if ( entityNamesResolver == null ) {
entityNamesResolver = new SessionFactoryEntityNamesResolver( sessionFactory );
}
return entityNamesResolver;
}
}