
com.hazelcast.query.impl.IndexUtils Maven / Gradle / Ivy
The newest version!
/*
* Copyright (c) 2008-2024, Hazelcast, Inc. All Rights Reserved.
*
* 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 com.hazelcast.query.impl;
import com.hazelcast.config.BTreeIndexConfig;
import com.hazelcast.config.BitmapIndexOptions.UniqueKeyTransformation;
import com.hazelcast.config.IndexConfig;
import com.hazelcast.config.IndexType;
import com.hazelcast.internal.util.StringUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;
import static com.hazelcast.internal.util.Preconditions.checkNotNull;
/**
* Utility methods for indexes.
*/
public final class IndexUtils {
/** Maximum number of attributes allowed in the index. */
public static final int MAX_ATTRIBUTES = 255;
/** Pattern to stripe away "this." prefix. */
private static final Pattern THIS_PATTERN = Pattern.compile("^this\\.");
private IndexUtils() {
// No-op.
}
/**
* Validate provided index config and normalize it's name and attribute names.
*
* @param mapName Name of the map
* @param config Index config.
* @return Normalized index config.
* @throws IllegalArgumentException If index configuration is invalid.
*/
@SuppressWarnings("checkstyle:npathcomplexity")
public static IndexConfig validateAndNormalize(String mapName, IndexConfig config) {
assert config != null;
// Validate attributes.
List originalAttributeNames = config.getAttributes();
if (originalAttributeNames.isEmpty()) {
throw new IllegalArgumentException("Index must have at least one attribute: " + config);
}
if (originalAttributeNames.size() > MAX_ATTRIBUTES) {
throw new IllegalArgumentException("Index cannot have more than " + MAX_ATTRIBUTES
+ " attributes: " + config);
}
if (config.getType() == IndexType.BITMAP && originalAttributeNames.size() > 1) {
throw new IllegalArgumentException("Composite bitmap indexes are not supported: " + config);
}
List normalizedAttributeNames = new ArrayList<>(originalAttributeNames.size());
for (String originalAttributeName : originalAttributeNames) {
validateAttribute(config, originalAttributeName);
originalAttributeName = originalAttributeName.trim();
String normalizedAttributeName = canonicalizeAttribute(originalAttributeName);
assert !normalizedAttributeName.isEmpty();
int existingIdx = normalizedAttributeNames.indexOf(normalizedAttributeName);
if (existingIdx != -1) {
String duplicateOriginalAttributeName = originalAttributeNames.get(existingIdx);
if (duplicateOriginalAttributeName.equals(originalAttributeName)) {
throw new IllegalArgumentException("Duplicate attribute name [attributeName="
+ originalAttributeName + ", indexConfig=" + config + ']');
} else {
throw new IllegalArgumentException("Duplicate attribute names ["
+ "attributeName1=" + duplicateOriginalAttributeName + ", attributeName2="
+ originalAttributeName + ", indexConfig=" + config + ']');
}
}
normalizedAttributeNames.add(normalizedAttributeName);
}
// Construct final index.
String name = config.getName();
if (StringUtil.isNullOrEmptyAfterTrim(name)) {
name = null;
}
IndexConfig normalizedConfig =
buildNormalizedConfig(mapName, config.getType(), name, normalizedAttributeNames, config.getBTreeIndexConfig());
if (config.getType() == IndexType.BITMAP) {
String uniqueKey = config.getBitmapIndexOptions().getUniqueKey();
UniqueKeyTransformation uniqueKeyTransformation = config.getBitmapIndexOptions().getUniqueKeyTransformation();
validateAttribute(uniqueKey);
uniqueKey = canonicalizeAttribute(uniqueKey);
normalizedConfig.getBitmapIndexOptions().setUniqueKey(uniqueKey).setUniqueKeyTransformation(uniqueKeyTransformation);
}
return normalizedConfig;
}
private static IndexConfig buildNormalizedConfig(String mapName, IndexType indexType, String indexName,
List normalizedAttributeNames,
BTreeIndexConfig btreeIndexConfig) {
IndexConfig newConfig = new IndexConfig().setType(indexType);
StringBuilder nameBuilder = indexName == null
? new StringBuilder(mapName + "_" + getIndexTypeName(indexType)) : null;
for (String normalizedAttributeName : normalizedAttributeNames) {
newConfig.addAttribute(normalizedAttributeName);
if (nameBuilder != null) {
nameBuilder.append("_").append(normalizedAttributeName);
}
}
if (nameBuilder != null) {
indexName = nameBuilder.toString();
}
newConfig.setName(indexName);
newConfig.setBTreeIndexConfig(new BTreeIndexConfig(btreeIndexConfig));
return newConfig;
}
/**
* Validate attribute name.
*
* @param config Index config.
* @param attributeName Attribute name.
*/
public static void validateAttribute(IndexConfig config, String attributeName) {
if (attributeName == null) {
throw new NullPointerException("Attribute name cannot be null: " + config);
}
String attributeName0 = attributeName.trim();
if (attributeName0.isEmpty()) {
throw new IllegalArgumentException("Attribute name cannot be empty: " + config);
}
if (attributeName0.endsWith(".")) {
throw new IllegalArgumentException("Attribute name cannot end with dot [config=" + config
+ ", attribute=" + attributeName + ']');
}
}
/**
* Validate attribute name.
*
* @param attributeName Attribute name.
*/
public static void validateAttribute(String attributeName) {
if (attributeName == null) {
throw new NullPointerException("Attribute name cannot be null.");
}
String attributeName0 = attributeName.trim();
if (attributeName0.isEmpty()) {
throw new IllegalArgumentException("Attribute name cannot be empty.");
}
if (attributeName0.endsWith(".")) {
throw new IllegalArgumentException("Attribute name cannot end with dot: " + attributeName);
}
}
/**
* Produces canonical attribute representation by stripping an unnecessary
* "this." qualifier from the passed attribute, if any.
*
* @param attribute the attribute to canonicalize.
* @return the canonical attribute representation.
*/
public static String canonicalizeAttribute(String attribute) {
return THIS_PATTERN.matcher(attribute).replaceFirst("");
}
/**
* Create simple index definition with the given attributes
*
* @param type Index type.
* @param attributes Attribute names.
* @return Index definition.
*/
public static IndexConfig createIndexConfig(IndexType type, String... attributes) {
IndexConfig res = new IndexConfig();
res.setType(type);
checkNotNull(attributes, "Index attributes cannot be null.");
for (String attribute : attributes) {
res.addAttribute(attribute);
}
return res;
}
private static String getIndexTypeName(IndexType type) {
switch (type) {
case SORTED:
return "sorted";
case HASH:
return "hash";
case BITMAP:
return "bitmap";
default:
throw new IllegalArgumentException("Unsupported index type: " + type);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy