/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.portal.search.elasticsearch.internal.index;
import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil;
import com.liferay.portal.kernel.log.Log;
import com.liferay.portal.kernel.log.LogFactoryUtil;
import com.liferay.portal.kernel.util.MapUtil;
import com.liferay.portal.kernel.util.Validator;
import com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration;
import com.liferay.portal.search.elasticsearch.index.IndexFactory;
import com.liferay.portal.search.elasticsearch.index.IndexNameBuilder;
import com.liferay.portal.search.elasticsearch.internal.util.LogUtil;
import com.liferay.portal.search.elasticsearch.internal.util.ResourceUtil;
import com.liferay.portal.search.elasticsearch.settings.IndexSettingsContributor;
import com.liferay.portal.search.elasticsearch.settings.IndexSettingsHelper;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentSkipListSet;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequestBuilder;
import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsResponse;
import org.elasticsearch.client.AdminClient;
import org.elasticsearch.client.IndicesAdminClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.settings.Settings.Builder;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.osgi.service.component.annotations.ReferencePolicy;
import org.osgi.service.component.annotations.ReferencePolicyOption;
/**
* @author Michael C. Han
*/
@Component(
configurationPid = "com.liferay.portal.search.elasticsearch.configuration.ElasticsearchConfiguration",
immediate = true,
property = {
"typeMappings.KeywordQueryDocumentType=/META-INF/mappings/keyword-query-type-mappings.json",
"typeMappings.SpellCheckDocumentType=/META-INF/mappings/spellcheck-type-mappings.json"
}
)
public class CompanyIndexFactory implements IndexFactory {
@Override
public void createIndices(AdminClient adminClient, long companyId)
throws Exception {
IndicesAdminClient indicesAdminClient = adminClient.indices();
String indexName = getIndexName(companyId);
if (hasIndex(indicesAdminClient, indexName)) {
return;
}
createIndex(indexName, indicesAdminClient);
}
@Override
public void deleteIndices(AdminClient adminClient, long companyId)
throws Exception {
IndicesAdminClient indicesAdminClient = adminClient.indices();
String indexName = getIndexName(companyId);
if (!hasIndex(indicesAdminClient, indexName)) {
return;
}
DeleteIndexRequestBuilder deleteIndexRequestBuilder =
indicesAdminClient.prepareDelete(indexName);
DeleteIndexResponse deleteIndexResponse =
deleteIndexRequestBuilder.get();
LogUtil.logActionResponse(_log, deleteIndexResponse);
}
public void setTypeMappings(Map<String, String> typeMappings) {
_typeMappings = typeMappings;
}
@Activate
@Modified
protected void activate(Map<String, Object> properties) {
ElasticsearchConfiguration elasticsearchConfiguration =
ConfigurableUtil.createConfigurable(
ElasticsearchConfiguration.class, properties);
setAdditionalIndexConfigurations(
elasticsearchConfiguration.additionalIndexConfigurations());
setAdditionalTypeMappings(
elasticsearchConfiguration.additionalTypeMappings());
setOverrideTypeMappings(
elasticsearchConfiguration.overrideTypeMappings());
Map<String, String> typeMappings = getTypeMappings(properties);
setTypeMappings(typeMappings);
}
@Reference(
cardinality = ReferenceCardinality.MULTIPLE,
policy = ReferencePolicy.DYNAMIC,
policyOption = ReferencePolicyOption.GREEDY,
unbind = "removeIndexSettingsContributor"
)
protected void addIndexSettingsContributor(
IndexSettingsContributor indexSettingsContributor) {
_indexSettingsContributors.add(indexSettingsContributor);
}
protected void addLiferayDocumentTypeMappings(
CreateIndexRequestBuilder createIndexRequestBuilder,
LiferayDocumentTypeFactory liferayDocumentTypeFactory) {
if (Validator.isNotNull(_overrideTypeMappings)) {
liferayDocumentTypeFactory.createLiferayDocumentTypeMappings(
createIndexRequestBuilder, _overrideTypeMappings);
}
else {
liferayDocumentTypeFactory.createRequiredDefaultTypeMappings(
createIndexRequestBuilder);
}
}
protected void addTypeMappings(
CreateIndexRequestBuilder createIndexRequestBuilder)
throws Exception {
for (Map.Entry<String, String> entry : _typeMappings.entrySet()) {
String mappingDefinition = ResourceUtil.getResourceAsString(
getClass(), entry.getValue());
createIndexRequestBuilder.addMapping(
entry.getKey(), mappingDefinition);
}
}
protected void createIndex(
String indexName, IndicesAdminClient indicesAdminClient)
throws Exception {
CreateIndexRequestBuilder createIndexRequestBuilder =
indicesAdminClient.prepareCreate(indexName);
LiferayDocumentTypeFactory liferayDocumentTypeFactory =
new LiferayDocumentTypeFactory(indicesAdminClient);
addTypeMappings(createIndexRequestBuilder);
setSettings(createIndexRequestBuilder, liferayDocumentTypeFactory);
addLiferayDocumentTypeMappings(
createIndexRequestBuilder, liferayDocumentTypeFactory);
CreateIndexResponse createIndexResponse =
createIndexRequestBuilder.get();
LogUtil.logActionResponse(_log, createIndexResponse);
updateLiferayDocumentType(indexName, liferayDocumentTypeFactory);
}
protected String getIndexName(long companyId) {
return indexNameBuilder.getIndexName(companyId);
}
protected Map<String, String> getTypeMappings(
Map<String, Object> properties) {
Map<String, String> typeMappings = new HashMap<>();
for (String key : properties.keySet()) {
if (key.startsWith(_TYPE_MAPPINGS_PREFIX)) {
String value = MapUtil.getString(properties, key);
typeMappings.put(
key.substring(_TYPE_MAPPINGS_PREFIX.length()), value);
}
}
return typeMappings;
}
protected boolean hasIndex(
IndicesAdminClient indicesAdminClient, String indexName)
throws Exception {
IndicesExistsRequestBuilder indicesExistsRequestBuilder =
indicesAdminClient.prepareExists(indexName);
IndicesExistsResponse indicesExistsResponse =
indicesExistsRequestBuilder.get();
return indicesExistsResponse.isExists();
}
protected void loadAdditionalIndexConfigurations(Builder builder) {
if (Validator.isNull(_additionalIndexConfigurations)) {
return;
}
builder.loadFromSource(_additionalIndexConfigurations);
}
protected void loadAdditionalTypeMappings(
String indexName,
LiferayDocumentTypeFactory liferayDocumentTypeFactory) {
if (Validator.isNull(_additionalTypeMappings)) {
return;
}
liferayDocumentTypeFactory.addTypeMappings(
indexName, _additionalTypeMappings);
}
protected void loadIndexSettingsContributors(
final Settings.Builder builder) {
IndexSettingsHelper indexSettingsHelper = new IndexSettingsHelper() {
@Override
public void put(String setting, String value) {
builder.put(setting, value);
}
};
for (IndexSettingsContributor indexSettingsContributor :
_indexSettingsContributors) {
indexSettingsContributor.populate(indexSettingsHelper);
}
}
protected void loadTypeMappingsContributors(
String indexName,
LiferayDocumentTypeFactory liferayDocumentTypeFactory) {
for (IndexSettingsContributor indexSettingsContributor :
_indexSettingsContributors) {
indexSettingsContributor.contribute(liferayDocumentTypeFactory);
indexSettingsContributor.contribute(
indexName, liferayDocumentTypeFactory);
}
}
protected void removeIndexSettingsContributor(
IndexSettingsContributor indexSettingsContributor) {
_indexSettingsContributors.remove(indexSettingsContributor);
}
protected void setAdditionalIndexConfigurations(
String additionalIndexConfigurations) {
_additionalIndexConfigurations = additionalIndexConfigurations;
}
protected void setAdditionalTypeMappings(String additionalTypeMappings) {
_additionalTypeMappings = additionalTypeMappings;
}
protected void setOverrideTypeMappings(String overrideTypeMappings) {
_overrideTypeMappings = overrideTypeMappings;
}
protected void setSettings(
CreateIndexRequestBuilder createIndexRequestBuilder,
LiferayDocumentTypeFactory liferayDocumentTypeFactory) {
Settings.Builder builder = Settings.settingsBuilder();
liferayDocumentTypeFactory.createRequiredDefaultAnalyzers(builder);
loadAdditionalIndexConfigurations(builder);
loadIndexSettingsContributors(builder);
createIndexRequestBuilder.setSettings(builder);
}
protected void updateLiferayDocumentType(
String indexName,
LiferayDocumentTypeFactory liferayDocumentTypeFactory) {
if (Validator.isNotNull(_overrideTypeMappings)) {
return;
}
loadAdditionalTypeMappings(indexName, liferayDocumentTypeFactory);
loadTypeMappingsContributors(indexName, liferayDocumentTypeFactory);
liferayDocumentTypeFactory.createOptionalDefaultTypeMappings(indexName);
}
@Reference
protected IndexNameBuilder indexNameBuilder;
private static final String _TYPE_MAPPINGS_PREFIX = "typeMappings.";
private static final Log _log = LogFactoryUtil.getLog(
CompanyIndexFactory.class);
private volatile String _additionalIndexConfigurations;
private String _additionalTypeMappings;
private final Set<IndexSettingsContributor> _indexSettingsContributors =
new ConcurrentSkipListSet<>();
private String _overrideTypeMappings;
private Map<String, String> _typeMappings = new HashMap<>();
}