blob: 846f5456806f19a9abb0dcecd2a816d6f89ac2e5 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.apache.jackrabbit.oak.benchmark.util;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.commons.JcrUtils;
import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants;
import org.apache.jackrabbit.oak.plugins.index.property.OrderedIndex;
import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants;
import org.apache.jackrabbit.oak.spi.nodetype.NodeTypeConstants;
import org.jetbrains.annotations.Nullable;
import javax.jcr.Node;
import javax.jcr.Property;
import javax.jcr.PropertyType;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import javax.jcr.Value;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* A simple utility class for Oak indexes.
*/
public class OakIndexUtils {
/**
* A property index
*/
public static class PropertyIndex {
private String indexName;
private String propertyName;
private String[] nodeTypeNames;
/**
* Set the index name. If not set, the index name is the property name.
*
* @param indexName the index name
* @return this
*/
public PropertyIndex name(String indexName) {
this.indexName = indexName;
return this;
}
/**
* Set the property name. This field is mandatory.
*
* @param propertyName the property name
* @return this
*/
public PropertyIndex property(String propertyName) {
this.propertyName = propertyName;
return this;
}
/**
* Restrict the node types.
*
* @param nodeTypeNames the list of declaring node types
* @return this
*/
public PropertyIndex nodeTypes(String... nodeTypeNames) {
this.nodeTypeNames = nodeTypeNames;
return this;
}
/**
* Create the index.
* <p>
* If this is not a Oak repository, this method does nothing.
* <p>
* If a matching index already exists, this method verifies that the
* definition matches. If no such index exists, a new one is created.
*
* @param session the session to use for creating the index
* @return the index node
* @throws RepositoryException if writing to the repository failed, the
* index definition is incorrect, or if such an index exists
* but is not compatible with this definition (for example,
* a different property is indexed)
*/
public @Nullable Node create(Session session) throws RepositoryException {
return create(session,PropertyIndexEditorProvider.TYPE);
}
public @Nullable Node create(Session session,String indexType) throws RepositoryException {
Node index;
if (!session.getWorkspace().getNodeTypeManager().hasNodeType(
"oak:QueryIndexDefinition")) {
// not an Oak repository
return null;
}
if (session.hasPendingChanges()) {
throw new RepositoryException("The session has pending changes");
}
if (indexName == null) {
indexName = propertyName;
}
if (propertyName == null) {
throw new RepositoryException("Index property name not set");
}
if (nodeTypeNames != null) {
if (nodeTypeNames.length == 0) {
// setting the node types to an empty array means "all node types"
// (same as not setting it)
nodeTypeNames = null;
} else {
Arrays.sort(nodeTypeNames);
}
}
Node root = session.getRootNode();
Node indexDef;
if (!root.hasNode(IndexConstants.INDEX_DEFINITIONS_NAME)) {
indexDef = root.addNode(IndexConstants.INDEX_DEFINITIONS_NAME,
JcrConstants.NT_UNSTRUCTURED);
session.save();
} else {
indexDef = root.getNode(IndexConstants.INDEX_DEFINITIONS_NAME);
}
if (indexDef.hasNode(indexName)) {
// verify the index matches
index = indexDef.getNode(indexName);
if (index.hasProperty(IndexConstants.UNIQUE_PROPERTY_NAME)) {
Property p = index.getProperty(IndexConstants.UNIQUE_PROPERTY_NAME);
if (p.getBoolean()) {
throw new RepositoryException(
"Index already exists, but is unique");
}
}
String type = index.getProperty(
IndexConstants.TYPE_PROPERTY_NAME).getString();
if (!type.equals(indexType)) {
throw new RepositoryException(
"Index already exists, but is of type " + type);
}
Value[] v = index.getProperty(IndexConstants.PROPERTY_NAMES).getValues();
if (v.length != 1) {
String[] list = new String[v.length];
for (int i = 0; i < v.length; i++) {
list[i] = v[i].getString();
}
throw new RepositoryException(
"Index already exists, but is not just one property, but " + Arrays.toString(list));
}
if (!propertyName.equals(v[0].getString())) {
throw new RepositoryException(
"Index already exists, but is for property " + v[0].getString());
}
if (index.hasProperty(IndexConstants.DECLARING_NODE_TYPES)) {
v = index.getProperty(IndexConstants.DECLARING_NODE_TYPES).getValues();
String[] list = new String[v.length];
for (int i = 0; i < v.length; i++) {
list[i] = v[i].getString();
}
Arrays.sort(list);
if (Arrays.equals(list, nodeTypeNames)) {
throw new RepositoryException(
"Index already exists, but with different node types: " + Arrays.toString(list));
}
} else if (nodeTypeNames != null) {
throw new RepositoryException(
"Index already exists, but without node type restriction");
}
// matches
return index;
}
index = indexDef.addNode(indexName, IndexConstants.INDEX_DEFINITIONS_NODE_TYPE);
index.setProperty(IndexConstants.TYPE_PROPERTY_NAME, indexType);
index.setProperty(IndexConstants.REINDEX_PROPERTY_NAME,
true);
index.setProperty(IndexConstants.PROPERTY_NAMES,
new String[] { propertyName }, PropertyType.NAME);
if (nodeTypeNames != null) {
index.setProperty(IndexConstants.DECLARING_NODE_TYPES,
nodeTypeNames);
}
session.save();
return index;
}
}
/**
* Helper method to create or update a property index definition.
*
* @param session the session
* @param indexDefinitionName the name of the node for the index definition
* @param propertyNames the list of properties to index
* @param unique if unique or not
* @param enclosingNodeTypes the enclosing node types
* @return the node just created
* @throws RepositoryException the repository exception
*/
public static Node propertyIndexDefinition(Session session, String indexDefinitionName,
String[] propertyNames, boolean unique,
String[] enclosingNodeTypes) throws RepositoryException {
Node root = session.getRootNode();
Node indexDefRoot = JcrUtils.getOrAddNode(root, IndexConstants.INDEX_DEFINITIONS_NAME,
NodeTypeConstants.NT_UNSTRUCTURED);
Node indexDef = JcrUtils.getOrAddNode(indexDefRoot, indexDefinitionName,
IndexConstants.INDEX_DEFINITIONS_NODE_TYPE);
indexDef.setProperty(IndexConstants.TYPE_PROPERTY_NAME, PropertyIndexEditorProvider.TYPE);
indexDef.setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
indexDef.setProperty(IndexConstants.PROPERTY_NAMES, propertyNames,
PropertyType.NAME);
indexDef.setProperty(IndexConstants.UNIQUE_PROPERTY_NAME, unique);
if (enclosingNodeTypes != null && enclosingNodeTypes.length != 0) {
indexDef.setProperty(IndexConstants.DECLARING_NODE_TYPES, enclosingNodeTypes,
PropertyType.NAME);
}
session.save();
return indexDef;
}
/**
* Helper method to create or update an ordered index definition.
*
* @param session the session
* @param indexDefinitionName the name of the node for the index definition
* @param async whether the indexing is async or not
* @param propertyNames the list of properties to index
* @param unique if unique or not
* @param enclosingNodeTypes the enclosing node types
* @param direction the direction
* @return the node just created
* @throws RepositoryException the repository exception
*/
public static Node orderedIndexDefinition(Session session, String indexDefinitionName,
String async, String[] propertyNames, boolean unique,
String[] enclosingNodeTypes, String direction) throws RepositoryException {
Node root = session.getRootNode();
Node indexDefRoot = JcrUtils.getOrAddNode(root, IndexConstants.INDEX_DEFINITIONS_NAME,
NodeTypeConstants.NT_UNSTRUCTURED);
Node indexDef = JcrUtils.getOrAddNode(indexDefRoot, indexDefinitionName,
IndexConstants.INDEX_DEFINITIONS_NODE_TYPE);
indexDef.setProperty(IndexConstants.TYPE_PROPERTY_NAME, OrderedIndex.TYPE);
indexDef.setProperty(IndexConstants.PROPERTY_NAMES, propertyNames,
PropertyType.NAME);
if (enclosingNodeTypes != null && enclosingNodeTypes.length != 0) {
indexDef.setProperty(IndexConstants.DECLARING_NODE_TYPES, enclosingNodeTypes,
PropertyType.NAME);
}
if (direction != null) {
indexDef.setProperty(OrderedIndex.DIRECTION, direction);
}
if (async != null) {
indexDef.setProperty(IndexConstants.ASYNC_PROPERTY_NAME, async);
}
indexDef.setProperty(IndexConstants.UNIQUE_PROPERTY_NAME, unique);
indexDef.setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
session.save();
return indexDef;
}
/**
* Helper method to create or update a lucene property index definition.
*
* @param session the session
* @param indexDefinitionName the name of the node for the index definition
* @param propertyNames the list of properties to index
* @param type the types of the properties in order of the properties
* @param orderedPropsMap the ordered props and its properties
* @param persistencePath the path if the persistence=file (default is repository)
* @return the node just created
* @throws RepositoryException the repository exception
*/
public static Node luceneIndexDefinition(Session session, String indexDefinitionName,
String async, String[] propertyNames, String[] type,
Map<String, Map<String, String>> orderedPropsMap, String persistencePath)
throws RepositoryException {
Node root = session.getRootNode();
Node indexDefRoot = JcrUtils.getOrAddNode(root, IndexConstants.INDEX_DEFINITIONS_NAME,
NodeTypeConstants.NT_UNSTRUCTURED);
Node indexDef = JcrUtils.getOrAddNode(indexDefRoot, indexDefinitionName,
IndexConstants.INDEX_DEFINITIONS_NODE_TYPE);
indexDef.setProperty(IndexConstants.TYPE_PROPERTY_NAME, LuceneIndexConstants.TYPE_LUCENE);
indexDef.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false);
if (async != null) {
indexDef.setProperty(IndexConstants.ASYNC_PROPERTY_NAME, async);
}
// Set indexed property names
indexDef.setProperty(FulltextIndexConstants.INCLUDE_PROPERTY_NAMES, propertyNames,
PropertyType.NAME);
Node propsNode = JcrUtils.getOrAddNode(indexDef, FulltextIndexConstants.PROP_NODE);
for (int i = 0; i < propertyNames.length; i++) {
Node propNode =
JcrUtils.getOrAddNode(propsNode, propertyNames[i], NodeTypeConstants.NT_OAK_UNSTRUCTURED);
propNode.setProperty(FulltextIndexConstants.PROP_TYPE, type[i]);
}
// Set ordered property names
if ((orderedPropsMap != null) && !orderedPropsMap.isEmpty()) {
List<String> orderedProps = Lists.newArrayList();
for (Map.Entry<String, Map<String, String>> orderedPropEntry : orderedPropsMap
.entrySet()) {
Node propNode = JcrUtils.getOrAddNode(propsNode, orderedPropEntry.getKey(),
NodeTypeConstants.NT_OAK_UNSTRUCTURED);
propNode.setProperty(FulltextIndexConstants.PROP_TYPE,
orderedPropEntry.getValue().get(FulltextIndexConstants.PROP_TYPE));
orderedProps.add(orderedPropEntry.getKey());
}
if (!orderedProps.isEmpty()) {
indexDef.setProperty(FulltextIndexConstants.ORDERED_PROP_NAMES,
orderedProps.toArray(new String[orderedProps.size()]),
PropertyType.NAME);
}
}
// Set file persistence if specified
if (!Strings.isNullOrEmpty(persistencePath)) {
indexDef.setProperty(FulltextIndexConstants.PERSISTENCE_NAME,
FulltextIndexConstants.PERSISTENCE_FILE);
indexDef.setProperty(FulltextIndexConstants.PERSISTENCE_PATH,
persistencePath);
}
session.save();
return indexDef;
}
}