/*
* Copyright 2012 - 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.solr.core;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrRequest;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.client.solrj.response.SolrPingResponse;
import org.apache.solr.client.solrj.response.UpdateResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.solr.UncategorizedSolrException;
import org.springframework.data.solr.core.QueryParserBase.NamedObjectsFacetAndHighlightQuery;
import org.springframework.data.solr.core.QueryParserBase.NamedObjectsFacetQuery;
import org.springframework.data.solr.core.QueryParserBase.NamedObjectsHighlightQuery;
import org.springframework.data.solr.core.QueryParserBase.NamedObjectsQuery;
import org.springframework.data.solr.core.convert.MappingSolrConverter;
import org.springframework.data.solr.core.convert.SolrConverter;
import org.springframework.data.solr.core.mapping.SimpleSolrMappingContext;
import org.springframework.data.solr.core.mapping.SolrPersistentEntity;
import org.springframework.data.solr.core.mapping.SolrPersistentProperty;
import org.springframework.data.solr.core.query.FacetAndHighlightQuery;
import org.springframework.data.solr.core.query.FacetQuery;
import org.springframework.data.solr.core.query.HighlightQuery;
import org.springframework.data.solr.core.query.Query;
import org.springframework.data.solr.core.query.SolrDataQuery;
import org.springframework.data.solr.core.query.TermsQuery;
import org.springframework.data.solr.core.query.result.Cursor;
import org.springframework.data.solr.core.query.result.DelegatingCursor;
import org.springframework.data.solr.core.query.result.FacetAndHighlightPage;
import org.springframework.data.solr.core.query.result.FacetPage;
import org.springframework.data.solr.core.query.result.GroupPage;
import org.springframework.data.solr.core.query.result.HighlightPage;
import org.springframework.data.solr.core.query.result.ScoredPage;
import org.springframework.data.solr.core.query.result.SolrResultPage;
import org.springframework.data.solr.core.query.result.SpellcheckQueryResult.Alternative;
import org.springframework.data.solr.core.query.result.StatsPage;
import org.springframework.data.solr.core.query.result.TermsPage;
import org.springframework.data.solr.core.query.result.TermsResultPage;
import org.springframework.data.solr.core.schema.DefaultSchemaOperations;
import org.springframework.data.solr.core.schema.SchemaOperations;
import org.springframework.data.solr.core.schema.SolrPersistentEntitySchemaCreator;
import org.springframework.data.solr.core.schema.SolrPersistentEntitySchemaCreator.Feature;
import org.springframework.data.solr.server.SolrClientFactory;
import org.springframework.data.solr.server.support.HttpSolrClientFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Implementation of {@link SolrOperations}
*
* @author Christoph Strobl
* @author Joachim Uhrlass
* @author Francisco Spaeth
* @author Shiradwade Sateesh Krishna
* @author David Webb
* @author Petar Tahchiev
*/
public class SolrTemplate implements SolrOperations, InitializingBean, ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(SolrTemplate.class);
private static final PersistenceExceptionTranslator EXCEPTION_TRANSLATOR = new SolrExceptionTranslator();
private final QueryParsers queryParsers = new QueryParsers();
private MappingContext<? extends SolrPersistentEntity<?>, SolrPersistentProperty> mappingContext;
private ApplicationContext applicationContext;
private final RequestMethod defaultRequestMethod;
@SuppressWarnings("serial") //
private static final List<String> ITERABLE_CLASSES = new ArrayList<String>() {
{
add(List.class.getName());
add(Collection.class.getName());
add(Iterator.class.getName());
}
};
private SolrClientFactory solrClientFactory;
private SolrConverter solrConverter;
private Set<Feature> schemaCreationFeatures;
public SolrTemplate(SolrClient solrClient) {
this(new HttpSolrClientFactory(solrClient));
}
public SolrTemplate(SolrClient solrClient, RequestMethod requestMethod) {
this(new HttpSolrClientFactory(solrClient), requestMethod);
}
public SolrTemplate(SolrClientFactory solrClientFactory) {
this(solrClientFactory, (SolrConverter) null);
}
public SolrTemplate(SolrClientFactory solrClientFactory, RequestMethod requestMethod) {
this(solrClientFactory, null, requestMethod);
}
public SolrTemplate(SolrClientFactory solrClientFactory, SolrConverter solrConverter) {
this(solrClientFactory, solrConverter, RequestMethod.GET);
}
/**
* @param solrClientFactory must not be {@literal null}.
* @param solrConverter must not be {@literal null}.
* @param defaultRequestMethod can be {@literal null}. Will be defaulted to {@link RequestMethod#GET}
* @since 2.0
*/
public SolrTemplate(SolrClientFactory solrClientFactory, SolrConverter solrConverter,
RequestMethod defaultRequestMethod) {
Assert.notNull(solrClientFactory, "SolrClientFactory must not be 'null'.");
this.solrClientFactory = solrClientFactory;
this.defaultRequestMethod = defaultRequestMethod != null ? defaultRequestMethod : RequestMethod.GET;
this.solrConverter = solrConverter;
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#execute(org.springframework.data.solr.core.SolrCallback)
*/
@Override
public <T> T execute(SolrCallback<T> action) {
Assert.notNull(action, "SolrCallback must not be null!");
try {
SolrClient solrClient = this.getSolrClient();
return action.doInSolr(solrClient);
} catch (Exception e) {
DataAccessException resolved = getExceptionTranslator().translateExceptionIfPossible(
e instanceof RuntimeException ? (RuntimeException) e : new RuntimeException(e.getMessage(), e));
throw resolved == null ? new UncategorizedSolrException(e.getMessage(), e) : resolved;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#ping()
*/
@Override
public SolrPingResponse ping() {
return execute(SolrClient::ping);
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#count(org.springframework.data.solr.core.query.SolrDataQuery)
*/
@Override
public long count(final SolrDataQuery query) {
return count(query, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#count(org.springframework.data.solr.core.query.SolrDataQuery, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public long count(final SolrDataQuery query, final RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(method, "Method must not be 'null'.");
return execute(solrClient -> {
SolrQuery solrQuery = queryParsers.getForClass(query.getClass()).constructSolrQuery(query);
solrQuery.setStart(0);
solrQuery.setRows(0);
return solrClient.query(solrQuery, getSolrRequestMethod(method)).getResults().getNumFound();
});
}
@Override
public long count(String collection, SolrDataQuery query) {
return count(collection, query, getDefaultRequestMethod());
}
@Override
public long count(String collection, SolrDataQuery query, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(method, "Method must not be 'null'.");
return execute(solrClient -> {
SolrQuery solrQuery = queryParsers.getForClass(query.getClass()).constructSolrQuery(query);
solrQuery.setStart(0);
solrQuery.setRows(0);
return solrClient.query(collection, solrQuery, getSolrRequestMethod(method)).getResults().getNumFound();
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveBean(java.lang.Object)
*/
@Override
public UpdateResponse saveBean(Object obj) {
return saveBean(obj, -1);
}
@Override
public UpdateResponse saveBean(String collection, Object obj) {
return saveBean(collection, obj, -1);
}
@Override
public UpdateResponse saveBean(String collection, Object obj, int commitWithinMs) {
assertNoCollection(obj);
return execute(solrClient -> solrClient.add(collection, convertBeanToSolrInputDocument(obj), commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveBean(java.lang.Object, int)
*/
@Override
public UpdateResponse saveBean(final Object objectToAdd, final int commitWithinMs) {
assertNoCollection(objectToAdd);
return execute(solrClient -> solrClient.add(convertBeanToSolrInputDocument(objectToAdd), commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveBeans(java.util.Collection)
*/
@Override
public UpdateResponse saveBeans(Collection<?> beans) {
return saveBeans(beans, -1);
}
@Override
public UpdateResponse saveBeans(String collection, Collection<?> beans) {
return saveBeans(collection, beans, -1);
}
@Override
public UpdateResponse saveBeans(String collection, Collection<?> beans, int commitWithinMs) {
return execute(solrClient -> solrClient.add(collection, convertBeansToSolrInputDocuments(beans), commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveBeans(java.util.Collection, int)
*/
@Override
public UpdateResponse saveBeans(final Collection<?> beansToAdd, final int commitWithinMs) {
return execute(solrClient -> solrClient.add(convertBeansToSolrInputDocuments(beansToAdd), commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveDocument(org.apache.solr.common.SolrInputDocument)
*/
@Override
public UpdateResponse saveDocument(SolrInputDocument document) {
return saveDocument(document, -1);
}
@Override
public UpdateResponse saveDocument(String collection, SolrInputDocument document) {
return saveDocument(collection, document, -1);
}
@Override
public UpdateResponse saveDocument(String collection, SolrInputDocument document, int commitWithinMs) {
return execute(solrClient -> solrClient.add(collection, document, commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveDocument(org.apache.solr.common.SolrInputDocument, int)
*/
@Override
public UpdateResponse saveDocument(final SolrInputDocument documentToAdd, final int commitWithinMs) {
return execute(solrClient -> solrClient.add(documentToAdd, commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveDocuments(java.util.Collection)
*/
@Override
public UpdateResponse saveDocuments(Collection<SolrInputDocument> documents) {
return saveDocuments(documents, -1);
}
@Override
public UpdateResponse saveDocuments(String collection, Collection<SolrInputDocument> documents) {
return saveDocuments(collection, documents, -1);
}
@Override
public UpdateResponse saveDocuments(String collection, Collection<SolrInputDocument> documents, int commitWithinMs) {
return execute(solrClient -> solrClient.add(collection, documents, commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#saveDocuments(java.util.Collection, int)
*/
@Override
public UpdateResponse saveDocuments(final Collection<SolrInputDocument> documentsToAdd, final int commitWithinMs) {
return execute(solrClient -> solrClient.add(documentsToAdd, commitWithinMs));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#delete(org.springframework.data.solr.core.query.SolrDataQuery)
*/
@Override
public UpdateResponse delete(SolrDataQuery query) {
Assert.notNull(query, "Query must not be 'null'.");
final String queryString = this.queryParsers.getForClass(query.getClass()).getQueryString(query);
return execute(solrClient -> solrClient.deleteByQuery(queryString));
}
@Override
public UpdateResponse delete(String collection, SolrDataQuery query) {
Assert.notNull(query, "Query must not be 'null'.");
final String queryString = this.queryParsers.getForClass(query.getClass()).getQueryString(query);
return execute(solrClient -> solrClient.deleteByQuery(collection, queryString));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#deleteById(java.lang.String)
*/
@Override
public UpdateResponse deleteById(final String id) {
Assert.notNull(id, "Cannot delete 'null' id.");
return execute(solrClient -> solrClient.deleteById(id));
}
@Override
public UpdateResponse deleteById(String collection, String id) {
Assert.notNull(id, "Cannot delete 'null' id.");
return execute(solrClient -> solrClient.deleteById(collection, id));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#deleteById(java.util.Collection)
*/
@Override
public UpdateResponse deleteById(Collection<String> ids) {
Assert.notNull(ids, "Cannot delete 'null' collection.");
return execute(solrClient -> solrClient.deleteById(ids.stream().collect(Collectors.toList())));
}
@Override
public UpdateResponse deleteById(String collection, Collection<String> ids) {
Assert.notNull(ids, "Cannot delete 'null' collection.");
return execute(solrClient -> solrClient.deleteById(collection, ids.stream().collect(Collectors.toList())));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForObject(org.springframework.data.solr.core.query.Query, java.lang.Class)
*/
@Override
public <T> Optional<T> queryForObject(Query query, Class<T> clazz) {
return queryForObject(query, clazz, getDefaultRequestMethod());
}
@Override
public <T> Optional<T> queryForObject(String collection, Query query, Class<T> clazz) {
return queryForObject(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForObject(org.springframework.data.solr.core.query.Query, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> Optional<T> queryForObject(String collection, Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
query.setPageRequest(PageRequest.of(0, 1));
QueryResponse response = querySolr(collection, query, clazz, method);
if (response.getResults().size() > 0) {
if (response.getResults().size() > 1) {
LOGGER.warn("More than 1 result found for singe result query ('{}'), returning first entry in list");
}
return Optional.ofNullable(convertSolrDocumentListToBeans(response.getResults(), clazz).get(0));
}
return Optional.empty();
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForObject(org.springframework.data.solr.core.query.Query, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> Optional<T> queryForObject(Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
query.setPageRequest(PageRequest.of(0, 1));
QueryResponse response = querySolr(query, clazz, method);
if (response.getResults().size() > 0) {
if (response.getResults().size() > 1) {
LOGGER.warn("More than 1 result found for singe result query ('{}'), returning first entry in list");
}
return Optional.ofNullable(convertSolrDocumentListToBeans(response.getResults(), clazz).get(0));
}
return Optional.empty();
}
private <T> SolrResultPage<T> doQueryForPage(Query query, Class<T> clazz, RequestMethod requestMethod) {
return doQueryForPage(null, query, clazz, requestMethod);
}
private <T> SolrResultPage<T> doQueryForPage(String collection, Query query, Class<T> clazz,
RequestMethod requestMethod) {
QueryResponse response = null;
NamedObjectsQuery namedObjectsQuery = new NamedObjectsQuery(query);
response = querySolr(collection, namedObjectsQuery, clazz,
requestMethod != null ? requestMethod : getDefaultRequestMethod());
Map<String, Object> objectsName = namedObjectsQuery.getNamesAssociation();
return createSolrResultPage(query, clazz, response, objectsName);
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForPage(org.springframework.data.solr.core.query.Query, java.lang.Class)
*/
@Override
public <T> ScoredPage<T> queryForPage(Query query, Class<T> clazz) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
return doQueryForPage(query, clazz, getDefaultRequestMethod());
}
@Override
public <T> ScoredPage<T> queryForPage(String collection, Query query, Class<T> clazz) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
return doQueryForPage(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryFor(org.springframework.data.solr.core.query.Query, java.lang.Class)
*/
@Override
public <T, S extends Page<T>> S query(Query query, Class<T> clazz) {
return query(query, clazz, getDefaultRequestMethod());
}
@Override
public <T, S extends Page<T>> S query(String collection, Query query, Class<T> clazz) {
return query(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#query(org.springframework.data.solr.core.query.Query, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T, S extends Page<T>> S query(Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(clazz, "Method must not be 'null'.");
return (S) doQueryForPage(query, clazz, method);
}
public <T, S extends Page<T>> S query(String collection, Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(clazz, "Method must not be 'null'.");
return (S) doQueryForPage(collection, query, clazz, method);
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForPage(org.springframework.data.solr.core.query.Query, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> ScoredPage<T> queryForPage(Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(method, "Method class must not be 'null'.");
return doQueryForPage(query, clazz, method);
}
@Override
public <T> ScoredPage<T> queryForPage(String collection, Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(method, "Method class must not be 'null'.");
return doQueryForPage(collection, query, clazz, method);
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForGroupPage(org.springframework.data.solr.core.query.Query, java.lang.Class)
*/
@Override
public <T> GroupPage<T> queryForGroupPage(Query query, Class<T> clazz) {
return queryForGroupPage(query, clazz, getDefaultRequestMethod());
}
@Override
public <T> GroupPage<T> queryForGroupPage(String collection, Query query, Class<T> clazz) {
return queryForGroupPage(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForGroupPage(org.springframework.data.solr.core.query.Query, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> GroupPage<T> queryForGroupPage(Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(method, "Method class must not be 'null'.");
return doQueryForPage(query, clazz, method);
}
@Override
public <T> GroupPage<T> queryForGroupPage(String collection, Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(method, "Method class must not be 'null'.");
return doQueryForPage(collection, query, clazz, method);
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForStatsPage(org.springframework.data.solr.core.query.Query, java.lang.Class)
*/
@Override
public <T> StatsPage<T> queryForStatsPage(Query query, Class<T> clazz) {
return queryForStatsPage(query, clazz, getDefaultRequestMethod());
}
@Override
public <T> StatsPage<T> queryForStatsPage(String collection, Query query, Class<T> clazz) {
return queryForStatsPage(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForStatsPage(org.springframework.data.solr.core.query.Query, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> StatsPage<T> queryForStatsPage(Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(method, "Method class must not be 'null'.");
return doQueryForPage(query, clazz, method);
}
public <T> StatsPage<T> queryForStatsPage(String collection, Query query, Class<T> clazz, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
Assert.notNull(method, "Method class must not be 'null'.");
return doQueryForPage(collection, query, clazz, method);
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForFacetPage(org.springframework.data.solr.core.query.FacetQuery, java.lang.Class)
*/
@Override
public <T> FacetPage<T> queryForFacetPage(FacetQuery query, Class<T> clazz) {
return queryForFacetPage(query, clazz, getDefaultRequestMethod());
}
@Override
public <T> FacetPage<T> queryForFacetPage(String collection, FacetQuery query, Class<T> clazz) {
return queryForFacetPage(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForFacetPage(org.springframework.data.solr.core.query.FacetQuery, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> FacetPage<T> queryForFacetPage(FacetQuery query, Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
NamedObjectsFacetQuery namedObjectsQuery = new NamedObjectsFacetQuery(query);
return createSolrResultPage(query, clazz, querySolr(namedObjectsQuery, clazz, method),
namedObjectsQuery.getNamesAssociation());
}
@Override
public <T> FacetPage<T> queryForFacetPage(String collection, FacetQuery query, Class<T> clazz, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
NamedObjectsFacetQuery namedObjectsQuery = new NamedObjectsFacetQuery(query);
return createSolrResultPage(query, clazz, querySolr(collection, namedObjectsQuery, clazz, method),
namedObjectsQuery.getNamesAssociation());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForHighlightPage(org.springframework.data.solr.core.query.HighlightQuery, java.lang.Class)
*/
@Override
public <T> HighlightPage<T> queryForHighlightPage(HighlightQuery query, Class<T> clazz) {
return queryForHighlightPage(query, clazz, getDefaultRequestMethod());
}
@Override
public <T> HighlightPage<T> queryForHighlightPage(String collection, HighlightQuery query, Class<T> clazz) {
return queryForHighlightPage(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForHighlightPage(org.springframework.data.solr.core.query.HighlightQuery, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> HighlightPage<T> queryForHighlightPage(HighlightQuery query, Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
NamedObjectsHighlightQuery namedObjectsQuery = new NamedObjectsHighlightQuery(query);
QueryResponse response = querySolr(namedObjectsQuery, clazz, getDefaultRequestMethod());
return createSolrResultPage(query, clazz, response, namedObjectsQuery.getNamesAssociation());
}
@Override
public <T> HighlightPage<T> queryForHighlightPage(String collection, HighlightQuery query, Class<T> clazz,
RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
NamedObjectsHighlightQuery namedObjectsQuery = new NamedObjectsHighlightQuery(query);
QueryResponse response = querySolr(collection, namedObjectsQuery, clazz, getDefaultRequestMethod());
return createSolrResultPage(query, clazz, response, namedObjectsQuery.getNamesAssociation());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForFacetAndHighlightPage(org.springframework.data.solr.core.query.FacetAndHighlightQuery, java.lang.Class)
*/
@Override
public <T> FacetAndHighlightPage<T> queryForFacetAndHighlightPage(FacetAndHighlightQuery query, Class<T> clazz) {
return queryForFacetAndHighlightPage(query, clazz, getDefaultRequestMethod());
}
@Override
public <T> FacetAndHighlightPage<T> queryForFacetAndHighlightPage(String collection, FacetAndHighlightQuery query,
Class<T> clazz) {
return queryForFacetAndHighlightPage(collection, query, clazz, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForFacetAndHighlightPage(org.springframework.data.solr.core.query.FacetAndHighlightQuery, java.lang.Class, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public <T> FacetAndHighlightPage<T> queryForFacetAndHighlightPage(FacetAndHighlightQuery query, Class<T> clazz,
RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
NamedObjectsFacetAndHighlightQuery namedObjectsFacetAndHighlightQuery = new NamedObjectsFacetAndHighlightQuery(
query);
QueryResponse response = querySolr(namedObjectsFacetAndHighlightQuery, clazz, method);
Map<String, Object> objectsName = namedObjectsFacetAndHighlightQuery.getNamesAssociation();
return createSolrResultPage(query, clazz, response, objectsName);
}
@Override
public <T> FacetAndHighlightPage<T> queryForFacetAndHighlightPage(String collection, FacetAndHighlightQuery query,
Class<T> clazz, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
Assert.notNull(clazz, "Target class must not be 'null'.");
NamedObjectsFacetAndHighlightQuery namedObjectsFacetAndHighlightQuery = new NamedObjectsFacetAndHighlightQuery(
query);
QueryResponse response = querySolr(collection, namedObjectsFacetAndHighlightQuery, clazz, method);
Map<String, Object> objectsName = namedObjectsFacetAndHighlightQuery.getNamesAssociation();
return createSolrResultPage(query, clazz, response, objectsName);
}
private <T> SolrResultPage<T> createSolrResultPage(Query query, Class<T> clazz, QueryResponse response,
Map<String, Object> objectsName) {
List<T> beans = convertQueryResponseToBeans(response, clazz);
SolrDocumentList results = response.getResults();
long numFound = results == null ? 0 : results.getNumFound();
Float maxScore = results == null ? null : results.getMaxScore();
Pageable pageRequest = query.getPageRequest();
SolrResultPage<T> page = new SolrResultPage<>(beans, pageRequest, numFound, maxScore);
page.setFieldStatsResults(ResultHelper.convertFieldStatsInfoToFieldStatsResultMap(response.getFieldStatsInfo()));
page.setGroupResults(
ResultHelper.convertGroupQueryResponseToGroupResultMap(query, objectsName, response, this, clazz));
if (query instanceof HighlightQuery) {
ResultHelper.convertAndAddHighlightQueryResponseToResultPage(response, page);
}
if (query instanceof FacetQuery) {
page.setFacetQueryResultPage(
ResultHelper.convertFacetQueryResponseToFacetQueryResult((FacetQuery) query, response));
page.addAllFacetFieldResultPages(
ResultHelper.convertFacetQueryResponseToFacetPageMap((FacetQuery) query, response));
page.addAllFacetPivotFieldResult(
ResultHelper.convertFacetQueryResponseToFacetPivotMap((FacetQuery) query, response));
page.addAllRangeFacetFieldResultPages(
ResultHelper.convertFacetQueryResponseToRangeFacetPageMap((FacetQuery) query, response));
}
if (query.getSpellcheckOptions() != null) {
Map<String, List<Alternative>> suggestions = ResultHelper.extreactSuggestions(response);
for (Entry<String, List<Alternative>> entry : suggestions.entrySet()) {
page.addSuggestions(entry.getKey(), entry.getValue());
}
}
return page;
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForTermsPage(org.springframework.data.solr.core.query.TermsQuery)
*/
@Override
public TermsPage queryForTermsPage(TermsQuery query) {
return queryForTermsPage(query, getDefaultRequestMethod());
}
@Override
public TermsPage queryForTermsPage(String collection, TermsQuery query) {
return queryForTermsPage(collection, query, getDefaultRequestMethod());
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForTermsPage(org.springframework.data.solr.core.query.TermsQuery, org.springframework.data.solr.core.RequestMethod)
*/
@Override
public TermsPage queryForTermsPage(TermsQuery query, RequestMethod method) {
Assert.notNull(query, "Query must not be 'null'.");
QueryResponse response = querySolr(null, query, null, method);
TermsResultPage page = new TermsResultPage();
page.addAllTerms(ResultHelper.convertTermsQueryResponseToTermsMap(response));
return page;
}
@Override
public TermsPage queryForTermsPage(String collection, TermsQuery query, RequestMethod method) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(query, "Query must not be 'null'.");
QueryResponse response = querySolr(collection, query, null, method);
TermsResultPage page = new TermsResultPage();
page.addAllTerms(ResultHelper.convertTermsQueryResponseToTermsMap(response));
return page;
}
final QueryResponse querySolr(SolrDataQuery query, Class<?> clazz) {
return querySolr(query, clazz, defaultRequestMethod);
}
final QueryResponse querySolr(SolrDataQuery query, Class<?> clazz, RequestMethod requestMethod) {
return querySolr(null, query, clazz, requestMethod);
}
final QueryResponse querySolr(String collection, SolrDataQuery query, Class<?> clazz, RequestMethod requestMethod) {
Assert.notNull(query, "Query must not be 'null'");
Assert.notNull(requestMethod, "RequestMethod must not be 'null'");
SolrQuery solrQuery = queryParsers.getForClass(query.getClass()).constructSolrQuery(query);
if (clazz != null) {
SolrPersistentEntity<?> persistedEntity = mappingContext.getPersistentEntity(clazz)
.orElseThrow(() -> new IllegalArgumentException("No persistent entity found."));
if (persistedEntity.hasScoreProperty()) {
solrQuery.setIncludeScore(true);
}
}
LOGGER.debug("Executing query '" + solrQuery + "' against solr.");
return executeSolrQuery(collection, solrQuery, getSolrRequestMethod(requestMethod));
}
final QueryResponse executeSolrQuery(final SolrQuery solrQuery, final SolrRequest.METHOD method) {
return executeSolrQuery(null, solrQuery, method);
}
final QueryResponse executeSolrQuery(String collection, final SolrQuery solrQuery, final SolrRequest.METHOD method) {
return execute(solrServer -> solrServer.query(collection, solrQuery, method));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#commit()
*/
@Override
public void commit() {
execute(SolrClient::commit);
}
@Override
public void commit(String collection) {
execute(solrClient -> solrClient.commit(collection));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#softCommit()
*/
@Override
public void softCommit() {
execute(solrClient -> solrClient.commit(true, true, true));
}
@Override
public void softCommit(String collection) {
execute(solrClient -> solrClient.commit(collection, true, true, true));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#rollback()
*/
@Override
public void rollback() {
execute(SolrClient::rollback);
}
@Override
public void rollback(String collection) {
execute(SolrClient::rollback);
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#convertBeanToSolrInputDocument(java.lang.Object)
*/
@Override
public SolrInputDocument convertBeanToSolrInputDocument(Object bean) {
if (bean instanceof SolrInputDocument) {
return (SolrInputDocument) bean;
}
SolrInputDocument document = new SolrInputDocument();
getConverter().write(bean, document);
return document;
}
/**
* @param collectionName
* @return
* @since 1.3
*/
public String getSchemaName(final String collectionName) {
return getSchemaOperations(collectionName).getSchemaName();
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#queryForCursor(org.springframework.data.solr.core.query.Query, java.lang.Class)
*/
@Override
@SuppressWarnings("resource")
public <T> Cursor<T> queryForCursor(Query query, final Class<T> clazz) {
return new DelegatingCursor<T>(queryParsers.getForClass(query.getClass()).constructSolrQuery(query)) {
@Override
protected org.springframework.data.solr.core.query.result.DelegatingCursor.PartialResult<T> doLoad(
SolrQuery nativeQuery) {
QueryResponse response = executeSolrQuery(nativeQuery, getSolrRequestMethod(getDefaultRequestMethod()));
if (response == null) {
return new PartialResult<>("", Collections.<T> emptyList());
}
return new PartialResult<>(response.getNextCursorMark(), convertQueryResponseToBeans(response, clazz));
}
}.open();
}
public <T> Cursor<T> queryForCursor(String collection, Query query, final Class<T> clazz) {
return new DelegatingCursor<T>(queryParsers.getForClass(query.getClass()).constructSolrQuery(query)) {
@Override
protected org.springframework.data.solr.core.query.result.DelegatingCursor.PartialResult<T> doLoad(
SolrQuery nativeQuery) {
QueryResponse response = executeSolrQuery(collection, nativeQuery,
getSolrRequestMethod(getDefaultRequestMethod()));
if (response == null) {
return new PartialResult<>("", Collections.<T> emptyList());
}
return new PartialResult<>(response.getNextCursorMark(), convertQueryResponseToBeans(response, clazz));
}
}.open();
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#getById(java.util.Collection, java.lang.Class)
*/
@Override
public <T> Collection<T> getById(final Collection<? extends Serializable> ids, final Class<T> clazz) {
if (CollectionUtils.isEmpty(ids)) {
return Collections.emptyList();
}
return execute(solrClient -> convertSolrDocumentListToBeans(
solrClient.getById(ids.stream().map(Object::toString).collect(Collectors.toList())), clazz));
}
@Override
public <T> Collection<T> getById(String collection, final Collection<? extends Serializable> ids,
final Class<T> clazz) {
if (CollectionUtils.isEmpty(ids)) {
return Collections.emptyList();
}
return execute(solrClient -> convertSolrDocumentListToBeans(
solrClient.getById(collection, ids.stream().map(Object::toString).collect(Collectors.toList())), clazz));
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#getById(java.io.Serializable, java.lang.Class)
*/
@Override
public <T> T getById(Serializable id, Class<T> clazz) {
Assert.notNull(id, "Id must not be 'null'.");
Collection<T> result = getById(Collections.singletonList(id), clazz);
if (result.isEmpty()) {
return null;
}
return result.iterator().next();
}
public <T> T getById(String collection, Serializable id, Class<T> clazz) {
Assert.notNull(collection, "Collection must not be null!");
Assert.notNull(id, "Id must not be 'null'.");
Collection<T> result = getById(collection, Collections.singletonList(id), clazz);
if (result.isEmpty()) {
return null;
}
return result.iterator().next();
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#getSchemaOperations(java.lang.String)
*/
@Override
public SchemaOperations getSchemaOperations(String collection) {
return new DefaultSchemaOperations(collection, this);
}
private Collection<SolrInputDocument> convertBeansToSolrInputDocuments(Iterable<?> beans) {
if (beans == null) {
return Collections.emptyList();
}
List<SolrInputDocument> resultList = new ArrayList<>();
for (Object bean : beans) {
resultList.add(convertBeanToSolrInputDocument(bean));
}
return resultList;
}
public <T> List<T> convertQueryResponseToBeans(QueryResponse response, Class<T> targetClass) {
return response != null ? convertSolrDocumentListToBeans(response.getResults(), targetClass)
: Collections.<T> emptyList();
}
public <T> List<T> convertSolrDocumentListToBeans(SolrDocumentList documents, Class<T> targetClass) {
if (documents == null) {
return Collections.<T> emptyList();
}
return getConverter().read(documents, targetClass);
}
public <T> T convertSolrDocumentToBean(SolrDocument document, Class<T> targetClass) {
return getConverter().read(targetClass, document);
}
protected void assertNoCollection(Object o) {
if (null != o && (o.getClass().isArray() || ITERABLE_CLASSES.contains(o.getClass().getName()))) {
throw new IllegalArgumentException("Collections are not supported for this operation");
}
}
private SolrConverter getDefaultSolrConverter() {
MappingSolrConverter converter = new MappingSolrConverter(this.mappingContext);
converter.afterPropertiesSet(); // have to call this one to initialize default converters
return converter;
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#getSolrClient()
*/
@Override
public final SolrClient getSolrClient() {
return solrClientFactory.getSolrClient();
}
/*
* (non-Javadoc)
* @see org.springframework.data.solr.core.SolrOperations#getConverter()
*/
@Override
public SolrConverter getConverter() {
return this.solrConverter;
}
public static PersistenceExceptionTranslator getExceptionTranslator() {
return EXCEPTION_TRANSLATOR;
}
/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
public void registerQueryParser(Class<? extends SolrDataQuery> clazz, QueryParser queryParser) {
this.queryParsers.registerParser(clazz, queryParser);
}
public void setSolrConverter(SolrConverter solrConverter) {
this.solrConverter = solrConverter;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() {
if (this.mappingContext == null) {
this.mappingContext = new SimpleSolrMappingContext(
new SolrPersistentEntitySchemaCreator(this.solrClientFactory).enable(this.schemaCreationFeatures));
}
if (this.solrConverter == null) {
this.solrConverter = getDefaultSolrConverter();
}
registerPersistenceExceptionTranslator();
}
private void registerPersistenceExceptionTranslator() {
if (this.applicationContext != null
&& this.applicationContext.getBeansOfType(PersistenceExceptionTranslator.class).isEmpty()) {
if (this.applicationContext instanceof ConfigurableApplicationContext) {
((ConfigurableApplicationContext) this.applicationContext).getBeanFactory()
.registerSingleton("solrExceptionTranslator", EXCEPTION_TRANSLATOR);
}
}
}
private SolrRequest.METHOD getSolrRequestMethod(RequestMethod requestMethod) {
RequestMethod rm = requestMethod != null ? requestMethod : getDefaultRequestMethod();
switch (rm) {
case GET:
return SolrRequest.METHOD.GET;
case POST:
return SolrRequest.METHOD.POST;
case PUT:
return SolrRequest.METHOD.PUT;
}
throw new IllegalArgumentException("Unsupported request method type");
}
/**
* @since 1.3
* @param mappingContext
*/
public void setMappingContext(
MappingContext<? extends SolrPersistentEntity<?>, SolrPersistentProperty> mappingContext) {
this.mappingContext = mappingContext;
}
/**
* @since 1.3
* @param schemaCreationFeatures
*/
public void setSchemaCreationFeatures(Collection<Feature> schemaCreationFeatures) {
this.schemaCreationFeatures = new HashSet<>(schemaCreationFeatures);
}
/**
* @since 1.3
* @return
*/
public Set<Feature> getSchemaCreationFeatures() {
if (CollectionUtils.isEmpty(this.schemaCreationFeatures)) {
return Collections.emptySet();
}
return Collections.unmodifiableSet(this.schemaCreationFeatures);
}
/**
* @return never {@literal null}.
*/
public RequestMethod getDefaultRequestMethod() {
return defaultRequestMethod;
}
}