package org.jai.search.setup.impl;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import org.jai.search.client.SearchClientService;
import org.jai.search.config.ElasticSearchIndexConfig;
import org.jai.search.data.SampleDataGeneratorService;
import org.jai.search.index.IndexProductDataService;
import org.jai.search.model.ProductGroup;
import org.jai.search.setup.IndexSchemaBuilder;
import org.jai.search.setup.SetupIndexService;
import org.apache.commons.lang.StringUtils;
import org.elasticsearch.action.admin.cluster.state.ClusterStateResponse;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeRequestBuilder;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse;
import org.elasticsearch.action.admin.indices.analyze.AnalyzeResponse.AnalyzeToken;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.routing.IndexRoutingTable;
import org.elasticsearch.common.xcontent.ToXContent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.Set;
@Service
public class SetupIndexServiceImpl implements SetupIndexService
{
private static final Logger logger = LoggerFactory.getLogger(SetupIndexServiceImpl.class);
@Autowired
private SearchClientService searchClientService;
@Autowired
private IndexProductDataService indexProductData;
@Autowired
private SampleDataGeneratorService sampleDataGenerator;
@Override
public void setupAllIndices(final boolean parentRelationship)
{
for (final ElasticSearchIndexConfig config : ElasticSearchIndexConfig.values())
{
reCreateIndex(config);
// add mappings
updateDocumentTypeMapping(config, null, config.getGroupDocumentType(), parentRelationship);
updateDocumentTypeMapping(config, null, config.getDocumentType(), parentRelationship);
updateDocumentTypeMapping(config, null, config.getPropertiesDocumentType(), parentRelationship);
// index all data
indexProductData.indexAllProducts(config, sampleDataGenerator.generateProductsSampleData());
}
}
@Override
public void setupAllIndices()
{
setupAllIndices(false);
}
@Override
public void reCreateIndex(final ElasticSearchIndexConfig config)
{
final Date date = new Date();
final String suffixedIndexName = getSuffixedIndexName(config.getIndexAliasName(), date);
if (isIndexExists(suffixedIndexName))
{
// drop existing index
deleteIndex(suffixedIndexName);
}
// create indices
createGivenIndex(config, suffixedIndexName, true);
}
@Override
public String createNewIndex(final ElasticSearchIndexConfig config)
{
final Date date = new Date();
final String suffixedIndexName = getSuffixedIndexName(config.getIndexAliasName(), date);
if (isIndexExists(suffixedIndexName))
{
// drop existing index
deleteIndex(suffixedIndexName);
}
// create indices
createGivenIndex(config, suffixedIndexName, false);
updateIndexDocumentTypeMappings(config, suffixedIndexName);
return suffixedIndexName;
}
@Override
public void createIndex(final ElasticSearchIndexConfig config)
{
final String suffixedIndexName = getSuffixedIndexName(config.getIndexAliasName(), new Date());
createGivenIndex(config, suffixedIndexName, true);
}
private void createGivenIndex(final ElasticSearchIndexConfig config, final String indexName, final boolean createAlias)
{
final Client client = searchClientService.getClient();
CreateIndexRequestBuilder createIndexRequestBuilder;
try
{
createIndexRequestBuilder = client.admin().indices().prepareCreate(indexName)
.setSettings(new IndexSchemaBuilder().getSettingForIndex(config));
}
catch (final IOException e)
{
throw new RuntimeException("Error occurred while generating settings for index", e);
}
// update mapping on server
createIndexRequestBuilder.execute().actionGet();
if (createAlias)
{
createAlias(config.getIndexAliasName(), indexName);
}
logger.debug("Index {} created! ", indexName);
}
private void createAlias(final String aliasName, final String indexName)
{
// add new alias
searchClientService.getClient().admin().indices().prepareAliases().addAlias(indexName, aliasName).get();
// clean up old alias
cleanupExistingOldIndex(indexName, aliasName);
}
@Override
public void replaceAlias(final String newIndexName, final String indexAliasName)
{
createAlias(indexAliasName, newIndexName);
}
private void cleanupExistingOldIndex(final String newIndex, final String aliasName)
{
final Set<String> indices = new HashSet<String>();
final ClusterStateResponse stateResponse = searchClientService.getClient().admin().cluster().prepareState().execute().actionGet();
if (stateResponse != null && stateResponse.getState() != null)
{
for (final Entry<String, IndexRoutingTable> entry : stateResponse.getState().getRoutingTable().getIndicesRouting().entrySet())
{
indices.add(entry.getKey());
}
}
for (final String indexName : indices)
{
// Don't remove alias to newly created index
if (indexName.startsWith(aliasName) && !indexName.equals(newIndex))
{
try
{
searchClientService.getClient().admin().indices().prepareAliases().removeAlias(indexName, aliasName).execute()
.actionGet();
logger.debug("Alias {} has been removed from old index {}", aliasName, indexName);
}
catch (final Exception ex)
{
logger.error("Error occurred while removing alias: " + aliasName + " from index: " + indexName, ex);
}
// Try to delete old index itself
try
{
searchClientService.getClient().admin().indices().prepareDelete(indexName).execute().actionGet();
logger.debug("Old index {} removed sucessfully!", indexName);
}
catch (final Exception ex)
{
logger.error("Error occurred while removing old index: " + indexName, ex);
}
}
}
}
private String getSuffixedIndexName(final String indexName, final Date date)
{
final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
final String suffix = sdf.format(date);
return indexName + suffix + new Random().nextInt(86400);
}
@Override
public void updateIndexSettings(final ElasticSearchIndexConfig config, final Map<String, Object> settings)
{
// close index
searchClientService.getClient().admin().indices().prepareClose(config.getIndexAliasName()).get();
searchClientService.getClient().admin().indices().prepareUpdateSettings(config.getIndexAliasName()).setSettings(settings).get();
// close index
searchClientService.getClient().admin().indices().prepareOpen(config.getIndexAliasName()).get();
}
@Override
public void updateDocumentTypeMapping(final ElasticSearchIndexConfig config, final String indexName, final String documentType,
final boolean parentRelationship)
{
String usedIndexName = indexName;
if (StringUtils.isBlank(indexName))
{
usedIndexName = config.getIndexAliasName();
}
try
{
searchClientService.getClient().admin().indices().preparePutMapping(usedIndexName).setType(documentType)
.setSource(new IndexSchemaBuilder().getDocumentTypeMapping(config, documentType, parentRelationship)).get();
}
catch (final IOException e)
{
throw new RuntimeException("Error occurend while generating mapping for document type", e);
}
}
@Override
public void updateIndexDocumentTypeMappings(final ElasticSearchIndexConfig config, final String indexName)
{
// add mappings, no parent-child for now
updateDocumentTypeMapping(config, indexName, config.getGroupDocumentType(), false);
updateDocumentTypeMapping(config, indexName, config.getDocumentType(), false);
updateDocumentTypeMapping(config, indexName, config.getPropertiesDocumentType(), false);
}
@Override
public void indexProductGroupData(final List<ProductGroup> productGroups)
{
for (final ElasticSearchIndexConfig config : ElasticSearchIndexConfig.values())
{
reCreateIndex(config);
// indexProductData.indexAllProductGroupData(config, productGroups, true);
}
}
@Override
public boolean isIndexExists(final String indexName)
{
return searchClientService.getClient().admin().indices().prepareExists(indexName).get().isExists();
}
@Override
public boolean deleteIndex(final String indexName)
{
return searchClientService.getClient().admin().indices().prepareDelete(indexName).execute().actionGet().isAcknowledged();
}
@Override
public String getIndexSettings(final ElasticSearchIndexConfig config, final String settingName)
{
String settingValue = null;
final ClusterStateResponse clusterStateResponse = searchClientService.getClient().admin().cluster().prepareState()
.setRoutingTable(true).setNodes(true).setIndices(config.getIndexAliasName()).get();
for (final IndexMetaData indexMetaData : clusterStateResponse.getState().getMetaData())
{
settingValue = indexMetaData.getSettings().get(settingName);
}
return settingValue;
}
@Override
public boolean isAliasExists(final String indexAliasName)
{
return searchClientService.getClient().admin().indices().prepareAliasesExist(indexAliasName).get().isExists();
}
@Override
public List<String> analyzeText(final String indexAliasName, final String analyzer, final String[] tokenFilters, final String text)
{
final List<String> tokens = new ArrayList<String>();
final AnalyzeRequestBuilder analyzeRequestBuilder = searchClientService.getClient().admin().indices().prepareAnalyze(text);
if (analyzer != null)
{
analyzeRequestBuilder.setIndex(indexAliasName);
}
if (analyzer != null)
{
analyzeRequestBuilder.setAnalyzer(analyzer);
}
if (tokenFilters != null)
{
analyzeRequestBuilder.setTokenFilters(tokenFilters);
}
logger.debug("Analyze request is text: {}, analyzer: {}, tokenfilters: {}", new Object[] { analyzeRequestBuilder.request().text(),
analyzeRequestBuilder.request().analyzer(), analyzeRequestBuilder.request().tokenFilters() });
final AnalyzeResponse analyzeResponse = analyzeRequestBuilder.get();
try
{
if (analyzeResponse != null)
{
logger.debug("Analyze response is : {}", analyzeResponse.toXContent(jsonBuilder().startObject(), ToXContent.EMPTY_PARAMS)
.prettyPrint().string());
}
}
catch (final IOException e)
{
logger.error("Error printing response.", e);
}
for (final AnalyzeToken analyzeToken : analyzeResponse.getTokens())
{
tokens.add(analyzeToken.getTerm());
}
return tokens;
}
}