This closes #205 GEODE-11: Adding stats option to list lucene index gfsh command
Added an option to display lucene index stats in the list lucene index command. Added junit and dunit tests to verify the same. GEODE-11: Added describe lucene index gfsh command Added describe lucene index gfsh command that takes the index name and region as arguments and displays information about the lucene index (fields, analyzers and stats). Also added junit and dunit tests for the same. Signed-off-by: Gester Zhou <[email protected]> Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/ce0b7e7d Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/ce0b7e7d Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/ce0b7e7d Branch: refs/heads/master Commit: ce0b7e7df89616c5f4a4d9d41ed0d63a24978dd0 Parents: 8a28f52 Author: Aparna Dharmakkan <[email protected]> Authored: Tue Jul 12 16:31:02 2016 -0700 Committer: zhouxh <[email protected]> Committed: Thu Jul 14 11:14:28 2016 -0700 ---------------------------------------------------------------------- .../lucene/internal/cli/LuceneCliStrings.java | 24 +++- .../internal/cli/LuceneIndexCommands.java | 80 ++++++++++-- .../lucene/internal/cli/LuceneIndexDetails.java | 30 ++++- .../lucene/internal/cli/LuceneIndexInfo.java | 4 + .../functions/LuceneDescribeIndexFunction.java | 68 ++++++++++ .../cli/LuceneIndexCommandsDUnitTest.java | 72 +++++++++-- .../cli/LuceneIndexCommandsJUnitTest.java | 124 +++++++++++++++++-- .../LuceneDescribeIndexFunctionJUnitTest.java | 103 +++++++++++++++ 8 files changed, 463 insertions(+), 42 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneCliStrings.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneCliStrings.java b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneCliStrings.java index 3248e75..282e691 100644 --- a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneCliStrings.java +++ b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneCliStrings.java @@ -19,17 +19,22 @@ package com.gemstone.gemfire.cache.lucene.internal.cli; public class LuceneCliStrings { - //List Lucene Index commands + //Common parameters/options + public static final String LUCENE__INDEX_NAME = "name"; + public static final String LUCENE__REGION_PATH = "region"; + + //List lucene index commands public static final String LUCENE_LIST_INDEX = "list lucene index"; public static final String LUCENE_LIST_INDEX__HELP = "Display the list of lucene indexes created for all members."; public static final String LUCENE_LIST_INDEX__ERROR_MESSAGE = "An error occurred while collecting all lucene index information across the Geode cluster: %1$s"; - public static final String LUCENE_LIST_INDEX__INDEXES_NOT_FOUND_MESSAGE = "No lucene indexes Found"; + public static final String LUCENE_LIST_INDEX__INDEXES_NOT_FOUND_MESSAGE = "No lucene indexes found"; + public static final String LUCENE_LIST_INDEX__STATS = "stats"; + public static final String LUCENE_LIST_INDEX__STATS__HELP = "Display lucene index stats"; + //Create lucene index commands public static final String LUCENE_CREATE_INDEX = "create lucene index"; public static final String LUCENE_CREATE_INDEX__HELP = "Create a lucene index that can be used to execute queries."; - public static final String LUCENE_CREATE_INDEX__NAME = "name"; public static final String LUCENE_CREATE_INDEX__NAME__HELP = "Name of the lucene index to create."; - public static final String LUCENE_CREATE_INDEX__REGION = "region"; public static final String LUCENE_CREATE_INDEX__REGION_HELP = "Name/Path of the region where the lucene index is created on."; public static final String LUCENE_CREATE_INDEX__FIELD = "field"; public static final String LUCENE_CREATE_INDEX__FIELD_HELP = "fields on the region values which are stored in the lucene index."; @@ -37,7 +42,6 @@ public class LuceneCliStrings { public static final String LUCENE_CREATE_INDEX__ANALYZER_HELP = "Type of the analyzer for each field."; public static final String LUCENE_CREATE_INDEX__GROUP = "group"; public static final String LUCENE_CREATE_INDEX__GROUP__HELP = "Group of members in which the lucene index will be created."; - public static final String CREATE_INDEX__SUCCESS__MSG = "Index successfully created with following details"; public static final String CREATE_INDEX__FAILURE__MSG = "Failed to create index \"{0}\" due to following reasons"; public static final String CREATE_INDEX__NAME__MSG = "Name : {0}"; @@ -45,4 +49,14 @@ public class LuceneCliStrings { public static final String CREATE_INDEX__MEMBER__MSG = "Members which contain the index"; public static final String CREATE_INDEX__NUMBER__AND__MEMBER = "{0}. {1}"; public static final String CREATE_INDEX__EXCEPTION__OCCURRED__ON = "Occurred on following members"; + + //Describe lucene index commands + public static final String LUCENE_DESCRIBE_INDEX = "describe lucene index"; + public static final String LUCENE_DESCRIBE_INDEX__HELP = "Display the describe of lucene indexes created for all members."; + public static final String LUCENE_DESCRIBE_INDEX__ERROR_MESSAGE = "An error occurred while collecting lucene index information across the Geode cluster: %1$s"; + public static final String LUCENE_DESCRIBE_INDEX__NAME__HELP = "Name of the lucene index to describe."; + public static final String LUCENE_DESCRIBE_INDEX__REGION_HELP = "Name/Path of the region where the lucene index to be described exists."; + + + } http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommands.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommands.java b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommands.java index 4715389..30028be 100755 --- a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommands.java +++ b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommands.java @@ -16,6 +16,10 @@ */ package com.gemstone.gemfire.cache.lucene.internal.cli; +import static com.gemstone.gemfire.cache.operations.OperationContext.*; + +import java.util.ArrayList; + import java.util.HashMap; import java.util.List; import java.util.Map; @@ -31,9 +35,11 @@ import org.springframework.shell.core.annotation.CliOption; import com.gemstone.gemfire.SystemFailure; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.execute.Execution; +import com.gemstone.gemfire.cache.execute.FunctionAdapter; import com.gemstone.gemfire.cache.execute.FunctionInvocationTargetException; import com.gemstone.gemfire.cache.execute.ResultCollector; import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneCreateIndexFunction; +import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneDescribeIndexFunction; import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneListIndexFunction; import com.gemstone.gemfire.distributed.DistributedMember; import com.gemstone.gemfire.internal.cache.execute.AbstractExecution; @@ -62,15 +68,21 @@ import com.gemstone.gemfire.management.internal.security.ResourceOperation; */ @SuppressWarnings("unused") public class LuceneIndexCommands extends AbstractCommandsSupport { - private static final LuceneCreateIndexFunction createIndexFunction = new LuceneCreateIndexFunction(); + private static final LuceneDescribeIndexFunction describeIndexFunction = new LuceneDescribeIndexFunction(); @CliCommand(value = LuceneCliStrings.LUCENE_LIST_INDEX, help = LuceneCliStrings.LUCENE_LIST_INDEX__HELP) @CliMetaData(shellOnly = false, relatedTopic={CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA }) @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ) - public Result listIndex() { + public Result listIndex( + @CliOption(key = LuceneCliStrings.LUCENE_LIST_INDEX__STATS, + mandatory=false, + specifiedDefaultValue = "true", + unspecifiedDefaultValue = "false", + help = LuceneCliStrings.LUCENE_LIST_INDEX__STATS__HELP) final boolean stats) { + try { - return toTabularResult(getIndexListing()); + return toTabularResult(getIndexListing(),stats); } catch (FunctionInvocationTargetException ignore) { return ResultBuilder.createGemFireErrorResult(CliStrings.format(CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN, @@ -105,7 +117,7 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { .collect(Collectors.toList()); } - protected Result toTabularResult(final List<LuceneIndexDetails> indexDetailsList) { + protected Result toTabularResult(final List<LuceneIndexDetails> indexDetailsList, boolean stats) { if (!indexDetailsList.isEmpty()) { final TabularResultData indexData = ResultBuilder.createTabularResultData(); @@ -114,6 +126,13 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { indexData.accumulate("Region Path", indexDetails.getRegionPath()); indexData.accumulate("Indexed Fields", indexDetails.getSearchableFieldNamesString()); indexData.accumulate("Field Analyzer", indexDetails.getFieldAnalyzersString()); + + if (stats==true) { + indexData.accumulate("Query Executions",indexDetails.getIndexStats().get("queryExecutions")); + indexData.accumulate("Updates",indexDetails.getIndexStats().get("updates")); + indexData.accumulate("Commits",indexDetails.getIndexStats().get("commits")); + indexData.accumulate("Documents",indexDetails.getIndexStats().get("documents")); + } } return ResultBuilder.buildResult(indexData); } @@ -126,11 +145,11 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { @CliMetaData(shellOnly = false, relatedTopic={CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA }, writesToSharedConfiguration=true) //TODO : Add optionContext for indexName public Result createIndex( - @CliOption(key = LuceneCliStrings.LUCENE_CREATE_INDEX__NAME, + @CliOption(key = LuceneCliStrings.LUCENE__INDEX_NAME, mandatory=true, help = LuceneCliStrings.LUCENE_CREATE_INDEX__NAME__HELP) final String indexName, - @CliOption (key = LuceneCliStrings.LUCENE_CREATE_INDEX__REGION, + @CliOption (key = LuceneCliStrings.LUCENE__REGION_PATH, mandatory = true, optionContext = ConverterHint.REGIONPATH, help = LuceneCliStrings.LUCENE_CREATE_INDEX__REGION_HELP) final String regionPath, @@ -159,7 +178,7 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { try { final Cache cache = getCache(); LuceneIndexInfo indexInfo = new LuceneIndexInfo(indexName, regionPath, fields, analyzers); - final ResultCollector<?, ?> rc = this.createIndexOnGroups(groups, indexInfo); + final ResultCollector<?, ?> rc = this.executeFunctionOnGroups(createIndexFunction, groups, indexInfo); final List<CliFunctionResult> funcResults = (List<CliFunctionResult>) rc.getResult(); final Set<String> successfulMembers = new TreeSet<String>(); @@ -236,8 +255,49 @@ public class LuceneIndexCommands extends AbstractCommandsSupport { return result; } - protected ResultCollector<?, ?> createIndexOnGroups( String[] groups, final LuceneIndexInfo indexInfo) throws CommandResultException { + @CliCommand(value = LuceneCliStrings.LUCENE_DESCRIBE_INDEX, help = LuceneCliStrings.LUCENE_DESCRIBE_INDEX__HELP) + @CliMetaData(shellOnly = false, relatedTopic={CliStrings.TOPIC_GEODE_REGION, CliStrings.TOPIC_GEODE_DATA }) + @ResourceOperation(resource = Resource.CLUSTER, operation = Operation.READ) + public Result describeIndex( + @CliOption(key = LuceneCliStrings.LUCENE__INDEX_NAME, + mandatory=true, + help = LuceneCliStrings.LUCENE_DESCRIBE_INDEX__NAME__HELP) final String indexName, + + @CliOption (key = LuceneCliStrings.LUCENE__REGION_PATH, + mandatory = true, + optionContext = ConverterHint.REGIONPATH, + help = LuceneCliStrings.LUCENE_DESCRIBE_INDEX__REGION_HELP) final String regionPath) { + try { + LuceneIndexInfo indexInfo = new LuceneIndexInfo(indexName, regionPath); + return toTabularResult(getIndexDetails(indexInfo),true); + } + catch (FunctionInvocationTargetException ignore) { + return ResultBuilder.createGemFireErrorResult(CliStrings.format(CliStrings.COULD_NOT_EXECUTE_COMMAND_TRY_AGAIN, + LuceneCliStrings.LUCENE_DESCRIBE_INDEX)); + } + catch (VirtualMachineError e) { + SystemFailure.initiateFailure(e); + throw e; + } + catch (Throwable t) { + SystemFailure.checkFailure(); + getCache().getLogger().error(t); + return ResultBuilder.createGemFireErrorResult(String.format(LuceneCliStrings.LUCENE_DESCRIBE_INDEX__ERROR_MESSAGE, + toString(t, isDebugging()))); + } + } + + @SuppressWarnings("unchecked") + protected List<LuceneIndexDetails> getIndexDetails(LuceneIndexInfo indexInfo) throws Exception { + GeodeSecurityUtil.authorizeRegionManage(indexInfo.getRegionPath()); + final String[] groups = {}; + final ResultCollector<?, ?> rc = this.executeFunctionOnGroups(describeIndexFunction, groups, indexInfo); + final List<LuceneIndexDetails> funcResults = (List<LuceneIndexDetails>) rc.getResult(); + return funcResults.stream().filter(indexDetails -> indexDetails != null).collect(Collectors.toList()); + } + + protected ResultCollector<?, ?> executeFunctionOnGroups(FunctionAdapter function, String[]groups, final LuceneIndexInfo indexInfo) throws CommandResultException { final Set<DistributedMember> targetMembers = CliUtil.findAllMatchingMembers(groups, null); - return CliUtil.executeFunction(createIndexFunction, indexInfo, targetMembers); + return CliUtil.executeFunction(function, indexInfo, targetMembers); } -} +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexDetails.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexDetails.java b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexDetails.java index c967530..7526425 100644 --- a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexDetails.java +++ b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexDetails.java @@ -22,7 +22,9 @@ import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; + import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexImpl; +import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexStats; import org.apache.lucene.analysis.Analyzer; @@ -33,16 +35,35 @@ public class LuceneIndexDetails implements Comparable<LuceneIndexDetails>, Seria private final String regionPath; private final String[] searchableFieldNames; private final Map<String, String> fieldAnalyzers; + private final Map<String,Integer> indexStats; - public LuceneIndexDetails(final String indexName, final String regionPath, final String[] searchableFieldNames, Map<String, Analyzer> fieldAnalyzers) { + public LuceneIndexDetails(final String indexName, final String regionPath, final String[] searchableFieldNames, Map<String, Analyzer> fieldAnalyzers, LuceneIndexStats indexStats) { this.indexName = indexName; this.regionPath = regionPath; this.searchableFieldNames = searchableFieldNames; this.fieldAnalyzers = getAnalyzerStrings(fieldAnalyzers); + this.indexStats=getIndexStatsMap(indexStats); } public LuceneIndexDetails(LuceneIndexImpl index) { - this(index.getName(), index.getRegionPath(), index.getFieldNames(), index.getFieldAnalyzers()); + this(index.getName(), index.getRegionPath(), index.getFieldNames(), index.getFieldAnalyzers(),index.getIndexStats()); + } + + public Map<String,Integer> getIndexStats() { + return indexStats; + } + private Map<String,Integer> getIndexStatsMap(LuceneIndexStats indexStats) { + Map<String,Integer> statsMap = new HashMap<>(); + if (indexStats==null) return statsMap; + statsMap.put("queryExecutions",indexStats.getQueryExecutions()); + statsMap.put("updates",indexStats.getUpdates()); + statsMap.put("commits",indexStats.getCommits()); + statsMap.put("documents",indexStats.getDocuments()); + return statsMap; + } + + public String getIndexStatsString() { + return indexStats.toString(); } private Map<String, String> getAnalyzerStrings(Map<String, Analyzer> fieldAnalyzers) { @@ -77,6 +98,7 @@ public class LuceneIndexDetails implements Comparable<LuceneIndexDetails>, Seria buffer.append(",\tRegion Path = "+regionPath); buffer.append(",\tIndexed Fields = "+getSearchableFieldNamesString()); buffer.append(",\tField Analyzer = "+getFieldAnalyzersString()); + buffer.append(",\tIndex Statistics =\n\t"+getIndexStatsString()); buffer.append("\n}\n"); return buffer.toString(); } @@ -90,10 +112,6 @@ public class LuceneIndexDetails implements Comparable<LuceneIndexDetails>, Seria return regionPath; } - public String[] getSearchableFieldNames() { - return searchableFieldNames; - } - private static <T extends Comparable<T>> int compare(final T obj1, final T obj2) { return (obj1 == null && obj2 == null ? 0 : (obj1 == null ? 1 : (obj2 == null ? -1 : obj1.compareTo(obj2)))); } http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexInfo.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexInfo.java b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexInfo.java index 76b7b60..3df292c 100644 --- a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexInfo.java +++ b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexInfo.java @@ -41,6 +41,10 @@ public class LuceneIndexInfo implements Serializable { this.fieldAnalyzers = fieldAnalyzers; } + public LuceneIndexInfo(final String indexName, final String regionPath) { + this(indexName,regionPath,null,null); + } + public String getIndexName() { return indexName; } http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java new file mode 100755 index 0000000..b91db60 --- /dev/null +++ b/geode-lucene/src/main/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunction.java @@ -0,0 +1,68 @@ +/* + * 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 com.gemstone.gemfire.cache.lucene.internal.cli.functions; + +import java.util.HashSet; +import java.util.Set; + +import com.gemstone.gemfire.cache.Cache; +import com.gemstone.gemfire.cache.CacheFactory; +import com.gemstone.gemfire.cache.execute.FunctionAdapter; +import com.gemstone.gemfire.cache.execute.FunctionContext; +import com.gemstone.gemfire.cache.lucene.LuceneIndex; +import com.gemstone.gemfire.cache.lucene.LuceneService; +import com.gemstone.gemfire.cache.lucene.LuceneServiceProvider; +import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexImpl; +import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexDetails; +import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexInfo; +import com.gemstone.gemfire.internal.InternalEntity; + +/** + * The LuceneDescribeIndexFunction class is a function used to collect the information on a particular lucene index. + * </p> + * @see Cache + * @see com.gemstone.gemfire.cache.execute.Function + * @see FunctionAdapter + * @see FunctionContext + * @see InternalEntity + * @see LuceneIndexDetails + * @see LuceneIndexInfo + */ +@SuppressWarnings("unused") +public class LuceneDescribeIndexFunction extends FunctionAdapter implements InternalEntity { + + protected Cache getCache() { + return CacheFactory.getAnyInstance(); + } + + public String getId() { + return LuceneDescribeIndexFunction.class.getName(); + } + + public void execute(final FunctionContext context) { + LuceneIndexDetails result = null; + final Cache cache = getCache(); + final LuceneIndexInfo indexInfo = (LuceneIndexInfo) context.getArguments(); + LuceneService service = LuceneServiceProvider.get(cache); + LuceneIndex index = service.getIndex(indexInfo.getIndexName(), indexInfo.getRegionPath()); + if (index != null) { + result = new LuceneIndexDetails((LuceneIndexImpl) index); + } + context.getResultSender().lastResult(result); + } +} http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java b/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java index 2647bcc..06cc410 100755 --- a/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java +++ b/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsDUnitTest.java @@ -28,7 +28,6 @@ import com.gemstone.gemfire.management.internal.cli.result.CommandResult; import com.gemstone.gemfire.management.internal.cli.util.CommandStringBuilder; import com.gemstone.gemfire.test.dunit.*; import com.gemstone.gemfire.test.junit.categories.DistributedTest; -import com.sun.org.apache.regexp.internal.RE; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.core.KeywordAnalyzer; @@ -45,7 +44,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Properties; -import java.util.stream.Collectors; @Category(DistributedTest.class) public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { @@ -57,15 +55,41 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { } @Test - public void listIndexShouldReturnExistingIndex() throws Exception { + public void listIndexShouldReturnExistingIndexWithStats() throws Exception { final VM vm1 = Host.getHost(0).getVM(1); createIndex(vm1); CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_LIST_INDEX); + csb.addOption(LuceneCliStrings.LUCENE_LIST_INDEX__STATS,"true"); String resultAsString = executeCommandAndLogResult(csb); assertTrue(resultAsString.contains(INDEX_NAME)); + assertTrue(resultAsString.contains("Documents")); + } + + @Test + public void listIndexShouldReturnExistingIndexWithoutStats() throws Exception { + final VM vm1 = Host.getHost(0).getVM(1); + + createIndex(vm1); + CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); + + CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_LIST_INDEX); + String resultAsString = executeCommandAndLogResult(csb); + assertTrue(resultAsString.contains(INDEX_NAME)); + assertFalse(resultAsString.contains("Documents")); + } + + @Test + public void listIndexWhenNoExistingIndexShouldReturnNoIndex() throws Exception { + final VM vm1 = Host.getHost(0).getVM(1); + + CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); + + CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_LIST_INDEX); + String resultAsString = executeCommandAndLogResult(csb); + assertTrue(resultAsString.contains("No lucene indexes found")); } @Test @@ -76,8 +100,8 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_CREATE_INDEX); - csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__NAME,INDEX_NAME); - csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__REGION,REGION_NAME); + csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME,INDEX_NAME); + csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH,REGION_NAME); csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD,"field1,field2,field3"); String resultAsString = executeCommandAndLogResult(csb); @@ -104,8 +128,8 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_CREATE_INDEX); - csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__NAME,INDEX_NAME); - csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__REGION,REGION_NAME); + csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME,INDEX_NAME); + csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH,REGION_NAME); csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD,"field1,field2,field3"); csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__ANALYZER,String.join(",",analyzerNames)); @@ -136,12 +160,11 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { getCache(); }); - CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_CREATE_INDEX); - csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__NAME,INDEX_NAME); - csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__REGION,REGION_NAME); + csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME,INDEX_NAME); + csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH,REGION_NAME); csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__FIELD,"field1,field2,field3"); csb.addOption(LuceneCliStrings.LUCENE_CREATE_INDEX__GROUP,"group1"); String resultAsString = executeCommandAndLogResult(csb); @@ -164,6 +187,35 @@ public class LuceneIndexCommandsDUnitTest extends CliCommandTestBase { }); } + @Test + public void describeIndexShouldReturnExistingIndex() throws Exception { + final VM vm1 = Host.getHost(0).getVM(1); + + createIndex(vm1); + CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); + + CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_DESCRIBE_INDEX); + csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME,INDEX_NAME); + csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH,REGION_NAME); + String resultAsString = executeCommandAndLogResult(csb); + assertTrue(resultAsString.contains(INDEX_NAME)); + } + + @Test + public void describeIndexShouldNotReturnResultWhenIndexNotFound() throws Exception { + final VM vm1 = Host.getHost(0).getVM(1); + + createIndex(vm1); + CommandManager.getInstance().add(LuceneIndexCommands.class.newInstance()); + + CommandStringBuilder csb = new CommandStringBuilder(LuceneCliStrings.LUCENE_DESCRIBE_INDEX); + csb.addOption(LuceneCliStrings.LUCENE__INDEX_NAME,"notAnIndex"); + csb.addOption(LuceneCliStrings.LUCENE__REGION_PATH,REGION_NAME); + String resultAsString = executeCommandAndLogResult(csb); + + assertTrue(resultAsString.contains("No lucene indexes found")); + } + private void createRegion() { getCache().createRegionFactory(RegionShortcut.PARTITION).create(REGION_NAME); } http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java b/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java index 0ebfa10..41f1ca4 100644 --- a/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java +++ b/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/LuceneIndexCommandsJUnitTest.java @@ -38,6 +38,9 @@ import org.junit.experimental.categories.Category; import com.gemstone.gemfire.cache.Cache; import com.gemstone.gemfire.cache.execute.Execution; import com.gemstone.gemfire.cache.execute.ResultCollector; +import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexStats; +import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneCreateIndexFunction; +import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneDescribeIndexFunction; import com.gemstone.gemfire.cache.lucene.internal.cli.functions.LuceneListIndexFunction; import com.gemstone.gemfire.distributed.DistributedMember; import com.gemstone.gemfire.internal.cache.execute.AbstractExecution; @@ -45,6 +48,7 @@ import com.gemstone.gemfire.internal.util.CollectionUtils; import com.gemstone.gemfire.management.cli.Result; import com.gemstone.gemfire.management.cli.Result.Status; import com.gemstone.gemfire.management.internal.cli.functions.CliFunctionResult; +import com.gemstone.gemfire.management.internal.cli.functions.CreateIndexFunction; import com.gemstone.gemfire.management.internal.cli.i18n.CliStrings; import com.gemstone.gemfire.management.internal.cli.result.CommandResult; import com.gemstone.gemfire.management.internal.cli.result.CommandResultException; @@ -70,16 +74,8 @@ import com.gemstone.gemfire.test.junit.categories.UnitTest; @Category(UnitTest.class) public class LuceneIndexCommandsJUnitTest { - private LuceneIndexCommands createIndexCommands(final Cache cache, final Execution functionExecutor) { - return new LuceneTestIndexCommands(cache, functionExecutor); - } - - private LuceneIndexDetails createIndexDetails(final String indexName, final String regionPath, final String[] searchableFields, final Map<String, Analyzer> fieldAnalyzers) { - return new LuceneIndexDetails(indexName, regionPath, searchableFields, fieldAnalyzers); - } - @Test - public void testListIndex() { + public void testListIndexWithoutStats() { final Cache mockCache = mock(Cache.class, "Cache"); final AbstractExecution mockFunctionExecutor = mock(AbstractExecution.class, "Function Executor"); @@ -112,7 +108,7 @@ public class LuceneIndexCommandsJUnitTest { final LuceneIndexCommands commands = createIndexCommands(mockCache, mockFunctionExecutor); - CommandResult result = (CommandResult) commands.listIndex(); + CommandResult result = (CommandResult) commands.listIndex(false); TabularResultData data = (TabularResultData) result.getResultData(); assertEquals(Arrays.asList("memberFive", "memberSix", "memberTen"), data.retrieveAllValues("Index Name")); assertEquals(Arrays.asList("/Employees", "/Employees", "/Employees"), data.retrieveAllValues("Region Path")); @@ -121,6 +117,53 @@ public class LuceneIndexCommandsJUnitTest { } @Test + public void testListIndexWithStats() { + + final Cache mockCache = mock(Cache.class, "Cache"); + final AbstractExecution mockFunctionExecutor = mock(AbstractExecution.class, "Function Executor"); + final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector"); + final LuceneIndexStats mockIndexStats1=getMockIndexStats(1,10,5,1); + final LuceneIndexStats mockIndexStats2=getMockIndexStats(2,20,10,2); + final LuceneIndexStats mockIndexStats3=getMockIndexStats(3,30,15,3); + String[] searchableFields={"field1","field2","field3"}; + Map<String, Analyzer> fieldAnalyzers = new HashMap<>(); + fieldAnalyzers.put("field1", new StandardAnalyzer()); + fieldAnalyzers.put("field2", new KeywordAnalyzer()); + fieldAnalyzers.put("field3", null); + final LuceneIndexDetails indexDetails1 = createIndexDetails("memberFive", "/Employees", searchableFields, fieldAnalyzers,mockIndexStats1); + final LuceneIndexDetails indexDetails2 = createIndexDetails("memberSix", "/Employees", searchableFields, fieldAnalyzers,mockIndexStats2); + final LuceneIndexDetails indexDetails3 = createIndexDetails("memberTen", "/Employees", searchableFields, fieldAnalyzers,mockIndexStats3); + + + final List<LuceneIndexDetails> expectedIndexDetails = new ArrayList<>(3); + expectedIndexDetails.add(indexDetails1); + expectedIndexDetails.add(indexDetails2); + expectedIndexDetails.add(indexDetails3); + + final List<Set<LuceneIndexDetails>> results = new ArrayList<>(); + + results.add(CollectionUtils.asSet(indexDetails2, indexDetails1,indexDetails3)); + + when(mockFunctionExecutor.execute(isA(LuceneListIndexFunction.class))) + .thenReturn(mockResultCollector); + when(mockResultCollector.getResult()) + .thenReturn(results); + + final LuceneIndexCommands commands = createIndexCommands(mockCache, mockFunctionExecutor); + + CommandResult result = (CommandResult) commands.listIndex(true); + TabularResultData data = (TabularResultData) result.getResultData(); + assertEquals(Arrays.asList("memberFive", "memberSix", "memberTen"), data.retrieveAllValues("Index Name")); + assertEquals(Arrays.asList("/Employees", "/Employees", "/Employees"), data.retrieveAllValues("Region Path")); + assertEquals(Arrays.asList("[field1, field2, field3]", "[field1, field2, field3]", "[field1, field2, field3]"), data.retrieveAllValues("Indexed Fields")); + assertEquals(Arrays.asList("{field1=StandardAnalyzer, field2=KeywordAnalyzer}", "{field1=StandardAnalyzer, field2=KeywordAnalyzer}", "{field1=StandardAnalyzer, field2=KeywordAnalyzer}"), data.retrieveAllValues("Field Analyzer")); + assertEquals(Arrays.asList("1","2","3"), data.retrieveAllValues("Query Executions")); + assertEquals(Arrays.asList("10","20","30"), data.retrieveAllValues("Commits")); + assertEquals(Arrays.asList("5","10","15"), data.retrieveAllValues("Updates")); + assertEquals(Arrays.asList("1","2","3"), data.retrieveAllValues("Documents")); + } + + @Test public void testCreateIndex() throws CommandResultException { final Cache mockCache=mock(Cache.class); final ResultCollector mockResultCollector = mock(ResultCollector.class); @@ -129,7 +172,7 @@ public class LuceneIndexCommandsJUnitTest { final List<CliFunctionResult> cliFunctionResults=new ArrayList<>(); cliFunctionResults.add(new CliFunctionResult(memberId,true,"Index Created")); - doReturn(mockResultCollector).when(commands).createIndexOnGroups(any(),any(LuceneIndexInfo.class)); + doReturn(mockResultCollector).when(commands).executeFunctionOnGroups(isA(LuceneCreateIndexFunction.class),any(),any(LuceneIndexInfo.class)); doReturn(cliFunctionResults).when(mockResultCollector).getResult(); String indexName ="index"; @@ -153,6 +196,65 @@ public class LuceneIndexCommandsJUnitTest { return ResultBuilder.buildResult(infoResult); } + @Test + public void testDescribeIndex() throws CommandResultException { + + final Cache mockCache = mock(Cache.class, "Cache"); + final ResultCollector mockResultCollector = mock(ResultCollector.class, "ResultCollector"); + final LuceneIndexCommands commands=spy(createIndexCommands(mockCache,null)); + + String[] searchableFields={"field1","field2","field3"}; + Map<String, Analyzer> fieldAnalyzers = new HashMap<>(); + fieldAnalyzers.put("field1", new StandardAnalyzer()); + fieldAnalyzers.put("field2", new KeywordAnalyzer()); + fieldAnalyzers.put("field3", null); + final LuceneIndexStats mockIndexStats=getMockIndexStats(1,10,5,1); + final List<LuceneIndexDetails> indexDetails = new ArrayList<>(); + indexDetails.add(createIndexDetails("memberFive", "/Employees", searchableFields, fieldAnalyzers,mockIndexStats)); + + doReturn(mockResultCollector).when(commands).executeFunctionOnGroups(isA(LuceneDescribeIndexFunction.class),any(),any(LuceneIndexInfo.class)); + doReturn(indexDetails).when(mockResultCollector).getResult(); + + CommandResult result = (CommandResult) commands.describeIndex("memberFive","/Employees"); + + TabularResultData data = (TabularResultData) result.getResultData(); + assertEquals(Arrays.asList("memberFive"), data.retrieveAllValues("Index Name")); + assertEquals(Arrays.asList("/Employees"), data.retrieveAllValues("Region Path")); + assertEquals(Arrays.asList("[field1, field2, field3]"), data.retrieveAllValues("Indexed Fields")); + assertEquals(Arrays.asList("{field1=StandardAnalyzer, field2=KeywordAnalyzer}"), data.retrieveAllValues("Field Analyzer")); + assertEquals(Arrays.asList("1"), data.retrieveAllValues("Query Executions")); + assertEquals(Arrays.asList("10"), data.retrieveAllValues("Commits")); + assertEquals(Arrays.asList("5"), data.retrieveAllValues("Updates")); + assertEquals(Arrays.asList("1"), data.retrieveAllValues("Documents")); + + } + + + private LuceneIndexStats getMockIndexStats(int queries, int commits, int updates, int docs) { + LuceneIndexStats mockIndexStats=mock(LuceneIndexStats.class); + when(mockIndexStats.getQueryExecutions()) + .thenReturn(queries); + when(mockIndexStats.getCommits()) + .thenReturn(commits); + when(mockIndexStats.getUpdates()) + .thenReturn(updates); + when(mockIndexStats.getDocuments()) + .thenReturn(docs); + return mockIndexStats; + } + + private LuceneIndexCommands createIndexCommands(final Cache cache, final Execution functionExecutor) { + return new LuceneTestIndexCommands(cache, functionExecutor); + } + + private LuceneIndexDetails createIndexDetails(final String indexName, final String regionPath, final String[] searchableFields, final Map<String, Analyzer> fieldAnalyzers, LuceneIndexStats indexStats) { + return new LuceneIndexDetails(indexName, regionPath, searchableFields, fieldAnalyzers,indexStats); + } + + private LuceneIndexDetails createIndexDetails(final String indexName, final String regionPath, final String[] searchableFields, final Map<String, Analyzer> fieldAnalyzers) { + return new LuceneIndexDetails(indexName, regionPath, searchableFields, fieldAnalyzers,null); + } + private static class LuceneTestIndexCommands extends LuceneIndexCommands { private final Cache cache; http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ce0b7e7d/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunctionJUnitTest.java ---------------------------------------------------------------------- diff --git a/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunctionJUnitTest.java b/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunctionJUnitTest.java new file mode 100644 index 0000000..3669b90 --- /dev/null +++ b/geode-lucene/src/test/java/com/gemstone/gemfire/cache/lucene/internal/cli/functions/LuceneDescribeIndexFunctionJUnitTest.java @@ -0,0 +1,103 @@ +/* + * 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 com.gemstone.gemfire.cache.lucene.internal.cli.functions; + +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +import com.gemstone.gemfire.cache.execute.FunctionContext; +import com.gemstone.gemfire.cache.execute.ResultSender; +import com.gemstone.gemfire.cache.lucene.LuceneIndex; +import com.gemstone.gemfire.cache.lucene.internal.InternalLuceneService; +import com.gemstone.gemfire.cache.lucene.internal.LuceneIndexImpl; +import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexDetails; +import com.gemstone.gemfire.cache.lucene.internal.cli.LuceneIndexInfo; +import com.gemstone.gemfire.internal.cache.GemFireCacheImpl; +import com.gemstone.gemfire.test.fake.Fakes; +import com.gemstone.gemfire.test.junit.categories.UnitTest; + +import org.apache.lucene.analysis.Analyzer; +import org.apache.lucene.analysis.core.KeywordAnalyzer; +import org.apache.lucene.analysis.standard.StandardAnalyzer; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.mockito.ArgumentCaptor; +import org.mockito.Mockito; + +@Category(UnitTest.class) + +public class LuceneDescribeIndexFunctionJUnitTest { + + @Test + @SuppressWarnings("unchecked") + public void testExecute() throws Throwable { + GemFireCacheImpl cache = Fakes.cache(); + InternalLuceneService service = mock(InternalLuceneService.class); + when(cache.getService(InternalLuceneService.class)).thenReturn(service); + FunctionContext context = mock(FunctionContext.class); + ResultSender resultSender = mock(ResultSender.class); + LuceneIndexInfo indexInfo=getMockLuceneInfo("index1"); + LuceneIndexImpl index1 = getMockLuceneIndex("index1"); + LuceneDescribeIndexFunction function = spy(LuceneDescribeIndexFunction.class); + + doReturn(indexInfo).when(context).getArguments(); + doReturn(resultSender).when(context).getResultSender(); + doReturn(cache).when(function).getCache(); + when(service.getIndex(indexInfo.getIndexName(),indexInfo.getRegionPath())).thenReturn(index1); + + function.execute(context); + ArgumentCaptor<LuceneIndexDetails> resultCaptor = ArgumentCaptor.forClass(LuceneIndexDetails.class); + verify(resultSender).lastResult(resultCaptor.capture()); + LuceneIndexDetails result = resultCaptor.getValue(); + LuceneIndexDetails expected=new LuceneIndexDetails(index1); + + assertEquals(expected.getIndexName(),result.getIndexName()); + assertEquals(expected.getRegionPath(),result.getRegionPath()); + assertEquals(expected.getIndexStats(),result.getIndexStats()); + assertEquals(expected.getFieldAnalyzersString(),result.getFieldAnalyzersString()); + assertEquals(expected.getSearchableFieldNamesString(),result.getSearchableFieldNamesString()); + } + + private LuceneIndexInfo getMockLuceneInfo(final String index1) { + LuceneIndexInfo mockInfo=mock(LuceneIndexInfo.class); + doReturn(index1).when(mockInfo).getIndexName(); + doReturn("/region").when(mockInfo).getRegionPath(); + return mockInfo; + } + + private LuceneIndexImpl getMockLuceneIndex(final String indexName) + { + String[] searchableFields={"field1","field2"}; + Map<String, Analyzer> fieldAnalyzers = new HashMap<>(); + fieldAnalyzers.put("field1", new StandardAnalyzer()); + fieldAnalyzers.put("field2", new KeywordAnalyzer()); + + LuceneIndexImpl index = mock(LuceneIndexImpl.class); + when(index.getName()).thenReturn(indexName); + when(index.getRegionPath()).thenReturn("/region"); + when(index.getFieldNames()).thenReturn(searchableFields); + when(index.getFieldAnalyzers()).thenReturn(fieldAnalyzers); + return index; + } + +}
