blob: f2adbe44707234b740c7c4a0be05769ecbbc0f1f [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.console.commands
import com.google.common.base.Stopwatch
import org.apache.jackrabbit.oak.commons.PathUtils
import org.apache.jackrabbit.oak.console.ConsoleSession
import org.apache.jackrabbit.oak.plugins.index.lucene.directory.OakDirectory
import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition
import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants
import org.apache.jackrabbit.oak.spi.commit.CommitInfo
import org.apache.jackrabbit.oak.spi.commit.EmptyHook
import org.apache.jackrabbit.oak.spi.state.NodeBuilder
import org.apache.jackrabbit.oak.spi.state.NodeState
import org.apache.jackrabbit.oak.spi.state.NodeStore
import org.apache.jackrabbit.oak.spi.state.ReadOnlyBuilder
import org.apache.lucene.index.DirectoryReader
import org.apache.lucene.store.Directory
import org.apache.lucene.store.FSDirectory
import org.apache.lucene.store.IOContext
import org.codehaus.groovy.tools.shell.ComplexCommandSupport
import org.codehaus.groovy.tools.shell.Groovysh
import org.apache.jackrabbit.oak.spi.state.NodeBuilder as OakNodeBuilder
import static com.google.common.base.Preconditions.checkNotNull
class LuceneCommand extends ComplexCommandSupport {
public static final String COMMAND_NAME = 'lucene'
public LuceneCommand(final Groovysh shell) {
super(shell, COMMAND_NAME, 'lc', ['info', 'rmdata', 'dump'], 'info')
}
def do_info = { args ->
String idxPath = args ? args[0] : '/oak:index/lucene'
Directory dir = getDirectory(idxPath)
if (!dir) {
io.out.println("No Lucene directory found at path [$idxPath]")
return
}
try {
io.out.println("Index size : ${humanReadableByteCount(dirSize(dir))}")
DirectoryReader reader = DirectoryReader.open(dir)
try {
io.out.println("Number of documents : ${reader.numDocs()}")
io.out.println("Number of deleted documents : ${reader.numDeletedDocs()}")
}finally{
reader.close()
}
} finally {
dir.close()
}
}
def do_dump = { args ->
String idxPath = args && args.size() == 2 ? args[1] : '/oak:index/lucene'
String destPath = args ? args[0] : 'luceneIndex'
Directory source = getDirectory(idxPath)
if (!source) {
io.out.println("No Lucene directory found at path [$idxPath]")
return
}
try {
File destDir = new File(destPath)
Stopwatch w = Stopwatch.createStarted()
io.out.println("Copying Lucene indexes to [${destDir.absolutePath}]")
Directory dest = FSDirectory.open(destDir)
long size = 0
source.listAll().each { file ->
size += source.fileLength(file)
source.copy(dest, file, file, IOContext.DEFAULT)
}
io.out.println("Copied ${humanReadableByteCount(size)} in $w")
} finally {
source.close()
}
}
def do_rmdata = {args ->
if (!args || args.size() != 1) {
io.out.println("Please provide a single argument (index-path). Current arg list: ${args}")
return
}
String idxPath = args[0]
NodeStore ns = getSession().getStore();
OakNodeBuilder nb = ns.root.builder()
String dataPath = idxPath + "/:data"
NodeBuilder data = getNode(nb, dataPath)
if (!data.exists()){
io.out.println("Index at [$idxPath] is empty. Nothing to remove")
return
}
data.remove()
ns.merge(nb, EmptyHook.INSTANCE, CommitInfo.EMPTY)
io.out.println("Removed the index data for [$idxPath]")
}
private Directory getDirectory(String path) {
NodeState definition = variables.session.getRoot();
for (String element : PathUtils.elements(path)) {
definition = definition.getChildNode(element);
}
NodeState data = definition.getChildNode(FulltextIndexConstants.INDEX_DATA_CHILD_NAME);
if (data.exists()) {
//OakDirectory is package scope but Groovy allows us
//to use it. Good or bad but its helpful debug scripts
//can access inner classes and prod code cannot. Win win :)
return new OakDirectory(new ReadOnlyBuilder(definition), new IndexDefinition(session.root, definition, path), true);
}
return null
}
private static long dirSize(Directory dir) {
dir.listAll().inject(0L, { sum, fileName -> sum + dir.fileLength(fileName) })
}
private static def humanReadableByteCount(long bytes) {
if(bytes == 0){
return ""
}
int exp = (int) (Math.log(bytes) / Math.log(1024));
String pre = "KMGTPE".charAt(exp - 1);
return String.format("%.1f %sB", bytes / Math.pow(1024, exp), pre);
}
private ConsoleSession getSession(){
return (ConsoleSession)variables.session
}
private static NodeBuilder getNode(NodeBuilder node, String path) {
for (String name : PathUtils.elements(checkNotNull(path))) {
node = node.getChildNode(checkNotNull(name));
}
return node;
}
}