mirror of https://github.com/apache/lucene.git
LUCENE-9570: code reformatting [final].
This commit is contained in:
parent
2695624a9f
commit
2cbf261032
|
@ -227,6 +227,22 @@ configure(project(":lucene:queryparser")) {
|
|||
}
|
||||
}
|
||||
|
||||
task regenerate() {
|
||||
description "Regenerate any generated sources"
|
||||
group "generation"
|
||||
|
||||
// Run regeneration tasks.
|
||||
dependsOn javaccParserClassic, javaccParserSurround, javaccParserFlexible
|
||||
|
||||
// Clean up and reformat the generated sources after generation.
|
||||
dependsOn "tidy"
|
||||
}
|
||||
|
||||
// Make sure tidy runs after generation, if they're defined.
|
||||
tasks.matching { it.name == "tidy" }.configureEach {
|
||||
mustRunAfter javaccParserClassic, javaccParserSurround, javaccParserFlexible
|
||||
}
|
||||
|
||||
task javacc() {
|
||||
description "Regenerate query parsers (javacc syntax definitions)."
|
||||
group "generation"
|
||||
|
@ -244,7 +260,6 @@ configure(project(":solr:core")) {
|
|||
|
||||
javaccFile = file('src/java/org/apache/solr/parser/QueryParser.jj')
|
||||
|
||||
|
||||
afterGenerate << commonCleanups
|
||||
afterGenerate << { FileTree generatedFiles ->
|
||||
generatedFiles.matching { include "QueryParser.java" }.each { file ->
|
||||
|
|
|
@ -16,116 +16,89 @@
|
|||
*/
|
||||
|
||||
/*
|
||||
* LUCENE-9564: This adds automatic (and enforced) code formatting.
|
||||
* LUCENE-9564: This adds automatic (and enforced) code formatting using
|
||||
* spotless and Google Java Format.
|
||||
*/
|
||||
|
||||
def resources = scriptResources(buildscript)
|
||||
|
||||
allprojects { prj ->
|
||||
configure(project(":lucene").subprojects) { prj ->
|
||||
plugins.withType(JavaPlugin) {
|
||||
prj.apply plugin: 'com.diffplug.spotless'
|
||||
|
||||
spotless {
|
||||
java {
|
||||
// TODO: work out how to have multiple different header files (we have
|
||||
// classes in the codebase that have original headers).
|
||||
// TODO: Work out how to support multiple different header files (we have
|
||||
// classes in the codebase that have original headers). We currently use
|
||||
// Apache RAT to enforce headers so this is of lesser priority.
|
||||
//
|
||||
// licenseHeaderFile file("${resources}/asl-header.txt"), '^(\\s*package)'
|
||||
|
||||
lineEndings 'UNIX'
|
||||
endWithNewline()
|
||||
googleJavaFormat('1.9')
|
||||
|
||||
// Apply to all Java sources
|
||||
target "src/**/*.java"
|
||||
|
||||
// Exclude certain files (generated ones, mostly).
|
||||
switch (project.path) {
|
||||
// These modules are complete - all sources scanned.
|
||||
case ":lucene:core":
|
||||
target "src/java/**/*.java",
|
||||
"src/test/**/*.java"
|
||||
targetExclude "**/resources/**", "**/StandardTokenizerImpl.java"
|
||||
break
|
||||
|
||||
case ":lucene:highlighter":
|
||||
target "src/java/**/*.java",
|
||||
"src/test/**/*.java"
|
||||
targetExclude "**/resources/**"
|
||||
break
|
||||
|
||||
case ":lucene:queries":
|
||||
target "src/java/**/*.java",
|
||||
"src/test/**/*.java"
|
||||
targetExclude "**/resources/**"
|
||||
targetExclude "**/StandardTokenizerImpl.java"
|
||||
break
|
||||
|
||||
case ":lucene:analysis:common":
|
||||
target "src/**/*.java"
|
||||
targetExclude "**/resources/**",
|
||||
"**/HTMLStripCharFilter.java",
|
||||
targetExclude "**/HTMLStripCharFilter.java",
|
||||
"**/UAX29URLEmailTokenizerImpl.java",
|
||||
"**/tartarus/**"
|
||||
break
|
||||
|
||||
case ":lucene:demo":
|
||||
case ":lucene:analysis:morfologik":
|
||||
case ":lucene:analysis:icu":
|
||||
case ":lucene:analysis:kuromoji":
|
||||
case ":lucene:memory":
|
||||
case ":lucene:benchmark":
|
||||
case ":lucene:analysis:nori":
|
||||
case ":lucene:analysis:opennlp":
|
||||
case ":lucene:analysis:phonetic":
|
||||
case ":lucene:analysis:smartcn":
|
||||
case ":lucene:analysis:stempel":
|
||||
case ":lucene:classification":
|
||||
case ":lucene:backward-codecs":
|
||||
case ":lucene:codecs":
|
||||
case ":lucene:join":
|
||||
target "src/**/*.java"
|
||||
targetExclude "**/resources/**"
|
||||
case ":lucene:test-framework":
|
||||
targetExclude "**/EmojiTokenizationTestUnicode_11_0.java",
|
||||
"**/WordBreakTestUnicode_9_0_0.java"
|
||||
break
|
||||
|
||||
case ":lucene:expressions":
|
||||
target "src/**/*.java"
|
||||
targetExclude "**/resources/**", "**/JavascriptLexer.java", "**/JavascriptParser.java",
|
||||
targetExclude "**/JavascriptLexer.java",
|
||||
"**/JavascriptParser.java",
|
||||
"**/JavascriptVisitor.java"
|
||||
break
|
||||
|
||||
// Partially complete.
|
||||
|
||||
case ":lucene:facet":
|
||||
target "src/**/*.java"
|
||||
targetExclude "**/taxonomy.8.6.3-cfs.zip"
|
||||
break
|
||||
|
||||
// All others - disable reformatting/ checks for now.
|
||||
case ":lucene:grouping":
|
||||
case ":lucene:luke":
|
||||
case ":lucene:misc":
|
||||
case ":lucene:monitor":
|
||||
case ":lucene:queryparser":
|
||||
case ":lucene:replicator":
|
||||
case ":lucene:sandbox":
|
||||
case ":lucene:spatial3d":
|
||||
case ":lucene:spatial-extras":
|
||||
case ":lucene:suggest":
|
||||
case ":lucene:test-framework":
|
||||
|
||||
default:
|
||||
target 'non-existing/**'
|
||||
targetExclude "**/classic/ParseException.java",
|
||||
"**/classic/QueryParser.java",
|
||||
"**/classic/QueryParserConstants.java",
|
||||
"**/classic/QueryParserTokenManager.java",
|
||||
"**/classic/Token.java",
|
||||
"**/classic/TokenMgrError.java",
|
||||
"**/standard/parser/ParseException.java",
|
||||
"**/standard/parser/StandardSyntaxParser.java",
|
||||
"**/standard/parser/StandardSyntaxParserConstants.java",
|
||||
"**/standard/parser/StandardSyntaxParserTokenManager.java",
|
||||
"**/standard/parser/Token.java",
|
||||
"**/standard/parser/TokenMgrError.java",
|
||||
"**/surround/parser/ParseException.java",
|
||||
"**/surround/parser/QueryParser.java",
|
||||
"**/surround/parser/QueryParserConstants.java",
|
||||
"**/surround/parser/QueryParserTokenManager.java",
|
||||
"**/surround/parser/Token.java",
|
||||
"**/surround/parser/TokenMgrError.java"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for an odd problem in spotless where it fails because
|
||||
// of a missing folder.
|
||||
spotlessJava {
|
||||
doFirst {
|
||||
project.mkdir("${buildDir}/spotless/spotlessJava")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add an alias to 'spotlessApply' simply called 'tidy' and add
|
||||
// spotlessCheck to check.
|
||||
allprojects { prj ->
|
||||
// Add an alias to 'spotlessApply' simply called 'tidy' and wire up
|
||||
// spotlessCheck to convention's check.
|
||||
task tidy() {
|
||||
description "Applies formatters and cleanups to sources."
|
||||
group "verification"
|
||||
|
|
|
@ -191,6 +191,10 @@ Bug fixes
|
|||
|
||||
Other
|
||||
|
||||
* LUCENE-9570, LUCENE-9564: Apply google java format and enforce it on source Java files.
|
||||
Review diffs and correct automatic formatting oddities. (Erick Erickson,
|
||||
Bruno Roustant, Dawid Weiss)
|
||||
|
||||
* LUCENE-9631: Properly override slice() on subclasses of OffsetRange. (Dawid Weiss)
|
||||
|
||||
* LUCENE-9312: Allow gradle builds against arbitrary JVMs. (Tomoko Uchida, Dawid Weiss)
|
||||
|
|
|
@ -76,7 +76,10 @@ public final class HitQueue extends PriorityQueue<ScoreDoc> {
|
|||
|
||||
@Override
|
||||
protected final boolean lessThan(ScoreDoc hitA, ScoreDoc hitB) {
|
||||
if (hitA.score == hitB.score) return hitA.doc > hitB.doc;
|
||||
else return hitA.score < hitB.score;
|
||||
if (hitA.score == hitB.score) {
|
||||
return hitA.doc > hitB.doc;
|
||||
} else {
|
||||
return hitA.score < hitB.score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,9 +177,8 @@ public class NIOFSDirectory extends FSDirectory {
|
|||
b.limit(b.position() + toRead);
|
||||
assert b.remaining() == toRead;
|
||||
final int i = channel.read(b, pos);
|
||||
if (i
|
||||
< 0) { // be defensive here, even though we checked before hand, something could have
|
||||
// changed
|
||||
if (i < 0) {
|
||||
// be defensive here, even though we checked before hand, something could have changed
|
||||
throw new EOFException(
|
||||
"read past EOF: "
|
||||
+ this
|
||||
|
@ -191,7 +190,8 @@ public class NIOFSDirectory extends FSDirectory {
|
|||
+ end);
|
||||
}
|
||||
assert i > 0
|
||||
: "FileChannel.read with non zero-length bb.remaining() must always read at least one byte (FileChannel is in blocking mode, see spec of ReadableByteChannel)";
|
||||
: "FileChannel.read with non zero-length bb.remaining() must always read at least "
|
||||
+ "one byte (FileChannel is in blocking mode, see spec of ReadableByteChannel)";
|
||||
pos += i;
|
||||
readLength -= i;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.FieldComparator;
|
||||
|
@ -33,10 +32,10 @@ import org.apache.lucene.search.SortField;
|
|||
import org.apache.lucene.util.FixedBitSet;
|
||||
|
||||
/**
|
||||
* This collector specializes in collecting the most relevant document (group head) for each
|
||||
* group that matches the query.
|
||||
* This collector specializes in collecting the most relevant document (group head) for each group
|
||||
* that matches the query.
|
||||
*
|
||||
* Clients should create new collectors by calling {@link #newCollector(GroupSelector, Sort)}
|
||||
* <p>Clients should create new collectors by calling {@link #newCollector(GroupSelector, Sort)}
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
|
@ -56,13 +55,15 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
|
||||
/**
|
||||
* Create a new AllGroupHeadsCollector based on the type of within-group Sort required
|
||||
*
|
||||
* @param selector a GroupSelector to define the groups
|
||||
* @param sort the within-group sort to use to choose the group head document
|
||||
* @param <T> the group value type
|
||||
*/
|
||||
public static <T> AllGroupHeadsCollector<T> newCollector(GroupSelector<T> selector, Sort sort) {
|
||||
if (sort.equals(Sort.RELEVANCE))
|
||||
if (sort.equals(Sort.RELEVANCE)) {
|
||||
return new ScoringGroupHeadsCollector<>(selector, sort);
|
||||
}
|
||||
return new SortingGroupHeadsCollector<>(selector, sort);
|
||||
}
|
||||
|
||||
|
@ -93,7 +94,8 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return an int array containing all group heads. The size of the array is equal to number of collected unique groups.
|
||||
* @return an int array containing all group heads. The size of the array is equal to number of
|
||||
* collected unique groups.
|
||||
*/
|
||||
public int[] retrieveGroupHeads() {
|
||||
Collection<? extends GroupHead<T>> groupHeads = getCollectedGroupHeads();
|
||||
|
@ -107,16 +109,13 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
return docHeads;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the number of group heads found for a query.
|
||||
*/
|
||||
/** @return the number of group heads found for a query. */
|
||||
public int groupHeadsSize() {
|
||||
return getCollectedGroupHeads().size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the collected group heads.
|
||||
* Subsequent calls should return the same group heads.
|
||||
* Returns the collected group heads. Subsequent calls should return the same group heads.
|
||||
*
|
||||
* @return the collected group heads
|
||||
*/
|
||||
|
@ -179,49 +178,46 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
/**
|
||||
* Create a new GroupHead for the given group value, initialized with a doc, context and scorer
|
||||
*/
|
||||
protected abstract GroupHead<T> newGroupHead(int doc, T value, LeafReaderContext context, Scorable scorer) throws IOException;
|
||||
protected abstract GroupHead<T> newGroupHead(
|
||||
int doc, T value, LeafReaderContext context, Scorable scorer) throws IOException;
|
||||
|
||||
/**
|
||||
* Represents a group head. A group head is the most relevant document for a particular group.
|
||||
* The relevancy is based is usually based on the sort.
|
||||
* Represents a group head. A group head is the most relevant document for a particular group. The
|
||||
* relevancy is based is usually based on the sort.
|
||||
*
|
||||
* The group head contains a group value with its associated most relevant document id.
|
||||
* <p>The group head contains a group value with its associated most relevant document id.
|
||||
*/
|
||||
public static abstract class GroupHead<T> {
|
||||
public abstract static class GroupHead<T> {
|
||||
|
||||
public final T groupValue;
|
||||
public int doc;
|
||||
|
||||
protected int docBase;
|
||||
|
||||
/**
|
||||
* Create a new GroupHead for the given value
|
||||
*/
|
||||
/** Create a new GroupHead for the given value */
|
||||
protected GroupHead(T groupValue, int doc, int docBase) {
|
||||
this.groupValue = groupValue;
|
||||
this.doc = doc + docBase;
|
||||
this.docBase = docBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for each segment
|
||||
*/
|
||||
/** Called for each segment */
|
||||
protected void setNextReader(LeafReaderContext ctx) throws IOException {
|
||||
this.docBase = ctx.docBase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called for each segment
|
||||
*/
|
||||
/** Called for each segment */
|
||||
protected abstract void setScorer(Scorable scorer) throws IOException;
|
||||
|
||||
/**
|
||||
* Compares the specified document for a specified comparator against the current most relevant document.
|
||||
* Compares the specified document for a specified comparator against the current most relevant
|
||||
* document.
|
||||
*
|
||||
* @param compIDX The comparator index of the specified comparator.
|
||||
* @param doc The specified document.
|
||||
* @return -1 if the specified document wasn't competitive against the current most relevant document, 1 if the
|
||||
* specified document was competitive against the current most relevant document. Otherwise 0.
|
||||
* @return -1 if the specified document wasn't competitive against the current most relevant
|
||||
* document, 1 if the specified document was competitive against the current most relevant
|
||||
* document. Otherwise 0.
|
||||
* @throws IOException If I/O related errors occur
|
||||
*/
|
||||
protected abstract int compare(int compIDX, int doc) throws IOException;
|
||||
|
@ -233,12 +229,9 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
* @throws IOException If I/O related errors occur
|
||||
*/
|
||||
protected abstract void updateDocHead(int doc) throws IOException;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* General implementation using a {@link FieldComparator} to select the group head
|
||||
*/
|
||||
/** General implementation using a {@link FieldComparator} to select the group head */
|
||||
private static class SortingGroupHeadsCollector<T> extends AllGroupHeadsCollector<T> {
|
||||
|
||||
protected SortingGroupHeadsCollector(GroupSelector<T> selector, Sort sort) {
|
||||
|
@ -246,7 +239,8 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected GroupHead<T> newGroupHead(int doc, T value, LeafReaderContext ctx, Scorable scorer) throws IOException {
|
||||
protected GroupHead<T> newGroupHead(int doc, T value, LeafReaderContext ctx, Scorable scorer)
|
||||
throws IOException {
|
||||
return new SortingGroupHead<>(sort, value, doc, ctx, scorer);
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +250,9 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
final FieldComparator[] comparators;
|
||||
final LeafFieldComparator[] leafComparators;
|
||||
|
||||
protected SortingGroupHead(Sort sort, T groupValue, int doc, LeafReaderContext context, Scorable scorer) throws IOException {
|
||||
protected SortingGroupHead(
|
||||
Sort sort, T groupValue, int doc, LeafReaderContext context, Scorable scorer)
|
||||
throws IOException {
|
||||
super(groupValue, doc, context.docBase);
|
||||
final SortField[] sortFields = sort.getSort();
|
||||
comparators = new FieldComparator[sortFields.length];
|
||||
|
@ -300,9 +296,7 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Specialized implementation for sorting by score
|
||||
*/
|
||||
/** Specialized implementation for sorting by score */
|
||||
private static class ScoringGroupHeadsCollector<T> extends AllGroupHeadsCollector<T> {
|
||||
|
||||
protected ScoringGroupHeadsCollector(GroupSelector<T> selector, Sort sort) {
|
||||
|
@ -310,7 +304,8 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected GroupHead<T> newGroupHead(int doc, T value, LeafReaderContext context, Scorable scorer) throws IOException {
|
||||
protected GroupHead<T> newGroupHead(
|
||||
int doc, T value, LeafReaderContext context, Scorable scorer) throws IOException {
|
||||
return new ScoringGroupHead<>(scorer, value, doc, context.docBase);
|
||||
}
|
||||
}
|
||||
|
@ -320,7 +315,8 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
private Scorable scorer;
|
||||
private float topScore;
|
||||
|
||||
protected ScoringGroupHead(Scorable scorer, T groupValue, int doc, int docBase) throws IOException {
|
||||
protected ScoringGroupHead(Scorable scorer, T groupValue, int doc, int docBase)
|
||||
throws IOException {
|
||||
super(groupValue, doc, docBase);
|
||||
assert scorer.docID() == doc;
|
||||
this.scorer = scorer;
|
||||
|
@ -338,8 +334,9 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
assert compIDX == 0;
|
||||
float score = scorer.score();
|
||||
int c = Float.compare(score, topScore);
|
||||
if (c > 0)
|
||||
if (c > 0) {
|
||||
topScore = score;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -348,5 +345,4 @@ public abstract class AllGroupHeadsCollector<T> extends SimpleCollector {
|
|||
this.doc = doc + docBase;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,17 +20,14 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
import org.apache.lucene.search.ScoreMode;
|
||||
import org.apache.lucene.search.SimpleCollector;
|
||||
|
||||
/**
|
||||
* A collector that collects all groups that match the
|
||||
* query. Only the group value is collected, and the order
|
||||
* is undefined. This collector does not determine
|
||||
* the most relevant document of a group.
|
||||
* A collector that collects all groups that match the query. Only the group value is collected, and
|
||||
* the order is undefined. This collector does not determine the most relevant document of a group.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
|
@ -42,6 +39,7 @@ public class AllGroupsCollector<T> extends SimpleCollector {
|
|||
|
||||
/**
|
||||
* Create a new AllGroupsCollector
|
||||
*
|
||||
* @param groupSelector the GroupSelector to determine groups
|
||||
*/
|
||||
public AllGroupsCollector(GroupSelector<T> groupSelector) {
|
||||
|
@ -49,8 +47,10 @@ public class AllGroupsCollector<T> extends SimpleCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of groups for the executed search.
|
||||
* This is a convenience method. The following code snippet has the same effect: <pre>getGroups().size()</pre>
|
||||
* Returns the total number of groups for the executed search. This is a convenience method. The
|
||||
* following code snippet has the same effect:
|
||||
*
|
||||
* <pre>getGroups().size()</pre>
|
||||
*
|
||||
* @return The total number of groups for the executed search
|
||||
*/
|
||||
|
@ -60,8 +60,8 @@ public class AllGroupsCollector<T> extends SimpleCollector {
|
|||
|
||||
/**
|
||||
* Returns the group values
|
||||
* <p>
|
||||
* This is an unordered collections of group values.
|
||||
*
|
||||
* <p>This is an unordered collections of group values.
|
||||
*
|
||||
* @return the group values
|
||||
*/
|
||||
|
@ -80,8 +80,9 @@ public class AllGroupsCollector<T> extends SimpleCollector {
|
|||
@Override
|
||||
public void collect(int doc) throws IOException {
|
||||
groupSelector.advanceTo(doc);
|
||||
if (groups.contains(groupSelector.currentValue()))
|
||||
if (groups.contains(groupSelector.currentValue())) {
|
||||
return;
|
||||
}
|
||||
groups.add(groupSelector.copyValue());
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.apache.lucene.search.grouping;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DocIdSetIterator;
|
||||
|
@ -40,29 +39,22 @@ import org.apache.lucene.util.ArrayUtil;
|
|||
import org.apache.lucene.util.PriorityQueue;
|
||||
|
||||
// TODO: this sentence is too long for the class summary.
|
||||
/** BlockGroupingCollector performs grouping with a
|
||||
* single pass collector, as long as you are grouping by a
|
||||
* doc block field, ie all documents sharing a given group
|
||||
* value were indexed as a doc block using the atomic
|
||||
* {@link IndexWriter#addDocuments IndexWriter.addDocuments()}
|
||||
* or {@link IndexWriter#updateDocuments IndexWriter.updateDocuments()}
|
||||
* API.
|
||||
/**
|
||||
* BlockGroupingCollector performs grouping with a single pass collector, as long as you are
|
||||
* grouping by a doc block field, ie all documents sharing a given group value were indexed as a doc
|
||||
* block using the atomic {@link IndexWriter#addDocuments IndexWriter.addDocuments()} or {@link
|
||||
* IndexWriter#updateDocuments IndexWriter.updateDocuments()} API.
|
||||
*
|
||||
* <p>This results in faster performance (~25% faster QPS)
|
||||
* than the two-pass grouping collectors, with the tradeoff
|
||||
* being that the documents in each group must always be
|
||||
* indexed as a block. This collector also fills in
|
||||
* TopGroups.totalGroupCount without requiring the separate
|
||||
* {@link org.apache.lucene.search.grouping.AllGroupsCollector}. However, this collector does
|
||||
* not fill in the groupValue of each group; this field
|
||||
* will always be null.
|
||||
* <p>This results in faster performance (~25% faster QPS) than the two-pass grouping collectors,
|
||||
* with the tradeoff being that the documents in each group must always be indexed as a block. This
|
||||
* collector also fills in TopGroups.totalGroupCount without requiring the separate {@link
|
||||
* org.apache.lucene.search.grouping.AllGroupsCollector}. However, this collector does not fill in
|
||||
* the groupValue of each group; this field will always be null.
|
||||
*
|
||||
* <p><b>NOTE</b>: this collector makes no effort to verify
|
||||
* the docs were in fact indexed as a block, so it's up to
|
||||
* you to ensure this was the case.
|
||||
* <p><b>NOTE</b>: this collector makes no effort to verify the docs were in fact indexed as a
|
||||
* block, so it's up to you to ensure this was the case.
|
||||
*
|
||||
* <p>See {@link org.apache.lucene.search.grouping} for more
|
||||
* details including a full code example.</p>
|
||||
* <p>See {@link org.apache.lucene.search.grouping} for more details including a full code example.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
|
@ -128,7 +120,9 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
|
||||
final int numComparators = comparators.length;
|
||||
for (int compIDX = 0; compIDX < numComparators; compIDX++) {
|
||||
final int c = reversed[compIDX] * comparators[compIDX].compare(group1.comparatorSlot, group2.comparatorSlot);
|
||||
final int c =
|
||||
reversed[compIDX]
|
||||
* comparators[compIDX].compare(group1.comparatorSlot, group2.comparatorSlot);
|
||||
if (c != 0) {
|
||||
// Short circuit
|
||||
return c > 0;
|
||||
|
@ -144,7 +138,8 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
// group is competitive we insert into the group queue
|
||||
private void processGroup() throws IOException {
|
||||
totalGroupCount++;
|
||||
//System.out.println(" processGroup ord=" + lastGroupOrd + " competes=" + groupCompetes + " count=" + subDocUpto + " groupDoc=" + topGroupDoc);
|
||||
// System.out.println(" processGroup ord=" + lastGroupOrd + " competes=" + groupCompetes + "
|
||||
// count=" + subDocUpto + " groupDoc=" + topGroupDoc);
|
||||
if (groupCompetes) {
|
||||
if (!queueFull) {
|
||||
// Startup transient: always add a new OneGroup
|
||||
|
@ -161,7 +156,8 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
// og.groupOrd = lastGroupOrd;
|
||||
og.comparatorSlot = bottomSlot;
|
||||
final OneGroup bottomGroup = groupQueue.add(og);
|
||||
//System.out.println(" ADD group=" + getGroupString(lastGroupOrd) + " newBottom=" + getGroupString(bottomGroup.groupOrd));
|
||||
// System.out.println(" ADD group=" + getGroupString(lastGroupOrd) + " newBottom=" +
|
||||
// getGroupString(bottomGroup.groupOrd));
|
||||
queueFull = groupQueue.size() == topNGroups;
|
||||
if (queueFull) {
|
||||
// Queue just became full; now set the real bottom
|
||||
|
@ -208,22 +204,17 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
/**
|
||||
* Create the single pass collector.
|
||||
*
|
||||
* @param groupSort The {@link Sort} used to sort the
|
||||
* groups. The top sorted document within each group
|
||||
* according to groupSort, determines how that group
|
||||
* sorts against other groups. This must be non-null,
|
||||
* ie, if you want to groupSort by relevance use
|
||||
* Sort.RELEVANCE.
|
||||
* @param groupSort The {@link Sort} used to sort the groups. The top sorted document within each
|
||||
* group according to groupSort, determines how that group sorts against other groups. This
|
||||
* must be non-null, ie, if you want to groupSort by relevance use Sort.RELEVANCE.
|
||||
* @param topNGroups How many top groups to keep.
|
||||
* @param needsScores true if the collected documents
|
||||
* require scores, either because relevance is included
|
||||
* in the withinGroupSort or because you plan to pass true
|
||||
* for either getSscores or getMaxScores to {@link
|
||||
* #getTopGroups}
|
||||
* @param lastDocPerGroup a {@link Weight} that marks the
|
||||
* last document in each group.
|
||||
* @param needsScores true if the collected documents require scores, either because relevance is
|
||||
* included in the withinGroupSort or because you plan to pass true for either getSscores or
|
||||
* getMaxScores to {@link #getTopGroups}
|
||||
* @param lastDocPerGroup a {@link Weight} that marks the last document in each group.
|
||||
*/
|
||||
public BlockGroupingCollector(Sort groupSort, int topNGroups, boolean needsScores, Weight lastDocPerGroup) {
|
||||
public BlockGroupingCollector(
|
||||
Sort groupSort, int topNGroups, boolean needsScores, Weight lastDocPerGroup) {
|
||||
|
||||
if (topNGroups < 1) {
|
||||
throw new IllegalArgumentException("topNGroups must be >= 1 (got " + topNGroups + ")");
|
||||
|
@ -259,25 +250,22 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
// typically they will be presented as a "single" result
|
||||
// in the UI?
|
||||
|
||||
/** Returns the grouped results. Returns null if the
|
||||
* number of groups collected is <= groupOffset.
|
||||
/**
|
||||
* Returns the grouped results. Returns null if the number of groups collected is <=
|
||||
* groupOffset.
|
||||
*
|
||||
* <p><b>NOTE</b>: This collector is unable to compute
|
||||
* the groupValue per group so it will always be null.
|
||||
* This is normally not a problem, as you can obtain the
|
||||
* value just like you obtain other values for each
|
||||
* matching document (eg, via stored fields, via
|
||||
* DocValues, etc.)
|
||||
* <p><b>NOTE</b>: This collector is unable to compute the groupValue per group so it will always
|
||||
* be null. This is normally not a problem, as you can obtain the value just like you obtain other
|
||||
* values for each matching document (eg, via stored fields, via DocValues, etc.)
|
||||
*
|
||||
* @param withinGroupSort The {@link Sort} used to sort
|
||||
* documents within each group.
|
||||
* @param withinGroupSort The {@link Sort} used to sort documents within each group.
|
||||
* @param groupOffset Which group to start from
|
||||
* @param withinGroupOffset Which document to start from
|
||||
* within each group
|
||||
* @param maxDocsPerGroup How many top documents to keep
|
||||
* within each group.
|
||||
* @param withinGroupOffset Which document to start from within each group
|
||||
* @param maxDocsPerGroup How many top documents to keep within each group.
|
||||
*/
|
||||
public TopGroups<?> getTopGroups(Sort withinGroupSort, int groupOffset, int withinGroupOffset, int maxDocsPerGroup) throws IOException {
|
||||
public TopGroups<?> getTopGroups(
|
||||
Sort withinGroupSort, int groupOffset, int withinGroupOffset, int maxDocsPerGroup)
|
||||
throws IOException {
|
||||
|
||||
// if (queueFull) {
|
||||
// System.out.println("getTopGroups groupOffset=" + groupOffset + " topNGroups=" + topNGroups);
|
||||
|
@ -305,12 +293,15 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
if (withinGroupSort.equals(Sort.RELEVANCE)) {
|
||||
// Sort by score
|
||||
if (!needsScores) {
|
||||
throw new IllegalArgumentException("cannot sort by relevance within group: needsScores=false");
|
||||
throw new IllegalArgumentException(
|
||||
"cannot sort by relevance within group: needsScores=false");
|
||||
}
|
||||
collector = TopScoreDocCollector.create(maxDocsPerGroup, Integer.MAX_VALUE);
|
||||
} else {
|
||||
// Sort by fields
|
||||
collector = TopFieldCollector.create(withinGroupSort, maxDocsPerGroup, Integer.MAX_VALUE); // TODO: disable exact counts?
|
||||
collector =
|
||||
TopFieldCollector.create(
|
||||
withinGroupSort, maxDocsPerGroup, Integer.MAX_VALUE); // TODO: disable exact counts?
|
||||
}
|
||||
|
||||
float groupMaxScore = needsScores ? Float.NEGATIVE_INFINITY : Float.NaN;
|
||||
|
@ -338,7 +329,9 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
|
||||
// TODO: we could aggregate scores across children
|
||||
// by Sum/Avg instead of passing NaN:
|
||||
groups[downTo] = new GroupDocs<>(Float.NaN,
|
||||
groups[downTo] =
|
||||
new GroupDocs<>(
|
||||
Float.NaN,
|
||||
groupMaxScore,
|
||||
new TotalHits(og.count, TotalHits.Relation.EQUAL_TO),
|
||||
topDocs.scoreDocs,
|
||||
|
@ -355,9 +348,14 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
}
|
||||
*/
|
||||
|
||||
return new TopGroups<>(new TopGroups<>(groupSort.getSort(),
|
||||
return new TopGroups<>(
|
||||
new TopGroups<>(
|
||||
groupSort.getSort(),
|
||||
withinGroupSort.getSort(),
|
||||
totalHitCount, totalGroupedHitCount, groups, maxScore),
|
||||
totalHitCount,
|
||||
totalGroupedHitCount,
|
||||
groups,
|
||||
maxScore),
|
||||
totalGroupCount);
|
||||
}
|
||||
|
||||
|
@ -513,6 +511,5 @@ public class BlockGroupingCollector extends SimpleCollector {
|
|||
public float score() {
|
||||
return score;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@ package org.apache.lucene.search.grouping;
|
|||
import org.apache.lucene.search.FieldComparator; // javadocs
|
||||
|
||||
/**
|
||||
* Expert: representation of a group in {@link FirstPassGroupingCollector},
|
||||
* tracking the top doc and {@link FieldComparator} slot.
|
||||
* @lucene.internal */
|
||||
* Expert: representation of a group in {@link FirstPassGroupingCollector}, tracking the top doc and
|
||||
* {@link FieldComparator} slot.
|
||||
*
|
||||
* @lucene.internal
|
||||
*/
|
||||
public class CollectedSearchGroup<T> extends SearchGroup<T> {
|
||||
int topDoc;
|
||||
int comparatorSlot;
|
||||
|
|
|
@ -22,13 +22,13 @@ import java.util.Collection;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.ScoreMode;
|
||||
import org.apache.lucene.search.SimpleCollector;
|
||||
|
||||
/**
|
||||
* A second pass grouping collector that keeps track of distinct values for a specified field for the top N group.
|
||||
* A second pass grouping collector that keeps track of distinct values for a specified field for
|
||||
* the top N group.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
|
@ -36,11 +36,14 @@ public class DistinctValuesCollector<T, R> extends SecondPassGroupingCollector<T
|
|||
|
||||
/**
|
||||
* Create a DistinctValuesCollector
|
||||
*
|
||||
* @param groupSelector the group selector to determine the top-level groups
|
||||
* @param groups the top-level groups to collect for
|
||||
* @param valueSelector a group selector to determine which values to collect per-group
|
||||
*/
|
||||
public DistinctValuesCollector(GroupSelector<T> groupSelector, Collection<SearchGroup<T>> groups,
|
||||
public DistinctValuesCollector(
|
||||
GroupSelector<T> groupSelector,
|
||||
Collection<SearchGroup<T>> groups,
|
||||
GroupSelector<R> valueSelector) {
|
||||
super(groupSelector, groups, new DistinctValuesReducer<>(valueSelector));
|
||||
}
|
||||
|
@ -58,12 +61,9 @@ public class DistinctValuesCollector<T, R> extends SecondPassGroupingCollector<T
|
|||
public void collect(int doc) throws IOException {
|
||||
if (valueSelector.advanceTo(doc) == GroupSelector.State.ACCEPT) {
|
||||
R value = valueSelector.currentValue();
|
||||
if (values.contains(value) == false)
|
||||
values.add(valueSelector.copyValue());
|
||||
}
|
||||
else {
|
||||
if (values.contains(null) == false)
|
||||
values.add(null);
|
||||
if (values.contains(value) == false) values.add(valueSelector.copyValue());
|
||||
} else {
|
||||
if (values.contains(null) == false) values.add(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,8 +113,8 @@ public class DistinctValuesCollector<T, R> extends SecondPassGroupingCollector<T
|
|||
}
|
||||
|
||||
/**
|
||||
* Returned by {@link DistinctValuesCollector#getGroups()},
|
||||
* representing the value and set of distinct values for the group.
|
||||
* Returned by {@link DistinctValuesCollector#getGroups()}, representing the value and set of
|
||||
* distinct values for the group.
|
||||
*/
|
||||
public static class GroupCount<T, R> {
|
||||
|
||||
|
@ -126,5 +126,4 @@ public class DistinctValuesCollector<T, R> extends SecondPassGroupingCollector<T
|
|||
this.uniqueValues = values;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,8 +20,7 @@ package org.apache.lucene.search.grouping;
|
|||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a contiguous range of double values, with an inclusive minimum and
|
||||
* exclusive maximum
|
||||
* Represents a contiguous range of double values, with an inclusive minimum and exclusive maximum
|
||||
*/
|
||||
public class DoubleRange {
|
||||
|
||||
|
@ -30,9 +29,7 @@ public class DoubleRange {
|
|||
/** The exclusive maximum value of this range */
|
||||
public double max;
|
||||
|
||||
/**
|
||||
* Creates a new double range, running from {@code min} inclusive to {@code max} exclusive
|
||||
*/
|
||||
/** Creates a new double range, running from {@code min} inclusive to {@code max} exclusive */
|
||||
public DoubleRange(double min, double max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
|
@ -48,8 +45,7 @@ public class DoubleRange {
|
|||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
DoubleRange that = (DoubleRange) o;
|
||||
return Double.compare(that.min, min) == 0 &&
|
||||
Double.compare(that.max, max) == 0;
|
||||
return Double.compare(that.min, min) == 0 && Double.compare(that.max, max) == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
|
||||
package org.apache.lucene.search.grouping;
|
||||
|
||||
/**
|
||||
* Groups double values into ranges
|
||||
*/
|
||||
/** Groups double values into ranges */
|
||||
public class DoubleRangeFactory {
|
||||
|
||||
private final double min;
|
||||
|
@ -28,10 +26,11 @@ public class DoubleRangeFactory {
|
|||
|
||||
/**
|
||||
* Creates a new DoubleRangeFactory
|
||||
*
|
||||
* @param min a minimum value; all doubles below this value are grouped into a single range
|
||||
* @param width a standard width; all ranges between {@code min} and {@code max} are this wide,
|
||||
* with the exception of the final range which may be up to this width. Ranges
|
||||
* are inclusive at the lower end, and exclusive at the upper end.
|
||||
* with the exception of the final range which may be up to this width. Ranges are inclusive
|
||||
* at the lower end, and exclusive at the upper end.
|
||||
* @param max a maximum value; all doubles above this value are grouped into a single range
|
||||
*/
|
||||
public DoubleRangeFactory(double min, double width, double max) {
|
||||
|
@ -42,12 +41,14 @@ public class DoubleRangeFactory {
|
|||
|
||||
/**
|
||||
* Finds the DoubleRange that a value should be grouped into
|
||||
*
|
||||
* @param value the value to group
|
||||
* @param reuse an existing DoubleRange object to reuse
|
||||
*/
|
||||
public DoubleRange getRange(double value, DoubleRange reuse) {
|
||||
if (reuse == null)
|
||||
if (reuse == null) {
|
||||
reuse = new DoubleRange(Double.MIN_VALUE, Double.MAX_VALUE);
|
||||
}
|
||||
if (value < min) {
|
||||
reuse.max = min;
|
||||
reuse.min = Double.MIN_VALUE;
|
||||
|
@ -63,5 +64,4 @@ public class DoubleRangeFactory {
|
|||
reuse.max = reuse.min + width;
|
||||
return reuse;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,15 +21,12 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValues;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
|
||||
/**
|
||||
* A GroupSelector implementation that groups documents by double values
|
||||
*/
|
||||
/** A GroupSelector implementation that groups documents by double values */
|
||||
public class DoubleRangeGroupSelector extends GroupSelector<DoubleRange> {
|
||||
|
||||
private final DoubleValuesSource source;
|
||||
|
@ -45,8 +42,10 @@ public class DoubleRangeGroupSelector extends GroupSelector<DoubleRange> {
|
|||
|
||||
/**
|
||||
* Creates a new DoubleRangeGroupSelector
|
||||
*
|
||||
* @param source a DoubleValuesSource to retrieve double values per document
|
||||
* @param rangeFactory a DoubleRangeFactory that defines how to group the double values into range buckets
|
||||
* @param rangeFactory a DoubleRangeFactory that defines how to group the double values into range
|
||||
* buckets
|
||||
*/
|
||||
public DoubleRangeGroupSelector(DoubleValuesSource source, DoubleRangeFactory rangeFactory) {
|
||||
this.source = source;
|
||||
|
@ -91,10 +90,11 @@ public class DoubleRangeGroupSelector extends GroupSelector<DoubleRange> {
|
|||
inSecondPass = new HashSet<>();
|
||||
includeEmpty = false;
|
||||
for (SearchGroup<DoubleRange> group : searchGroups) {
|
||||
if (group.groupValue == null)
|
||||
if (group.groupValue == null) {
|
||||
includeEmpty = true;
|
||||
else
|
||||
} else {
|
||||
inSecondPass.add(group.groupValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.util.Collection;
|
|||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.FieldComparator;
|
||||
import org.apache.lucene.search.LeafFieldComparator;
|
||||
|
@ -32,12 +31,11 @@ import org.apache.lucene.search.SimpleCollector;
|
|||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.search.SortField;
|
||||
|
||||
/** FirstPassGroupingCollector is the first of two passes necessary
|
||||
* to collect grouped hits. This pass gathers the top N sorted
|
||||
* groups. Groups are defined by a {@link GroupSelector}
|
||||
/**
|
||||
* FirstPassGroupingCollector is the first of two passes necessary to collect grouped hits. This
|
||||
* pass gathers the top N sorted groups. Groups are defined by a {@link GroupSelector}
|
||||
*
|
||||
* <p>See {@link org.apache.lucene.search.grouping} for more
|
||||
* details including a full code example.</p>
|
||||
* <p>See {@link org.apache.lucene.search.grouping} for more details including a full code example.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
|
@ -56,6 +54,7 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
// Set once we reach topNGroups unique groups:
|
||||
/** @lucene.internal */
|
||||
protected TreeSet<CollectedSearchGroup<T>> orderedGroups;
|
||||
|
||||
private int docBase;
|
||||
private int spareSlot;
|
||||
|
||||
|
@ -63,16 +62,14 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
* Create the first pass collector.
|
||||
*
|
||||
* @param groupSelector a GroupSelector used to defined groups
|
||||
* @param groupSort The {@link Sort} used to sort the
|
||||
* groups. The top sorted document within each group
|
||||
* according to groupSort, determines how that group
|
||||
* sorts against other groups. This must be non-null,
|
||||
* ie, if you want to groupSort by relevance use
|
||||
* Sort.RELEVANCE.
|
||||
* @param groupSort The {@link Sort} used to sort the groups. The top sorted document within each
|
||||
* group according to groupSort, determines how that group sorts against other groups. This
|
||||
* must be non-null, ie, if you want to groupSort by relevance use Sort.RELEVANCE.
|
||||
* @param topNGroups How many top groups to keep.
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public FirstPassGroupingCollector(GroupSelector<T> groupSelector, Sort groupSort, int topNGroups) {
|
||||
public FirstPassGroupingCollector(
|
||||
GroupSelector<T> groupSelector, Sort groupSort, int topNGroups) {
|
||||
this.groupSelector = groupSelector;
|
||||
if (topNGroups < 1) {
|
||||
throw new IllegalArgumentException("topNGroups must be >= 1 (got " + topNGroups + ")");
|
||||
|
@ -91,7 +88,8 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
for (int i = 0; i < sortFields.length; i++) {
|
||||
final SortField sortField = sortFields[i];
|
||||
|
||||
// use topNGroups + 1 so we have a spare slot to use for comparing (tracked by this.spareSlot):
|
||||
// use topNGroups + 1 so we have a spare slot to use for comparing (tracked by
|
||||
// this.spareSlot):
|
||||
comparators[i] = sortField.getComparator(topNGroups + 1, i);
|
||||
reversed[i] = sortField.getReverse() ? -1 : 1;
|
||||
}
|
||||
|
@ -106,16 +104,16 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns top groups, starting from offset. This may
|
||||
* return null, if no groups were collected, or if the
|
||||
* number of unique groups collected is <= offset.
|
||||
* Returns top groups, starting from offset. This may return null, if no groups were collected, or
|
||||
* if the number of unique groups collected is <= offset.
|
||||
*
|
||||
* @param groupOffset The offset in the collected groups
|
||||
* @return top groups, starting from offset
|
||||
*/
|
||||
public Collection<SearchGroup<T>> getTopGroups(int groupOffset) throws IOException {
|
||||
|
||||
//System.out.println("FP.getTopGroups groupOffset=" + groupOffset + " fillFields=" + fillFields + " groupMap.size()=" + groupMap.size());
|
||||
// System.out.println("FP.getTopGroups groupOffset=" + groupOffset + " fillFields=" + fillFields
|
||||
// + " groupMap.size()=" + groupMap.size());
|
||||
|
||||
if (groupOffset < 0) {
|
||||
throw new IllegalArgumentException("groupOffset must be >= 0 (got " + groupOffset + ")");
|
||||
|
@ -136,12 +134,14 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
if (upto++ < groupOffset) {
|
||||
continue;
|
||||
}
|
||||
// System.out.println(" group=" + (group.groupValue == null ? "null" : group.groupValue.toString()));
|
||||
// System.out.println(" group=" + (group.groupValue == null ? "null" :
|
||||
// group.groupValue.toString()));
|
||||
SearchGroup<T> searchGroup = new SearchGroup<>();
|
||||
searchGroup.groupValue = group.groupValue;
|
||||
searchGroup.sortValues = new Object[sortFieldCount];
|
||||
for (int sortFieldIDX = 0; sortFieldIDX < sortFieldCount; sortFieldIDX++) {
|
||||
searchGroup.sortValues[sortFieldIDX] = comparators[sortFieldIDX].value(group.comparatorSlot);
|
||||
searchGroup.sortValues[sortFieldIDX] =
|
||||
comparators[sortFieldIDX].value(group.comparatorSlot);
|
||||
}
|
||||
result.add(searchGroup);
|
||||
}
|
||||
|
@ -190,8 +190,9 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
@Override
|
||||
public void collect(int doc) throws IOException {
|
||||
|
||||
if (isCompetitive(doc) == false)
|
||||
if (isCompetitive(doc) == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: should we add option to mean "ignore docs that
|
||||
// don't have the group field" (instead of stuffing them
|
||||
|
@ -264,7 +265,8 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
for (int compIDX = 0; ; compIDX++) {
|
||||
leafComparators[compIDX].copy(spareSlot, doc);
|
||||
|
||||
final int c = reversed[compIDX] * comparators[compIDX].compare(group.comparatorSlot, spareSlot);
|
||||
final int c =
|
||||
reversed[compIDX] * comparators[compIDX].compare(group.comparatorSlot, spareSlot);
|
||||
if (c < 0) {
|
||||
// Definitely not competitive.
|
||||
return;
|
||||
|
@ -306,7 +308,8 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
orderedGroups.add(group);
|
||||
assert orderedGroups.size() == topNGroups;
|
||||
final CollectedSearchGroup<?> newLast = orderedGroups.last();
|
||||
// If we changed the value of the last group, or changed which group was last, then update bottom:
|
||||
// If we changed the value of the last group, or changed which group was last, then update
|
||||
// bottom:
|
||||
if (group == newLast || prevLast != newLast) {
|
||||
for (LeafFieldComparator fc : leafComparators) {
|
||||
fc.setBottom(newLast.comparatorSlot);
|
||||
|
@ -316,7 +319,8 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
}
|
||||
|
||||
private void buildSortedSet() throws IOException {
|
||||
final Comparator<CollectedSearchGroup<?>> comparator = new Comparator<CollectedSearchGroup<?>>() {
|
||||
final Comparator<CollectedSearchGroup<?>> comparator =
|
||||
new Comparator<CollectedSearchGroup<?>>() {
|
||||
@Override
|
||||
public int compare(CollectedSearchGroup<?> o1, CollectedSearchGroup<?> o2) {
|
||||
for (int compIDX = 0; ; compIDX++) {
|
||||
|
@ -349,12 +353,8 @@ public class FirstPassGroupingCollector<T> extends SimpleCollector {
|
|||
groupSelector.setNextReader(readerContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the GroupSelector used for this Collector
|
||||
*/
|
||||
/** @return the GroupSelector used for this Collector */
|
||||
public GroupSelector<T> getGroupSelector() {
|
||||
return groupSelector;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -19,34 +19,38 @@ package org.apache.lucene.search.grouping;
|
|||
import org.apache.lucene.search.ScoreDoc;
|
||||
import org.apache.lucene.search.TotalHits;
|
||||
|
||||
/** Represents one group in the results.
|
||||
/**
|
||||
* Represents one group in the results.
|
||||
*
|
||||
* @lucene.experimental */
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class GroupDocs<T> {
|
||||
/** The groupField value for all docs in this group; this
|
||||
* may be null if hits did not have the groupField. */
|
||||
/**
|
||||
* The groupField value for all docs in this group; this may be null if hits did not have the
|
||||
* groupField.
|
||||
*/
|
||||
public final T groupValue;
|
||||
|
||||
/** Max score in this group */
|
||||
public final float maxScore;
|
||||
|
||||
/** Overall aggregated score of this group (currently only
|
||||
* set by join queries). */
|
||||
/** Overall aggregated score of this group (currently only set by join queries). */
|
||||
public final float score;
|
||||
|
||||
/** Hits; this may be {@link
|
||||
* org.apache.lucene.search.FieldDoc} instances if the
|
||||
* withinGroupSort sorted by fields. */
|
||||
/**
|
||||
* Hits; this may be {@link org.apache.lucene.search.FieldDoc} instances if the withinGroupSort
|
||||
* sorted by fields.
|
||||
*/
|
||||
public final ScoreDoc[] scoreDocs;
|
||||
|
||||
/** Total hits within this group */
|
||||
public final TotalHits totalHits;
|
||||
|
||||
/** Matches the groupSort passed to {@link
|
||||
* FirstPassGroupingCollector}. */
|
||||
/** Matches the groupSort passed to {@link FirstPassGroupingCollector}. */
|
||||
public final Object[] groupSortValues;
|
||||
|
||||
public GroupDocs(float score,
|
||||
public GroupDocs(
|
||||
float score,
|
||||
float maxScore,
|
||||
TotalHits totalHits,
|
||||
ScoreDoc[] scoreDocs,
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.util.LinkedList;
|
|||
import java.util.List;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.lucene.search.Scorable;
|
||||
import org.apache.lucene.search.ScoreMode;
|
||||
import org.apache.lucene.search.SimpleCollector;
|
||||
|
@ -55,17 +54,19 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns grouped facet results that were computed over zero or more segments.
|
||||
* Grouped facet counts are merged from zero or more segment results.
|
||||
* Returns grouped facet results that were computed over zero or more segments. Grouped facet
|
||||
* counts are merged from zero or more segment results.
|
||||
*
|
||||
* @param size The total number of facets to include. This is typically offset + limit
|
||||
* @param minCount The minimum count a facet entry should have to be included in the grouped facet result
|
||||
* @param orderByCount Whether to sort the facet entries by facet entry count. If <code>false</code> then the facets
|
||||
* are sorted lexicographically in ascending order.
|
||||
* @param minCount The minimum count a facet entry should have to be included in the grouped facet
|
||||
* result
|
||||
* @param orderByCount Whether to sort the facet entries by facet entry count. If <code>false
|
||||
* </code> then the facets are sorted lexicographically in ascending order.
|
||||
* @return grouped facet results
|
||||
* @throws IOException If I/O related errors occur during merging segment grouped facet counts.
|
||||
*/
|
||||
public GroupedFacetResult mergeSegmentResults(int size, int minCount, boolean orderByCount) throws IOException {
|
||||
public GroupedFacetResult mergeSegmentResults(int size, int minCount, boolean orderByCount)
|
||||
throws IOException {
|
||||
if (segmentFacetCounts != null) {
|
||||
segmentResults.add(createSegmentResult());
|
||||
segmentFacetCounts = null; // reset
|
||||
|
@ -83,7 +84,8 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
segments.add(segmentResult);
|
||||
}
|
||||
|
||||
GroupedFacetResult facetResult = new GroupedFacetResult(size, minCount, orderByCount, totalCount, missingCount);
|
||||
GroupedFacetResult facetResult =
|
||||
new GroupedFacetResult(size, minCount, orderByCount, totalCount, missingCount);
|
||||
while (segments.size() > 0) {
|
||||
SegmentResult segmentResult = segments.top();
|
||||
BytesRef currentFacetValue = BytesRef.deepCopyOf(segmentResult.mergeTerm);
|
||||
|
@ -110,8 +112,7 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
protected abstract SegmentResult createSegmentResult() throws IOException;
|
||||
|
||||
@Override
|
||||
public void setScorer(Scorable scorer) throws IOException {
|
||||
}
|
||||
public void setScorer(Scorable scorer) throws IOException {}
|
||||
|
||||
@Override
|
||||
public ScoreMode scoreMode() {
|
||||
|
@ -119,11 +120,13 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* The grouped facet result. Containing grouped facet entries, total count and total missing count.
|
||||
* The grouped facet result. Containing grouped facet entries, total count and total missing
|
||||
* count.
|
||||
*/
|
||||
public static class GroupedFacetResult {
|
||||
|
||||
private final static Comparator<FacetEntry> orderByCountAndValue = new Comparator<FacetEntry>() {
|
||||
private static final Comparator<FacetEntry> orderByCountAndValue =
|
||||
new Comparator<FacetEntry>() {
|
||||
|
||||
@Override
|
||||
public int compare(FacetEntry a, FacetEntry b) {
|
||||
|
@ -133,16 +136,15 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
}
|
||||
return a.value.compareTo(b.value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final static Comparator<FacetEntry> orderByValue = new Comparator<FacetEntry>() {
|
||||
private static final Comparator<FacetEntry> orderByValue =
|
||||
new Comparator<FacetEntry>() {
|
||||
|
||||
@Override
|
||||
public int compare(FacetEntry a, FacetEntry b) {
|
||||
return a.value.compareTo(b.value);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final int maxSize;
|
||||
|
@ -152,7 +154,8 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
|
||||
private int currentMin;
|
||||
|
||||
public GroupedFacetResult(int size, int minCount, boolean orderByCount, int totalCount, int totalMissingCount) {
|
||||
public GroupedFacetResult(
|
||||
int size, int minCount, boolean orderByCount, int totalCount, int totalMissingCount) {
|
||||
this.facetEntries = new TreeSet<>(orderByCount ? orderByCountAndValue : orderByValue);
|
||||
this.totalMissingCount = totalMissingCount;
|
||||
this.totalCount = totalCount;
|
||||
|
@ -180,8 +183,8 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns a list of facet entries to be rendered based on the specified offset and limit.
|
||||
* The facet entries are retrieved from the facet entries collected during merging.
|
||||
* Returns a list of facet entries to be rendered based on the specified offset and limit. The
|
||||
* facet entries are retrieved from the facet entries collected during merging.
|
||||
*
|
||||
* @param offset The offset in the collected facet entries during merging
|
||||
* @param limit The number of facets to return starting from the offset.
|
||||
|
@ -224,9 +227,7 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a facet entry with a value and a count.
|
||||
*/
|
||||
/** Represents a facet entry with a value and a count. */
|
||||
public static class FacetEntry {
|
||||
|
||||
private final BytesRef value;
|
||||
|
@ -259,30 +260,23 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "FacetEntry{" +
|
||||
"value=" + value.utf8ToString() +
|
||||
", count=" + count +
|
||||
'}';
|
||||
return "FacetEntry{" + "value=" + value.utf8ToString() + ", count=" + count + '}';
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The value of this facet entry
|
||||
*/
|
||||
/** @return The value of this facet entry */
|
||||
public BytesRef getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The count (number of groups) of this facet entry.
|
||||
*/
|
||||
/** @return The count (number of groups) of this facet entry. */
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the local grouped segment counts for a particular segment.
|
||||
* Each <code>SegmentResult</code> must be added together.
|
||||
* Contains the local grouped segment counts for a particular segment. Each <code>SegmentResult
|
||||
* </code> must be added together.
|
||||
*/
|
||||
protected abstract static class SegmentResult {
|
||||
|
||||
|
@ -302,12 +296,12 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
}
|
||||
|
||||
/**
|
||||
* Go to next term in this <code>SegmentResult</code> in order to retrieve the grouped facet counts.
|
||||
* Go to next term in this <code>SegmentResult</code> in order to retrieve the grouped facet
|
||||
* counts.
|
||||
*
|
||||
* @throws IOException If I/O related errors occur
|
||||
*/
|
||||
protected abstract void nextTerm() throws IOException;
|
||||
|
||||
}
|
||||
|
||||
private static class SegmentResultPriorityQueue extends PriorityQueue<SegmentResult> {
|
||||
|
@ -321,5 +315,4 @@ public abstract class GroupFacetCollector extends SimpleCollector {
|
|||
return a.mergeTerm.compareTo(b.mergeTerm) < 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,22 +21,19 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.Collector;
|
||||
import org.apache.lucene.search.LeafCollector;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
|
||||
/**
|
||||
* Concrete implementations of this class define what to collect for individual
|
||||
* groups during the second-pass of a grouping search.
|
||||
* Concrete implementations of this class define what to collect for individual groups during the
|
||||
* second-pass of a grouping search.
|
||||
*
|
||||
* Each group is assigned a Collector returned by {@link #newCollector()}, and
|
||||
* {@link LeafCollector#collect(int)} is called for each document that is in
|
||||
* a group
|
||||
* <p>Each group is assigned a Collector returned by {@link #newCollector()}, and {@link
|
||||
* LeafCollector#collect(int)} is called for each document that is in a group
|
||||
*
|
||||
* @see SecondPassGroupingCollector
|
||||
*
|
||||
* @param <T> the type of the value used for grouping
|
||||
* @param <C> the type of {@link Collector} used to reduce each group
|
||||
*/
|
||||
|
@ -47,7 +44,7 @@ public abstract class GroupReducer<T, C extends Collector> {
|
|||
/**
|
||||
* Define which groups should be reduced.
|
||||
*
|
||||
* Called by {@link SecondPassGroupingCollector}
|
||||
* <p>Called by {@link SecondPassGroupingCollector}
|
||||
*/
|
||||
public void setGroups(Collection<SearchGroup<T>> groups) {
|
||||
for (SearchGroup<T> group : groups) {
|
||||
|
@ -55,25 +52,20 @@ public abstract class GroupReducer<T, C extends Collector> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not this reducer requires collected documents to be scored
|
||||
*/
|
||||
/** Whether or not this reducer requires collected documents to be scored */
|
||||
public abstract boolean needsScores();
|
||||
|
||||
/**
|
||||
* Creates a new Collector for each group
|
||||
*/
|
||||
/** Creates a new Collector for each group */
|
||||
protected abstract C newCollector();
|
||||
|
||||
/**
|
||||
* Get the Collector for a given group
|
||||
*/
|
||||
/** Get the Collector for a given group */
|
||||
public final C getCollector(T value) {
|
||||
return groups.get(value).collector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect a given document into a given group
|
||||
*
|
||||
* @throws IOException on error
|
||||
*/
|
||||
public final void collect(T value, int doc) throws IOException {
|
||||
|
@ -81,18 +73,14 @@ public abstract class GroupReducer<T, C extends Collector> {
|
|||
collector.leafCollector.collect(doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Scorer on all group collectors
|
||||
*/
|
||||
/** Set the Scorer on all group collectors */
|
||||
public final void setScorer(Scorable scorer) throws IOException {
|
||||
for (GroupCollector<C> collector : groups.values()) {
|
||||
collector.leafCollector.setScorer(scorer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the parent {@link SecondPassGroupingCollector} moves to a new segment
|
||||
*/
|
||||
/** Called when the parent {@link SecondPassGroupingCollector} moves to a new segment */
|
||||
public final void setNextReader(LeafReaderContext ctx) throws IOException {
|
||||
for (GroupCollector<C> collector : groups.values()) {
|
||||
collector.leafCollector = collector.collector.getLeafCollector(ctx);
|
||||
|
@ -108,5 +96,4 @@ public abstract class GroupReducer<T, C extends Collector> {
|
|||
this.collector = collector;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,61 +19,52 @@ package org.apache.lucene.search.grouping;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
|
||||
/**
|
||||
* Defines a group, for use by grouping collectors
|
||||
*
|
||||
* A GroupSelector acts as an iterator over documents. For each segment, clients
|
||||
* should call {@link #setNextReader(LeafReaderContext)}, and then {@link #advanceTo(int)}
|
||||
* for each matching document.
|
||||
* <p>A GroupSelector acts as an iterator over documents. For each segment, clients should call
|
||||
* {@link #setNextReader(LeafReaderContext)}, and then {@link #advanceTo(int)} for each matching
|
||||
* document.
|
||||
*
|
||||
* @param <T> the type of the group value
|
||||
*/
|
||||
public abstract class GroupSelector<T> {
|
||||
|
||||
/**
|
||||
* What to do with the current value
|
||||
*/
|
||||
public enum State { SKIP, ACCEPT }
|
||||
/** What to do with the current value */
|
||||
public enum State {
|
||||
SKIP,
|
||||
ACCEPT
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the LeafReaderContext
|
||||
*/
|
||||
/** Set the LeafReaderContext */
|
||||
public abstract void setNextReader(LeafReaderContext readerContext) throws IOException;
|
||||
|
||||
/**
|
||||
* Set the current Scorer
|
||||
*/
|
||||
/** Set the current Scorer */
|
||||
public abstract void setScorer(Scorable scorer) throws IOException;
|
||||
|
||||
/**
|
||||
* Advance the GroupSelector's iterator to the given document
|
||||
*/
|
||||
/** Advance the GroupSelector's iterator to the given document */
|
||||
public abstract State advanceTo(int doc) throws IOException;
|
||||
|
||||
/**
|
||||
* Get the group value of the current document
|
||||
*
|
||||
* N.B. this object may be reused, for a persistent version use {@link #copyValue()}
|
||||
* <p>N.B. this object may be reused, for a persistent version use {@link #copyValue()}
|
||||
*/
|
||||
public abstract T currentValue() throws IOException;
|
||||
|
||||
/**
|
||||
* @return a copy of the group value of the current document
|
||||
*/
|
||||
/** @return a copy of the group value of the current document */
|
||||
public abstract T copyValue() throws IOException;
|
||||
|
||||
/**
|
||||
* Set a restriction on the group values returned by this selector
|
||||
*
|
||||
* If the selector is positioned on a document whose group value is not contained
|
||||
* within this set, then {@link #advanceTo(int)} will return {@link State#SKIP}
|
||||
* <p>If the selector is positioned on a document whose group value is not contained within this
|
||||
* set, then {@link #advanceTo(int)} will return {@link State#SKIP}
|
||||
*
|
||||
* @param groups a set of {@link SearchGroup} objects to limit selections to
|
||||
*/
|
||||
public abstract void setGroups(Collection<SearchGroup<T>> groups);
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.CachingCollector;
|
||||
import org.apache.lucene.search.Collector;
|
||||
|
@ -62,8 +61,9 @@ public class GroupingSearch {
|
|||
private Bits matchingGroupHeads;
|
||||
|
||||
/**
|
||||
* Constructs a <code>GroupingSearch</code> instance that groups documents by index terms using DocValues.
|
||||
* The group field can only have one token per document. This means that the field must not be analysed.
|
||||
* Constructs a <code>GroupingSearch</code> instance that groups documents by index terms using
|
||||
* DocValues. The group field can only have one token per document. This means that the field must
|
||||
* not be analysed.
|
||||
*
|
||||
* @param groupField The name of the field to group by.
|
||||
*/
|
||||
|
@ -72,7 +72,9 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>GroupingSearch</code> instance that groups documents using a {@link GroupSelector}
|
||||
* Constructs a <code>GroupingSearch</code> instance that groups documents using a {@link
|
||||
* GroupSelector}
|
||||
*
|
||||
* @param groupSelector a {@link GroupSelector} that defines groups for this GroupingSearch
|
||||
*/
|
||||
public GroupingSearch(GroupSelector<?> groupSelector) {
|
||||
|
@ -80,8 +82,8 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a <code>GroupingSearch</code> instance that groups documents by function using a {@link ValueSource}
|
||||
* instance.
|
||||
* Constructs a <code>GroupingSearch</code> instance that groups documents by function using a
|
||||
* {@link ValueSource} instance.
|
||||
*
|
||||
* @param groupFunction The function to group by specified as {@link ValueSource}
|
||||
* @param valueSourceContext The context of the specified groupFunction
|
||||
|
@ -91,8 +93,8 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructor for grouping documents by doc block.
|
||||
* This constructor can only be used when documents belonging in a group are indexed in one block.
|
||||
* Constructor for grouping documents by doc block. This constructor can only be used when
|
||||
* documents belonging in a group are indexed in one block.
|
||||
*
|
||||
* @param groupEndDocs The query that marks the last document in all doc blocks
|
||||
*/
|
||||
|
@ -106,9 +108,11 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Executes a grouped search. Both the first pass and second pass are executed on the specified searcher.
|
||||
* Executes a grouped search. Both the first pass and second pass are executed on the specified
|
||||
* searcher.
|
||||
*
|
||||
* @param searcher The {@link org.apache.lucene.search.IndexSearcher} instance to execute the grouped search on.
|
||||
* @param searcher The {@link org.apache.lucene.search.IndexSearcher} instance to execute the
|
||||
* grouped search on.
|
||||
* @param query The query to execute with the grouping
|
||||
* @param groupOffset The group offset
|
||||
* @param groupLimit The number of groups to return from the specified group offset
|
||||
|
@ -116,26 +120,32 @@ public class GroupingSearch {
|
|||
* @throws IOException If any I/O related errors occur
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> TopGroups<T> search(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) throws IOException {
|
||||
public <T> TopGroups<T> search(
|
||||
IndexSearcher searcher, Query query, int groupOffset, int groupLimit) throws IOException {
|
||||
if (grouper != null) {
|
||||
return groupByFieldOrFunction(searcher, query, groupOffset, groupLimit);
|
||||
} else if (groupEndDocs != null) {
|
||||
return (TopGroups<T>) groupByDocBlock(searcher, query, groupOffset, groupLimit);
|
||||
} else {
|
||||
throw new IllegalStateException("Either groupField, groupFunction or groupEndDocs must be set."); // This can't happen...
|
||||
throw new IllegalStateException(
|
||||
"Either groupField, groupFunction or groupEndDocs must be set."); // This can't happen...
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
protected TopGroups groupByFieldOrFunction(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) throws IOException {
|
||||
protected TopGroups groupByFieldOrFunction(
|
||||
IndexSearcher searcher, Query query, int groupOffset, int groupLimit) throws IOException {
|
||||
int topN = groupOffset + groupLimit;
|
||||
|
||||
final FirstPassGroupingCollector firstPassCollector = new FirstPassGroupingCollector(grouper, groupSort, topN);
|
||||
final AllGroupsCollector allGroupsCollector = allGroups ? new AllGroupsCollector(grouper) : null;
|
||||
final AllGroupHeadsCollector allGroupHeadsCollector
|
||||
= allGroupHeads ? AllGroupHeadsCollector.newCollector(grouper, sortWithinGroup) : null;
|
||||
final FirstPassGroupingCollector firstPassCollector =
|
||||
new FirstPassGroupingCollector(grouper, groupSort, topN);
|
||||
final AllGroupsCollector allGroupsCollector =
|
||||
allGroups ? new AllGroupsCollector(grouper) : null;
|
||||
final AllGroupHeadsCollector allGroupHeadsCollector =
|
||||
allGroupHeads ? AllGroupHeadsCollector.newCollector(grouper, sortWithinGroup) : null;
|
||||
|
||||
final Collector firstRound = MultiCollector.wrap(firstPassCollector, allGroupsCollector, allGroupHeadsCollector);
|
||||
final Collector firstRound =
|
||||
MultiCollector.wrap(firstPassCollector, allGroupsCollector, allGroupHeadsCollector);
|
||||
|
||||
CachingCollector cachedCollector = null;
|
||||
if (maxCacheRAMMB != null || maxDocsToCache != null) {
|
||||
|
@ -150,7 +160,9 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
matchingGroups = allGroups ? allGroupsCollector.getGroups() : Collections.emptyList();
|
||||
matchingGroupHeads = allGroupHeads ? allGroupHeadsCollector.retrieveGroupHeads(searcher.getIndexReader().maxDoc())
|
||||
matchingGroupHeads =
|
||||
allGroupHeads
|
||||
? allGroupHeadsCollector.retrieveGroupHeads(searcher.getIndexReader().maxDoc())
|
||||
: new Bits.MatchNoBits(searcher.getIndexReader().maxDoc());
|
||||
|
||||
Collection<SearchGroup> topSearchGroups = firstPassCollector.getTopGroups(groupOffset);
|
||||
|
@ -159,8 +171,9 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
int topNInsideGroup = groupDocsOffset + groupDocsLimit;
|
||||
TopGroupsCollector secondPassCollector
|
||||
= new TopGroupsCollector(grouper, topSearchGroups, groupSort, sortWithinGroup, topNInsideGroup, includeMaxScore);
|
||||
TopGroupsCollector secondPassCollector =
|
||||
new TopGroupsCollector(
|
||||
grouper, topSearchGroups, groupSort, sortWithinGroup, topNInsideGroup, includeMaxScore);
|
||||
|
||||
if (cachedCollector != null && cachedCollector.isCached()) {
|
||||
cachedCollector.replay(secondPassCollector);
|
||||
|
@ -169,26 +182,35 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
if (allGroups) {
|
||||
return new TopGroups(secondPassCollector.getTopGroups(groupDocsOffset), matchingGroups.size());
|
||||
return new TopGroups(
|
||||
secondPassCollector.getTopGroups(groupDocsOffset), matchingGroups.size());
|
||||
} else {
|
||||
return secondPassCollector.getTopGroups(groupDocsOffset);
|
||||
}
|
||||
}
|
||||
|
||||
protected TopGroups<?> groupByDocBlock(IndexSearcher searcher, Query query, int groupOffset, int groupLimit) throws IOException {
|
||||
protected TopGroups<?> groupByDocBlock(
|
||||
IndexSearcher searcher, Query query, int groupOffset, int groupLimit) throws IOException {
|
||||
int topN = groupOffset + groupLimit;
|
||||
final Query endDocsQuery = searcher.rewrite(this.groupEndDocs);
|
||||
final Weight groupEndDocs = searcher.createWeight(endDocsQuery, ScoreMode.COMPLETE_NO_SCORES, 1);
|
||||
BlockGroupingCollector c = new BlockGroupingCollector(groupSort, topN, groupSort.needsScores() || sortWithinGroup.needsScores(), groupEndDocs);
|
||||
final Weight groupEndDocs =
|
||||
searcher.createWeight(endDocsQuery, ScoreMode.COMPLETE_NO_SCORES, 1);
|
||||
BlockGroupingCollector c =
|
||||
new BlockGroupingCollector(
|
||||
groupSort,
|
||||
topN,
|
||||
groupSort.needsScores() || sortWithinGroup.needsScores(),
|
||||
groupEndDocs);
|
||||
searcher.search(query, c);
|
||||
int topNInsideGroup = groupDocsOffset + groupDocsLimit;
|
||||
return c.getTopGroups(sortWithinGroup, groupOffset, groupDocsOffset, topNInsideGroup);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables caching for the second pass search. The cache will not grow over a specified limit in MB.
|
||||
* The cache is filled during the first pass searched and then replayed during the second pass searched.
|
||||
* If the cache grows beyond the specified limit, then the cache is purged and not used in the second pass search.
|
||||
* Enables caching for the second pass search. The cache will not grow over a specified limit in
|
||||
* MB. The cache is filled during the first pass searched and then replayed during the second pass
|
||||
* searched. If the cache grows beyond the specified limit, then the cache is purged and not used
|
||||
* in the second pass search.
|
||||
*
|
||||
* @param maxCacheRAMMB The maximum amount in MB the cache is allowed to hold
|
||||
* @param cacheScores Whether to cache the scores
|
||||
|
@ -202,9 +224,10 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Enables caching for the second pass search. The cache will not contain more than the maximum specified documents.
|
||||
* The cache is filled during the first pass searched and then replayed during the second pass searched.
|
||||
* If the cache grows beyond the specified limit, then the cache is purged and not used in the second pass search.
|
||||
* Enables caching for the second pass search. The cache will not contain more than the maximum
|
||||
* specified documents. The cache is filled during the first pass searched and then replayed
|
||||
* during the second pass searched. If the cache grows beyond the specified limit, then the cache
|
||||
* is purged and not used in the second pass search.
|
||||
*
|
||||
* @param maxDocsToCache The maximum number of documents the cache is allowed to hold
|
||||
* @param cacheScores Whether to cache the scores
|
||||
|
@ -229,8 +252,7 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Specifies how groups are sorted.
|
||||
* Defaults to {@link Sort#RELEVANCE}.
|
||||
* Specifies how groups are sorted. Defaults to {@link Sort#RELEVANCE}.
|
||||
*
|
||||
* @param groupSort The sort for the groups.
|
||||
* @return <code>this</code>
|
||||
|
@ -241,8 +263,7 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Specified how documents inside a group are sorted.
|
||||
* Defaults to {@link Sort#RELEVANCE}.
|
||||
* Specified how documents inside a group are sorted. Defaults to {@link Sort#RELEVANCE}.
|
||||
*
|
||||
* @param sortWithinGroup The sort for documents inside a group
|
||||
* @return <code>this</code>
|
||||
|
@ -286,11 +307,11 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Whether to also compute all groups matching the query.
|
||||
* This can be used to determine the number of groups, which can be used for accurate pagination.
|
||||
* <p>
|
||||
* When grouping by doc block the number of groups are automatically included in the {@link TopGroups} and this
|
||||
* option doesn't have any influence.
|
||||
* Whether to also compute all groups matching the query. This can be used to determine the number
|
||||
* of groups, which can be used for accurate pagination.
|
||||
*
|
||||
* <p>When grouping by doc block the number of groups are automatically included in the {@link
|
||||
* TopGroups} and this option doesn't have any influence.
|
||||
*
|
||||
* @param allGroups to also compute all groups matching the query
|
||||
* @return <code>this</code>
|
||||
|
@ -301,11 +322,11 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* If {@link #setAllGroups(boolean)} was set to <code>true</code> then all matching groups are returned, otherwise
|
||||
* an empty collection is returned.
|
||||
* If {@link #setAllGroups(boolean)} was set to <code>true</code> then all matching groups are
|
||||
* returned, otherwise an empty collection is returned.
|
||||
*
|
||||
* @param <T> The group value type. This can be a {@link BytesRef} or a {@link MutableValue} instance. If grouping
|
||||
* by doc block this the group value is always <code>null</code>.
|
||||
* @param <T> The group value type. This can be a {@link BytesRef} or a {@link MutableValue}
|
||||
* instance. If grouping by doc block this the group value is always <code>null</code>.
|
||||
* @return all matching groups are returned, or an empty collection
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
|
@ -315,10 +336,11 @@ public class GroupingSearch {
|
|||
|
||||
/**
|
||||
* Whether to compute all group heads (most relevant document per group) matching the query.
|
||||
* <p>
|
||||
* This feature isn't enabled when grouping by doc block.
|
||||
*
|
||||
* @param allGroupHeads Whether to compute all group heads (most relevant document per group) matching the query
|
||||
* <p>This feature isn't enabled when grouping by doc block.
|
||||
*
|
||||
* @param allGroupHeads Whether to compute all group heads (most relevant document per group)
|
||||
* matching the query
|
||||
* @return <code>this</code>
|
||||
*/
|
||||
public GroupingSearch setAllGroupHeads(boolean allGroupHeads) {
|
||||
|
@ -327,12 +349,13 @@ public class GroupingSearch {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns the matching group heads if {@link #setAllGroupHeads(boolean)} was set to true or an empty bit set.
|
||||
* Returns the matching group heads if {@link #setAllGroupHeads(boolean)} was set to true or an
|
||||
* empty bit set.
|
||||
*
|
||||
* @return The matching group heads if {@link #setAllGroupHeads(boolean)} was set to true or an empty bit set
|
||||
* @return The matching group heads if {@link #setAllGroupHeads(boolean)} was set to true or an
|
||||
* empty bit set
|
||||
*/
|
||||
public Bits getAllGroupHeads() {
|
||||
return matchingGroupHeads;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,10 +19,7 @@ package org.apache.lucene.search.grouping;
|
|||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a contiguous range of long values, with an inclusive minimum and
|
||||
* exclusive maximum
|
||||
*/
|
||||
/** Represents a contiguous range of long values, with an inclusive minimum and exclusive maximum */
|
||||
public class LongRange {
|
||||
|
||||
/** The inclusive minimum value of this range */
|
||||
|
@ -30,9 +27,7 @@ public class LongRange {
|
|||
/** The exclusive maximum value of this range */
|
||||
public long max;
|
||||
|
||||
/**
|
||||
* Creates a new double range, running from {@code min} inclusive to {@code max} exclusive
|
||||
*/
|
||||
/** Creates a new double range, running from {@code min} inclusive to {@code max} exclusive */
|
||||
public LongRange(long min, long max) {
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
|
|
|
@ -17,9 +17,7 @@
|
|||
|
||||
package org.apache.lucene.search.grouping;
|
||||
|
||||
/**
|
||||
* Groups double values into ranges
|
||||
*/
|
||||
/** Groups double values into ranges */
|
||||
public class LongRangeFactory {
|
||||
|
||||
private final long min;
|
||||
|
@ -28,10 +26,11 @@ public class LongRangeFactory {
|
|||
|
||||
/**
|
||||
* Creates a new LongRangeFactory
|
||||
*
|
||||
* @param min a minimum value; all longs below this value are grouped into a single range
|
||||
* @param width a standard width; all ranges between {@code min} and {@code max} are this wide,
|
||||
* with the exception of the final range which may be up to this width. Ranges
|
||||
* are inclusive at the lower end, and exclusive at the upper end.
|
||||
* with the exception of the final range which may be up to this width. Ranges are inclusive
|
||||
* at the lower end, and exclusive at the upper end.
|
||||
* @param max a maximum value; all longs above this value are grouped into a single range
|
||||
*/
|
||||
public LongRangeFactory(long min, long width, long max) {
|
||||
|
@ -42,12 +41,14 @@ public class LongRangeFactory {
|
|||
|
||||
/**
|
||||
* Finds the LongRange that a value should be grouped into
|
||||
*
|
||||
* @param value the value to group
|
||||
* @param reuse an existing LongRange object to reuse
|
||||
*/
|
||||
public LongRange getRange(long value, LongRange reuse) {
|
||||
if (reuse == null)
|
||||
if (reuse == null) {
|
||||
reuse = new LongRange(Long.MIN_VALUE, Long.MAX_VALUE);
|
||||
}
|
||||
if (value < min) {
|
||||
reuse.max = min;
|
||||
reuse.min = Long.MIN_VALUE;
|
||||
|
@ -63,5 +64,4 @@ public class LongRangeFactory {
|
|||
reuse.max = reuse.min + width;
|
||||
return reuse;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,16 +21,13 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.DoubleValuesSource;
|
||||
import org.apache.lucene.search.LongValues;
|
||||
import org.apache.lucene.search.LongValuesSource;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
|
||||
/**
|
||||
* A GroupSelector implementation that groups documents by long values
|
||||
*/
|
||||
/** A GroupSelector implementation that groups documents by long values */
|
||||
public class LongRangeGroupSelector extends GroupSelector<LongRange> {
|
||||
|
||||
private final LongValuesSource source;
|
||||
|
@ -46,8 +43,10 @@ public class LongRangeGroupSelector extends GroupSelector<LongRange> {
|
|||
|
||||
/**
|
||||
* Creates a new LongRangeGroupSelector
|
||||
*
|
||||
* @param source a LongValuesSource to retrieve long values per document
|
||||
* @param rangeFactory a LongRangeFactory that defines how to group the long values into range buckets
|
||||
* @param rangeFactory a LongRangeFactory that defines how to group the long values into range
|
||||
* buckets
|
||||
*/
|
||||
public LongRangeGroupSelector(LongValuesSource source, LongRangeFactory rangeFactory) {
|
||||
this.source = source;
|
||||
|
@ -92,10 +91,11 @@ public class LongRangeGroupSelector extends GroupSelector<LongRange> {
|
|||
inSecondPass = new HashSet<>();
|
||||
includeEmpty = false;
|
||||
for (SearchGroup<LongRange> group : searchGroups) {
|
||||
if (group.groupValue == null)
|
||||
if (group.groupValue == null) {
|
||||
includeEmpty = true;
|
||||
else
|
||||
} else {
|
||||
inSecondPass.add(group.groupValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.lucene.search.FieldComparator;
|
||||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.search.SortField;
|
||||
|
@ -41,16 +40,20 @@ public class SearchGroup<T> {
|
|||
/** The value that defines this group */
|
||||
public T groupValue;
|
||||
|
||||
/** The sort values used during sorting. These are the
|
||||
* groupSort field values of the highest rank document
|
||||
* (by the groupSort) within the group. Can be
|
||||
* <code>null</code> if <code>fillFields=false</code> had
|
||||
* been passed to {@link FirstPassGroupingCollector#getTopGroups} */
|
||||
/**
|
||||
* The sort values used during sorting. These are the groupSort field values of the highest rank
|
||||
* document (by the groupSort) within the group. Can be <code>null</code> if <code>
|
||||
* fillFields=false</code> had been passed to {@link FirstPassGroupingCollector#getTopGroups}
|
||||
*/
|
||||
public Object[] sortValues;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return("SearchGroup(groupValue=" + groupValue + " sortValues=" + Arrays.toString(sortValues) + ")");
|
||||
return ("SearchGroup(groupValue="
|
||||
+ groupValue
|
||||
+ " sortValues="
|
||||
+ Arrays.toString(sortValues)
|
||||
+ ")");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,7 +93,8 @@ public class SearchGroup<T> {
|
|||
assert iter.hasNext();
|
||||
final SearchGroup<T> group = iter.next();
|
||||
if (group.sortValues == null) {
|
||||
throw new IllegalArgumentException("group.sortValues is null; you must pass fillFields=true to the first pass collector");
|
||||
throw new IllegalArgumentException(
|
||||
"group.sortValues is null; you must pass fillFields=true to the first pass collector");
|
||||
}
|
||||
return group;
|
||||
}
|
||||
|
@ -188,8 +192,9 @@ public class SearchGroup<T> {
|
|||
final Object[] otherValues = other.topValues;
|
||||
// System.out.println(" groupValues=" + groupValues + " otherValues=" + otherValues);
|
||||
for (int compIDX = 0; compIDX < comparators.length; compIDX++) {
|
||||
final int c = reversed[compIDX] * comparators[compIDX].compareValues(groupValues[compIDX],
|
||||
otherValues[compIDX]);
|
||||
final int c =
|
||||
reversed[compIDX]
|
||||
* comparators[compIDX].compareValues(groupValues[compIDX], otherValues[compIDX]);
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
|
@ -219,7 +224,8 @@ public class SearchGroup<T> {
|
|||
final SearchGroup<T> group = shard.next();
|
||||
MergedGroup<T> mergedGroup = groupsSeen.get(group.groupValue);
|
||||
final boolean isNew = mergedGroup == null;
|
||||
//System.out.println(" next group=" + (group.groupValue == null ? "null" : ((BytesRef) group.groupValue).utf8ToString()) + " sort=" + Arrays.toString(group.sortValues));
|
||||
// System.out.println(" next group=" + (group.groupValue == null ? "null" : ((BytesRef)
|
||||
// group.groupValue).utf8ToString()) + " sort=" + Arrays.toString(group.sortValues));
|
||||
|
||||
if (isNew) {
|
||||
// Start a new group:
|
||||
|
@ -239,8 +245,10 @@ public class SearchGroup<T> {
|
|||
// System.out.println(" old");
|
||||
boolean competes = false;
|
||||
for (int compIDX = 0; compIDX < groupComp.comparators.length; compIDX++) {
|
||||
final int cmp = groupComp.reversed[compIDX] * groupComp.comparators[compIDX].compareValues(group.sortValues[compIDX],
|
||||
mergedGroup.topValues[compIDX]);
|
||||
final int cmp =
|
||||
groupComp.reversed[compIDX]
|
||||
* groupComp.comparators[compIDX].compareValues(
|
||||
group.sortValues[compIDX], mergedGroup.topValues[compIDX]);
|
||||
if (cmp < 0) {
|
||||
// Definitely competes
|
||||
competes = true;
|
||||
|
@ -281,7 +289,8 @@ public class SearchGroup<T> {
|
|||
}
|
||||
}
|
||||
|
||||
public Collection<SearchGroup<T>> merge(List<Collection<SearchGroup<T>>> shards, int offset, int topN) {
|
||||
public Collection<SearchGroup<T>> merge(
|
||||
List<Collection<SearchGroup<T>>> shards, int offset, int topN) {
|
||||
|
||||
final int maxQueueSize = offset + topN;
|
||||
|
||||
|
@ -303,7 +312,9 @@ public class SearchGroup<T> {
|
|||
while (!queue.isEmpty()) {
|
||||
final MergedGroup<T> group = queue.pollFirst();
|
||||
group.processed = true;
|
||||
//System.out.println(" pop: shards=" + group.shards + " group=" + (group.groupValue == null ? "null" : (((BytesRef) group.groupValue).utf8ToString())) + " sortValues=" + Arrays.toString(group.topValues));
|
||||
// System.out.println(" pop: shards=" + group.shards + " group=" + (group.groupValue ==
|
||||
// null ? "null" : (((BytesRef) group.groupValue).utf8ToString())) + " sortValues=" +
|
||||
// Arrays.toString(group.topValues));
|
||||
if (count++ >= offset) {
|
||||
final SearchGroup<T> newGroup = new SearchGroup<>();
|
||||
newGroup.groupValue = group.groupValue;
|
||||
|
@ -330,16 +341,16 @@ public class SearchGroup<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/** Merges multiple collections of top groups, for example
|
||||
* obtained from separate index shards. The provided
|
||||
* groupSort must match how the groups were sorted, and
|
||||
* the provided SearchGroups must have been computed
|
||||
* with fillFields=true passed to {@link
|
||||
/**
|
||||
* Merges multiple collections of top groups, for example obtained from separate index shards. The
|
||||
* provided groupSort must match how the groups were sorted, and the provided SearchGroups must
|
||||
* have been computed with fillFields=true passed to {@link
|
||||
* FirstPassGroupingCollector#getTopGroups}.
|
||||
*
|
||||
* <p>NOTE: this returns null if the topGroups is empty.
|
||||
*/
|
||||
public static <T> Collection<SearchGroup<T>> merge(List<Collection<SearchGroup<T>>> topGroups, int offset, int topN, Sort groupSort) {
|
||||
public static <T> Collection<SearchGroup<T>> merge(
|
||||
List<Collection<SearchGroup<T>>> topGroups, int offset, int topN, Sort groupSort) {
|
||||
if (topGroups.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
|
|
|
@ -19,19 +19,17 @@ package org.apache.lucene.search.grouping;
|
|||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
import org.apache.lucene.search.ScoreMode;
|
||||
import org.apache.lucene.search.SimpleCollector;
|
||||
|
||||
/**
|
||||
* SecondPassGroupingCollector runs over an already collected set of
|
||||
* groups, further applying a {@link GroupReducer} to each group
|
||||
* SecondPassGroupingCollector runs over an already collected set of groups, further applying a
|
||||
* {@link GroupReducer} to each group
|
||||
*
|
||||
* @see TopGroupsCollector
|
||||
* @see DistinctValuesCollector
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class SecondPassGroupingCollector<T> extends SimpleCollector {
|
||||
|
@ -45,11 +43,15 @@ public class SecondPassGroupingCollector<T> extends SimpleCollector {
|
|||
|
||||
/**
|
||||
* Create a new SecondPassGroupingCollector
|
||||
*
|
||||
* @param groupSelector the GroupSelector that defines groups for this search
|
||||
* @param groups the groups to collect documents for
|
||||
* @param reducer the reducer to apply to each group
|
||||
*/
|
||||
public SecondPassGroupingCollector(GroupSelector<T> groupSelector, Collection<SearchGroup<T>> groups, GroupReducer<T, ?> reducer) {
|
||||
public SecondPassGroupingCollector(
|
||||
GroupSelector<T> groupSelector,
|
||||
Collection<SearchGroup<T>> groups,
|
||||
GroupReducer<T, ?> reducer) {
|
||||
|
||||
// System.out.println("SP init");
|
||||
if (groups.isEmpty()) {
|
||||
|
@ -64,9 +66,7 @@ public class SecondPassGroupingCollector<T> extends SimpleCollector {
|
|||
reducer.setGroups(groups);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the GroupSelector used in this collector
|
||||
*/
|
||||
/** @return the GroupSelector used in this collector */
|
||||
public GroupSelector<T> getGroupSelector() {
|
||||
return groupSelector;
|
||||
}
|
||||
|
@ -85,8 +85,9 @@ public class SecondPassGroupingCollector<T> extends SimpleCollector {
|
|||
@Override
|
||||
public void collect(int doc) throws IOException {
|
||||
totalHitCount++;
|
||||
if (groupSelector.advanceTo(doc) == GroupSelector.State.SKIP)
|
||||
if (groupSelector.advanceTo(doc) == GroupSelector.State.SKIP) {
|
||||
return;
|
||||
}
|
||||
totalGroupedHitCount++;
|
||||
T value = groupSelector.currentValue();
|
||||
groupReducer.collect(value, doc);
|
||||
|
@ -97,5 +98,4 @@ public class SecondPassGroupingCollector<T> extends SimpleCollector {
|
|||
groupReducer.setNextReader(readerContext);
|
||||
groupSelector.setNextReader(readerContext);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.lucene.search.grouping;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.SortedDocValues;
|
||||
|
@ -31,8 +30,8 @@ import org.apache.lucene.util.SentinelIntSet;
|
|||
import org.apache.lucene.util.UnicodeUtil;
|
||||
|
||||
/**
|
||||
* An implementation of {@link GroupFacetCollector} that computes grouped facets based on the indexed terms
|
||||
* from DocValues.
|
||||
* An implementation of {@link GroupFacetCollector} that computes grouped facets based on the
|
||||
* indexed terms from DocValues.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
|
@ -44,19 +43,20 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
SortedDocValues groupFieldTermsIndex;
|
||||
|
||||
/**
|
||||
* Factory method for creating the right implementation based on the fact whether the facet field contains
|
||||
* multiple tokens per documents.
|
||||
* Factory method for creating the right implementation based on the fact whether the facet field
|
||||
* contains multiple tokens per documents.
|
||||
*
|
||||
* @param groupField The group field
|
||||
* @param facetField The facet field
|
||||
* @param facetFieldMultivalued Whether the facet field has multiple tokens per document
|
||||
* @param facetPrefix The facet prefix a facet entry should start with to be included.
|
||||
* @param initialSize The initial allocation size of the internal int set and group facet list which should roughly
|
||||
* match the total number of expected unique groups. Be aware that the heap usage is
|
||||
* 4 bytes * initialSize.
|
||||
* @param initialSize The initial allocation size of the internal int set and group facet list
|
||||
* which should roughly match the total number of expected unique groups. Be aware that the
|
||||
* heap usage is 4 bytes * initialSize.
|
||||
* @return <code>TermGroupFacetCollector</code> implementation
|
||||
*/
|
||||
public static TermGroupFacetCollector createTermGroupFacetCollector(String groupField,
|
||||
public static TermGroupFacetCollector createTermGroupFacetCollector(
|
||||
String groupField,
|
||||
String facetField,
|
||||
boolean facetFieldMultivalued,
|
||||
BytesRef facetPrefix,
|
||||
|
@ -68,7 +68,8 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
}
|
||||
}
|
||||
|
||||
TermGroupFacetCollector(String groupField, String facetField, BytesRef facetPrefix, int initialSize) {
|
||||
TermGroupFacetCollector(
|
||||
String groupField, String facetField, BytesRef facetPrefix, int initialSize) {
|
||||
super(groupField, facetField, facetPrefix);
|
||||
groupedFacetHits = new ArrayList<>(initialSize);
|
||||
segmentGroupedFacetHits = new SentinelIntSet(initialSize, Integer.MIN_VALUE);
|
||||
|
@ -110,7 +111,8 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
} else {
|
||||
groupOrd = -1;
|
||||
}
|
||||
int segmentGroupedFacetsIndex = groupOrd * (facetFieldTermsIndex.getValueCount()+1) + facetOrd;
|
||||
int segmentGroupedFacetsIndex =
|
||||
groupOrd * (facetFieldTermsIndex.getValueCount() + 1) + facetOrd;
|
||||
if (segmentGroupedFacetHits.exists(segmentGroupedFacetsIndex)) {
|
||||
return;
|
||||
}
|
||||
|
@ -152,17 +154,24 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
|
||||
segmentGroupedFacetHits.clear();
|
||||
for (GroupedFacetHit groupedFacetHit : groupedFacetHits) {
|
||||
int facetOrd = groupedFacetHit.facetValue == null ? -1 : facetFieldTermsIndex.lookupTerm(groupedFacetHit.facetValue);
|
||||
int facetOrd =
|
||||
groupedFacetHit.facetValue == null
|
||||
? -1
|
||||
: facetFieldTermsIndex.lookupTerm(groupedFacetHit.facetValue);
|
||||
if (groupedFacetHit.facetValue != null && facetOrd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int groupOrd = groupedFacetHit.groupValue == null ? -1 : groupFieldTermsIndex.lookupTerm(groupedFacetHit.groupValue);
|
||||
int groupOrd =
|
||||
groupedFacetHit.groupValue == null
|
||||
? -1
|
||||
: groupFieldTermsIndex.lookupTerm(groupedFacetHit.groupValue);
|
||||
if (groupedFacetHit.groupValue != null && groupOrd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int segmentGroupedFacetsIndex = groupOrd * (facetFieldTermsIndex.getValueCount()+1) + facetOrd;
|
||||
int segmentGroupedFacetsIndex =
|
||||
groupOrd * (facetFieldTermsIndex.getValueCount() + 1) + facetOrd;
|
||||
segmentGroupedFacetHits.put(segmentGroupedFacetsIndex);
|
||||
}
|
||||
|
||||
|
@ -186,14 +195,20 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
|
||||
@Override
|
||||
protected SegmentResult createSegmentResult() throws IOException {
|
||||
return new SegmentResult(segmentFacetCounts, segmentTotalCount, facetFieldTermsIndex.termsEnum(), startFacetOrd, endFacetOrd);
|
||||
return new SegmentResult(
|
||||
segmentFacetCounts,
|
||||
segmentTotalCount,
|
||||
facetFieldTermsIndex.termsEnum(),
|
||||
startFacetOrd,
|
||||
endFacetOrd);
|
||||
}
|
||||
|
||||
private static class SegmentResult extends GroupFacetCollector.SegmentResult {
|
||||
|
||||
final TermsEnum tenum;
|
||||
|
||||
SegmentResult(int[] counts, int total, TermsEnum tenum, int startFacetOrd, int endFacetOrd) throws IOException {
|
||||
SegmentResult(int[] counts, int total, TermsEnum tenum, int startFacetOrd, int endFacetOrd)
|
||||
throws IOException {
|
||||
super(counts, total - counts[0], counts[0], endFacetOrd + 1);
|
||||
this.tenum = tenum;
|
||||
this.mergePos = startFacetOrd == -1 ? 1 : startFacetOrd + 1;
|
||||
|
@ -268,7 +283,9 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
}
|
||||
|
||||
if (empty) {
|
||||
process(groupOrd, facetFieldNumTerms); // this facet ord is reserved for docs not containing facet field.
|
||||
process(
|
||||
groupOrd,
|
||||
facetFieldNumTerms); // this facet ord is reserved for docs not containing facet field.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -317,20 +334,25 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
} else {
|
||||
facetOrdTermsEnum = facetFieldDocTermOrds.termsEnum();
|
||||
}
|
||||
// [facetFieldNumTerms() + 1] for all possible facet values and docs not containing facet field
|
||||
// [facetFieldNumTerms() + 1] for all possible facet values and docs not containing facet
|
||||
// field
|
||||
segmentFacetCounts = new int[facetFieldNumTerms + 1];
|
||||
segmentTotalCount = 0;
|
||||
|
||||
segmentGroupedFacetHits.clear();
|
||||
for (GroupedFacetHit groupedFacetHit : groupedFacetHits) {
|
||||
int groupOrd = groupedFacetHit.groupValue == null ? -1 : groupFieldTermsIndex.lookupTerm(groupedFacetHit.groupValue);
|
||||
int groupOrd =
|
||||
groupedFacetHit.groupValue == null
|
||||
? -1
|
||||
: groupFieldTermsIndex.lookupTerm(groupedFacetHit.groupValue);
|
||||
if (groupedFacetHit.groupValue != null && groupOrd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int facetOrd;
|
||||
if (groupedFacetHit.facetValue != null) {
|
||||
if (facetOrdTermsEnum == null || !facetOrdTermsEnum.seekExact(groupedFacetHit.facetValue)) {
|
||||
if (facetOrdTermsEnum == null
|
||||
|| !facetOrdTermsEnum.seekExact(groupedFacetHit.facetValue)) {
|
||||
continue;
|
||||
}
|
||||
facetOrd = (int) facetOrdTermsEnum.ord();
|
||||
|
@ -338,7 +360,8 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
facetOrd = facetFieldNumTerms;
|
||||
}
|
||||
|
||||
// (facetFieldDocTermOrds.numTerms() + 1) for all possible facet values and docs not containing facet field
|
||||
// (facetFieldDocTermOrds.numTerms() + 1) for all possible facet values and docs not
|
||||
// containing facet field
|
||||
int segmentGroupedFacetsIndex = groupOrd * (facetFieldNumTerms + 1) + facetOrd;
|
||||
segmentGroupedFacetHits.put(segmentGroupedFacetsIndex);
|
||||
}
|
||||
|
@ -376,15 +399,31 @@ public abstract class TermGroupFacetCollector extends GroupFacetCollector {
|
|||
|
||||
@Override
|
||||
protected SegmentResult createSegmentResult() throws IOException {
|
||||
return new SegmentResult(segmentFacetCounts, segmentTotalCount, facetFieldNumTerms, facetOrdTermsEnum, startFacetOrd, endFacetOrd);
|
||||
return new SegmentResult(
|
||||
segmentFacetCounts,
|
||||
segmentTotalCount,
|
||||
facetFieldNumTerms,
|
||||
facetOrdTermsEnum,
|
||||
startFacetOrd,
|
||||
endFacetOrd);
|
||||
}
|
||||
|
||||
private static class SegmentResult extends GroupFacetCollector.SegmentResult {
|
||||
|
||||
final TermsEnum tenum;
|
||||
|
||||
SegmentResult(int[] counts, int total, int missingCountIndex, TermsEnum tenum, int startFacetOrd, int endFacetOrd) throws IOException {
|
||||
super(counts, total - counts[missingCountIndex], counts[missingCountIndex],
|
||||
SegmentResult(
|
||||
int[] counts,
|
||||
int total,
|
||||
int missingCountIndex,
|
||||
TermsEnum tenum,
|
||||
int startFacetOrd,
|
||||
int endFacetOrd)
|
||||
throws IOException {
|
||||
super(
|
||||
counts,
|
||||
total - counts[missingCountIndex],
|
||||
counts[missingCountIndex],
|
||||
endFacetOrd == missingCountIndex + 1 ? missingCountIndex : endFacetOrd);
|
||||
this.tenum = tenum;
|
||||
this.mergePos = startFacetOrd;
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.DocValues;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.SortedDocValues;
|
||||
|
@ -29,9 +28,7 @@ import org.apache.lucene.search.Scorable;
|
|||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.BytesRefHash;
|
||||
|
||||
/**
|
||||
* A GroupSelector implementation that groups via SortedDocValues
|
||||
*/
|
||||
/** A GroupSelector implementation that groups via SortedDocValues */
|
||||
public class TermGroupSelector extends GroupSelector<BytesRef> {
|
||||
|
||||
private final String field;
|
||||
|
@ -46,6 +43,7 @@ public class TermGroupSelector extends GroupSelector<BytesRef> {
|
|||
|
||||
/**
|
||||
* Create a new TermGroupSelector
|
||||
*
|
||||
* @param field the SortedDocValues field to use for grouping
|
||||
*/
|
||||
public TermGroupSelector(String field) {
|
||||
|
@ -60,10 +58,11 @@ public class TermGroupSelector extends GroupSelector<BytesRef> {
|
|||
for (int i = 0; i < values.size(); i++) {
|
||||
values.get(i, scratch);
|
||||
int ord = this.docValues.lookupTerm(scratch);
|
||||
if (ord >= 0)
|
||||
if (ord >= 0) {
|
||||
ordsToGroupIds.put(ord, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScorer(Scorable scorer) throws IOException {}
|
||||
|
@ -79,8 +78,9 @@ public class TermGroupSelector extends GroupSelector<BytesRef> {
|
|||
groupId = ordsToGroupIds.get(ord);
|
||||
return State.ACCEPT;
|
||||
}
|
||||
if (secondPass)
|
||||
if (secondPass) {
|
||||
return State.SKIP;
|
||||
}
|
||||
groupId = values.add(docValues.binaryValue());
|
||||
ordsToGroupIds.put(ord, groupId);
|
||||
return State.ACCEPT;
|
||||
|
@ -90,16 +90,18 @@ public class TermGroupSelector extends GroupSelector<BytesRef> {
|
|||
|
||||
@Override
|
||||
public BytesRef currentValue() {
|
||||
if (groupId == -1)
|
||||
if (groupId == -1) {
|
||||
return null;
|
||||
}
|
||||
values.get(groupId, scratch);
|
||||
return scratch;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BytesRef copyValue() {
|
||||
if (groupId == -1)
|
||||
if (groupId == -1) {
|
||||
return null;
|
||||
}
|
||||
return BytesRef.deepCopyOf(currentValue());
|
||||
}
|
||||
|
||||
|
@ -108,11 +110,12 @@ public class TermGroupSelector extends GroupSelector<BytesRef> {
|
|||
this.values.clear();
|
||||
this.values.reinit();
|
||||
for (SearchGroup<BytesRef> sg : searchGroups) {
|
||||
if (sg.groupValue == null)
|
||||
if (sg.groupValue == null) {
|
||||
includeEmpty = true;
|
||||
else
|
||||
} else {
|
||||
this.values.add(sg.groupValue);
|
||||
}
|
||||
}
|
||||
this.secondPass = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,9 +24,11 @@ import org.apache.lucene.search.TopFieldDocs;
|
|||
import org.apache.lucene.search.TotalHits;
|
||||
import org.apache.lucene.search.TotalHits.Relation;
|
||||
|
||||
/** Represents result returned by a grouping search.
|
||||
/**
|
||||
* Represents result returned by a grouping search.
|
||||
*
|
||||
* @lucene.experimental */
|
||||
* @lucene.experimental
|
||||
*/
|
||||
public class TopGroups<T> {
|
||||
/** Number of documents matching the search */
|
||||
public final int totalHitCount;
|
||||
|
@ -46,11 +48,16 @@ public class TopGroups<T> {
|
|||
/** How docs are sorted within each group */
|
||||
public final SortField[] withinGroupSort;
|
||||
|
||||
/** Highest score across all hits, or
|
||||
* <code>Float.NaN</code> if scores were not computed. */
|
||||
/** Highest score across all hits, or <code>Float.NaN</code> if scores were not computed. */
|
||||
public final float maxScore;
|
||||
|
||||
public TopGroups(SortField[] groupSort, SortField[] withinGroupSort, int totalHitCount, int totalGroupedHitCount, GroupDocs<T>[] groups, float maxScore) {
|
||||
public TopGroups(
|
||||
SortField[] groupSort,
|
||||
SortField[] withinGroupSort,
|
||||
int totalHitCount,
|
||||
int totalGroupedHitCount,
|
||||
GroupDocs<T>[] groups,
|
||||
float maxScore) {
|
||||
this.groupSort = groupSort;
|
||||
this.withinGroupSort = withinGroupSort;
|
||||
this.totalHitCount = totalHitCount;
|
||||
|
@ -81,8 +88,9 @@ public class TopGroups<T> {
|
|||
}
|
||||
|
||||
/**
|
||||
* If either value is NaN then return the other value, otherwise
|
||||
* return the greater of the two values by calling Math.max.
|
||||
* If either value is NaN then return the other value, otherwise return the greater of the two
|
||||
* values by calling Math.max.
|
||||
*
|
||||
* @param a - one value
|
||||
* @param b - another value
|
||||
* @return ignoring any NaN return the greater of a and b
|
||||
|
@ -93,24 +101,25 @@ public class TopGroups<T> {
|
|||
return Math.max(a, b);
|
||||
}
|
||||
|
||||
/** Merges an array of TopGroups, for example obtained
|
||||
* from the second-pass collector across multiple
|
||||
* shards. Each TopGroups must have been sorted by the
|
||||
* same groupSort and docSort, and the top groups passed
|
||||
* to all second-pass collectors must be the same.
|
||||
/**
|
||||
* Merges an array of TopGroups, for example obtained from the second-pass collector across
|
||||
* multiple shards. Each TopGroups must have been sorted by the same groupSort and docSort, and
|
||||
* the top groups passed to all second-pass collectors must be the same.
|
||||
*
|
||||
* <b>NOTE</b>: We can't always compute an exact totalGroupCount.
|
||||
* Documents belonging to a group may occur on more than
|
||||
* one shard and thus the merged totalGroupCount can be
|
||||
* higher than the actual totalGroupCount. In this case the
|
||||
* totalGroupCount represents a upper bound. If the documents
|
||||
* of one group do only reside in one shard then the
|
||||
* totalGroupCount is exact.
|
||||
* <p><b>NOTE</b>: We can't always compute an exact totalGroupCount. Documents belonging to a
|
||||
* group may occur on more than one shard and thus the merged totalGroupCount can be higher than
|
||||
* the actual totalGroupCount. In this case the totalGroupCount represents a upper bound. If the
|
||||
* documents of one group do only reside in one shard then the totalGroupCount is exact.
|
||||
*
|
||||
* <b>NOTE</b>: the topDocs in each GroupDocs is actually
|
||||
* an instance of TopDocsAndShards
|
||||
* <p><b>NOTE</b>: the topDocs in each GroupDocs is actually an instance of TopDocsAndShards
|
||||
*/
|
||||
public static <T> TopGroups<T> merge(TopGroups<T>[] shardGroups, Sort groupSort, Sort docSort, int docOffset, int docTopN, ScoreMergeMode scoreMergeMode) {
|
||||
public static <T> TopGroups<T> merge(
|
||||
TopGroups<T>[] shardGroups,
|
||||
Sort groupSort,
|
||||
Sort docSort,
|
||||
int docOffset,
|
||||
int docTopN,
|
||||
ScoreMergeMode scoreMergeMode) {
|
||||
|
||||
// System.out.println("TopGroups.merge");
|
||||
|
||||
|
@ -126,7 +135,8 @@ public class TopGroups<T> {
|
|||
final int numGroups = shardGroups[0].groups.length;
|
||||
for (TopGroups<T> shard : shardGroups) {
|
||||
if (numGroups != shard.groups.length) {
|
||||
throw new IllegalArgumentException("number of groups differs across shards; you must pass same top groups to all shards' second-pass collector");
|
||||
throw new IllegalArgumentException(
|
||||
"number of groups differs across shards; you must pass same top groups to all shards' second-pass collector");
|
||||
}
|
||||
totalHitCount += shard.totalHitCount;
|
||||
totalGroupedHitCount += shard.totalGroupedHitCount;
|
||||
|
@ -152,7 +162,8 @@ public class TopGroups<T> {
|
|||
|
||||
for (int groupIDX = 0; groupIDX < numGroups; groupIDX++) {
|
||||
final T groupValue = shardGroups[0].groups[groupIDX].groupValue;
|
||||
//System.out.println(" merge groupValue=" + groupValue + " sortValues=" + Arrays.toString(shardGroups[0].groups[groupIDX].groupSortValues));
|
||||
// System.out.println(" merge groupValue=" + groupValue + " sortValues=" +
|
||||
// Arrays.toString(shardGroups[0].groups[groupIDX].groupSortValues));
|
||||
float maxScore = Float.NaN;
|
||||
int totalHits = 0;
|
||||
double scoreSum = 0.0;
|
||||
|
@ -162,10 +173,12 @@ public class TopGroups<T> {
|
|||
final GroupDocs<?> shardGroupDocs = shard.groups[groupIDX];
|
||||
if (groupValue == null) {
|
||||
if (shardGroupDocs.groupValue != null) {
|
||||
throw new IllegalArgumentException("group values differ across shards; you must pass same top groups to all shards' second-pass collector");
|
||||
throw new IllegalArgumentException(
|
||||
"group values differ across shards; you must pass same top groups to all shards' second-pass collector");
|
||||
}
|
||||
} else if (!groupValue.equals(shardGroupDocs.groupValue)) {
|
||||
throw new IllegalArgumentException("group values differ across shards; you must pass same top groups to all shards' second-pass collector");
|
||||
throw new IllegalArgumentException(
|
||||
"group values differ across shards; you must pass same top groups to all shards' second-pass collector");
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -175,12 +188,11 @@ public class TopGroups<T> {
|
|||
*/
|
||||
|
||||
if (docSort.equals(Sort.RELEVANCE)) {
|
||||
shardTopDocs[shardIDX] = new TopDocs(shardGroupDocs.totalHits,
|
||||
shardGroupDocs.scoreDocs);
|
||||
shardTopDocs[shardIDX] = new TopDocs(shardGroupDocs.totalHits, shardGroupDocs.scoreDocs);
|
||||
} else {
|
||||
shardTopDocs[shardIDX] = new TopFieldDocs(shardGroupDocs.totalHits,
|
||||
shardGroupDocs.scoreDocs,
|
||||
docSort.getSort());
|
||||
shardTopDocs[shardIDX] =
|
||||
new TopFieldDocs(
|
||||
shardGroupDocs.totalHits, shardGroupDocs.scoreDocs, docSort.getSort());
|
||||
}
|
||||
|
||||
for (int i = 0; i < shardTopDocs[shardIDX].scoreDocs.length; i++) {
|
||||
|
@ -208,7 +220,8 @@ public class TopGroups<T> {
|
|||
mergedScoreDocs = new ScoreDoc[0];
|
||||
} else {
|
||||
mergedScoreDocs = new ScoreDoc[mergedTopDocs.scoreDocs.length - docOffset];
|
||||
System.arraycopy(mergedTopDocs.scoreDocs,
|
||||
System.arraycopy(
|
||||
mergedTopDocs.scoreDocs,
|
||||
docOffset,
|
||||
mergedScoreDocs,
|
||||
0,
|
||||
|
@ -235,7 +248,9 @@ public class TopGroups<T> {
|
|||
}
|
||||
|
||||
// System.out.println("SHARDS=" + Arrays.toString(mergedTopDocs.shardIndex));
|
||||
mergedGroupDocs[groupIDX] = new GroupDocs<>(groupScore,
|
||||
mergedGroupDocs[groupIDX] =
|
||||
new GroupDocs<>(
|
||||
groupScore,
|
||||
maxScore,
|
||||
new TotalHits(totalHits, TotalHits.Relation.EQUAL_TO),
|
||||
mergedScoreDocs,
|
||||
|
@ -245,7 +260,9 @@ public class TopGroups<T> {
|
|||
}
|
||||
|
||||
if (totalGroupCount != null) {
|
||||
TopGroups<T> result = new TopGroups<>(groupSort.getSort(),
|
||||
TopGroups<T> result =
|
||||
new TopGroups<>(
|
||||
groupSort.getSort(),
|
||||
docSort.getSort(),
|
||||
totalHitCount,
|
||||
totalGroupedHitCount,
|
||||
|
@ -253,7 +270,8 @@ public class TopGroups<T> {
|
|||
totalMaxScore);
|
||||
return new TopGroups<>(result, totalGroupCount);
|
||||
} else {
|
||||
return new TopGroups<>(groupSort.getSort(),
|
||||
return new TopGroups<>(
|
||||
groupSort.getSort(),
|
||||
docSort.getSort(),
|
||||
totalHitCount,
|
||||
totalGroupedHitCount,
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.io.IOException;
|
|||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.apache.lucene.search.FilterCollector;
|
||||
import org.apache.lucene.search.MultiCollector;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
|
@ -36,8 +35,8 @@ import org.apache.lucene.search.TopScoreDocCollector;
|
|||
import org.apache.lucene.util.ArrayUtil;
|
||||
|
||||
/**
|
||||
* A second-pass collector that collects the TopDocs for each group, and
|
||||
* returns them as a {@link TopGroups} object
|
||||
* A second-pass collector that collects the TopDocs for each group, and returns them as a {@link
|
||||
* TopGroups} object
|
||||
*
|
||||
* @param <T> the type of the group value
|
||||
*/
|
||||
|
@ -49,6 +48,7 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
|
||||
/**
|
||||
* Create a new TopGroupsCollector
|
||||
*
|
||||
* @param groupSelector the group selector used to define groups
|
||||
* @param groups the groups to collect TopDocs for
|
||||
* @param groupSort the order in which groups are returned
|
||||
|
@ -56,14 +56,20 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
* @param maxDocsPerGroup the maximum number of docs to collect for each group
|
||||
* @param getMaxScores if true, record the maximum score for each group
|
||||
*/
|
||||
public TopGroupsCollector(GroupSelector<T> groupSelector, Collection<SearchGroup<T>> groups, Sort groupSort, Sort withinGroupSort,
|
||||
int maxDocsPerGroup, boolean getMaxScores) {
|
||||
super(groupSelector, groups,
|
||||
public TopGroupsCollector(
|
||||
GroupSelector<T> groupSelector,
|
||||
Collection<SearchGroup<T>> groups,
|
||||
Sort groupSort,
|
||||
Sort withinGroupSort,
|
||||
int maxDocsPerGroup,
|
||||
boolean getMaxScores) {
|
||||
super(
|
||||
groupSelector,
|
||||
groups,
|
||||
new TopDocsReducer<>(withinGroupSort, maxDocsPerGroup, getMaxScores));
|
||||
this.groupSort = Objects.requireNonNull(groupSort);
|
||||
this.withinGroupSort = Objects.requireNonNull(withinGroupSort);
|
||||
this.maxDocsPerGroup = maxDocsPerGroup;
|
||||
|
||||
}
|
||||
|
||||
private static class MaxScoreCollector extends SimpleCollector {
|
||||
|
@ -99,7 +105,10 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
private final MaxScoreCollector maxScoreCollector;
|
||||
private final boolean sortedByScore;
|
||||
|
||||
public TopDocsAndMaxScoreCollector(boolean sortedByScore, TopDocsCollector<?> topDocsCollector, MaxScoreCollector maxScoreCollector) {
|
||||
public TopDocsAndMaxScoreCollector(
|
||||
boolean sortedByScore,
|
||||
TopDocsCollector<?> topDocsCollector,
|
||||
MaxScoreCollector maxScoreCollector) {
|
||||
super(MultiCollector.wrap(topDocsCollector, maxScoreCollector));
|
||||
this.sortedByScore = sortedByScore;
|
||||
this.topDocsCollector = topDocsCollector;
|
||||
|
@ -112,14 +121,21 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
private final Supplier<TopDocsAndMaxScoreCollector> supplier;
|
||||
private final boolean needsScores;
|
||||
|
||||
TopDocsReducer(Sort withinGroupSort,
|
||||
int maxDocsPerGroup, boolean getMaxScores) {
|
||||
TopDocsReducer(Sort withinGroupSort, int maxDocsPerGroup, boolean getMaxScores) {
|
||||
this.needsScores = getMaxScores || withinGroupSort.needsScores();
|
||||
if (withinGroupSort == Sort.RELEVANCE) {
|
||||
supplier = () -> new TopDocsAndMaxScoreCollector(true, TopScoreDocCollector.create(maxDocsPerGroup, Integer.MAX_VALUE), null);
|
||||
supplier =
|
||||
() ->
|
||||
new TopDocsAndMaxScoreCollector(
|
||||
true, TopScoreDocCollector.create(maxDocsPerGroup, Integer.MAX_VALUE), null);
|
||||
} else {
|
||||
supplier = () -> {
|
||||
TopFieldCollector topDocsCollector = TopFieldCollector.create(withinGroupSort, maxDocsPerGroup, Integer.MAX_VALUE); // TODO: disable exact counts?
|
||||
supplier =
|
||||
() -> {
|
||||
TopFieldCollector topDocsCollector =
|
||||
TopFieldCollector.create(
|
||||
withinGroupSort,
|
||||
maxDocsPerGroup,
|
||||
Integer.MAX_VALUE); // TODO: disable exact counts?
|
||||
MaxScoreCollector maxScoreCollector = getMaxScores ? new MaxScoreCollector() : null;
|
||||
return new TopDocsAndMaxScoreCollector(false, topDocsCollector, maxScoreCollector);
|
||||
};
|
||||
|
@ -139,6 +155,7 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
|
||||
/**
|
||||
* Get the TopGroups recorded by this collector
|
||||
*
|
||||
* @param withinGroupOffset the offset within each group to start collecting documents
|
||||
*/
|
||||
public TopGroups<T> getTopGroups(int withinGroupOffset) {
|
||||
|
@ -148,16 +165,24 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
int groupIDX = 0;
|
||||
float maxScore = Float.MIN_VALUE;
|
||||
for (SearchGroup<T> group : groups) {
|
||||
TopDocsAndMaxScoreCollector collector = (TopDocsAndMaxScoreCollector) groupReducer.getCollector(group.groupValue);
|
||||
TopDocsAndMaxScoreCollector collector =
|
||||
(TopDocsAndMaxScoreCollector) groupReducer.getCollector(group.groupValue);
|
||||
final TopDocs topDocs;
|
||||
final float groupMaxScore;
|
||||
if (collector.sortedByScore) {
|
||||
TopDocs allTopDocs = collector.topDocsCollector.topDocs();
|
||||
groupMaxScore = allTopDocs.scoreDocs.length == 0 ? Float.NaN : allTopDocs.scoreDocs[0].score;
|
||||
groupMaxScore =
|
||||
allTopDocs.scoreDocs.length == 0 ? Float.NaN : allTopDocs.scoreDocs[0].score;
|
||||
if (allTopDocs.scoreDocs.length <= withinGroupOffset) {
|
||||
topDocs = new TopDocs(allTopDocs.totalHits, new ScoreDoc[0]);
|
||||
} else {
|
||||
topDocs = new TopDocs(allTopDocs.totalHits, ArrayUtil.copyOfSubArray(allTopDocs.scoreDocs, withinGroupOffset, Math.min(allTopDocs.scoreDocs.length, withinGroupOffset + maxDocsPerGroup)));
|
||||
topDocs =
|
||||
new TopDocs(
|
||||
allTopDocs.totalHits,
|
||||
ArrayUtil.copyOfSubArray(
|
||||
allTopDocs.scoreDocs,
|
||||
withinGroupOffset,
|
||||
Math.min(allTopDocs.scoreDocs.length, withinGroupOffset + maxDocsPerGroup)));
|
||||
}
|
||||
} else {
|
||||
topDocs = collector.topDocsCollector.topDocs(withinGroupOffset, maxDocsPerGroup);
|
||||
|
@ -168,7 +193,9 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
}
|
||||
}
|
||||
|
||||
groupDocsResult[groupIDX++] = new GroupDocs<>(Float.NaN,
|
||||
groupDocsResult[groupIDX++] =
|
||||
new GroupDocs<>(
|
||||
Float.NaN,
|
||||
groupMaxScore,
|
||||
topDocs.totalHits,
|
||||
topDocs.scoreDocs,
|
||||
|
@ -177,11 +204,12 @@ public class TopGroupsCollector<T> extends SecondPassGroupingCollector<T> {
|
|||
maxScore = Math.max(maxScore, groupMaxScore);
|
||||
}
|
||||
|
||||
return new TopGroups<>(groupSort.getSort(),
|
||||
return new TopGroups<>(
|
||||
groupSort.getSort(),
|
||||
withinGroupSort.getSort(),
|
||||
totalHitCount, totalGroupedHitCount, groupDocsResult,
|
||||
totalHitCount,
|
||||
totalGroupedHitCount,
|
||||
groupDocsResult,
|
||||
maxScore);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -22,16 +22,13 @@ import java.util.Collection;
|
|||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.queries.function.FunctionValues;
|
||||
import org.apache.lucene.queries.function.ValueSource;
|
||||
import org.apache.lucene.search.Scorable;
|
||||
import org.apache.lucene.util.mutable.MutableValue;
|
||||
|
||||
/**
|
||||
* A GroupSelector that groups via a ValueSource
|
||||
*/
|
||||
/** A GroupSelector that groups via a ValueSource */
|
||||
public class ValueSourceGroupSelector extends GroupSelector<MutableValue> {
|
||||
|
||||
private final ValueSource valueSource;
|
||||
|
@ -41,6 +38,7 @@ public class ValueSourceGroupSelector extends GroupSelector<MutableValue> {
|
|||
|
||||
/**
|
||||
* Create a new ValueSourceGroupSelector
|
||||
*
|
||||
* @param valueSource the ValueSource to group by
|
||||
* @param context a context map for the ValueSource
|
||||
*/
|
||||
|
@ -64,9 +62,10 @@ public class ValueSourceGroupSelector extends GroupSelector<MutableValue> {
|
|||
public State advanceTo(int doc) throws IOException {
|
||||
this.filler.fillValue(doc);
|
||||
if (secondPassGroups != null) {
|
||||
if (secondPassGroups.contains(filler.getValue()) == false)
|
||||
if (secondPassGroups.contains(filler.getValue()) == false) {
|
||||
return State.SKIP;
|
||||
}
|
||||
}
|
||||
return State.ACCEPT;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,82 +17,66 @@
|
|||
|
||||
/**
|
||||
* Grouping.
|
||||
* <p>
|
||||
* This module enables search result grouping with Lucene, where hits
|
||||
* with the same value in the specified single-valued group field are
|
||||
* grouped together. For example, if you group by the <code>author</code>
|
||||
* field, then all documents with the same value in the <code>author</code>
|
||||
* field fall into a single group.
|
||||
* </p>
|
||||
*
|
||||
* <p>Grouping requires a number of inputs:</p>
|
||||
* <p>This module enables search result grouping with Lucene, where hits with the same value in the
|
||||
* specified single-valued group field are grouped together. For example, if you group by the <code>
|
||||
* author</code> field, then all documents with the same value in the <code>author</code> field fall
|
||||
* into a single group.
|
||||
*
|
||||
* <p>Grouping requires a number of inputs:
|
||||
*
|
||||
* <ul>
|
||||
* <li><code>groupSelector</code>: this defines how groups are created
|
||||
* from values per-document. The grouping module ships with
|
||||
* selectors for grouping by term, and by long and double ranges.
|
||||
*
|
||||
* <li><code>groupSort</code>: how the groups are sorted. For sorting
|
||||
* purposes, each group is "represented" by the highest-sorted
|
||||
* document according to the <code>groupSort</code> within it. For
|
||||
* example, if you specify "price" (ascending) then the first group
|
||||
* is the one with the lowest price book within it. Or if you
|
||||
* specify relevance group sort, then the first group is the one
|
||||
* containing the highest scoring book.
|
||||
*
|
||||
* <li><code>topNGroups</code>: how many top groups to keep. For
|
||||
* example, 10 means the top 10 groups are computed.
|
||||
*
|
||||
* <li><code>groupOffset</code>: which "slice" of top groups you want to
|
||||
* retrieve. For example, 3 means you'll get 7 groups back
|
||||
* (assuming <code>topNGroups</code> is 10). This is useful for
|
||||
* <li><code>groupSelector</code>: this defines how groups are created from values per-document.
|
||||
* The grouping module ships with selectors for grouping by term, and by long and double
|
||||
* ranges.
|
||||
* <li><code>groupSort</code>: how the groups are sorted. For sorting purposes, each group is
|
||||
* "represented" by the highest-sorted document according to the <code>groupSort</code> within
|
||||
* it. For example, if you specify "price" (ascending) then the first group is the one with
|
||||
* the lowest price book within it. Or if you specify relevance group sort, then the first
|
||||
* group is the one containing the highest scoring book.
|
||||
* <li><code>topNGroups</code>: how many top groups to keep. For example, 10 means the top 10
|
||||
* groups are computed.
|
||||
* <li><code>groupOffset</code>: which "slice" of top groups you want to retrieve. For example, 3
|
||||
* means you'll get 7 groups back (assuming <code>topNGroups</code> is 10). This is useful for
|
||||
* paging, where you might show 5 groups per page.
|
||||
*
|
||||
* <li><code>withinGroupSort</code>: how the documents within each group
|
||||
* are sorted. This can be different from the group sort.
|
||||
*
|
||||
* <li><code>maxDocsPerGroup</code>: how many top documents within each
|
||||
* group to keep.
|
||||
*
|
||||
* <li><code>withinGroupOffset</code>: which "slice" of top
|
||||
* documents you want to retrieve from each group.
|
||||
*
|
||||
* <li><code>withinGroupSort</code>: how the documents within each group are sorted. This can be
|
||||
* different from the group sort.
|
||||
* <li><code>maxDocsPerGroup</code>: how many top documents within each group to keep.
|
||||
* <li><code>withinGroupOffset</code>: which "slice" of top documents you want to retrieve from
|
||||
* each group.
|
||||
* </ul>
|
||||
*
|
||||
* <p>The implementation is two-pass: the first pass ({@link
|
||||
* org.apache.lucene.search.grouping.FirstPassGroupingCollector})
|
||||
* gathers the top groups, and the second pass ({@link
|
||||
* org.apache.lucene.search.grouping.SecondPassGroupingCollector})
|
||||
* gathers documents within those groups. If the search is costly to
|
||||
* run you may want to use the {@link
|
||||
* org.apache.lucene.search.CachingCollector} class, which
|
||||
* caches hits and can (quickly) replay them for the second pass. This
|
||||
* way you only run the query once, but you pay a RAM cost to (briefly)
|
||||
* hold all hits. Results are returned as a {@link
|
||||
* org.apache.lucene.search.grouping.TopGroups} instance.</p>
|
||||
* org.apache.lucene.search.grouping.FirstPassGroupingCollector}) gathers the top groups, and the
|
||||
* second pass ({@link org.apache.lucene.search.grouping.SecondPassGroupingCollector}) gathers
|
||||
* documents within those groups. If the search is costly to run you may want to use the {@link
|
||||
* org.apache.lucene.search.CachingCollector} class, which caches hits and can (quickly) replay them
|
||||
* for the second pass. This way you only run the query once, but you pay a RAM cost to (briefly)
|
||||
* hold all hits. Results are returned as a {@link org.apache.lucene.search.grouping.TopGroups}
|
||||
* instance.
|
||||
*
|
||||
* <p>Groups are defined by {@link org.apache.lucene.search.grouping.GroupSelector} implementations:
|
||||
*
|
||||
* <p>Groups are defined by {@link org.apache.lucene.search.grouping.GroupSelector}
|
||||
* implementations:</p>
|
||||
* <ul>
|
||||
* <li>{@link org.apache.lucene.search.grouping.TermGroupSelector} groups based on
|
||||
* the value of a {@link org.apache.lucene.index.SortedDocValues} field</li>
|
||||
* <li>{@link org.apache.lucene.search.grouping.ValueSourceGroupSelector} groups based on
|
||||
* the value of a {@link org.apache.lucene.queries.function.ValueSource}</li>
|
||||
* <li>{@link org.apache.lucene.search.grouping.DoubleRangeGroupSelector} groups based on
|
||||
* the value of a {@link org.apache.lucene.search.DoubleValuesSource}</li>
|
||||
* <li>{@link org.apache.lucene.search.grouping.LongRangeGroupSelector} groups based on
|
||||
* the value of a {@link org.apache.lucene.search.LongValuesSource}</li>
|
||||
* <li>{@link org.apache.lucene.search.grouping.TermGroupSelector} groups based on the value of a
|
||||
* {@link org.apache.lucene.index.SortedDocValues} field
|
||||
* <li>{@link org.apache.lucene.search.grouping.ValueSourceGroupSelector} groups based on the
|
||||
* value of a {@link org.apache.lucene.queries.function.ValueSource}
|
||||
* <li>{@link org.apache.lucene.search.grouping.DoubleRangeGroupSelector} groups based on the
|
||||
* value of a {@link org.apache.lucene.search.DoubleValuesSource}
|
||||
* <li>{@link org.apache.lucene.search.grouping.LongRangeGroupSelector} groups based on the value
|
||||
* of a {@link org.apache.lucene.search.LongValuesSource}
|
||||
* </ul>
|
||||
*
|
||||
* <p>Known limitations:</p>
|
||||
* <p>Known limitations:
|
||||
*
|
||||
* <ul>
|
||||
* <li> Sharding is not directly supported, though is not too
|
||||
* difficult, if you can merge the top groups and top documents per
|
||||
* group yourself.
|
||||
* <li>Sharding is not directly supported, though is not too difficult, if you can merge the top
|
||||
* groups and top documents per group yourself.
|
||||
* </ul>
|
||||
*
|
||||
* <p>Typical usage for the generic two-pass grouping search looks like this using the grouping convenience utility
|
||||
* (optionally using caching for the second pass search):</p>
|
||||
* <p>Typical usage for the generic two-pass grouping search looks like this using the grouping
|
||||
* convenience utility (optionally using caching for the second pass search):
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* GroupingSearch groupingSearch = new GroupingSearch("author");
|
||||
|
@ -117,11 +101,9 @@
|
|||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p>To use the single-pass <code>BlockGroupingCollector</code>,
|
||||
* first, at indexing time, you must ensure all docs in each group
|
||||
* are added as a block, and you have some way to find the last
|
||||
* document of each group. One simple way to do this is to add a
|
||||
* marker binary field:</p>
|
||||
* <p>To use the single-pass <code>BlockGroupingCollector</code>, first, at indexing time, you must
|
||||
* ensure all docs in each group are added as a block, and you have some way to find the last
|
||||
* document of each group. One simple way to do this is to add a marker binary field:
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* // Create Documents from your source:
|
||||
|
@ -163,16 +145,16 @@
|
|||
* // Render groupsResult...
|
||||
* </pre>
|
||||
*
|
||||
* Note that the <code>groupValue</code> of each <code>GroupDocs</code>
|
||||
* will be <code>null</code>, so if you need to present this value you'll
|
||||
* have to separately retrieve it (for example using stored
|
||||
* fields, <code>FieldCache</code>, etc.).
|
||||
* Note that the <code>groupValue</code> of each <code>GroupDocs</code> will be <code>null</code>,
|
||||
* so if you need to present this value you'll have to separately retrieve it (for example using
|
||||
* stored fields, <code>FieldCache</code>, etc.).
|
||||
*
|
||||
* <p>Another collector is the <code>AllGroupHeadsCollector</code> that can be used to retrieve all most relevant
|
||||
* documents per group. Also known as group heads. This can be useful in situations when one wants to compute group
|
||||
* based facets / statistics on the complete query result. The collector can be executed during the first or second
|
||||
* phase. This collector can also be used with the <code>GroupingSearch</code> convenience utility, but when if one only
|
||||
* wants to compute the most relevant documents per group it is better to just use the collector as done here below.</p>
|
||||
* <p>Another collector is the <code>AllGroupHeadsCollector</code> that can be used to retrieve all
|
||||
* most relevant documents per group. Also known as group heads. This can be useful in situations
|
||||
* when one wants to compute group based facets / statistics on the complete query result. The
|
||||
* collector can be executed during the first or second phase. This collector can also be used with
|
||||
* the <code>GroupingSearch</code> convenience utility, but when if one only wants to compute the
|
||||
* most relevant documents per group it is better to just use the collector as done here below.
|
||||
*
|
||||
* <pre class="prettyprint">
|
||||
* TermGroupSelector grouper = new TermGroupSelector(groupField);
|
||||
|
@ -184,6 +166,5 @@
|
|||
* int maxDoc = s.maxDoc();
|
||||
* FixedBitSet groupHeadsBitSet = c.retrieveGroupHeads(maxDoc)
|
||||
* </pre>
|
||||
*
|
||||
*/
|
||||
package org.apache.lucene.search.grouping;
|
||||
|
|
|
@ -18,7 +18,6 @@ package org.apache.lucene.search.grouping;
|
|||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.index.RandomIndexWriter;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
|
@ -28,10 +27,9 @@ import org.apache.lucene.util.IOUtils;
|
|||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
|
||||
/**
|
||||
* Base class for grouping related tests.
|
||||
*/
|
||||
// TODO (MvG) : The grouping tests contain a lot of code duplication. Try to move the common code to this class..
|
||||
/** Base class for grouping related tests. */
|
||||
// TODO (MvG) : The grouping tests contain a lot of code duplication. Try to move the common code to
|
||||
// this class..
|
||||
public abstract class AbstractGroupingTestCase extends LuceneTestCase {
|
||||
|
||||
protected String generateRandomNonEmptyString() {
|
||||
|
@ -62,7 +60,10 @@ public abstract class AbstractGroupingTestCase extends LuceneTestCase {
|
|||
|
||||
Shard() throws IOException {
|
||||
this.directory = newDirectory();
|
||||
this.writer = new RandomIndexWriter(random(), directory,
|
||||
this.writer =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
directory,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ import java.io.IOException;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.NumericDocValuesField;
|
||||
|
@ -65,7 +64,8 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
for (int i = 0; i < topGroups.groups.length; i++) {
|
||||
// Each group should have a result set equal to that returned by the top-level query,
|
||||
// filtered by the group value.
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(filterQuery(topGroups.groups[i].groupValue), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -91,7 +91,10 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
|
||||
GroupingSearch grouper = new GroupingSearch(getGroupSelector());
|
||||
grouper.setGroupDocsLimit(10);
|
||||
Sort sort = new Sort(new SortField("sort1", SortField.Type.STRING), new SortField("sort2", SortField.Type.LONG));
|
||||
Sort sort =
|
||||
new Sort(
|
||||
new SortField("sort1", SortField.Type.STRING),
|
||||
new SortField("sort2", SortField.Type.LONG));
|
||||
grouper.setGroupSort(sort);
|
||||
TopGroups<T> topGroups = grouper.search(searcher, topLevel, 0, 5);
|
||||
TopDocs topDoc = searcher.search(topLevel, 1, sort);
|
||||
|
@ -99,7 +102,8 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
// We're sorting the groups by a defined Sort, but each group itself should be ordered
|
||||
// by doc relevance, and should be equal to the results of a top-level query filtered
|
||||
// by the group value
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(filterQuery(topGroups.groups[i].groupValue), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -111,7 +115,8 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
if (i > 0) {
|
||||
assertSortsBefore(topGroups.groups[i - 1], topGroups.groups[i]);
|
||||
} else {
|
||||
assertArrayEquals(((FieldDoc)topDoc.scoreDocs[0]).fields, topGroups.groups[0].groupSortValues);
|
||||
assertArrayEquals(
|
||||
((FieldDoc) topDoc.scoreDocs[0]).fields, topGroups.groups[0].groupSortValues);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,7 +134,10 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
|
||||
GroupingSearch grouper = new GroupingSearch(getGroupSelector());
|
||||
grouper.setGroupDocsLimit(10);
|
||||
Sort sort = new Sort(new SortField("sort1", SortField.Type.STRING), new SortField("sort2", SortField.Type.LONG));
|
||||
Sort sort =
|
||||
new Sort(
|
||||
new SortField("sort1", SortField.Type.STRING),
|
||||
new SortField("sort2", SortField.Type.LONG));
|
||||
grouper.setSortWithinGroup(sort);
|
||||
|
||||
TopGroups<T> topGroups = grouper.search(searcher, topLevel, 0, 5);
|
||||
|
@ -146,7 +154,8 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
}
|
||||
// Groups themselves are ordered by a defined Sort, and each should give the same result as
|
||||
// the top-level query, filtered by the group value, with the same Sort
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(filterQuery(topGroups.groups[i].groupValue), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -155,7 +164,6 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
}
|
||||
|
||||
shard.close();
|
||||
|
||||
}
|
||||
|
||||
public void testGroupHeads() throws IOException {
|
||||
|
@ -180,7 +188,8 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
int totalHits = searcher.count(topLevel);
|
||||
int groupHits = 0;
|
||||
for (T groupValue : matchingGroups) {
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(filterQuery(groupValue), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -195,12 +204,14 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
cardinality++;
|
||||
}
|
||||
}
|
||||
assertEquals(matchingGroups.size(), cardinality); // We should have one set bit per matching group
|
||||
assertEquals(
|
||||
matchingGroups.size(), cardinality); // We should have one set bit per matching group
|
||||
|
||||
// Each group head should correspond to the topdoc of a search filtered by
|
||||
// that group
|
||||
for (T groupValue : matchingGroups) {
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(filterQuery(groupValue), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -220,7 +231,10 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
String[] query = new String[] {"foo", "bar", "baz"};
|
||||
Query topLevel = new TermQuery(new Term("text", query[random().nextInt(query.length)]));
|
||||
|
||||
Sort sort = new Sort(new SortField("sort1", SortField.Type.STRING), new SortField("sort2", SortField.Type.LONG));
|
||||
Sort sort =
|
||||
new Sort(
|
||||
new SortField("sort1", SortField.Type.STRING),
|
||||
new SortField("sort2", SortField.Type.LONG));
|
||||
GroupSelector<T> groupSelector = getGroupSelector();
|
||||
GroupingSearch grouping = new GroupingSearch(groupSelector);
|
||||
grouping.setAllGroups(true);
|
||||
|
@ -237,12 +251,14 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
cardinality++;
|
||||
}
|
||||
}
|
||||
assertEquals(matchingGroups.size(), cardinality); // We should have one set bit per matching group
|
||||
assertEquals(
|
||||
matchingGroups.size(), cardinality); // We should have one set bit per matching group
|
||||
|
||||
// Each group head should correspond to the topdoc of a search filtered by
|
||||
// that group using the same within-group sort
|
||||
for (T groupValue : matchingGroups) {
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(filterQuery(groupValue), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -285,26 +301,32 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
String[] query = new String[] {"foo", "bar", "baz"};
|
||||
Query topLevel = new TermQuery(new Term("text", query[random().nextInt(query.length)]));
|
||||
|
||||
Sort sort = new Sort(new SortField("sort1", SortField.Type.STRING), new SortField("sort2", SortField.Type.LONG));
|
||||
Sort sort =
|
||||
new Sort(
|
||||
new SortField("sort1", SortField.Type.STRING),
|
||||
new SortField("sort2", SortField.Type.LONG));
|
||||
|
||||
// A grouped query run in two phases against the control should give us the same
|
||||
// result as the query run against shards and merged back together after each phase.
|
||||
|
||||
FirstPassGroupingCollector<T> singletonFirstPass = new FirstPassGroupingCollector<>(getGroupSelector(), sort, 5);
|
||||
FirstPassGroupingCollector<T> singletonFirstPass =
|
||||
new FirstPassGroupingCollector<>(getGroupSelector(), sort, 5);
|
||||
control.getIndexSearcher().search(topLevel, singletonFirstPass);
|
||||
Collection<SearchGroup<T>> singletonGroups = singletonFirstPass.getTopGroups(0);
|
||||
|
||||
List<Collection<SearchGroup<T>>> shardGroups = new ArrayList<>();
|
||||
for (Shard shard : shards) {
|
||||
FirstPassGroupingCollector<T> fc = new FirstPassGroupingCollector<>(getGroupSelector(), sort, 5);
|
||||
FirstPassGroupingCollector<T> fc =
|
||||
new FirstPassGroupingCollector<>(getGroupSelector(), sort, 5);
|
||||
shard.getIndexSearcher().search(topLevel, fc);
|
||||
shardGroups.add(fc.getTopGroups(0));
|
||||
}
|
||||
Collection<SearchGroup<T>> mergedGroups = SearchGroup.merge(shardGroups, 0, 5, sort);
|
||||
assertEquals(singletonGroups, mergedGroups);
|
||||
|
||||
TopGroupsCollector<T> singletonSecondPass = new TopGroupsCollector<>(getGroupSelector(), singletonGroups, sort,
|
||||
Sort.RELEVANCE, 5, true);
|
||||
TopGroupsCollector<T> singletonSecondPass =
|
||||
new TopGroupsCollector<>(
|
||||
getGroupSelector(), singletonGroups, sort, Sort.RELEVANCE, 5, true);
|
||||
control.getIndexSearcher().search(topLevel, singletonSecondPass);
|
||||
TopGroups<T> singletonTopGroups = singletonSecondPass.getTopGroups(0);
|
||||
|
||||
|
@ -313,12 +335,14 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
TopGroups<T>[] shardTopGroups = (TopGroups<T>[]) new TopGroups<?>[shards.length];
|
||||
int j = 0;
|
||||
for (Shard shard : shards) {
|
||||
TopGroupsCollector<T> sc = new TopGroupsCollector<>(getGroupSelector(), mergedGroups, sort, Sort.RELEVANCE, 5, true);
|
||||
TopGroupsCollector<T> sc =
|
||||
new TopGroupsCollector<>(getGroupSelector(), mergedGroups, sort, Sort.RELEVANCE, 5, true);
|
||||
shard.getIndexSearcher().search(topLevel, sc);
|
||||
shardTopGroups[j] = sc.getTopGroups(0);
|
||||
j++;
|
||||
}
|
||||
TopGroups<T> mergedTopGroups = TopGroups.merge(shardTopGroups, sort, Sort.RELEVANCE, 0, 5, TopGroups.ScoreMergeMode.None);
|
||||
TopGroups<T> mergedTopGroups =
|
||||
TopGroups.merge(shardTopGroups, sort, Sort.RELEVANCE, 0, 5, TopGroups.ScoreMergeMode.None);
|
||||
assertNotNull(mergedTopGroups);
|
||||
|
||||
assertEquals(singletonTopGroups.totalGroupedHitCount, mergedTopGroups.totalGroupedHitCount);
|
||||
|
@ -327,14 +351,15 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
assertEquals(singletonTopGroups.groups.length, mergedTopGroups.groups.length);
|
||||
for (int i = 0; i < singletonTopGroups.groups.length; i++) {
|
||||
assertEquals(singletonTopGroups.groups[i].groupValue, mergedTopGroups.groups[i].groupValue);
|
||||
assertEquals(singletonTopGroups.groups[i].scoreDocs.length, mergedTopGroups.groups[i].scoreDocs.length);
|
||||
assertEquals(
|
||||
singletonTopGroups.groups[i].scoreDocs.length,
|
||||
mergedTopGroups.groups[i].scoreDocs.length);
|
||||
}
|
||||
|
||||
control.close();
|
||||
for (Shard shard : shards) {
|
||||
shard.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void indexRandomDocs(RandomIndexWriter w) throws IOException {
|
||||
|
@ -361,5 +386,4 @@ public abstract class BaseGroupSelectorTestCase<T> extends AbstractGroupingTestC
|
|||
assertTrue((long) prevSortValues[1] <= (long) groupSortValues[1]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.BinaryDocValuesField;
|
||||
import org.apache.lucene.document.Document;
|
||||
|
@ -61,7 +60,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
public void testBasic() throws Exception {
|
||||
final String groupField = "author";
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
|
||||
|
@ -137,34 +137,45 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
int maxDoc = reader.maxDoc();
|
||||
|
||||
Sort sortWithinGroup = new Sort(new SortField("id_1", SortField.Type.INT, true));
|
||||
AllGroupHeadsCollector<?> allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup);
|
||||
AllGroupHeadsCollector<?> allGroupHeadsCollector =
|
||||
createRandomCollector(groupField, sortWithinGroup);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "random")), allGroupHeadsCollector);
|
||||
assertTrue(arrayContains(new int[] {2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads()));
|
||||
assertTrue(openBitSetContains(new int[]{2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
assertTrue(
|
||||
openBitSetContains(
|
||||
new int[] {2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
|
||||
allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "some")), allGroupHeadsCollector);
|
||||
assertTrue(arrayContains(new int[] {2, 3, 4}, allGroupHeadsCollector.retrieveGroupHeads()));
|
||||
assertTrue(openBitSetContains(new int[]{2, 3, 4}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
assertTrue(
|
||||
openBitSetContains(
|
||||
new int[] {2, 3, 4}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
|
||||
allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "blob")), allGroupHeadsCollector);
|
||||
assertTrue(arrayContains(new int[] {1, 5}, allGroupHeadsCollector.retrieveGroupHeads()));
|
||||
assertTrue(openBitSetContains(new int[]{1, 5}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
assertTrue(
|
||||
openBitSetContains(
|
||||
new int[] {1, 5}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
|
||||
// STRING sort type triggers different implementation
|
||||
Sort sortWithinGroup2 = new Sort(new SortField("id_2", SortField.Type.STRING, true));
|
||||
allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup2);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "random")), allGroupHeadsCollector);
|
||||
assertTrue(arrayContains(new int[] {2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads()));
|
||||
assertTrue(openBitSetContains(new int[]{2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
assertTrue(
|
||||
openBitSetContains(
|
||||
new int[] {2, 3, 5, 7}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
|
||||
Sort sortWithinGroup3 = new Sort(new SortField("id_2", SortField.Type.STRING, false));
|
||||
allGroupHeadsCollector = createRandomCollector(groupField, sortWithinGroup3);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "random")), allGroupHeadsCollector);
|
||||
// 7 b/c higher doc id wins, even if order of field is in not in reverse.
|
||||
assertTrue(arrayContains(new int[] {0, 3, 4, 6}, allGroupHeadsCollector.retrieveGroupHeads()));
|
||||
assertTrue(openBitSetContains(new int[]{0, 3, 4, 6}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
assertTrue(
|
||||
openBitSetContains(
|
||||
new int[] {0, 3, 4, 6}, allGroupHeadsCollector.retrieveGroupHeads(maxDoc), maxDoc));
|
||||
|
||||
indexSearcher.getIndexReader().close();
|
||||
dir.close();
|
||||
|
@ -174,7 +185,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
int numberOfRuns = atLeast(1);
|
||||
for (int iter = 0; iter < numberOfRuns; iter++) {
|
||||
if (VERBOSE) {
|
||||
System.out.println(String.format(Locale.ROOT, "TEST: iter=%d total=%d", iter, numberOfRuns));
|
||||
System.out.println(
|
||||
String.format(Locale.ROOT, "TEST: iter=%d total=%d", iter, numberOfRuns));
|
||||
}
|
||||
|
||||
final int numDocs = TestUtil.nextInt(random(), 100, 1000) * RANDOM_MULTIPLIER;
|
||||
|
@ -188,7 +200,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
for (int i = 0; i < numGroups; i++) {
|
||||
String randomValue;
|
||||
do {
|
||||
// B/c of DV based impl we can't see the difference between an empty string and a null value.
|
||||
// B/c of DV based impl we can't see the difference between an empty string and a null
|
||||
// value.
|
||||
// For that reason we don't generate empty string groups.
|
||||
randomValue = TestUtil.randomRealisticUnicodeString(random());
|
||||
// randomValue = TestUtil.randomSimpleString(random());
|
||||
|
@ -213,10 +226,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
}
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())));
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(random(), dir, newIndexWriterConfig(new MockAnalyzer(random())));
|
||||
DocValuesType valueType = DocValuesType.SORTED;
|
||||
|
||||
Document doc = new Document();
|
||||
|
@ -250,17 +261,29 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
groupValue = groups.get(random().nextInt(groups.size()));
|
||||
}
|
||||
|
||||
final GroupDoc groupDoc = new GroupDoc(
|
||||
final GroupDoc groupDoc =
|
||||
new GroupDoc(
|
||||
i,
|
||||
groupValue,
|
||||
groups.get(random().nextInt(groups.size())),
|
||||
groups.get(random().nextInt(groups.size())),
|
||||
new BytesRef(String.format(Locale.ROOT, "%05d", i)),
|
||||
contentStrings[random().nextInt(contentStrings.length)]
|
||||
);
|
||||
contentStrings[random().nextInt(contentStrings.length)]);
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println(" doc content=" + groupDoc.content + " id=" + i + " group=" + (groupDoc.group == null ? "null" : groupDoc.group.utf8ToString()) + " sort1=" + groupDoc.sort1.utf8ToString() + " sort2=" + groupDoc.sort2.utf8ToString() + " sort3=" + groupDoc.sort3.utf8ToString());
|
||||
System.out.println(
|
||||
" doc content="
|
||||
+ groupDoc.content
|
||||
+ " id="
|
||||
+ i
|
||||
+ " group="
|
||||
+ (groupDoc.group == null ? "null" : groupDoc.group.utf8ToString())
|
||||
+ " sort1="
|
||||
+ groupDoc.sort1.utf8ToString()
|
||||
+ " sort2="
|
||||
+ groupDoc.sort2.utf8ToString()
|
||||
+ " sort3="
|
||||
+ groupDoc.sort3.utf8ToString());
|
||||
}
|
||||
|
||||
groupDocs[i] = groupDoc;
|
||||
|
@ -296,7 +319,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
|
||||
Set<Integer> seenIDs = new HashSet<>();
|
||||
for (int contentID = 0; contentID < 3; contentID++) {
|
||||
final ScoreDoc[] hits = s.search(new TermQuery(new Term("content", "real" + contentID)), numDocs).scoreDocs;
|
||||
final ScoreDoc[] hits =
|
||||
s.search(new TermQuery(new Term("content", "real" + contentID)), numDocs).scoreDocs;
|
||||
for (ScoreDoc hit : hits) {
|
||||
int idValue = docIDToFieldId[hit.doc];
|
||||
final GroupDoc gd = groupDocs[idValue];
|
||||
|
@ -325,9 +349,12 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
final String searchTerm = "real" + random().nextInt(3);
|
||||
boolean sortByScoreOnly = random().nextBoolean();
|
||||
Sort sortWithinGroup = getRandomSort(sortByScoreOnly);
|
||||
AllGroupHeadsCollector<?> allGroupHeadsCollector = createRandomCollector("group", sortWithinGroup);
|
||||
AllGroupHeadsCollector<?> allGroupHeadsCollector =
|
||||
createRandomCollector("group", sortWithinGroup);
|
||||
s.search(new TermQuery(new Term("content", searchTerm)), allGroupHeadsCollector);
|
||||
int[] expectedGroupHeads = createExpectedGroupHeads(searchTerm, groupDocs, sortWithinGroup, sortByScoreOnly, fieldIdToDocID);
|
||||
int[] expectedGroupHeads =
|
||||
createExpectedGroupHeads(
|
||||
searchTerm, groupDocs, sortWithinGroup, sortByScoreOnly, fieldIdToDocID);
|
||||
int[] actualGroupHeads = allGroupHeadsCollector.retrieveGroupHeads();
|
||||
// The actual group heads contains Lucene ids. Need to change them into our id value.
|
||||
for (int i = 0; i < actualGroupHeads.length; i++) {
|
||||
|
@ -345,28 +372,37 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
System.out.println("\n=== Expected: \n");
|
||||
for (int expectedDocId : expectedGroupHeads) {
|
||||
GroupDoc expectedGroupDoc = groupDocs[expectedDocId];
|
||||
String expectedGroup = expectedGroupDoc.group == null ? null : expectedGroupDoc.group.utf8ToString();
|
||||
String expectedGroup =
|
||||
expectedGroupDoc.group == null ? null : expectedGroupDoc.group.utf8ToString();
|
||||
System.out.println(
|
||||
String.format(Locale.ROOT,
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Group:%10s score%5f Sort1:%10s Sort2:%10s Sort3:%10s doc:%5d",
|
||||
expectedGroup, expectedGroupDoc.score, expectedGroupDoc.sort1.utf8ToString(),
|
||||
expectedGroupDoc.sort2.utf8ToString(), expectedGroupDoc.sort3.utf8ToString(), expectedDocId
|
||||
)
|
||||
);
|
||||
expectedGroup,
|
||||
expectedGroupDoc.score,
|
||||
expectedGroupDoc.sort1.utf8ToString(),
|
||||
expectedGroupDoc.sort2.utf8ToString(),
|
||||
expectedGroupDoc.sort3.utf8ToString(),
|
||||
expectedDocId));
|
||||
}
|
||||
System.out.println("\n=== Actual: \n");
|
||||
for (int actualDocId : actualGroupHeads) {
|
||||
GroupDoc actualGroupDoc = groupDocs[actualDocId];
|
||||
String actualGroup = actualGroupDoc.group == null ? null : actualGroupDoc.group.utf8ToString();
|
||||
String actualGroup =
|
||||
actualGroupDoc.group == null ? null : actualGroupDoc.group.utf8ToString();
|
||||
System.out.println(
|
||||
String.format(Locale.ROOT,
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"Group:%10s score%5f Sort1:%10s Sort2:%10s Sort3:%10s doc:%5d",
|
||||
actualGroup, actualGroupDoc.score, actualGroupDoc.sort1.utf8ToString(),
|
||||
actualGroupDoc.sort2.utf8ToString(), actualGroupDoc.sort3.utf8ToString(), actualDocId
|
||||
)
|
||||
);
|
||||
actualGroup,
|
||||
actualGroupDoc.score,
|
||||
actualGroupDoc.sort1.utf8ToString(),
|
||||
actualGroupDoc.sort2.utf8ToString(),
|
||||
actualGroupDoc.sort3.utf8ToString(),
|
||||
actualDocId));
|
||||
}
|
||||
System.out.println("\n===================================================================================");
|
||||
System.out.println(
|
||||
"\n===================================================================================");
|
||||
}
|
||||
|
||||
assertArrayEquals(expectedGroupHeads, actualGroupHeads);
|
||||
|
@ -377,9 +413,9 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
private boolean arrayContains(int[] expected, int[] actual) {
|
||||
Arrays.sort(actual); // in some cases the actual docs aren't sorted by docid. This method expects that.
|
||||
// in some cases the actual docs aren't sorted by docid. This method expects that.
|
||||
Arrays.sort(actual);
|
||||
if (expected.length != actual.length) {
|
||||
return false;
|
||||
}
|
||||
|
@ -401,7 +437,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean openBitSetContains(int[] expectedDocs, Bits actual, int maxDoc) throws IOException {
|
||||
private boolean openBitSetContains(int[] expectedDocs, Bits actual, int maxDoc)
|
||||
throws IOException {
|
||||
assert actual instanceof FixedBitSet;
|
||||
if (expectedDocs.length != ((FixedBitSet) actual).cardinality()) {
|
||||
return false;
|
||||
|
@ -412,7 +449,12 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
expected.set(expectedDoc);
|
||||
}
|
||||
|
||||
for (int docId = expected.nextSetBit(0); docId != DocIdSetIterator.NO_MORE_DOCS; docId = docId + 1 >= expected.length() ? DocIdSetIterator.NO_MORE_DOCS : expected.nextSetBit(docId + 1)) {
|
||||
for (int docId = expected.nextSetBit(0);
|
||||
docId != DocIdSetIterator.NO_MORE_DOCS;
|
||||
docId =
|
||||
docId + 1 >= expected.length()
|
||||
? DocIdSetIterator.NO_MORE_DOCS
|
||||
: expected.nextSetBit(docId + 1)) {
|
||||
if (!actual.get(docId)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -421,7 +463,12 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
return true;
|
||||
}
|
||||
|
||||
private int[] createExpectedGroupHeads(String searchTerm, GroupDoc[] groupDocs, Sort docSort, boolean sortByScoreOnly, int[] fieldIdToDocID) {
|
||||
private int[] createExpectedGroupHeads(
|
||||
String searchTerm,
|
||||
GroupDoc[] groupDocs,
|
||||
Sort docSort,
|
||||
boolean sortByScoreOnly,
|
||||
int[] fieldIdToDocID) {
|
||||
Map<BytesRef, List<GroupDoc>> groupHeads = new HashMap<>();
|
||||
for (GroupDoc groupDoc : groupDocs) {
|
||||
if (!groupDoc.content.startsWith(searchTerm)) {
|
||||
|
@ -473,7 +520,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
return new Sort(sortFields.toArray(new SortField[sortFields.size()]));
|
||||
}
|
||||
|
||||
private Comparator<GroupDoc> getComparator(Sort sort, final boolean sortByScoreOnly, final int[] fieldIdToDocID) {
|
||||
private Comparator<GroupDoc> getComparator(
|
||||
Sort sort, final boolean sortByScoreOnly, final int[] fieldIdToDocID) {
|
||||
final SortField[] sortFields = sort.getSort();
|
||||
return new Comparator<GroupDoc>() {
|
||||
@Override
|
||||
|
@ -513,13 +561,16 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
private AllGroupHeadsCollector<?> createRandomCollector(String groupField, Sort sortWithinGroup) {
|
||||
if (random().nextBoolean()) {
|
||||
ValueSource vs = new BytesRefFieldSource(groupField);
|
||||
return AllGroupHeadsCollector.newCollector(new ValueSourceGroupSelector(vs, new HashMap<>()), sortWithinGroup);
|
||||
return AllGroupHeadsCollector.newCollector(
|
||||
new ValueSourceGroupSelector(vs, new HashMap<>()), sortWithinGroup);
|
||||
} else {
|
||||
return AllGroupHeadsCollector.newCollector(new TermGroupSelector(groupField), sortWithinGroup);
|
||||
return AllGroupHeadsCollector.newCollector(
|
||||
new TermGroupSelector(groupField), sortWithinGroup);
|
||||
}
|
||||
}
|
||||
|
||||
private void addGroupField(Document doc, String groupField, String value, DocValuesType valueType) {
|
||||
private void addGroupField(
|
||||
Document doc, String groupField, String value, DocValuesType valueType) {
|
||||
Field valuesField = null;
|
||||
switch (valueType) {
|
||||
case BINARY:
|
||||
|
@ -544,7 +595,8 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
final String content;
|
||||
float score;
|
||||
|
||||
public GroupDoc(int id, BytesRef group, BytesRef sort1, BytesRef sort2, BytesRef sort3, String content) {
|
||||
public GroupDoc(
|
||||
int id, BytesRef group, BytesRef sort1, BytesRef sort2, BytesRef sort3, String content) {
|
||||
this.id = id;
|
||||
this.group = group;
|
||||
this.sort1 = sort1;
|
||||
|
@ -552,7 +604,5 @@ public class TestAllGroupHeadsCollector extends LuceneTestCase {
|
|||
this.sort3 = sort3;
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.apache.lucene.search.grouping;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
|
@ -43,7 +42,8 @@ public class TestAllGroupsCollector extends LuceneTestCase {
|
|||
customType.setStored(true);
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
|
||||
|
@ -124,11 +124,9 @@ public class TestAllGroupsCollector extends LuceneTestCase {
|
|||
private AllGroupsCollector<?> createRandomCollector(String groupField) {
|
||||
if (random().nextBoolean()) {
|
||||
return new AllGroupsCollector<>(new TermGroupSelector(groupField));
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
ValueSource vs = new BytesRefFieldSource(groupField);
|
||||
return new AllGroupsCollector<>(new ValueSourceGroupSelector(vs, new HashMap<>()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.apache.lucene.search.grouping;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.NumericDocValuesField;
|
||||
|
@ -65,7 +64,8 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
String bookName = searcher.doc(tg.groups[i].scoreDocs[0].doc).get("book");
|
||||
// The contents of each group should be equal to the results of a search for
|
||||
// that group alone
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(new TermQuery(new Term("book", bookName)), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -74,7 +74,6 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
}
|
||||
|
||||
shard.close();
|
||||
|
||||
}
|
||||
|
||||
public void testTopLevelSort() throws IOException {
|
||||
|
@ -88,7 +87,8 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
Query blockEndQuery = new TermQuery(new Term("blockEnd", "true"));
|
||||
GroupingSearch grouper = new GroupingSearch(blockEndQuery);
|
||||
grouper.setGroupDocsLimit(10);
|
||||
grouper.setGroupSort(sort); // groups returned sorted by length, chapters within group sorted by relevancy
|
||||
// groups returned sorted by length, chapters within group sorted by relevancy
|
||||
grouper.setGroupSort(sort);
|
||||
|
||||
Query topLevel = new TermQuery(new Term("text", "grandmother"));
|
||||
TopGroups<?> tg = grouper.search(searcher, topLevel, 0, 5);
|
||||
|
@ -102,7 +102,8 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
String bookName = searcher.doc(tg.groups[i].scoreDocs[0].doc).get("book");
|
||||
// The contents of each group should be equal to the results of a search for
|
||||
// that group alone, sorted by score
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(new TermQuery(new Term("book", bookName)), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -114,7 +115,6 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
}
|
||||
|
||||
shard.close();
|
||||
|
||||
}
|
||||
|
||||
public void testWithinGroupSort() throws IOException {
|
||||
|
@ -128,7 +128,8 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
Query blockEndQuery = new TermQuery(new Term("blockEnd", "true"));
|
||||
GroupingSearch grouper = new GroupingSearch(blockEndQuery);
|
||||
grouper.setGroupDocsLimit(10);
|
||||
grouper.setSortWithinGroup(sort); // groups returned sorted by relevancy, chapters within group sorted by length
|
||||
// groups returned sorted by relevancy, chapters within group sorted by length
|
||||
grouper.setSortWithinGroup(sort);
|
||||
|
||||
Query topLevel = new TermQuery(new Term("text", "grandmother"));
|
||||
TopGroups<?> tg = grouper.search(searcher, topLevel, 0, 5);
|
||||
|
@ -142,7 +143,8 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
String bookName = searcher.doc(tg.groups[i].scoreDocs[0].doc).get("book");
|
||||
// The contents of each group should be equal to the results of a search for
|
||||
// that group alone, sorted by length
|
||||
Query filtered = new BooleanQuery.Builder()
|
||||
Query filtered =
|
||||
new BooleanQuery.Builder()
|
||||
.add(topLevel, BooleanClause.Occur.MUST)
|
||||
.add(new TermQuery(new Term("book", bookName)), BooleanClause.Occur.FILTER)
|
||||
.build();
|
||||
|
@ -188,7 +190,8 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
return block;
|
||||
}
|
||||
|
||||
private static final String[] TEXT = new String[]{
|
||||
private static final String[] TEXT =
|
||||
new String[] {
|
||||
"It was the day my grandmother exploded",
|
||||
"It was the best of times, it was the worst of times",
|
||||
"It was a bright cold morning in April",
|
||||
|
@ -221,5 +224,4 @@ public class TestBlockGrouping extends AbstractGroupingTestCase {
|
|||
assertArrayEquals(e.fields, a.fields);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
|
@ -51,7 +50,7 @@ import org.apache.lucene.util.mutable.MutableValueStr;
|
|||
|
||||
public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
||||
|
||||
private final static NullComparator nullComparator = new NullComparator();
|
||||
private static final NullComparator nullComparator = new NullComparator();
|
||||
|
||||
private static final String GROUP_FIELD = "author";
|
||||
private static final String COUNT_FIELD = "publisher";
|
||||
|
@ -59,7 +58,8 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
public void testSimple() throws Exception {
|
||||
Random random = random();
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random,
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy()));
|
||||
|
@ -120,7 +120,8 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
IndexSearcher indexSearcher = newSearcher(w.getReader());
|
||||
w.close();
|
||||
|
||||
Comparator<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> cmp = (groupCount1, groupCount2) -> {
|
||||
Comparator<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> cmp =
|
||||
(groupCount1, groupCount2) -> {
|
||||
if (groupCount1.groupValue == null) {
|
||||
if (groupCount2.groupValue == null) {
|
||||
return 0;
|
||||
|
@ -134,13 +135,15 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
};
|
||||
|
||||
// === Search for content:random
|
||||
FirstPassGroupingCollector<Comparable<Object>> firstCollector = createRandomFirstPassCollector(new Sort(), GROUP_FIELD, 10);
|
||||
FirstPassGroupingCollector<Comparable<Object>> firstCollector =
|
||||
createRandomFirstPassCollector(new Sort(), GROUP_FIELD, 10);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "random")), firstCollector);
|
||||
DistinctValuesCollector<Comparable<Object>, Comparable<Object>> distinctValuesCollector
|
||||
= createDistinctCountCollector(firstCollector, COUNT_FIELD);
|
||||
DistinctValuesCollector<Comparable<Object>, Comparable<Object>> distinctValuesCollector =
|
||||
createDistinctCountCollector(firstCollector, COUNT_FIELD);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "random")), distinctValuesCollector);
|
||||
|
||||
List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> gcs = distinctValuesCollector.getGroups();
|
||||
List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> gcs =
|
||||
distinctValuesCollector.getGroups();
|
||||
Collections.sort(gcs, cmp);
|
||||
assertEquals(4, gcs.size());
|
||||
|
||||
|
@ -229,21 +232,26 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
Sort groupSort = new Sort(new SortField("id", SortField.Type.STRING));
|
||||
int topN = 1 + random.nextInt(10);
|
||||
|
||||
List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> expectedResult = createExpectedResult(context, term, groupSort, topN);
|
||||
List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>>
|
||||
expectedResult = createExpectedResult(context, term, groupSort, topN);
|
||||
|
||||
FirstPassGroupingCollector<Comparable<Object>> firstCollector = createRandomFirstPassCollector(groupSort, GROUP_FIELD, topN);
|
||||
FirstPassGroupingCollector<Comparable<Object>> firstCollector =
|
||||
createRandomFirstPassCollector(groupSort, GROUP_FIELD, topN);
|
||||
searcher.search(new TermQuery(new Term("content", term)), firstCollector);
|
||||
DistinctValuesCollector<Comparable<Object>, Comparable<Object>> distinctValuesCollector
|
||||
= createDistinctCountCollector(firstCollector, COUNT_FIELD);
|
||||
DistinctValuesCollector<Comparable<Object>, Comparable<Object>> distinctValuesCollector =
|
||||
createDistinctCountCollector(firstCollector, COUNT_FIELD);
|
||||
searcher.search(new TermQuery(new Term("content", term)), distinctValuesCollector);
|
||||
@SuppressWarnings("unchecked")
|
||||
List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> actualResult = distinctValuesCollector.getGroups();
|
||||
List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>>
|
||||
actualResult = distinctValuesCollector.getGroups();
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("Index iter=" + indexIter);
|
||||
System.out.println("Search iter=" + searchIter);
|
||||
System.out.println("1st pass collector class name=" + firstCollector.getClass().getName());
|
||||
System.out.println("2nd pass collector class name=" + distinctValuesCollector.getClass().getName());
|
||||
System.out.println(
|
||||
"1st pass collector class name=" + firstCollector.getClass().getName());
|
||||
System.out.println(
|
||||
"2nd pass collector class name=" + distinctValuesCollector.getClass().getName());
|
||||
System.out.println("Search term=" + term);
|
||||
System.out.println("1st pass groups=" + firstCollector.getTopGroups(0));
|
||||
System.out.println("Expected:");
|
||||
|
@ -254,8 +262,10 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
|
||||
assertEquals(expectedResult.size(), actualResult.size());
|
||||
for (int i = 0; i < expectedResult.size(); i++) {
|
||||
DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>> expected = expectedResult.get(i);
|
||||
DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>> actual = actualResult.get(i);
|
||||
DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>> expected =
|
||||
expectedResult.get(i);
|
||||
DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>> actual =
|
||||
actualResult.get(i);
|
||||
assertValues(expected.groupValue, actual.groupValue);
|
||||
assertEquals(expected.uniqueValues.size(), actual.uniqueValues.size());
|
||||
List<Comparable<?>> expectedUniqueValues = new ArrayList<>(expected.uniqueValues);
|
||||
|
@ -272,9 +282,11 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private void printGroups(List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> results) {
|
||||
private void printGroups(
|
||||
List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> results) {
|
||||
for (int i = 0; i < results.size(); i++) {
|
||||
DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>> group = results.get(i);
|
||||
DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>> group =
|
||||
results.get(i);
|
||||
Object gv = group.groupValue;
|
||||
if (gv instanceof BytesRef) {
|
||||
System.out.println(i + ": groupValue=" + ((BytesRef) gv).utf8ToString());
|
||||
|
@ -339,12 +351,15 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private <T extends Comparable<Object>, R extends Comparable<Object>> DistinctValuesCollector<T, R> createDistinctCountCollector(FirstPassGroupingCollector<T> firstPassGroupingCollector,
|
||||
String countField) throws IOException {
|
||||
private <T extends Comparable<Object>, R extends Comparable<Object>>
|
||||
DistinctValuesCollector<T, R> createDistinctCountCollector(
|
||||
FirstPassGroupingCollector<T> firstPassGroupingCollector, String countField)
|
||||
throws IOException {
|
||||
Collection<SearchGroup<T>> searchGroups = firstPassGroupingCollector.getTopGroups(0);
|
||||
GroupSelector<T> selector = firstPassGroupingCollector.getGroupSelector();
|
||||
if (ValueSourceGroupSelector.class.isAssignableFrom(selector.getClass())) {
|
||||
GroupSelector gs = new ValueSourceGroupSelector(new BytesRefFieldSource(countField), new HashMap<>());
|
||||
GroupSelector gs =
|
||||
new ValueSourceGroupSelector(new BytesRefFieldSource(countField), new HashMap<>());
|
||||
return new DistinctValuesCollector<>(selector, searchGroups, gs);
|
||||
} else {
|
||||
GroupSelector ts = new TermGroupSelector(countField);
|
||||
|
@ -353,17 +368,25 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private <T> FirstPassGroupingCollector<T> createRandomFirstPassCollector(Sort groupSort, String groupField, int topNGroups) throws IOException {
|
||||
private <T> FirstPassGroupingCollector<T> createRandomFirstPassCollector(
|
||||
Sort groupSort, String groupField, int topNGroups) throws IOException {
|
||||
Random random = random();
|
||||
if (random.nextBoolean()) {
|
||||
return (FirstPassGroupingCollector<T>) new FirstPassGroupingCollector<>(new ValueSourceGroupSelector(new BytesRefFieldSource(groupField), new HashMap<>()), groupSort, topNGroups);
|
||||
return (FirstPassGroupingCollector<T>)
|
||||
new FirstPassGroupingCollector<>(
|
||||
new ValueSourceGroupSelector(new BytesRefFieldSource(groupField), new HashMap<>()),
|
||||
groupSort,
|
||||
topNGroups);
|
||||
} else {
|
||||
return (FirstPassGroupingCollector<T>) new FirstPassGroupingCollector<>(new TermGroupSelector(groupField), groupSort, topNGroups);
|
||||
return (FirstPassGroupingCollector<T>)
|
||||
new FirstPassGroupingCollector<>(
|
||||
new TermGroupSelector(groupField), groupSort, topNGroups);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>> createExpectedResult(IndexContext context, String term, Sort groupSort, int topN) {
|
||||
private List<DistinctValuesCollector.GroupCount<Comparable<Object>, Comparable<Object>>>
|
||||
createExpectedResult(IndexContext context, String term, Sort groupSort, int topN) {
|
||||
List result = new ArrayList();
|
||||
Map<String, Set<String>> groupCounts = context.searchTermToGroupCounts.get(term);
|
||||
int i = 0;
|
||||
|
@ -375,7 +398,9 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
for (String val : groupCounts.get(group)) {
|
||||
uniqueValues.add(val != null ? new BytesRef(val) : null);
|
||||
}
|
||||
result.add(new DistinctValuesCollector.GroupCount(group != null ? new BytesRef(group) : null, uniqueValues));
|
||||
result.add(
|
||||
new DistinctValuesCollector.GroupCount(
|
||||
group != null ? new BytesRef(group) : null, uniqueValues));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -384,11 +409,11 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
Random random = random();
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random,
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy())
|
||||
);
|
||||
newIndexWriterConfig(new MockAnalyzer(random)).setMergePolicy(newLogMergePolicy()));
|
||||
|
||||
int numDocs = 86 + random.nextInt(1087) * RANDOM_MULTIPLIER;
|
||||
String[] groupValues = new String[numDocs / 5];
|
||||
|
@ -403,8 +428,10 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
List<String> contentStrings = new ArrayList<>();
|
||||
Map<String, Map<String, Set<String>>> searchTermToGroupCounts = new HashMap<>();
|
||||
for (int i = 1; i <= numDocs; i++) {
|
||||
String groupValue = random.nextInt(23) == 14 ? null : groupValues[random.nextInt(groupValues.length)];
|
||||
String countValue = random.nextInt(21) == 13 ? null : countValues[random.nextInt(countValues.length)];
|
||||
String groupValue =
|
||||
random.nextInt(23) == 14 ? null : groupValues[random.nextInt(groupValues.length)];
|
||||
String countValue =
|
||||
random.nextInt(21) == 13 ? null : countValues[random.nextInt(countValues.length)];
|
||||
String content = "random" + random.nextInt(numDocs / 20);
|
||||
Map<String, Set<String>> groupToCounts = searchTermToGroupCounts.get(content);
|
||||
if (groupToCounts == null) {
|
||||
|
@ -436,12 +463,26 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
if (VERBOSE) {
|
||||
for (int docID = 0; docID < reader.maxDoc(); docID++) {
|
||||
Document doc = reader.document(docID);
|
||||
System.out.println("docID=" + docID + " id=" + doc.get("id") + " content=" + doc.get("content") + " author=" + doc.get("author") + " publisher=" + doc.get("publisher"));
|
||||
System.out.println(
|
||||
"docID="
|
||||
+ docID
|
||||
+ " id="
|
||||
+ doc.get("id")
|
||||
+ " content="
|
||||
+ doc.get("content")
|
||||
+ " author="
|
||||
+ doc.get("author")
|
||||
+ " publisher="
|
||||
+ doc.get("publisher"));
|
||||
}
|
||||
}
|
||||
|
||||
w.close();
|
||||
return new IndexContext(dir, reader, searchTermToGroupCounts, contentStrings.toArray(new String[contentStrings.size()]));
|
||||
return new IndexContext(
|
||||
dir,
|
||||
reader,
|
||||
searchTermToGroupCounts,
|
||||
contentStrings.toArray(new String[contentStrings.size()]));
|
||||
}
|
||||
|
||||
private static class IndexContext {
|
||||
|
@ -451,8 +492,11 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
final Map<String, Map<String, Set<String>>> searchTermToGroupCounts;
|
||||
final String[] contentStrings;
|
||||
|
||||
IndexContext(Directory directory, DirectoryReader indexReader,
|
||||
Map<String, Map<String, Set<String>>> searchTermToGroupCounts, String[] contentStrings) {
|
||||
IndexContext(
|
||||
Directory directory,
|
||||
DirectoryReader indexReader,
|
||||
Map<String, Map<String, Set<String>>> searchTermToGroupCounts,
|
||||
String[] contentStrings) {
|
||||
this.directory = directory;
|
||||
this.indexReader = indexReader;
|
||||
this.searchTermToGroupCounts = searchTermToGroupCounts;
|
||||
|
@ -475,7 +519,5 @@ public class TestDistinctValuesCollector extends AbstractGroupingTestCase {
|
|||
return a.compareTo(b);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,5 @@ public class TestDoubleRangeFactory extends LuceneTestCase {
|
|||
assertEquals(new DoubleRange(30, 40), factory.getRange(35, scratch));
|
||||
assertEquals(new DoubleRange(50, Double.MAX_VALUE), factory.getRange(50, scratch));
|
||||
assertEquals(new DoubleRange(50, Double.MAX_VALUE), factory.getRange(500, scratch));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ public class TestDoubleRangeGroupSelector extends BaseGroupSelectorTestCase<Doub
|
|||
|
||||
@Override
|
||||
protected GroupSelector<DoubleRange> getGroupSelector() {
|
||||
return new DoubleRangeGroupSelector(DoubleValuesSource.fromDoubleField("double"),
|
||||
new DoubleRangeFactory(100, 100, 900));
|
||||
return new DoubleRangeGroupSelector(
|
||||
DoubleValuesSource.fromDoubleField("double"), new DoubleRangeFactory(100, 100, 900));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,7 +29,6 @@ import java.util.NavigableSet;
|
|||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
|
@ -48,7 +47,6 @@ import org.apache.lucene.store.Directory;
|
|||
import org.apache.lucene.util.BytesRef;
|
||||
import org.apache.lucene.util.TestUtil;
|
||||
|
||||
|
||||
public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
||||
|
||||
public void testSimple() throws Exception {
|
||||
|
@ -57,7 +55,8 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
customType.setStored(true);
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
|
||||
|
@ -108,13 +107,14 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
for (int limit : new int[] {2, 10, 100, Integer.MAX_VALUE}) {
|
||||
// any of these limits is plenty for the data we have
|
||||
|
||||
groupedAirportFacetCollector = createRandomCollector
|
||||
(useDv ? "hotel_dv" : "hotel",
|
||||
useDv ? "airport_dv" : "airport", null, false);
|
||||
groupedAirportFacetCollector =
|
||||
createRandomCollector(
|
||||
useDv ? "hotel_dv" : "hotel", useDv ? "airport_dv" : "airport", null, false);
|
||||
indexSearcher.search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
|
||||
int maxOffset = 5;
|
||||
airportResult = groupedAirportFacetCollector.mergeSegmentResults
|
||||
(Integer.MAX_VALUE == limit ? limit : maxOffset + limit, 0, false);
|
||||
airportResult =
|
||||
groupedAirportFacetCollector.mergeSegmentResults(
|
||||
Integer.MAX_VALUE == limit ? limit : maxOffset + limit, 0, false);
|
||||
|
||||
assertEquals(3, airportResult.getTotalCount());
|
||||
assertEquals(0, airportResult.getTotalMissingCount());
|
||||
|
@ -135,9 +135,12 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
assertEquals(1, entries.get(0).getCount());
|
||||
}
|
||||
|
||||
GroupFacetCollector groupedDurationFacetCollector = createRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", null, false);
|
||||
GroupFacetCollector groupedDurationFacetCollector =
|
||||
createRandomCollector(
|
||||
useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", null, false);
|
||||
indexSearcher.search(new MatchAllDocsQuery(), groupedDurationFacetCollector);
|
||||
TermGroupFacetCollector.GroupedFacetResult durationResult = groupedDurationFacetCollector.mergeSegmentResults(10, 0, false);
|
||||
TermGroupFacetCollector.GroupedFacetResult durationResult =
|
||||
groupedDurationFacetCollector.mergeSegmentResults(10, 0, false);
|
||||
assertEquals(4, durationResult.getTotalCount());
|
||||
assertEquals(0, durationResult.getTotalMissingCount());
|
||||
|
||||
|
@ -181,7 +184,9 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
|
||||
indexSearcher.getIndexReader().close();
|
||||
indexSearcher = newSearcher(w.getReader());
|
||||
groupedAirportFacetCollector = createRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "airport_dv" : "airport", null, !useDv);
|
||||
groupedAirportFacetCollector =
|
||||
createRandomCollector(
|
||||
useDv ? "hotel_dv" : "hotel", useDv ? "airport_dv" : "airport", null, !useDv);
|
||||
indexSearcher.search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
|
||||
airportResult = groupedAirportFacetCollector.mergeSegmentResults(3, 0, true);
|
||||
entries = airportResult.getFacetEntries(1, 2);
|
||||
|
@ -202,7 +207,9 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
assertEquals(1, entries.get(1).getCount());
|
||||
}
|
||||
|
||||
groupedDurationFacetCollector = createRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", null, false);
|
||||
groupedDurationFacetCollector =
|
||||
createRandomCollector(
|
||||
useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", null, false);
|
||||
indexSearcher.search(new MatchAllDocsQuery(), groupedDurationFacetCollector);
|
||||
durationResult = groupedDurationFacetCollector.mergeSegmentResults(10, 2, true);
|
||||
assertEquals(5, durationResult.getTotalCount());
|
||||
|
@ -229,7 +236,9 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
|
||||
indexSearcher.getIndexReader().close();
|
||||
indexSearcher = newSearcher(w.getReader());
|
||||
groupedAirportFacetCollector = createRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "airport_dv" : "airport", null, false);
|
||||
groupedAirportFacetCollector =
|
||||
createRandomCollector(
|
||||
useDv ? "hotel_dv" : "hotel", useDv ? "airport_dv" : "airport", null, false);
|
||||
indexSearcher.search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
|
||||
airportResult = groupedAirportFacetCollector.mergeSegmentResults(10, 0, false);
|
||||
entries = airportResult.getFacetEntries(0, 10);
|
||||
|
@ -257,7 +266,9 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
assertEquals(2, entries.get(2).getCount());
|
||||
}
|
||||
|
||||
groupedDurationFacetCollector = createRandomCollector(useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", "1", false);
|
||||
groupedDurationFacetCollector =
|
||||
createRandomCollector(
|
||||
useDv ? "hotel_dv" : "hotel", useDv ? "duration_dv" : "duration", "1", false);
|
||||
indexSearcher.search(new MatchAllDocsQuery(), groupedDurationFacetCollector);
|
||||
durationResult = groupedDurationFacetCollector.mergeSegmentResults(10, 0, true);
|
||||
assertEquals(5, durationResult.getTotalCount());
|
||||
|
@ -281,10 +292,12 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
customType.setStored(true);
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(NoMergePolicy.INSTANCE));
|
||||
newIndexWriterConfig(new MockAnalyzer(random()))
|
||||
.setMergePolicy(NoMergePolicy.INSTANCE));
|
||||
boolean useDv = true;
|
||||
|
||||
// Cannot assert this since we use NoMergePolicy:
|
||||
|
@ -343,9 +356,11 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
|
||||
w.close();
|
||||
IndexSearcher indexSearcher = newSearcher(DirectoryReader.open(dir));
|
||||
GroupFacetCollector groupedAirportFacetCollector = createRandomCollector(groupField + "_dv", "airport", null, true);
|
||||
GroupFacetCollector groupedAirportFacetCollector =
|
||||
createRandomCollector(groupField + "_dv", "airport", null, true);
|
||||
indexSearcher.search(new MatchAllDocsQuery(), groupedAirportFacetCollector);
|
||||
TermGroupFacetCollector.GroupedFacetResult airportResult = groupedAirportFacetCollector.mergeSegmentResults(10, 0, false);
|
||||
TermGroupFacetCollector.GroupedFacetResult airportResult =
|
||||
groupedAirportFacetCollector.mergeSegmentResults(10, 0, false);
|
||||
assertEquals(3, airportResult.getTotalCount());
|
||||
assertEquals(1, airportResult.getTotalMissingCount());
|
||||
|
||||
|
@ -385,9 +400,11 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
int limit = random.nextInt(context.facetValues.size());
|
||||
int offset = random.nextInt(context.facetValues.size() - limit);
|
||||
int size = offset + limit;
|
||||
int minCount = random.nextBoolean() ? 0 : random.nextInt(1 + context.facetWithMostGroups / 10);
|
||||
int minCount =
|
||||
random.nextBoolean() ? 0 : random.nextInt(1 + context.facetWithMostGroups / 10);
|
||||
boolean orderByCount = random.nextBoolean();
|
||||
String randomStr = getFromSet(context.facetValues, random.nextInt(context.facetValues.size()));
|
||||
String randomStr =
|
||||
getFromSet(context.facetValues, random.nextInt(context.facetValues.size()));
|
||||
final String facetPrefix;
|
||||
if (randomStr == null) {
|
||||
facetPrefix = null;
|
||||
|
@ -402,13 +419,19 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
GroupedFacetResult expectedFacetResult = createExpectedFacetResult(searchTerm, context, offset, limit, minCount, orderByCount, facetPrefix);
|
||||
GroupFacetCollector groupFacetCollector = createRandomCollector("group", "facet", facetPrefix, multipleFacetsPerDocument);
|
||||
GroupedFacetResult expectedFacetResult =
|
||||
createExpectedFacetResult(
|
||||
searchTerm, context, offset, limit, minCount, orderByCount, facetPrefix);
|
||||
GroupFacetCollector groupFacetCollector =
|
||||
createRandomCollector("group", "facet", facetPrefix, multipleFacetsPerDocument);
|
||||
searcher.search(new TermQuery(new Term("content", searchTerm)), groupFacetCollector);
|
||||
TermGroupFacetCollector.GroupedFacetResult actualFacetResult = groupFacetCollector.mergeSegmentResults(size, minCount, orderByCount);
|
||||
TermGroupFacetCollector.GroupedFacetResult actualFacetResult =
|
||||
groupFacetCollector.mergeSegmentResults(size, minCount, orderByCount);
|
||||
|
||||
List<TermGroupFacetCollector.FacetEntry> expectedFacetEntries = expectedFacetResult.getFacetEntries();
|
||||
List<TermGroupFacetCollector.FacetEntry> actualFacetEntries = actualFacetResult.getFacetEntries(offset, limit);
|
||||
List<TermGroupFacetCollector.FacetEntry> expectedFacetEntries =
|
||||
expectedFacetResult.getFacetEntries();
|
||||
List<TermGroupFacetCollector.FacetEntry> actualFacetEntries =
|
||||
actualFacetResult.getFacetEntries(offset, limit);
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("Collector: " + groupFacetCollector.getClass().getSimpleName());
|
||||
|
@ -431,11 +454,12 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
int counter = 0;
|
||||
for (TermGroupFacetCollector.FacetEntry expectedFacetEntry : expectedFacetEntries) {
|
||||
System.out.println(
|
||||
String.format(Locale.ROOT,
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"%d. Expected facet value %s with count %d",
|
||||
counter++, expectedFacetEntry.getValue().utf8ToString(), expectedFacetEntry.getCount()
|
||||
)
|
||||
);
|
||||
counter++,
|
||||
expectedFacetEntry.getValue().utf8ToString(),
|
||||
expectedFacetEntry.getCount()));
|
||||
}
|
||||
|
||||
System.out.println("\n=== Actual: \n");
|
||||
|
@ -444,23 +468,42 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
counter = 0;
|
||||
for (TermGroupFacetCollector.FacetEntry actualFacetEntry : actualFacetEntries) {
|
||||
System.out.println(
|
||||
String.format(Locale.ROOT,
|
||||
String.format(
|
||||
Locale.ROOT,
|
||||
"%d. Actual facet value %s with count %d",
|
||||
counter++, actualFacetEntry.getValue().utf8ToString(), actualFacetEntry.getCount()
|
||||
)
|
||||
);
|
||||
counter++,
|
||||
actualFacetEntry.getValue().utf8ToString(),
|
||||
actualFacetEntry.getCount()));
|
||||
}
|
||||
System.out.println("\n===================================================================================");
|
||||
System.out.println(
|
||||
"\n===================================================================================");
|
||||
}
|
||||
|
||||
assertEquals(expectedFacetResult.getTotalCount(), actualFacetResult.getTotalCount());
|
||||
assertEquals(expectedFacetResult.getTotalMissingCount(), actualFacetResult.getTotalMissingCount());
|
||||
assertEquals(
|
||||
expectedFacetResult.getTotalMissingCount(), actualFacetResult.getTotalMissingCount());
|
||||
assertEquals(expectedFacetEntries.size(), actualFacetEntries.size());
|
||||
for (int i = 0; i < expectedFacetEntries.size(); i++) {
|
||||
TermGroupFacetCollector.FacetEntry expectedFacetEntry = expectedFacetEntries.get(i);
|
||||
TermGroupFacetCollector.FacetEntry actualFacetEntry = actualFacetEntries.get(i);
|
||||
assertEquals("i=" + i + ": " + expectedFacetEntry.getValue().utf8ToString() + " != " + actualFacetEntry.getValue().utf8ToString(), expectedFacetEntry.getValue(), actualFacetEntry.getValue());
|
||||
assertEquals("i=" + i + ": " + expectedFacetEntry.getCount() + " != " + actualFacetEntry.getCount(), expectedFacetEntry.getCount(), actualFacetEntry.getCount());
|
||||
assertEquals(
|
||||
"i="
|
||||
+ i
|
||||
+ ": "
|
||||
+ expectedFacetEntry.getValue().utf8ToString()
|
||||
+ " != "
|
||||
+ actualFacetEntry.getValue().utf8ToString(),
|
||||
expectedFacetEntry.getValue(),
|
||||
actualFacetEntry.getValue());
|
||||
assertEquals(
|
||||
"i="
|
||||
+ i
|
||||
+ ": "
|
||||
+ expectedFacetEntry.getCount()
|
||||
+ " != "
|
||||
+ actualFacetEntry.getCount(),
|
||||
expectedFacetEntry.getCount(),
|
||||
actualFacetEntry.getCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,7 +512,8 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private IndexContext createIndexContext(boolean multipleFacetValuesPerDocument) throws IOException {
|
||||
private IndexContext createIndexContext(boolean multipleFacetValuesPerDocument)
|
||||
throws IOException {
|
||||
final Random random = random();
|
||||
final int numDocs = TestUtil.nextInt(random, 138, 1145) * RANDOM_MULTIPLIER;
|
||||
final int numGroups = TestUtil.nextInt(random, 1, numDocs / 4);
|
||||
|
@ -499,11 +543,8 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter writer = new RandomIndexWriter(
|
||||
random,
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random))
|
||||
);
|
||||
RandomIndexWriter writer =
|
||||
new RandomIndexWriter(random, dir, newIndexWriterConfig(new MockAnalyzer(random)));
|
||||
Document doc = new Document();
|
||||
Document docNoGroup = new Document();
|
||||
Document docNoFacet = new Document();
|
||||
|
@ -524,7 +565,8 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
doc.add(facetFields[1]);
|
||||
docNoGroup.add(facetFields[1]);
|
||||
} else {
|
||||
facetFields = multipleFacetValuesPerDocument ? new Field[2 + random.nextInt(6)] : new Field[1];
|
||||
facetFields =
|
||||
multipleFacetValuesPerDocument ? new Field[2 + random.nextInt(6)] : new Field[1];
|
||||
for (int i = 0; i < facetFields.length; i++) {
|
||||
facetFields[i] = new SortedSetDocValuesField("facet", new BytesRef());
|
||||
doc.add(facetFields[i]);
|
||||
|
@ -537,7 +579,9 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
docNoFacet.add(content);
|
||||
docNoGroupNoFacet.add(content);
|
||||
|
||||
NavigableSet<String> uniqueFacetValues = new TreeSet<>(new Comparator<String>() {
|
||||
NavigableSet<String> uniqueFacetValues =
|
||||
new TreeSet<>(
|
||||
new Comparator<String>() {
|
||||
|
||||
@Override
|
||||
public int compare(String a, String b) {
|
||||
|
@ -551,7 +595,6 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
return a.compareTo(b);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
Map<String, Map<String, Set<String>>> searchTermToFacetToGroups = new HashMap<>();
|
||||
int facetWithMostGroups = 0;
|
||||
|
@ -604,7 +647,13 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println(" doc content=" + contentStr + " group=" + (groupValue == null ? "null" : groupValue) + " facetVals=" + facetVals);
|
||||
System.out.println(
|
||||
" doc content="
|
||||
+ contentStr
|
||||
+ " group="
|
||||
+ (groupValue == null ? "null" : groupValue)
|
||||
+ " facetVals="
|
||||
+ facetVals);
|
||||
}
|
||||
|
||||
if (groupValue != null) {
|
||||
|
@ -630,10 +679,25 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
DirectoryReader reader = writer.getReader();
|
||||
writer.close();
|
||||
|
||||
return new IndexContext(searchTermToFacetToGroups, reader, numDocs, dir, facetWithMostGroups, numGroups, contentBrs, uniqueFacetValues);
|
||||
return new IndexContext(
|
||||
searchTermToFacetToGroups,
|
||||
reader,
|
||||
numDocs,
|
||||
dir,
|
||||
facetWithMostGroups,
|
||||
numGroups,
|
||||
contentBrs,
|
||||
uniqueFacetValues);
|
||||
}
|
||||
|
||||
private GroupedFacetResult createExpectedFacetResult(String searchTerm, IndexContext context, int offset, int limit, int minCount, final boolean orderByCount, String facetPrefix) {
|
||||
private GroupedFacetResult createExpectedFacetResult(
|
||||
String searchTerm,
|
||||
IndexContext context,
|
||||
int offset,
|
||||
int limit,
|
||||
int minCount,
|
||||
final boolean orderByCount,
|
||||
String facetPrefix) {
|
||||
Map<String, Set<String>> facetGroups = context.searchTermToFacetGroups.get(searchTerm);
|
||||
if (facetGroups == null) {
|
||||
facetGroups = new HashMap<>();
|
||||
|
@ -676,10 +740,13 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
Collections.sort(entries, new Comparator<TermGroupFacetCollector.FacetEntry>() {
|
||||
Collections.sort(
|
||||
entries,
|
||||
new Comparator<TermGroupFacetCollector.FacetEntry>() {
|
||||
|
||||
@Override
|
||||
public int compare(TermGroupFacetCollector.FacetEntry a, TermGroupFacetCollector.FacetEntry b) {
|
||||
public int compare(
|
||||
TermGroupFacetCollector.FacetEntry a, TermGroupFacetCollector.FacetEntry b) {
|
||||
if (orderByCount) {
|
||||
int cmp = b.getCount() - a.getCount();
|
||||
if (cmp != 0) {
|
||||
|
@ -688,7 +755,6 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
}
|
||||
return a.getValue().compareTo(b.getValue());
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
int endOffset = offset + limit;
|
||||
|
@ -703,9 +769,11 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
return new GroupedFacetResult(totalCount, totalMissCount, entriesResult);
|
||||
}
|
||||
|
||||
private GroupFacetCollector createRandomCollector(String groupField, String facetField, String facetPrefix, boolean multipleFacetsPerDocument) {
|
||||
private GroupFacetCollector createRandomCollector(
|
||||
String groupField, String facetField, String facetPrefix, boolean multipleFacetsPerDocument) {
|
||||
BytesRef facetPrefixBR = facetPrefix == null ? null : new BytesRef(facetPrefix);
|
||||
return TermGroupFacetCollector.createTermGroupFacetCollector(groupField, facetField, multipleFacetsPerDocument, facetPrefixBR, random().nextInt(1024));
|
||||
return TermGroupFacetCollector.createTermGroupFacetCollector(
|
||||
groupField, facetField, multipleFacetsPerDocument, facetPrefixBR, random().nextInt(1024));
|
||||
}
|
||||
|
||||
private String getFromSet(Set<String> set, int index) {
|
||||
|
@ -730,8 +798,15 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
final int numGroups;
|
||||
final String[] contentStrings;
|
||||
|
||||
public IndexContext(Map<String, Map<String, Set<String>>> searchTermToFacetGroups, DirectoryReader r,
|
||||
int numDocs, Directory dir, int facetWithMostGroups, int numGroups, String[] contentStrings, NavigableSet<String> facetValues) {
|
||||
public IndexContext(
|
||||
Map<String, Map<String, Set<String>>> searchTermToFacetGroups,
|
||||
DirectoryReader r,
|
||||
int numDocs,
|
||||
Directory dir,
|
||||
int facetWithMostGroups,
|
||||
int numGroups,
|
||||
String[] contentStrings,
|
||||
NavigableSet<String> facetValues) {
|
||||
this.searchTermToFacetGroups = searchTermToFacetGroups;
|
||||
this.indexReader = r;
|
||||
this.numDocs = numDocs;
|
||||
|
@ -749,7 +824,10 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
final int totalMissingCount;
|
||||
final List<TermGroupFacetCollector.FacetEntry> facetEntries;
|
||||
|
||||
private GroupedFacetResult(int totalCount, int totalMissingCount, List<TermGroupFacetCollector.FacetEntry> facetEntries) {
|
||||
private GroupedFacetResult(
|
||||
int totalCount,
|
||||
int totalMissingCount,
|
||||
List<TermGroupFacetCollector.FacetEntry> facetEntries) {
|
||||
this.totalCount = totalCount;
|
||||
this.totalMissingCount = totalMissingCount;
|
||||
this.facetEntries = facetEntries;
|
||||
|
@ -767,5 +845,4 @@ public class TestGroupFacetCollector extends AbstractGroupingTestCase {
|
|||
return facetEntries;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
|
@ -84,7 +83,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
customType.setStored(true);
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
|
||||
|
@ -143,10 +143,12 @@ public class TestGrouping extends LuceneTestCase {
|
|||
|
||||
final Sort groupSort = Sort.RELEVANCE;
|
||||
|
||||
final FirstPassGroupingCollector<?> c1 = createRandomFirstPassCollector(groupField, groupSort, 10);
|
||||
final FirstPassGroupingCollector<?> c1 =
|
||||
createRandomFirstPassCollector(groupField, groupSort, 10);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "random")), c1);
|
||||
|
||||
final TopGroupsCollector<?> c2 = createSecondPassCollector(c1, groupSort, Sort.RELEVANCE, 0, 5, true);
|
||||
final TopGroupsCollector<?> c2 =
|
||||
createSecondPassCollector(c1, groupSort, Sort.RELEVANCE, 0, 5, true);
|
||||
indexSearcher.search(new TermQuery(new Term("content", "random")), c2);
|
||||
|
||||
final TopGroups<?> groups = c2.getTopGroups(0);
|
||||
|
@ -194,49 +196,74 @@ public class TestGrouping extends LuceneTestCase {
|
|||
doc.add(new SortedDocValuesField(groupField, new BytesRef(value)));
|
||||
}
|
||||
|
||||
private FirstPassGroupingCollector<?> createRandomFirstPassCollector(String groupField, Sort groupSort, int topDocs) throws IOException {
|
||||
private FirstPassGroupingCollector<?> createRandomFirstPassCollector(
|
||||
String groupField, Sort groupSort, int topDocs) throws IOException {
|
||||
if (random().nextBoolean()) {
|
||||
ValueSource vs = new BytesRefFieldSource(groupField);
|
||||
return new FirstPassGroupingCollector<>(new ValueSourceGroupSelector(vs, new HashMap<>()), groupSort, topDocs);
|
||||
return new FirstPassGroupingCollector<>(
|
||||
new ValueSourceGroupSelector(vs, new HashMap<>()), groupSort, topDocs);
|
||||
} else {
|
||||
return new FirstPassGroupingCollector<>(new TermGroupSelector(groupField), groupSort, topDocs);
|
||||
return new FirstPassGroupingCollector<>(
|
||||
new TermGroupSelector(groupField), groupSort, topDocs);
|
||||
}
|
||||
}
|
||||
|
||||
private FirstPassGroupingCollector<?> createFirstPassCollector(String groupField, Sort groupSort, int topDocs, FirstPassGroupingCollector<?> firstPassGroupingCollector) throws IOException {
|
||||
private FirstPassGroupingCollector<?> createFirstPassCollector(
|
||||
String groupField,
|
||||
Sort groupSort,
|
||||
int topDocs,
|
||||
FirstPassGroupingCollector<?> firstPassGroupingCollector)
|
||||
throws IOException {
|
||||
GroupSelector<?> selector = firstPassGroupingCollector.getGroupSelector();
|
||||
if (TermGroupSelector.class.isAssignableFrom(selector.getClass())) {
|
||||
ValueSource vs = new BytesRefFieldSource(groupField);
|
||||
return new FirstPassGroupingCollector<>(new ValueSourceGroupSelector(vs, new HashMap<>()), groupSort, topDocs);
|
||||
return new FirstPassGroupingCollector<>(
|
||||
new ValueSourceGroupSelector(vs, new HashMap<>()), groupSort, topDocs);
|
||||
} else {
|
||||
return new FirstPassGroupingCollector<>(new TermGroupSelector(groupField), groupSort, topDocs);
|
||||
return new FirstPassGroupingCollector<>(
|
||||
new TermGroupSelector(groupField), groupSort, topDocs);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private <T> TopGroupsCollector<T> createSecondPassCollector(FirstPassGroupingCollector firstPassGroupingCollector,
|
||||
private <T> TopGroupsCollector<T> createSecondPassCollector(
|
||||
FirstPassGroupingCollector firstPassGroupingCollector,
|
||||
Sort groupSort,
|
||||
Sort sortWithinGroup,
|
||||
int groupOffset,
|
||||
int maxDocsPerGroup,
|
||||
boolean getMaxScores) throws IOException {
|
||||
boolean getMaxScores)
|
||||
throws IOException {
|
||||
|
||||
Collection<SearchGroup<T>> searchGroups = firstPassGroupingCollector.getTopGroups(groupOffset);
|
||||
return new TopGroupsCollector<>(firstPassGroupingCollector.getGroupSelector(), searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getMaxScores);
|
||||
return new TopGroupsCollector<>(
|
||||
firstPassGroupingCollector.getGroupSelector(),
|
||||
searchGroups,
|
||||
groupSort,
|
||||
sortWithinGroup,
|
||||
maxDocsPerGroup,
|
||||
getMaxScores);
|
||||
}
|
||||
|
||||
// Basically converts searchGroups from MutableValue to BytesRef if grouping by ValueSource
|
||||
@SuppressWarnings("unchecked")
|
||||
private TopGroupsCollector<?> createSecondPassCollector(FirstPassGroupingCollector<?> firstPassGroupingCollector,
|
||||
private TopGroupsCollector<?> createSecondPassCollector(
|
||||
FirstPassGroupingCollector<?> firstPassGroupingCollector,
|
||||
String groupField,
|
||||
Collection<SearchGroup<BytesRef>> searchGroups,
|
||||
Sort groupSort,
|
||||
Sort sortWithinGroup,
|
||||
int maxDocsPerGroup,
|
||||
boolean getMaxScores) throws IOException {
|
||||
if (firstPassGroupingCollector.getGroupSelector().getClass().isAssignableFrom(TermGroupSelector.class)) {
|
||||
GroupSelector<BytesRef> selector = (GroupSelector<BytesRef>) firstPassGroupingCollector.getGroupSelector();
|
||||
return new TopGroupsCollector<>(selector, searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getMaxScores);
|
||||
boolean getMaxScores)
|
||||
throws IOException {
|
||||
if (firstPassGroupingCollector
|
||||
.getGroupSelector()
|
||||
.getClass()
|
||||
.isAssignableFrom(TermGroupSelector.class)) {
|
||||
GroupSelector<BytesRef> selector =
|
||||
(GroupSelector<BytesRef>) firstPassGroupingCollector.getGroupSelector();
|
||||
return new TopGroupsCollector<>(
|
||||
selector, searchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getMaxScores);
|
||||
} else {
|
||||
ValueSource vs = new BytesRefFieldSource(groupField);
|
||||
List<SearchGroup<MutableValue>> mvalSearchGroups = new ArrayList<>(searchGroups.size());
|
||||
|
@ -253,12 +280,13 @@ public class TestGrouping extends LuceneTestCase {
|
|||
mvalSearchGroups.add(sg);
|
||||
}
|
||||
ValueSourceGroupSelector selector = new ValueSourceGroupSelector(vs, new HashMap<>());
|
||||
return new TopGroupsCollector<>(selector, mvalSearchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getMaxScores);
|
||||
return new TopGroupsCollector<>(
|
||||
selector, mvalSearchGroups, groupSort, sortWithinGroup, maxDocsPerGroup, getMaxScores);
|
||||
}
|
||||
}
|
||||
|
||||
private AllGroupsCollector<?> createAllGroupsCollector(FirstPassGroupingCollector<?> firstPassGroupingCollector,
|
||||
String groupField) {
|
||||
private AllGroupsCollector<?> createAllGroupsCollector(
|
||||
FirstPassGroupingCollector<?> firstPassGroupingCollector, String groupField) {
|
||||
return new AllGroupsCollector<>(firstPassGroupingCollector.getGroupSelector());
|
||||
}
|
||||
|
||||
|
@ -285,15 +313,18 @@ public class TestGrouping extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private Collection<SearchGroup<BytesRef>> getSearchGroups(FirstPassGroupingCollector<?> c, int groupOffset) throws IOException {
|
||||
private Collection<SearchGroup<BytesRef>> getSearchGroups(
|
||||
FirstPassGroupingCollector<?> c, int groupOffset) throws IOException {
|
||||
if (TermGroupSelector.class.isAssignableFrom(c.getGroupSelector().getClass())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
FirstPassGroupingCollector<BytesRef> collector = (FirstPassGroupingCollector<BytesRef>) c;
|
||||
return collector.getTopGroups(groupOffset);
|
||||
} else if (ValueSourceGroupSelector.class.isAssignableFrom(c.getGroupSelector().getClass())) {
|
||||
@SuppressWarnings("unchecked")
|
||||
FirstPassGroupingCollector<MutableValue> collector = (FirstPassGroupingCollector<MutableValue>) c;
|
||||
Collection<SearchGroup<MutableValue>> mutableValueGroups = collector.getTopGroups(groupOffset);
|
||||
FirstPassGroupingCollector<MutableValue> collector =
|
||||
(FirstPassGroupingCollector<MutableValue>) c;
|
||||
Collection<SearchGroup<MutableValue>> mutableValueGroups =
|
||||
collector.getTopGroups(groupOffset);
|
||||
if (mutableValueGroups == null) {
|
||||
return null;
|
||||
}
|
||||
|
@ -301,7 +332,10 @@ public class TestGrouping extends LuceneTestCase {
|
|||
List<SearchGroup<BytesRef>> groups = new ArrayList<>(mutableValueGroups.size());
|
||||
for (SearchGroup<MutableValue> mutableValueGroup : mutableValueGroups) {
|
||||
SearchGroup<BytesRef> sg = new SearchGroup<>();
|
||||
sg.groupValue = mutableValueGroup.groupValue.exists() ? ((MutableValueStr) mutableValueGroup.groupValue).value.get() : null;
|
||||
sg.groupValue =
|
||||
mutableValueGroup.groupValue.exists()
|
||||
? ((MutableValueStr) mutableValueGroup.groupValue).value.get()
|
||||
: null;
|
||||
sg.sortValues = mutableValueGroup.sortValues;
|
||||
groups.add(sg);
|
||||
}
|
||||
|
@ -321,12 +355,27 @@ public class TestGrouping extends LuceneTestCase {
|
|||
TopGroups<MutableValue> mvalTopGroups = collector.getTopGroups(withinGroupOffset);
|
||||
List<GroupDocs<BytesRef>> groups = new ArrayList<>(mvalTopGroups.groups.length);
|
||||
for (GroupDocs<MutableValue> mvalGd : mvalTopGroups.groups) {
|
||||
BytesRef groupValue = mvalGd.groupValue.exists() ? ((MutableValueStr) mvalGd.groupValue).value.get() : null;
|
||||
groups.add(new GroupDocs<>(Float.NaN, mvalGd.maxScore, mvalGd.totalHits, mvalGd.scoreDocs, groupValue, mvalGd.groupSortValues));
|
||||
BytesRef groupValue =
|
||||
mvalGd.groupValue.exists() ? ((MutableValueStr) mvalGd.groupValue).value.get() : null;
|
||||
groups.add(
|
||||
new GroupDocs<>(
|
||||
Float.NaN,
|
||||
mvalGd.maxScore,
|
||||
mvalGd.totalHits,
|
||||
mvalGd.scoreDocs,
|
||||
groupValue,
|
||||
mvalGd.groupSortValues));
|
||||
}
|
||||
// NOTE: currenlty using diamond operator on MergedIterator (without explicit Term class) causes
|
||||
// NOTE: currenlty using diamond operator on MergedIterator (without explicit Term class)
|
||||
// causes
|
||||
// errors on Eclipse Compiler (ecj) used for javadoc lint
|
||||
return new TopGroups<BytesRef>(mvalTopGroups.groupSort, mvalTopGroups.withinGroupSort, mvalTopGroups.totalHitCount, mvalTopGroups.totalGroupedHitCount, groups.toArray(new GroupDocs[groups.size()]), Float.NaN);
|
||||
return new TopGroups<BytesRef>(
|
||||
mvalTopGroups.groupSort,
|
||||
mvalTopGroups.withinGroupSort,
|
||||
mvalTopGroups.totalHitCount,
|
||||
mvalTopGroups.totalGroupedHitCount,
|
||||
groups.toArray(new GroupDocs[groups.size()]),
|
||||
Float.NaN);
|
||||
}
|
||||
fail();
|
||||
return null;
|
||||
|
@ -436,7 +485,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private TopGroups<BytesRef> slowGrouping(GroupDoc[] groupDocs,
|
||||
private TopGroups<BytesRef> slowGrouping(
|
||||
GroupDoc[] groupDocs,
|
||||
String searchTerm,
|
||||
boolean getMaxScores,
|
||||
boolean doAllGroups,
|
||||
|
@ -514,7 +564,9 @@ public class TestGrouping extends LuceneTestCase {
|
|||
hits = new ScoreDoc[0];
|
||||
}
|
||||
|
||||
result[idx-groupOffset] = new GroupDocs<>(Float.NaN,
|
||||
result[idx - groupOffset] =
|
||||
new GroupDocs<>(
|
||||
Float.NaN,
|
||||
0.0f,
|
||||
new TotalHits(docs.size(), TotalHits.Relation.EQUAL_TO),
|
||||
hits,
|
||||
|
@ -524,15 +576,27 @@ public class TestGrouping extends LuceneTestCase {
|
|||
|
||||
if (doAllGroups) {
|
||||
return new TopGroups<>(
|
||||
new TopGroups<>(groupSort.getSort(), docSort.getSort(), totalHitCount, totalGroupedHitCount, result, Float.NaN),
|
||||
knownGroups.size()
|
||||
);
|
||||
new TopGroups<>(
|
||||
groupSort.getSort(),
|
||||
docSort.getSort(),
|
||||
totalHitCount,
|
||||
totalGroupedHitCount,
|
||||
result,
|
||||
Float.NaN),
|
||||
knownGroups.size());
|
||||
} else {
|
||||
return new TopGroups<>(groupSort.getSort(), docSort.getSort(), totalHitCount, totalGroupedHitCount, result, Float.NaN);
|
||||
return new TopGroups<>(
|
||||
groupSort.getSort(),
|
||||
docSort.getSort(),
|
||||
totalHitCount,
|
||||
totalGroupedHitCount,
|
||||
result,
|
||||
Float.NaN);
|
||||
}
|
||||
}
|
||||
|
||||
private DirectoryReader getDocBlockReader(Directory dir, GroupDoc[] groupDocs) throws IOException {
|
||||
private DirectoryReader getDocBlockReader(Directory dir, GroupDoc[] groupDocs)
|
||||
throws IOException {
|
||||
// Coalesce by group, but in random order:
|
||||
Collections.shuffle(Arrays.asList(groupDocs), random());
|
||||
final Map<BytesRef, List<GroupDoc>> groupMap = new HashMap<>();
|
||||
|
@ -546,10 +610,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
groupMap.get(groupDoc.group).add(groupDoc);
|
||||
}
|
||||
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())));
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(random(), dir, newIndexWriterConfig(new MockAnalyzer(random())));
|
||||
|
||||
final List<List<Document>> updateDocs = new ArrayList<>();
|
||||
|
||||
|
@ -574,7 +636,9 @@ public class TestGrouping extends LuceneTestCase {
|
|||
doc.add(new SortedDocValuesField("sort2", BytesRef.deepCopyOf(groupValue.sort2)));
|
||||
doc.add(new NumericDocValuesField("id", groupValue.id));
|
||||
doc.add(newTextField("content", groupValue.content, Field.Store.NO));
|
||||
//System.out.println("TEST: doc content=" + groupValue.content + " group=" + (groupValue.group == null ? "null" : groupValue.group.utf8ToString()) + " sort1=" + groupValue.sort1.utf8ToString() + " id=" + groupValue.id);
|
||||
// System.out.println("TEST: doc content=" + groupValue.content + " group=" +
|
||||
// (groupValue.group == null ? "null" : groupValue.group.utf8ToString()) + " sort1=" +
|
||||
// groupValue.sort1.utf8ToString() + " id=" + groupValue.id);
|
||||
}
|
||||
// So we can pull filter marking last doc in block:
|
||||
final Field groupEnd = newField("groupend", "x", groupEndType);
|
||||
|
@ -638,7 +702,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
for (int i = 0; i < numGroups; i++) {
|
||||
String randomValue;
|
||||
do {
|
||||
// B/c of DV based impl we can't see the difference between an empty string and a null value.
|
||||
// B/c of DV based impl we can't see the difference between an empty string and a null
|
||||
// value.
|
||||
// For that reason we don't generate empty string
|
||||
// groups.
|
||||
randomValue = TestUtil.randomRealisticUnicodeString(random());
|
||||
|
@ -665,10 +730,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
}
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())));
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(random(), dir, newIndexWriterConfig(new MockAnalyzer(random())));
|
||||
Document doc = new Document();
|
||||
Document docNoGroup = new Document();
|
||||
Field idvGroupField = new SortedDocValuesField("group", new BytesRef());
|
||||
|
@ -699,13 +762,25 @@ public class TestGrouping extends LuceneTestCase {
|
|||
} else {
|
||||
groupValue = groups.get(random().nextInt(groups.size()));
|
||||
}
|
||||
final GroupDoc groupDoc = new GroupDoc(i,
|
||||
final GroupDoc groupDoc =
|
||||
new GroupDoc(
|
||||
i,
|
||||
groupValue,
|
||||
groups.get(random().nextInt(groups.size())),
|
||||
groups.get(random().nextInt(groups.size())),
|
||||
contentStrings[random().nextInt(contentStrings.length)]);
|
||||
if (VERBOSE) {
|
||||
System.out.println(" doc content=" + groupDoc.content + " id=" + i + " group=" + (groupDoc.group == null ? "null" : groupDoc.group.utf8ToString()) + " sort1=" + groupDoc.sort1.utf8ToString() + " sort2=" + groupDoc.sort2.utf8ToString());
|
||||
System.out.println(
|
||||
" doc content="
|
||||
+ groupDoc.content
|
||||
+ " id="
|
||||
+ i
|
||||
+ " group="
|
||||
+ (groupDoc.group == null ? "null" : groupDoc.group.utf8ToString())
|
||||
+ " sort1="
|
||||
+ groupDoc.sort1.utf8ToString()
|
||||
+ " sort2="
|
||||
+ groupDoc.sort2.utf8ToString());
|
||||
}
|
||||
|
||||
groupDocs[i] = groupDoc;
|
||||
|
@ -757,7 +832,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
|
||||
Set<Integer> seenIDs = new HashSet<>();
|
||||
for (int contentID = 0; contentID < 3; contentID++) {
|
||||
final ScoreDoc[] hits = s.search(new TermQuery(new Term("content", "real"+contentID)), numDocs).scoreDocs;
|
||||
final ScoreDoc[] hits =
|
||||
s.search(new TermQuery(new Term("content", "real" + contentID)), numDocs).scoreDocs;
|
||||
for (ScoreDoc hit : hits) {
|
||||
int idValue = docIDToID[hit.doc];
|
||||
|
||||
|
@ -809,15 +885,19 @@ public class TestGrouping extends LuceneTestCase {
|
|||
// System.out.println(" term=real" + contentID);
|
||||
final Map<Float, Float> termScoreMap = new HashMap<>();
|
||||
scoreMap.put("real" + contentID, termScoreMap);
|
||||
//System.out.println("term=real" + contentID + " dfold=" + s.docFreq(new Term("content", "real"+contentID)) +
|
||||
// System.out.println("term=real" + contentID + " dfold=" + s.docFreq(new Term("content",
|
||||
// "real"+contentID)) +
|
||||
// " dfnew=" + sBlocks.docFreq(new Term("content", "real"+contentID)));
|
||||
final ScoreDoc[] hits = sBlocks.search(new TermQuery(new Term("content", "real"+contentID)), numDocs).scoreDocs;
|
||||
final ScoreDoc[] hits =
|
||||
sBlocks.search(new TermQuery(new Term("content", "real" + contentID)), numDocs)
|
||||
.scoreDocs;
|
||||
for (ScoreDoc hit : hits) {
|
||||
final GroupDoc gd = groupDocsByID[docIDToIDBlocks[hit.doc]];
|
||||
assertTrue(gd.score2 == 0.0);
|
||||
gd.score2 = hit.score;
|
||||
assertEquals(gd.id, docIDToIDBlocks[hit.doc]);
|
||||
//System.out.println(" score=" + gd.score + " score2=" + hit.score + " id=" + docIDToIDBlocks[hit.doc]);
|
||||
// System.out.println(" score=" + gd.score + " score2=" + hit.score + " id=" +
|
||||
// docIDToIDBlocks[hit.doc]);
|
||||
termScoreMap.put(gd.score, gd.score2);
|
||||
}
|
||||
}
|
||||
|
@ -831,7 +911,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
final String searchTerm = "real" + random().nextInt(3);
|
||||
final boolean getMaxScores = random().nextBoolean();
|
||||
final Sort groupSort = getRandomSort();
|
||||
//final Sort groupSort = new Sort(new SortField[] {new SortField("sort1", SortField.STRING), new SortField("id", SortField.INT)});
|
||||
// final Sort groupSort = new Sort(new SortField[] {new SortField("sort1",
|
||||
// SortField.STRING), new SortField("id", SortField.INT)});
|
||||
final Sort docSort = getRandomSort();
|
||||
|
||||
final int topNGroups = TestUtil.nextInt(random(), 1, 30);
|
||||
|
@ -847,14 +928,39 @@ public class TestGrouping extends LuceneTestCase {
|
|||
final boolean doCache = random().nextBoolean();
|
||||
final boolean doAllGroups = random().nextBoolean();
|
||||
if (VERBOSE) {
|
||||
System.out.println("TEST: groupSort=" + groupSort + " docSort=" + docSort + " searchTerm=" + searchTerm + " dF=" + r.docFreq(new Term("content", searchTerm)) +" dFBlock=" + rBlocks.docFreq(new Term("content", searchTerm)) + " topNGroups=" + topNGroups + " groupOffset=" + groupOffset + " docOffset=" + docOffset + " doCache=" + doCache + " docsPerGroup=" + docsPerGroup + " doAllGroups=" + doAllGroups + " getMaxScores=" + getMaxScores);
|
||||
System.out.println(
|
||||
"TEST: groupSort="
|
||||
+ groupSort
|
||||
+ " docSort="
|
||||
+ docSort
|
||||
+ " searchTerm="
|
||||
+ searchTerm
|
||||
+ " dF="
|
||||
+ r.docFreq(new Term("content", searchTerm))
|
||||
+ " dFBlock="
|
||||
+ rBlocks.docFreq(new Term("content", searchTerm))
|
||||
+ " topNGroups="
|
||||
+ topNGroups
|
||||
+ " groupOffset="
|
||||
+ groupOffset
|
||||
+ " docOffset="
|
||||
+ docOffset
|
||||
+ " doCache="
|
||||
+ doCache
|
||||
+ " docsPerGroup="
|
||||
+ docsPerGroup
|
||||
+ " doAllGroups="
|
||||
+ doAllGroups
|
||||
+ " getMaxScores="
|
||||
+ getMaxScores);
|
||||
}
|
||||
|
||||
String groupField = "group";
|
||||
if (VERBOSE) {
|
||||
System.out.println(" groupField=" + groupField);
|
||||
}
|
||||
final FirstPassGroupingCollector<?> c1 = createRandomFirstPassCollector(groupField, groupSort, groupOffset+topNGroups);
|
||||
final FirstPassGroupingCollector<?> c1 =
|
||||
createRandomFirstPassCollector(groupField, groupSort, groupOffset + topNGroups);
|
||||
final CachingCollector cCache;
|
||||
final Collector c;
|
||||
|
||||
|
@ -924,26 +1030,50 @@ public class TestGrouping extends LuceneTestCase {
|
|||
System.out.println(" null");
|
||||
} else {
|
||||
for (SearchGroup<BytesRef> searchGroup : topGroups) {
|
||||
System.out.println(" " + (searchGroup.groupValue == null ? "null" : searchGroup.groupValue) + ": " + Arrays.deepToString(searchGroup.sortValues));
|
||||
System.out.println(
|
||||
" "
|
||||
+ (searchGroup.groupValue == null ? "null" : searchGroup.groupValue)
|
||||
+ ": "
|
||||
+ Arrays.deepToString(searchGroup.sortValues));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get 1st pass top groups using shards
|
||||
|
||||
final TopGroups<BytesRef> topGroupsShards = searchShards(s, shards.subSearchers, query, groupSort, docSort,
|
||||
groupOffset, topNGroups, docOffset, docsPerGroup, getMaxScores, true, true);
|
||||
final TopGroups<BytesRef> topGroupsShards =
|
||||
searchShards(
|
||||
s,
|
||||
shards.subSearchers,
|
||||
query,
|
||||
groupSort,
|
||||
docSort,
|
||||
groupOffset,
|
||||
topNGroups,
|
||||
docOffset,
|
||||
docsPerGroup,
|
||||
getMaxScores,
|
||||
true,
|
||||
true);
|
||||
final TopGroupsCollector<?> c2;
|
||||
if (topGroups != null) {
|
||||
|
||||
if (VERBOSE) {
|
||||
System.out.println("TEST: topGroups");
|
||||
for (SearchGroup<BytesRef> searchGroup : topGroups) {
|
||||
System.out.println(" " + (searchGroup.groupValue == null ? "null" : searchGroup.groupValue.utf8ToString()) + ": " + Arrays.deepToString(searchGroup.sortValues));
|
||||
System.out.println(
|
||||
" "
|
||||
+ (searchGroup.groupValue == null
|
||||
? "null"
|
||||
: searchGroup.groupValue.utf8ToString())
|
||||
+ ": "
|
||||
+ Arrays.deepToString(searchGroup.sortValues));
|
||||
}
|
||||
}
|
||||
|
||||
c2 = createSecondPassCollector(c1, groupSort, docSort, groupOffset, docOffset + docsPerGroup, getMaxScores);
|
||||
c2 =
|
||||
createSecondPassCollector(
|
||||
c1, groupSort, docSort, groupOffset, docOffset + docsPerGroup, getMaxScores);
|
||||
if (doCache) {
|
||||
if (cCache.isCached()) {
|
||||
if (VERBOSE) {
|
||||
|
@ -974,15 +1104,34 @@ public class TestGrouping extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
final TopGroups<BytesRef> expectedGroups = slowGrouping(groupDocs, searchTerm, getMaxScores, doAllGroups, groupSort, docSort, topNGroups, docsPerGroup, groupOffset, docOffset);
|
||||
final TopGroups<BytesRef> expectedGroups =
|
||||
slowGrouping(
|
||||
groupDocs,
|
||||
searchTerm,
|
||||
getMaxScores,
|
||||
doAllGroups,
|
||||
groupSort,
|
||||
docSort,
|
||||
topNGroups,
|
||||
docsPerGroup,
|
||||
groupOffset,
|
||||
docOffset);
|
||||
|
||||
if (VERBOSE) {
|
||||
if (expectedGroups == null) {
|
||||
System.out.println("TEST: no expected groups");
|
||||
} else {
|
||||
System.out.println("TEST: expected groups totalGroupedHitCount=" + expectedGroups.totalGroupedHitCount);
|
||||
System.out.println(
|
||||
"TEST: expected groups totalGroupedHitCount="
|
||||
+ expectedGroups.totalGroupedHitCount);
|
||||
for (GroupDocs<BytesRef> gd : expectedGroups.groups) {
|
||||
System.out.println(" group=" + (gd.groupValue == null ? "null" : gd.groupValue) + " totalHits=" + gd.totalHits.value + " scoreDocs.len=" + gd.scoreDocs.length);
|
||||
System.out.println(
|
||||
" group="
|
||||
+ (gd.groupValue == null ? "null" : gd.groupValue)
|
||||
+ " totalHits="
|
||||
+ gd.totalHits.value
|
||||
+ " scoreDocs.len="
|
||||
+ gd.scoreDocs.length);
|
||||
for (ScoreDoc sd : gd.scoreDocs) {
|
||||
System.out.println(" id=" + sd.doc + " score=" + sd.score);
|
||||
}
|
||||
|
@ -992,9 +1141,14 @@ public class TestGrouping extends LuceneTestCase {
|
|||
if (groupsResult == null) {
|
||||
System.out.println("TEST: no matched groups");
|
||||
} else {
|
||||
System.out.println("TEST: matched groups totalGroupedHitCount=" + groupsResult.totalGroupedHitCount);
|
||||
System.out.println(
|
||||
"TEST: matched groups totalGroupedHitCount=" + groupsResult.totalGroupedHitCount);
|
||||
for (GroupDocs<BytesRef> gd : groupsResult.groups) {
|
||||
System.out.println(" group=" + (gd.groupValue == null ? "null" : gd.groupValue) + " totalHits=" + gd.totalHits.value);
|
||||
System.out.println(
|
||||
" group="
|
||||
+ (gd.groupValue == null ? "null" : gd.groupValue)
|
||||
+ " totalHits="
|
||||
+ gd.totalHits.value);
|
||||
for (ScoreDoc sd : gd.scoreDocs) {
|
||||
System.out.println(" id=" + docIDToID[sd.doc] + " score=" + sd.score);
|
||||
}
|
||||
|
@ -1002,7 +1156,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
|
||||
if (searchIter == 14) {
|
||||
for (int docIDX = 0; docIDX < s.getIndexReader().maxDoc(); docIDX++) {
|
||||
System.out.println("ID=" + docIDToID[docIDX] + " explain=" + s.explain(query, docIDX));
|
||||
System.out.println(
|
||||
"ID=" + docIDToID[docIDX] + " explain=" + s.explain(query, docIDX));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1010,9 +1165,15 @@ public class TestGrouping extends LuceneTestCase {
|
|||
if (topGroupsShards == null) {
|
||||
System.out.println("TEST: no matched-merged groups");
|
||||
} else {
|
||||
System.out.println("TEST: matched-merged groups totalGroupedHitCount=" + topGroupsShards.totalGroupedHitCount);
|
||||
System.out.println(
|
||||
"TEST: matched-merged groups totalGroupedHitCount="
|
||||
+ topGroupsShards.totalGroupedHitCount);
|
||||
for (GroupDocs<BytesRef> gd : topGroupsShards.groups) {
|
||||
System.out.println(" group=" + (gd.groupValue == null ? "null" : gd.groupValue) + " totalHits=" + gd.totalHits.value);
|
||||
System.out.println(
|
||||
" group="
|
||||
+ (gd.groupValue == null ? "null" : gd.groupValue)
|
||||
+ " totalHits="
|
||||
+ gd.totalHits.value);
|
||||
for (ScoreDoc sd : gd.scoreDocs) {
|
||||
System.out.println(" id=" + docIDToID[sd.doc] + " score=" + sd.score);
|
||||
}
|
||||
|
@ -1028,8 +1189,13 @@ public class TestGrouping extends LuceneTestCase {
|
|||
verifyShards(shards.docStarts, topGroupsShards);
|
||||
}
|
||||
|
||||
final BlockGroupingCollector c3 = new BlockGroupingCollector(groupSort, groupOffset+topNGroups,
|
||||
groupSort.needsScores() || docSort.needsScores(), sBlocks.createWeight(sBlocks.rewrite(lastDocInBlock), ScoreMode.COMPLETE_NO_SCORES, 1));
|
||||
final BlockGroupingCollector c3 =
|
||||
new BlockGroupingCollector(
|
||||
groupSort,
|
||||
groupOffset + topNGroups,
|
||||
groupSort.needsScores() || docSort.needsScores(),
|
||||
sBlocks.createWeight(
|
||||
sBlocks.rewrite(lastDocInBlock), ScoreMode.COMPLETE_NO_SCORES, 1));
|
||||
final AllGroupsCollector<BytesRef> allGroupsCollector2;
|
||||
final Collector c4;
|
||||
if (doAllGroups) {
|
||||
|
@ -1045,11 +1211,15 @@ public class TestGrouping extends LuceneTestCase {
|
|||
// Get block grouping result:
|
||||
sBlocks.search(query, c4);
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
final TopGroups<BytesRef> tempTopGroupsBlocks = (TopGroups<BytesRef>) c3.getTopGroups(docSort, groupOffset, docOffset, docOffset+docsPerGroup);
|
||||
final TopGroups<BytesRef> tempTopGroupsBlocks =
|
||||
(TopGroups<BytesRef>)
|
||||
c3.getTopGroups(docSort, groupOffset, docOffset, docOffset + docsPerGroup);
|
||||
final TopGroups<BytesRef> groupsResultBlocks;
|
||||
if (doAllGroups && tempTopGroupsBlocks != null) {
|
||||
assertEquals((int) tempTopGroupsBlocks.totalGroupCount, allGroupsCollector2.getGroupCount());
|
||||
groupsResultBlocks = new TopGroups<>(tempTopGroupsBlocks, allGroupsCollector2.getGroupCount());
|
||||
assertEquals(
|
||||
(int) tempTopGroupsBlocks.totalGroupCount, allGroupsCollector2.getGroupCount());
|
||||
groupsResultBlocks =
|
||||
new TopGroups<>(tempTopGroupsBlocks, allGroupsCollector2.getGroupCount());
|
||||
} else {
|
||||
groupsResultBlocks = tempTopGroupsBlocks;
|
||||
}
|
||||
|
@ -1058,10 +1228,16 @@ public class TestGrouping extends LuceneTestCase {
|
|||
if (groupsResultBlocks == null) {
|
||||
System.out.println("TEST: no block groups");
|
||||
} else {
|
||||
System.out.println("TEST: block groups totalGroupedHitCount=" + groupsResultBlocks.totalGroupedHitCount);
|
||||
System.out.println(
|
||||
"TEST: block groups totalGroupedHitCount="
|
||||
+ groupsResultBlocks.totalGroupedHitCount);
|
||||
boolean first = true;
|
||||
for (GroupDocs<BytesRef> gd : groupsResultBlocks.groups) {
|
||||
System.out.println(" group=" + (gd.groupValue == null ? "null" : gd.groupValue.utf8ToString()) + " totalHits=" + gd.totalHits.value);
|
||||
System.out.println(
|
||||
" group="
|
||||
+ (gd.groupValue == null ? "null" : gd.groupValue.utf8ToString())
|
||||
+ " totalHits="
|
||||
+ gd.totalHits.value);
|
||||
for (ScoreDoc sd : gd.scoreDocs) {
|
||||
System.out.println(" id=" + docIDToIDBlocks[sd.doc] + " score=" + sd.score);
|
||||
if (first) {
|
||||
|
@ -1074,8 +1250,20 @@ public class TestGrouping extends LuceneTestCase {
|
|||
}
|
||||
|
||||
// Get shard'd block grouping result:
|
||||
final TopGroups<BytesRef> topGroupsBlockShards = searchShards(sBlocks, shardsBlocks.subSearchers, query,
|
||||
groupSort, docSort, groupOffset, topNGroups, docOffset, docsPerGroup, getMaxScores, false, false);
|
||||
final TopGroups<BytesRef> topGroupsBlockShards =
|
||||
searchShards(
|
||||
sBlocks,
|
||||
shardsBlocks.subSearchers,
|
||||
query,
|
||||
groupSort,
|
||||
docSort,
|
||||
groupOffset,
|
||||
topNGroups,
|
||||
docOffset,
|
||||
docsPerGroup,
|
||||
getMaxScores,
|
||||
false,
|
||||
false);
|
||||
|
||||
if (expectedGroups != null) {
|
||||
// Fixup scores for reader2
|
||||
|
@ -1083,7 +1271,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
for (ScoreDoc hit : groupDocsHits.scoreDocs) {
|
||||
final GroupDoc gd = groupDocsByID[hit.doc];
|
||||
assertEquals(gd.id, hit.doc);
|
||||
//System.out.println("fixup score " + hit.score + " to " + gd.score2 + " vs " + gd.score);
|
||||
// System.out.println("fixup score " + hit.score + " to " + gd.score2 + " vs " +
|
||||
// gd.score);
|
||||
hit.score = gd.score2;
|
||||
}
|
||||
}
|
||||
|
@ -1094,8 +1283,10 @@ public class TestGrouping extends LuceneTestCase {
|
|||
if (sortFields[groupSortIDX].getType() == SortField.Type.SCORE) {
|
||||
for (GroupDocs<?> groupDocsHits : expectedGroups.groups) {
|
||||
if (groupDocsHits.groupSortValues != null) {
|
||||
//System.out.println("remap " + groupDocsHits.groupSortValues[groupSortIDX] + " to " + termScoreMap.get(groupDocsHits.groupSortValues[groupSortIDX]));
|
||||
groupDocsHits.groupSortValues[groupSortIDX] = termScoreMap.get(groupDocsHits.groupSortValues[groupSortIDX]);
|
||||
// System.out.println("remap " + groupDocsHits.groupSortValues[groupSortIDX] + "
|
||||
// to " + termScoreMap.get(groupDocsHits.groupSortValues[groupSortIDX]));
|
||||
groupDocsHits.groupSortValues[groupSortIDX] =
|
||||
termScoreMap.get(groupDocsHits.groupSortValues[groupSortIDX]);
|
||||
assertNotNull(groupDocsHits.groupSortValues[groupSortIDX]);
|
||||
}
|
||||
}
|
||||
|
@ -1134,23 +1325,49 @@ public class TestGrouping extends LuceneTestCase {
|
|||
for (GroupDocs<?> group : topGroups.groups) {
|
||||
for (int hitIDX = 0; hitIDX < group.scoreDocs.length; hitIDX++) {
|
||||
final ScoreDoc sd = group.scoreDocs[hitIDX];
|
||||
assertEquals("doc=" + sd.doc + " wrong shard",
|
||||
assertEquals(
|
||||
"doc=" + sd.doc + " wrong shard",
|
||||
ReaderUtil.subIndex(sd.doc, docStarts),
|
||||
sd.shardIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TopGroups<BytesRef> searchShards(IndexSearcher topSearcher, ShardSearcher[] subSearchers, Query query, Sort groupSort, Sort docSort, int groupOffset, int topNGroups, int docOffset,
|
||||
int topNDocs, boolean getMaxScores, boolean canUseIDV, boolean preFlex) throws Exception {
|
||||
private TopGroups<BytesRef> searchShards(
|
||||
IndexSearcher topSearcher,
|
||||
ShardSearcher[] subSearchers,
|
||||
Query query,
|
||||
Sort groupSort,
|
||||
Sort docSort,
|
||||
int groupOffset,
|
||||
int topNGroups,
|
||||
int docOffset,
|
||||
int topNDocs,
|
||||
boolean getMaxScores,
|
||||
boolean canUseIDV,
|
||||
boolean preFlex)
|
||||
throws Exception {
|
||||
|
||||
// TODO: swap in caching, all groups collector hereassertEquals(expected.totalHitCount, actual.totalHitCount);
|
||||
// TODO: swap in caching, all groups collector hereassertEquals(expected.totalHitCount,
|
||||
// actual.totalHitCount);
|
||||
// too...
|
||||
if (VERBOSE) {
|
||||
System.out.println("TEST: " + subSearchers.length + " shards: " + Arrays.toString(subSearchers) + " canUseIDV=" + canUseIDV);
|
||||
System.out.println(
|
||||
"TEST: "
|
||||
+ subSearchers.length
|
||||
+ " shards: "
|
||||
+ Arrays.toString(subSearchers)
|
||||
+ " canUseIDV="
|
||||
+ canUseIDV);
|
||||
}
|
||||
// Run 1st pass collector to get top groups per shard
|
||||
final Weight w = topSearcher.createWeight(topSearcher.rewrite(query), groupSort.needsScores() || docSort.needsScores() || getMaxScores ? ScoreMode.COMPLETE : ScoreMode.COMPLETE_NO_SCORES, 1);
|
||||
final Weight w =
|
||||
topSearcher.createWeight(
|
||||
topSearcher.rewrite(query),
|
||||
groupSort.needsScores() || docSort.needsScores() || getMaxScores
|
||||
? ScoreMode.COMPLETE
|
||||
: ScoreMode.COMPLETE_NO_SCORES,
|
||||
1);
|
||||
final List<Collection<SearchGroup<BytesRef>>> shardGroups = new ArrayList<>();
|
||||
List<FirstPassGroupingCollector<?>> firstPassGroupingCollectors = new ArrayList<>();
|
||||
FirstPassGroupingCollector<?> firstPassCollector = null;
|
||||
|
@ -1163,9 +1380,12 @@ public class TestGrouping extends LuceneTestCase {
|
|||
// First shard determines whether we use IDV or not;
|
||||
// all other shards match that:
|
||||
if (firstPassCollector == null) {
|
||||
firstPassCollector = createRandomFirstPassCollector(groupField, groupSort, groupOffset + topNGroups);
|
||||
firstPassCollector =
|
||||
createRandomFirstPassCollector(groupField, groupSort, groupOffset + topNGroups);
|
||||
} else {
|
||||
firstPassCollector = createFirstPassCollector(groupField, groupSort, groupOffset + topNGroups, firstPassCollector);
|
||||
firstPassCollector =
|
||||
createFirstPassCollector(
|
||||
groupField, groupSort, groupOffset + topNGroups, firstPassCollector);
|
||||
}
|
||||
if (VERBOSE) {
|
||||
System.out.println(" shard=" + shardIDX + " groupField=" + groupField);
|
||||
|
@ -1176,16 +1396,29 @@ public class TestGrouping extends LuceneTestCase {
|
|||
final Collection<SearchGroup<BytesRef>> topGroups = getSearchGroups(firstPassCollector, 0);
|
||||
if (topGroups != null) {
|
||||
if (VERBOSE) {
|
||||
System.out.println(" shard " + shardIDX + " s=" + subSearchers[shardIDX] + " totalGroupedHitCount=?" + " " + topGroups.size() + " groups:");
|
||||
System.out.println(
|
||||
" shard "
|
||||
+ shardIDX
|
||||
+ " s="
|
||||
+ subSearchers[shardIDX]
|
||||
+ " totalGroupedHitCount=?"
|
||||
+ " "
|
||||
+ topGroups.size()
|
||||
+ " groups:");
|
||||
for (SearchGroup<BytesRef> group : topGroups) {
|
||||
System.out.println(" " + groupToString(group.groupValue) + " groupSort=" + Arrays.toString(group.sortValues));
|
||||
System.out.println(
|
||||
" "
|
||||
+ groupToString(group.groupValue)
|
||||
+ " groupSort="
|
||||
+ Arrays.toString(group.sortValues));
|
||||
}
|
||||
}
|
||||
shardGroups.add(topGroups);
|
||||
}
|
||||
}
|
||||
|
||||
final Collection<SearchGroup<BytesRef>> mergedTopGroups = SearchGroup.merge(shardGroups, groupOffset, topNGroups, groupSort);
|
||||
final Collection<SearchGroup<BytesRef>> mergedTopGroups =
|
||||
SearchGroup.merge(shardGroups, groupOffset, topNGroups, groupSort);
|
||||
if (VERBOSE) {
|
||||
System.out.println(" top groups merged:");
|
||||
if (mergedTopGroups == null) {
|
||||
|
@ -1193,7 +1426,11 @@ public class TestGrouping extends LuceneTestCase {
|
|||
} else {
|
||||
System.out.println(" " + mergedTopGroups.size() + " top groups:");
|
||||
for (SearchGroup<BytesRef> group : mergedTopGroups) {
|
||||
System.out.println(" [" + groupToString(group.groupValue) + "] groupSort=" + Arrays.toString(group.sortValues));
|
||||
System.out.println(
|
||||
" ["
|
||||
+ groupToString(group.groupValue)
|
||||
+ "] groupSort="
|
||||
+ Arrays.toString(group.sortValues));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1203,23 +1440,50 @@ public class TestGrouping extends LuceneTestCase {
|
|||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
final TopGroups<BytesRef>[] shardTopGroups = new TopGroups[subSearchers.length];
|
||||
for (int shardIDX = 0; shardIDX < subSearchers.length; shardIDX++) {
|
||||
final TopGroupsCollector<?> secondPassCollector = createSecondPassCollector(firstPassGroupingCollectors.get(shardIDX),
|
||||
groupField, mergedTopGroups, groupSort, docSort, docOffset + topNDocs, getMaxScores);
|
||||
final TopGroupsCollector<?> secondPassCollector =
|
||||
createSecondPassCollector(
|
||||
firstPassGroupingCollectors.get(shardIDX),
|
||||
groupField,
|
||||
mergedTopGroups,
|
||||
groupSort,
|
||||
docSort,
|
||||
docOffset + topNDocs,
|
||||
getMaxScores);
|
||||
subSearchers[shardIDX].search(w, secondPassCollector);
|
||||
shardTopGroups[shardIDX] = getTopGroups(secondPassCollector, 0);
|
||||
if (VERBOSE) {
|
||||
System.out.println(" " + shardTopGroups[shardIDX].groups.length + " shard[" + shardIDX + "] groups:");
|
||||
System.out.println(
|
||||
" " + shardTopGroups[shardIDX].groups.length + " shard[" + shardIDX + "] groups:");
|
||||
for (GroupDocs<BytesRef> group : shardTopGroups[shardIDX].groups) {
|
||||
System.out.println(" [" + groupToString(group.groupValue) + "] groupSort=" + Arrays.toString(group.groupSortValues) + " numDocs=" + group.scoreDocs.length);
|
||||
System.out.println(
|
||||
" ["
|
||||
+ groupToString(group.groupValue)
|
||||
+ "] groupSort="
|
||||
+ Arrays.toString(group.groupSortValues)
|
||||
+ " numDocs="
|
||||
+ group.scoreDocs.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TopGroups<BytesRef> mergedGroups = TopGroups.merge(shardTopGroups, groupSort, docSort, docOffset, topNDocs, TopGroups.ScoreMergeMode.None);
|
||||
TopGroups<BytesRef> mergedGroups =
|
||||
TopGroups.merge(
|
||||
shardTopGroups,
|
||||
groupSort,
|
||||
docSort,
|
||||
docOffset,
|
||||
topNDocs,
|
||||
TopGroups.ScoreMergeMode.None);
|
||||
if (VERBOSE) {
|
||||
System.out.println(" " + mergedGroups.groups.length + " merged groups:");
|
||||
for (GroupDocs<BytesRef> group : mergedGroups.groups) {
|
||||
System.out.println(" [" + groupToString(group.groupValue) + "] groupSort=" + Arrays.toString(group.groupSortValues) + " numDocs=" + group.scoreDocs.length);
|
||||
System.out.println(
|
||||
" ["
|
||||
+ groupToString(group.groupValue)
|
||||
+ "] groupSort="
|
||||
+ Arrays.toString(group.groupSortValues)
|
||||
+ " numDocs="
|
||||
+ group.scoreDocs.length);
|
||||
}
|
||||
}
|
||||
return mergedGroups;
|
||||
|
@ -1228,18 +1492,36 @@ public class TestGrouping extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private void assertEquals(int[] docIDtoID, TopGroups<BytesRef> expected, TopGroups<BytesRef> actual, boolean verifyGroupValues, boolean verifyTotalGroupCount, boolean idvBasedImplsUsed) {
|
||||
private void assertEquals(
|
||||
int[] docIDtoID,
|
||||
TopGroups<BytesRef> expected,
|
||||
TopGroups<BytesRef> actual,
|
||||
boolean verifyGroupValues,
|
||||
boolean verifyTotalGroupCount,
|
||||
boolean idvBasedImplsUsed) {
|
||||
if (expected == null) {
|
||||
assertNull(actual);
|
||||
return;
|
||||
}
|
||||
assertNotNull(actual);
|
||||
|
||||
assertEquals("expected.groups.length != actual.groups.length", expected.groups.length, actual.groups.length);
|
||||
assertEquals("expected.totalHitCount != actual.totalHitCount", expected.totalHitCount, actual.totalHitCount);
|
||||
assertEquals("expected.totalGroupedHitCount != actual.totalGroupedHitCount", expected.totalGroupedHitCount, actual.totalGroupedHitCount);
|
||||
assertEquals(
|
||||
"expected.groups.length != actual.groups.length",
|
||||
expected.groups.length,
|
||||
actual.groups.length);
|
||||
assertEquals(
|
||||
"expected.totalHitCount != actual.totalHitCount",
|
||||
expected.totalHitCount,
|
||||
actual.totalHitCount);
|
||||
assertEquals(
|
||||
"expected.totalGroupedHitCount != actual.totalGroupedHitCount",
|
||||
expected.totalGroupedHitCount,
|
||||
actual.totalGroupedHitCount);
|
||||
if (expected.totalGroupCount != null && verifyTotalGroupCount) {
|
||||
assertEquals("expected.totalGroupCount != actual.totalGroupCount", expected.totalGroupCount, actual.totalGroupCount);
|
||||
assertEquals(
|
||||
"expected.totalGroupCount != actual.totalGroupCount",
|
||||
expected.totalGroupCount,
|
||||
actual.totalGroupCount);
|
||||
}
|
||||
|
||||
for (int groupIDX = 0; groupIDX < expected.groups.length; groupIDX++) {
|
||||
|
@ -1258,7 +1540,6 @@ public class TestGrouping extends LuceneTestCase {
|
|||
} else {
|
||||
assertEquals(expectedGroup.groupValue, actualGroup.groupValue);
|
||||
}
|
||||
|
||||
}
|
||||
assertArrayEquals(expectedGroup.groupSortValues, actualGroup.groupSortValues);
|
||||
|
||||
|
@ -1273,7 +1554,8 @@ public class TestGrouping extends LuceneTestCase {
|
|||
for (int docIDX = 0; docIDX < expectedFDs.length; docIDX++) {
|
||||
final FieldDoc expectedFD = (FieldDoc) expectedFDs[docIDX];
|
||||
final FieldDoc actualFD = (FieldDoc) actualFDs[docIDX];
|
||||
//System.out.println(" actual doc=" + docIDtoID[actualFD.doc] + " score=" + actualFD.score);
|
||||
// System.out.println(" actual doc=" + docIDtoID[actualFD.doc] + " score=" +
|
||||
// actualFD.score);
|
||||
assertEquals(expectedFD.doc, docIDtoID[actualFD.doc]);
|
||||
assertArrayEquals(expectedFD.fields, actualFD.fields);
|
||||
}
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
*/
|
||||
package org.apache.lucene.search.grouping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import org.apache.lucene.analysis.MockAnalyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
|
@ -37,10 +40,6 @@ import org.apache.lucene.util.BytesRef;
|
|||
import org.apache.lucene.util.LuceneTestCase;
|
||||
import org.apache.lucene.util.mutable.MutableValueStr;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class TestGroupingSearch extends LuceneTestCase {
|
||||
|
||||
// Tests some very basic usages...
|
||||
|
@ -52,7 +51,8 @@ public class TestGroupingSearch extends LuceneTestCase {
|
|||
customType.setStored(true);
|
||||
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
|
||||
|
@ -122,7 +122,8 @@ public class TestGroupingSearch extends LuceneTestCase {
|
|||
Sort groupSort = Sort.RELEVANCE;
|
||||
GroupingSearch groupingSearch = createRandomGroupingSearch(groupField, groupSort, 5, canUseIDV);
|
||||
|
||||
TopGroups<?> groups = groupingSearch.search(indexSearcher, new TermQuery(new Term("content", "random")), 0, 10);
|
||||
TopGroups<?> groups =
|
||||
groupingSearch.search(indexSearcher, new TermQuery(new Term("content", "random")), 0, 10);
|
||||
|
||||
assertEquals(7, groups.totalHitCount);
|
||||
assertEquals(7, groups.totalGroupedHitCount);
|
||||
|
@ -160,7 +161,8 @@ public class TestGroupingSearch extends LuceneTestCase {
|
|||
|
||||
Query lastDocInBlock = new TermQuery(new Term("groupend", "x"));
|
||||
groupingSearch = new GroupingSearch(lastDocInBlock);
|
||||
groups = groupingSearch.search(indexSearcher, new TermQuery(new Term("content", "random")), 0, 10);
|
||||
groups =
|
||||
groupingSearch.search(indexSearcher, new TermQuery(new Term("content", "random")), 0, 10);
|
||||
|
||||
assertEquals(7, groups.totalHitCount);
|
||||
assertEquals(7, groups.totalGroupedHitCount);
|
||||
|
@ -201,7 +203,8 @@ public class TestGroupingSearch extends LuceneTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
private GroupingSearch createRandomGroupingSearch(String groupField, Sort groupSort, int docsInGroup, boolean canUseIDV) {
|
||||
private GroupingSearch createRandomGroupingSearch(
|
||||
String groupField, Sort groupSort, int docsInGroup, boolean canUseIDV) {
|
||||
GroupingSearch groupingSearch;
|
||||
if (random().nextBoolean()) {
|
||||
ValueSource vs = new BytesRefFieldSource(groupField);
|
||||
|
@ -222,7 +225,8 @@ public class TestGroupingSearch extends LuceneTestCase {
|
|||
|
||||
public void testSetAllGroups() throws Exception {
|
||||
Directory dir = newDirectory();
|
||||
RandomIndexWriter w = new RandomIndexWriter(
|
||||
RandomIndexWriter w =
|
||||
new RandomIndexWriter(
|
||||
random(),
|
||||
dir,
|
||||
newIndexWriterConfig(new MockAnalyzer(random())).setMergePolicy(newLogMergePolicy()));
|
||||
|
|
|
@ -33,7 +33,5 @@ public class TestLongRangeFactory extends LuceneTestCase {
|
|||
assertEquals(new LongRange(30, 40), factory.getRange(35, scratch));
|
||||
assertEquals(new LongRange(50, Long.MAX_VALUE), factory.getRange(50, scratch));
|
||||
assertEquals(new LongRange(50, Long.MAX_VALUE), factory.getRange(500, scratch));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -42,8 +42,8 @@ public class TestLongRangeGroupSelector extends BaseGroupSelectorTestCase<LongRa
|
|||
|
||||
@Override
|
||||
protected GroupSelector<LongRange> getGroupSelector() {
|
||||
return new LongRangeGroupSelector(LongValuesSource.fromLongField("long"),
|
||||
new LongRangeFactory(100, 100, 900));
|
||||
return new LongRangeGroupSelector(
|
||||
LongValuesSource.fromLongField("long"), new LongRangeFactory(100, 100, 900));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -104,43 +104,74 @@ public class TestTopGroups extends LuceneTestCase {
|
|||
|
||||
final TopGroups<String> shard1TopGroups;
|
||||
{
|
||||
final GroupDocs<String> group1 = haveBlueWhale
|
||||
? createSingletonGroupDocs(blueGroupValue, new Object[] { blueWhaleSize }, 1 /* docId */, blueWhaleScore, 0 /* shardIndex */)
|
||||
final GroupDocs<String> group1 =
|
||||
haveBlueWhale
|
||||
? createSingletonGroupDocs(
|
||||
blueGroupValue,
|
||||
new Object[] {blueWhaleSize},
|
||||
1 /* docId */,
|
||||
blueWhaleScore,
|
||||
0 /* shardIndex */)
|
||||
: createEmptyGroupDocs(blueGroupValue, new Object[] {blueWhaleSize});
|
||||
|
||||
final GroupDocs<String> group2 = haveRedAnt
|
||||
? createSingletonGroupDocs(redGroupValue, new Object[] { redAntSize }, 2 /* docId */, redAntScore, 0 /* shardIndex */)
|
||||
final GroupDocs<String> group2 =
|
||||
haveRedAnt
|
||||
? createSingletonGroupDocs(
|
||||
redGroupValue,
|
||||
new Object[] {redAntSize},
|
||||
2 /* docId */,
|
||||
redAntScore,
|
||||
0 /* shardIndex */)
|
||||
: createEmptyGroupDocs(redGroupValue, new Object[] {redAntSize});
|
||||
|
||||
shard1TopGroups = new TopGroups<String>(
|
||||
shard1TopGroups =
|
||||
new TopGroups<String>(
|
||||
sort.getSort() /* groupSort */,
|
||||
sort.getSort() /* withinGroupSort */,
|
||||
group1.scoreDocs.length + group2.scoreDocs.length /* totalHitCount */,
|
||||
group1.scoreDocs.length + group2.scoreDocs.length /* totalGroupedHitCount */,
|
||||
combineGroupDocs(group1, group2) /* groups */,
|
||||
(haveBlueWhale ? blueWhaleScore : (haveRedAnt ? redAntScore : Float.NaN)) /* maxScore */);
|
||||
(haveBlueWhale
|
||||
? blueWhaleScore
|
||||
: (haveRedAnt ? redAntScore : Float.NaN)) /* maxScore */);
|
||||
}
|
||||
|
||||
final TopGroups<String> shard2TopGroups;
|
||||
{
|
||||
final GroupDocs<String> group1 = haveBlueDragonfly
|
||||
? createSingletonGroupDocs(blueGroupValue, new Object[] { blueDragonflySize }, 3 /* docId */, blueDragonflyScore, 1 /* shardIndex */)
|
||||
final GroupDocs<String> group1 =
|
||||
haveBlueDragonfly
|
||||
? createSingletonGroupDocs(
|
||||
blueGroupValue,
|
||||
new Object[] {blueDragonflySize},
|
||||
3 /* docId */,
|
||||
blueDragonflyScore,
|
||||
1 /* shardIndex */)
|
||||
: createEmptyGroupDocs(blueGroupValue, new Object[] {blueDragonflySize});
|
||||
|
||||
final GroupDocs<String> group2 = haveRedSquirrel
|
||||
? createSingletonGroupDocs(redGroupValue, new Object[] { redSquirrelSize }, 4 /* docId */, redSquirrelScore, 1 /* shardIndex */)
|
||||
final GroupDocs<String> group2 =
|
||||
haveRedSquirrel
|
||||
? createSingletonGroupDocs(
|
||||
redGroupValue,
|
||||
new Object[] {redSquirrelSize},
|
||||
4 /* docId */,
|
||||
redSquirrelScore,
|
||||
1 /* shardIndex */)
|
||||
: createEmptyGroupDocs(redGroupValue, new Object[] {redSquirrelSize});
|
||||
|
||||
shard2TopGroups = new TopGroups<String>(
|
||||
shard2TopGroups =
|
||||
new TopGroups<String>(
|
||||
sort.getSort() /* groupSort */,
|
||||
sort.getSort() /* withinGroupSort */,
|
||||
group1.scoreDocs.length + group2.scoreDocs.length /* totalHitCount */,
|
||||
group1.scoreDocs.length + group2.scoreDocs.length /* totalGroupedHitCount */,
|
||||
combineGroupDocs(group1, group2) /* groups */,
|
||||
(haveRedSquirrel ? redSquirrelScore : (haveBlueDragonfly ? blueDragonflyScore : Float.NaN)) /* maxScore */);
|
||||
(haveRedSquirrel
|
||||
? redSquirrelScore
|
||||
: (haveBlueDragonfly ? blueDragonflyScore : Float.NaN)) /* maxScore */);
|
||||
}
|
||||
|
||||
final TopGroups<String> mergedTopGroups = TopGroups.<String>merge(
|
||||
final TopGroups<String> mergedTopGroups =
|
||||
TopGroups.<String>merge(
|
||||
combineTopGroups(shard1TopGroups, shard2TopGroups),
|
||||
sort /* groupSort */,
|
||||
sort /* docSort */,
|
||||
|
@ -150,10 +181,10 @@ public class TestTopGroups extends LuceneTestCase {
|
|||
assertNotNull(mergedTopGroups);
|
||||
|
||||
final int expectedCount =
|
||||
(haveBlueWhale ? 1 : 0) +
|
||||
(haveRedAnt ? 1 : 0) +
|
||||
(haveBlueDragonfly ? 1 : 0) +
|
||||
(haveRedSquirrel ? 1 : 0);
|
||||
(haveBlueWhale ? 1 : 0)
|
||||
+ (haveRedAnt ? 1 : 0)
|
||||
+ (haveBlueDragonfly ? 1 : 0)
|
||||
+ (haveRedSquirrel ? 1 : 0);
|
||||
|
||||
assertEquals(expectedCount, mergedTopGroups.totalHitCount);
|
||||
assertEquals(expectedCount, mergedTopGroups.totalGroupedHitCount);
|
||||
|
@ -173,11 +204,13 @@ public class TestTopGroups extends LuceneTestCase {
|
|||
}
|
||||
|
||||
final float expectedMaxScore =
|
||||
(haveBlueWhale ? blueWhaleScore
|
||||
: (haveRedSquirrel ? redSquirrelScore
|
||||
: (haveBlueDragonfly ? blueDragonflyScore
|
||||
: (haveRedAnt ? redAntScore
|
||||
: Float.NaN))));
|
||||
(haveBlueWhale
|
||||
? blueWhaleScore
|
||||
: (haveRedSquirrel
|
||||
? redSquirrelScore
|
||||
: (haveBlueDragonfly
|
||||
? blueDragonflyScore
|
||||
: (haveRedAnt ? redAntScore : Float.NaN))));
|
||||
checkMaxScore(expectedMaxScore, mergedTopGroups.maxScore);
|
||||
}
|
||||
|
||||
|
@ -191,7 +224,8 @@ public class TestTopGroups extends LuceneTestCase {
|
|||
|
||||
// helper methods
|
||||
|
||||
private static GroupDocs<String> createEmptyGroupDocs(String groupValue, Object[] groupSortValues) {
|
||||
private static GroupDocs<String> createEmptyGroupDocs(
|
||||
String groupValue, Object[] groupSortValues) {
|
||||
return new GroupDocs<String>(
|
||||
Float.NaN /* score */,
|
||||
Float.NaN /* maxScore */,
|
||||
|
@ -201,8 +235,8 @@ public class TestTopGroups extends LuceneTestCase {
|
|||
groupSortValues);
|
||||
}
|
||||
|
||||
private static GroupDocs<String> createSingletonGroupDocs(String groupValue, Object[] groupSortValues,
|
||||
int docId, float docScore, int shardIndex) {
|
||||
private static GroupDocs<String> createSingletonGroupDocs(
|
||||
String groupValue, Object[] groupSortValues, int docId, float docScore, int shardIndex) {
|
||||
return new GroupDocs<String>(
|
||||
Float.NaN /* score */,
|
||||
docScore /* maxScore */,
|
||||
|
@ -212,7 +246,8 @@ public class TestTopGroups extends LuceneTestCase {
|
|||
groupSortValues);
|
||||
}
|
||||
|
||||
private static GroupDocs<String>[] combineGroupDocs(GroupDocs<String> group0, GroupDocs<String> group1) {
|
||||
private static GroupDocs<String>[] combineGroupDocs(
|
||||
GroupDocs<String> group0, GroupDocs<String> group1) {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
final GroupDocs<String>[] groups = new GroupDocs[2];
|
||||
groups[0] = group0;
|
||||
|
@ -220,12 +255,12 @@ public class TestTopGroups extends LuceneTestCase {
|
|||
return groups;
|
||||
}
|
||||
|
||||
private static TopGroups<String>[] combineTopGroups(TopGroups<String> group0, TopGroups<String> group1) {
|
||||
private static TopGroups<String>[] combineTopGroups(
|
||||
TopGroups<String> group0, TopGroups<String> group1) {
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
final TopGroups<String>[] groups = new TopGroups[2];
|
||||
groups[0] = group0;
|
||||
groups[1] = group1;
|
||||
return groups;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package org.apache.lucene.search.grouping;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.SortedDocValuesField;
|
||||
|
|
|
@ -20,7 +20,6 @@ package org.apache.lucene.luke.app;
|
|||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.luke.util.LoggerFactory;
|
||||
|
||||
|
@ -45,5 +44,4 @@ public abstract class AbstractHandler<T extends Observer> {
|
|||
}
|
||||
|
||||
protected abstract void notifyOne(T observer);
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.lucene.luke.app;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.lucene.luke.app.desktop.util.MessageUtils;
|
||||
import org.apache.lucene.luke.models.LukeException;
|
||||
import org.apache.lucene.luke.models.util.IndexUtils;
|
||||
|
@ -60,7 +59,8 @@ public final class DirectoryHandler extends AbstractHandler<DirectoryObserver> {
|
|||
try {
|
||||
dir = IndexUtils.openDirectory(indexPath, dirImpl);
|
||||
} catch (IOException e) {
|
||||
throw new LukeException(MessageUtils.getLocalizedMessage("openindex.message.index_path_invalid", indexPath), e);
|
||||
throw new LukeException(
|
||||
MessageUtils.getLocalizedMessage("openindex.message.index_path_invalid", indexPath), e);
|
||||
}
|
||||
|
||||
state = new LukeStateImpl();
|
||||
|
@ -108,5 +108,4 @@ public final class DirectoryHandler extends AbstractHandler<DirectoryObserver> {
|
|||
return dir;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,5 +23,4 @@ public interface DirectoryObserver extends Observer {
|
|||
void openDirectory(LukeState state);
|
||||
|
||||
void closeDirectory();
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.lucene.luke.app;
|
|||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.luke.app.desktop.util.MessageUtils;
|
||||
|
@ -57,7 +56,12 @@ public final class IndexHandler extends AbstractHandler<IndexObserver> {
|
|||
open(indexPath, dirImpl, false, false, false);
|
||||
}
|
||||
|
||||
public void open(String indexPath, String dirImpl, boolean readOnly, boolean useCompound, boolean keepAllCommits) {
|
||||
public void open(
|
||||
String indexPath,
|
||||
String dirImpl,
|
||||
boolean readOnly,
|
||||
boolean useCompound,
|
||||
boolean keepAllCommits) {
|
||||
Objects.requireNonNull(indexPath);
|
||||
|
||||
if (indexOpened()) {
|
||||
|
@ -69,7 +73,8 @@ public final class IndexHandler extends AbstractHandler<IndexObserver> {
|
|||
reader = IndexUtils.openIndex(indexPath, dirImpl);
|
||||
} catch (Exception e) {
|
||||
log.error("Error opening index", e);
|
||||
throw new LukeException(MessageUtils.getLocalizedMessage("openindex.message.index_path_invalid", indexPath), e);
|
||||
throw new LukeException(
|
||||
MessageUtils.getLocalizedMessage("openindex.message.index_path_invalid", indexPath), e);
|
||||
}
|
||||
|
||||
state = new LukeStateImpl();
|
||||
|
@ -96,7 +101,12 @@ public final class IndexHandler extends AbstractHandler<IndexObserver> {
|
|||
|
||||
public void reOpen() {
|
||||
close();
|
||||
open(state.getIndexPath(), state.getDirImpl(), state.readOnly(), state.useCompound(), state.keepAllCommits());
|
||||
open(
|
||||
state.getIndexPath(),
|
||||
state.getDirImpl(),
|
||||
state.readOnly(),
|
||||
state.useCompound(),
|
||||
state.keepAllCommits());
|
||||
}
|
||||
|
||||
public LukeState getState() {
|
||||
|
|
|
@ -23,5 +23,4 @@ public interface IndexObserver extends Observer {
|
|||
void openIndex(LukeState state);
|
||||
|
||||
void closeIndex();
|
||||
|
||||
}
|
||||
|
|
|
@ -21,9 +21,7 @@ import org.apache.lucene.index.DirectoryReader;
|
|||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.store.Directory;
|
||||
|
||||
/**
|
||||
* Holder for current index/directory.
|
||||
*/
|
||||
/** Holder for current index/directory. */
|
||||
public interface LukeState {
|
||||
|
||||
String getIndexPath();
|
||||
|
@ -53,5 +51,4 @@ public interface LukeState {
|
|||
default boolean hasDirectoryReader() {
|
||||
return getIndexReader() instanceof DirectoryReader;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,5 +18,4 @@
|
|||
package org.apache.lucene.luke.app;
|
||||
|
||||
/** Marker interface for observers */
|
||||
public interface Observer {
|
||||
}
|
||||
public interface Observer {}
|
||||
|
|
|
@ -17,13 +17,14 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.UIManager;
|
||||
import static org.apache.lucene.luke.app.desktop.util.ExceptionHandler.handle;
|
||||
|
||||
import java.awt.GraphicsEnvironment;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.nio.file.FileSystems;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.UIManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.luke.app.desktop.components.LukeWindowProvider;
|
||||
import org.apache.lucene.luke.app.desktop.components.dialog.menubar.OpenIndexDialogFactory;
|
||||
|
@ -32,18 +33,20 @@ import org.apache.lucene.luke.app.desktop.util.FontUtils;
|
|||
import org.apache.lucene.luke.app.desktop.util.MessageUtils;
|
||||
import org.apache.lucene.luke.util.LoggerFactory;
|
||||
|
||||
import static org.apache.lucene.luke.app.desktop.util.ExceptionHandler.handle;
|
||||
|
||||
/** Entry class for desktop Luke */
|
||||
public class LukeMain {
|
||||
|
||||
public static final String LOG_FILE = System.getProperty("user.home") +
|
||||
FileSystems.getDefault().getSeparator() + ".luke.d" +
|
||||
FileSystems.getDefault().getSeparator() + "luke.log";
|
||||
public static final String LOG_FILE =
|
||||
System.getProperty("user.home")
|
||||
+ FileSystems.getDefault().getSeparator()
|
||||
+ ".luke.d"
|
||||
+ FileSystems.getDefault().getSeparator()
|
||||
+ "luke.log";
|
||||
|
||||
static {
|
||||
LoggerFactory.initGuiLogging(LOG_FILE);
|
||||
}
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private static JFrame frame;
|
||||
|
@ -55,9 +58,7 @@ public class LukeMain {
|
|||
private static void createAndShowGUI() {
|
||||
// uncaught error handler
|
||||
MessageBroker messageBroker = MessageBroker.getInstance();
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, cause) ->
|
||||
handle(cause, messageBroker)
|
||||
);
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, cause) -> handle(cause, messageBroker));
|
||||
|
||||
try {
|
||||
frame = new LukeWindowProvider().get();
|
||||
|
@ -68,9 +69,12 @@ public class LukeMain {
|
|||
|
||||
// show open index dialog
|
||||
OpenIndexDialogFactory openIndexDialogFactory = OpenIndexDialogFactory.getInstance();
|
||||
new DialogOpener<>(openIndexDialogFactory).open(MessageUtils.getLocalizedMessage("openindex.dialog.title"), 600, 420,
|
||||
(factory) -> {
|
||||
});
|
||||
new DialogOpener<>(openIndexDialogFactory)
|
||||
.open(
|
||||
MessageUtils.getLocalizedMessage("openindex.dialog.title"),
|
||||
600,
|
||||
420,
|
||||
(factory) -> {});
|
||||
} catch (IOException e) {
|
||||
messageBroker.showUnknownErrorMessage();
|
||||
log.error("Cannot initialize components.", e);
|
||||
|
@ -79,7 +83,8 @@ public class LukeMain {
|
|||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String lookAndFeelClassName = UIManager.getSystemLookAndFeelClassName();
|
||||
if (!lookAndFeelClassName.contains("AquaLookAndFeel") && !lookAndFeelClassName.contains("PlasticXPLookAndFeel")) {
|
||||
if (!lookAndFeelClassName.contains("AquaLookAndFeel")
|
||||
&& !lookAndFeelClassName.contains("PlasticXPLookAndFeel")) {
|
||||
// may be running on linux platform
|
||||
lookAndFeelClassName = "javax.swing.plaf.metal.MetalLookAndFeel";
|
||||
}
|
||||
|
@ -89,6 +94,5 @@ public class LukeMain {
|
|||
genv.registerFont(FontUtils.createElegantIconFont());
|
||||
|
||||
javax.swing.SwingUtilities.invokeLater(LukeMain::createAndShowGUI);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,5 +61,4 @@ public class MessageBroker {
|
|||
|
||||
void clearStatusMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,7 +38,13 @@ public interface Preferences {
|
|||
|
||||
boolean isKeepAllCommits();
|
||||
|
||||
void setIndexOpenerPrefs(boolean readOnly, String dirImpl, boolean noReader, boolean useCompound, boolean keepAllCommits) throws IOException;
|
||||
void setIndexOpenerPrefs(
|
||||
boolean readOnly,
|
||||
String dirImpl,
|
||||
boolean noReader,
|
||||
boolean useCompound,
|
||||
boolean keepAllCommits)
|
||||
throws IOException;
|
||||
|
||||
ColorTheme getColorTheme();
|
||||
|
||||
|
|
|
@ -24,11 +24,10 @@ public class PreferencesFactory {
|
|||
|
||||
private static Preferences prefs;
|
||||
|
||||
public synchronized static Preferences getInstance() throws IOException {
|
||||
public static synchronized Preferences getInstance() throws IOException {
|
||||
if (prefs == null) {
|
||||
prefs = new PreferencesImpl();
|
||||
}
|
||||
return prefs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.lucene.luke.app.desktop.util.inifile.IniFile;
|
||||
import org.apache.lucene.luke.app.desktop.util.inifile.SimpleIniFile;
|
||||
import org.apache.lucene.store.FSDirectory;
|
||||
|
@ -31,14 +30,14 @@ import org.apache.lucene.store.FSDirectory;
|
|||
/** Default implementation of {@link Preferences} */
|
||||
public final class PreferencesImpl implements Preferences {
|
||||
|
||||
private static final String CONFIG_DIR = System.getProperty("user.home") + FileSystems.getDefault().getSeparator() + ".luke.d";
|
||||
private static final String CONFIG_DIR =
|
||||
System.getProperty("user.home") + FileSystems.getDefault().getSeparator() + ".luke.d";
|
||||
private static final String INIT_FILE = "luke.ini";
|
||||
private static final String HISTORY_FILE = "history";
|
||||
private static final int MAX_HISTORY = 10;
|
||||
|
||||
private final IniFile ini = new SimpleIniFile();
|
||||
|
||||
|
||||
private final List<String> history = new ArrayList<>();
|
||||
|
||||
public PreferencesImpl() throws IOException {
|
||||
|
@ -61,7 +60,6 @@ public final class PreferencesImpl implements Preferences {
|
|||
List<String> allHistory = Files.readAllLines(histFile);
|
||||
history.addAll(allHistory.subList(0, Math.min(MAX_HISTORY, allHistory.size())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public List<String> getHistory() {
|
||||
|
@ -128,7 +126,13 @@ public final class PreferencesImpl implements Preferences {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void setIndexOpenerPrefs(boolean readOnly, String dirImpl, boolean noReader, boolean useCompound, boolean keepAllCommits) throws IOException {
|
||||
public void setIndexOpenerPrefs(
|
||||
boolean readOnly,
|
||||
String dirImpl,
|
||||
boolean noReader,
|
||||
boolean useCompound,
|
||||
boolean keepAllCommits)
|
||||
throws IOException {
|
||||
ini.put("opener", "readOnly", readOnly);
|
||||
ini.put("opener", "dirImpl", dirImpl);
|
||||
ini.put("opener", "noReader", noReader);
|
||||
|
|
|
@ -17,16 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTextArea;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
|
@ -39,7 +29,16 @@ import java.io.IOException;
|
|||
import java.util.Objects;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTextArea;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.custom.CustomAnalyzer;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
|
@ -126,11 +125,16 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
|
||||
operatorRegistry.register(AnalysisTabOperator.class, this);
|
||||
|
||||
operatorRegistry.get(PresetAnalyzerPanelOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(PresetAnalyzerPanelOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
// Scanning all Analyzer types will take time...
|
||||
ExecutorService executorService =
|
||||
Executors.newFixedThreadPool(1, new NamedThreadFactory("load-preset-analyzer-types"));
|
||||
executorService.execute(() -> {
|
||||
Executors.newFixedThreadPool(
|
||||
1, new NamedThreadFactory("load-preset-analyzer-types"));
|
||||
executorService.execute(
|
||||
() -> {
|
||||
operator.setPresetAnalyzers(analysisModel.getPresetAnalyzerTypes());
|
||||
operator.setSelectedAnalyzer(analysisModel.currentAnalyzer().getClass());
|
||||
});
|
||||
|
@ -143,7 +147,8 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
splitPane.setOpaque(false);
|
||||
splitPane.setDividerLocation(320);
|
||||
panel.add(splitPane);
|
||||
|
@ -194,11 +199,13 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
|
||||
JPanel analyzerName = new JPanel(new FlowLayout(FlowLayout.LEADING, 10, 2));
|
||||
analyzerName.setOpaque(false);
|
||||
analyzerName.add(new JLabel(MessageUtils.getLocalizedMessage("analysis.label.selected_analyzer")));
|
||||
analyzerName.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis.label.selected_analyzer")));
|
||||
analyzerNameLbl.setText(analysisModel.currentAnalyzer().getClass().getName());
|
||||
analyzerName.add(analyzerNameLbl);
|
||||
showChainLbl.setText(MessageUtils.getLocalizedMessage("analysis.label.show_chain"));
|
||||
showChainLbl.addMouseListener(new MouseAdapter() {
|
||||
showChainLbl.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.showAnalysisChain(e);
|
||||
|
@ -217,8 +224,10 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
inputArea.setText(MessageUtils.getLocalizedMessage("analysis.textarea.prompt"));
|
||||
input.add(new JScrollPane(inputArea));
|
||||
|
||||
JButton executeBtn = new JButton(FontUtils.elegantIconHtml("",
|
||||
MessageUtils.getLocalizedMessage("analysis.button.test")));
|
||||
JButton executeBtn =
|
||||
new JButton(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("analysis.button.test")));
|
||||
executeBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
executeBtn.setMargin(new Insets(3, 3, 3, 3));
|
||||
executeBtn.addActionListener(listeners::executeAnalysis);
|
||||
|
@ -233,12 +242,15 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
JButton clearBtn = new JButton(MessageUtils.getLocalizedMessage("button.clear"));
|
||||
clearBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
clearBtn.setMargin(new Insets(5, 5, 5, 5));
|
||||
clearBtn.addActionListener(e -> {
|
||||
clearBtn.addActionListener(
|
||||
e -> {
|
||||
inputArea.setText("");
|
||||
operatorRegistry.get(SimpleAnalyzeResultPanelOperator.class).ifPresent(
|
||||
SimpleAnalyzeResultPanelOperator::clearTable);
|
||||
operatorRegistry.get(StepByStepAnalyzeResultPanelOperator.class).ifPresent(
|
||||
StepByStepAnalyzeResultPanelOperator::clearTable);
|
||||
operatorRegistry
|
||||
.get(SimpleAnalyzeResultPanelOperator.class)
|
||||
.ifPresent(SimpleAnalyzeResultPanelOperator::clearTable);
|
||||
operatorRegistry
|
||||
.get(StepByStepAnalyzeResultPanelOperator.class)
|
||||
.ifPresent(StepByStepAnalyzeResultPanelOperator::clearTable);
|
||||
});
|
||||
input.add(clearBtn);
|
||||
|
||||
|
@ -259,7 +271,10 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
mainPanel.remove(custom);
|
||||
mainPanel.add(preset, BorderLayout.CENTER);
|
||||
|
||||
operatorRegistry.get(PresetAnalyzerPanelOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(PresetAnalyzerPanelOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setPresetAnalyzers(analysisModel.getPresetAnalyzerTypes());
|
||||
operator.setSelectedAnalyzer(analysisModel.currentAnalyzer().getClass());
|
||||
});
|
||||
|
@ -269,7 +284,10 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
mainPanel.remove(preset);
|
||||
mainPanel.add(custom, BorderLayout.CENTER);
|
||||
|
||||
operatorRegistry.get(CustomAnalyzerPanelOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(CustomAnalyzerPanelOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setAnalysisModel(analysisModel);
|
||||
operator.resetAnalysisComponents();
|
||||
});
|
||||
|
@ -282,13 +300,17 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
void executeAnalysis() {
|
||||
String text = inputArea.getText();
|
||||
if (Objects.isNull(text) || text.isEmpty()) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("analysis.message.empry_input"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("analysis.message.empry_input"));
|
||||
}
|
||||
|
||||
lowerPanel.remove(stepByStepResult);
|
||||
lowerPanel.add(simpleResult, BorderLayout.CENTER);
|
||||
|
||||
operatorRegistry.get(SimpleAnalyzeResultPanelOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(SimpleAnalyzeResultPanelOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setAnalysisModel(analysisModel);
|
||||
operator.executeAnalysis(text);
|
||||
});
|
||||
|
@ -300,11 +322,15 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
void executeAnalysisStepByStep() {
|
||||
String text = inputArea.getText();
|
||||
if (Objects.isNull(text) || text.isEmpty()) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("analysis.message.empry_input"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("analysis.message.empry_input"));
|
||||
}
|
||||
lowerPanel.remove(simpleResult);
|
||||
lowerPanel.add(stepByStepResult, BorderLayout.CENTER);
|
||||
operatorRegistry.get(StepByStepAnalyzeResultPanelOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(StepByStepAnalyzeResultPanelOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setAnalysisModel(analysisModel);
|
||||
operator.executeAnalysisStepByStep(text);
|
||||
});
|
||||
|
@ -316,7 +342,11 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
void showAnalysisChainDialog() {
|
||||
if (getCurrentAnalyzer() instanceof CustomAnalyzer) {
|
||||
CustomAnalyzer analyzer = (CustomAnalyzer) getCurrentAnalyzer();
|
||||
new DialogOpener<>(analysisChainDialogFactory).open("Analysis chain", 600, 320,
|
||||
new DialogOpener<>(analysisChainDialogFactory)
|
||||
.open(
|
||||
"Analysis chain",
|
||||
600,
|
||||
320,
|
||||
(factory) -> {
|
||||
factory.setAnalyzer(analyzer);
|
||||
});
|
||||
|
@ -328,12 +358,15 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
analysisModel.createAnalyzerFromClassName(analyzerType);
|
||||
analyzerNameLbl.setText(analysisModel.currentAnalyzer().getClass().getName());
|
||||
showChainLbl.setVisible(false);
|
||||
operatorRegistry.get(AnalyzerTabOperator.class).ifPresent(operator ->
|
||||
operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry.get(MLTTabOperator.class).ifPresent(operator ->
|
||||
operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry.get(AddDocumentDialogOperator.class).ifPresent(operator ->
|
||||
operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry
|
||||
.get(AnalyzerTabOperator.class)
|
||||
.ifPresent(operator -> operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry
|
||||
.get(MLTTabOperator.class)
|
||||
.ifPresent(operator -> operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry
|
||||
.get(AddDocumentDialogOperator.class)
|
||||
.ifPresent(operator -> operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -341,12 +374,15 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
analysisModel.buildCustomAnalyzer(config);
|
||||
analyzerNameLbl.setText(analysisModel.currentAnalyzer().getClass().getName());
|
||||
showChainLbl.setVisible(true);
|
||||
operatorRegistry.get(AnalyzerTabOperator.class).ifPresent(operator ->
|
||||
operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry.get(MLTTabOperator.class).ifPresent(operator ->
|
||||
operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry.get(AddDocumentDialogOperator.class).ifPresent(operator ->
|
||||
operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry
|
||||
.get(AnalyzerTabOperator.class)
|
||||
.ifPresent(operator -> operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry
|
||||
.get(MLTTabOperator.class)
|
||||
.ifPresent(operator -> operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
operatorRegistry
|
||||
.get(AddDocumentDialogOperator.class)
|
||||
.ifPresent(operator -> operator.setAnalyzer(analysisModel.currentAnalyzer()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -372,9 +408,6 @@ public final class AnalysisPanelProvider implements AnalysisTabOperator {
|
|||
}
|
||||
}
|
||||
|
||||
void executeAnalysisStepByStep(ActionEvent e) {
|
||||
void executeAnalysisStepByStep(ActionEvent e) {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,4 @@ public interface AnalysisTabOperator extends ComponentOperatorRegistry.Component
|
|||
void setAnalyzerByCustomConfiguration(CustomAnalyzerConfig config);
|
||||
|
||||
Analyzer getCurrentAnalyzer();
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
|
@ -32,19 +44,6 @@ import javax.swing.JSplitPane;
|
|||
import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.index.DirectoryReader;
|
||||
import org.apache.lucene.luke.app.DirectoryHandler;
|
||||
import org.apache.lucene.luke.app.DirectoryObserver;
|
||||
|
@ -100,7 +99,8 @@ public final class CommitsPanelProvider {
|
|||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
splitPane.setOpaque(false);
|
||||
splitPane.setBorder(BorderFactory.createEmptyBorder());
|
||||
splitPane.setDividerLocation(120);
|
||||
|
@ -162,7 +162,11 @@ public final class CommitsPanelProvider {
|
|||
userDataTA.setLineWrap(true);
|
||||
userDataTA.setWrapStyleWord(true);
|
||||
userDataTA.setEditable(false);
|
||||
JScrollPane userDataScroll = new JScrollPane(userDataTA, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
JScrollPane userDataScroll =
|
||||
new JScrollPane(
|
||||
userDataTA,
|
||||
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
||||
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
c1.gridx = 1;
|
||||
c1.gridy = 2;
|
||||
c1.weightx = 0.5;
|
||||
|
@ -179,7 +183,8 @@ public final class CommitsPanelProvider {
|
|||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3));
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, initFilesPanel(), initSegmentsPanel());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, initFilesPanel(), initSegmentsPanel());
|
||||
splitPane.setOpaque(false);
|
||||
splitPane.setBorder(BorderFactory.createEmptyBorder());
|
||||
splitPane.setDividerLocation(300);
|
||||
|
@ -197,7 +202,12 @@ public final class CommitsPanelProvider {
|
|||
header.add(new JLabel(MessageUtils.getLocalizedMessage("commits.label.files")));
|
||||
panel.add(header, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(filesTable, ListSelectionModel.SINGLE_SELECTION, new FilesTableModel(), null, FilesTableModel.Column.FILENAME.getColumnWidth());
|
||||
TableUtils.setupTable(
|
||||
filesTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new FilesTableModel(),
|
||||
null,
|
||||
FilesTableModel.Column.FILENAME.getColumnWidth());
|
||||
panel.add(new JScrollPane(filesTable), BorderLayout.CENTER);
|
||||
|
||||
return panel;
|
||||
|
@ -213,7 +223,10 @@ public final class CommitsPanelProvider {
|
|||
segments.add(new JLabel(MessageUtils.getLocalizedMessage("commits.label.segments")));
|
||||
panel.add(segments);
|
||||
|
||||
TableUtils.setupTable(segmentsTable, ListSelectionModel.SINGLE_SELECTION, new SegmentsTableModel(),
|
||||
TableUtils.setupTable(
|
||||
segmentsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new SegmentsTableModel(),
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
@ -241,7 +254,8 @@ public final class CommitsPanelProvider {
|
|||
diagRB.setSelected(true);
|
||||
diagRB.setEnabled(false);
|
||||
diagRB.setOpaque(false);
|
||||
diagRB.addMouseListener(new MouseAdapter() {
|
||||
diagRB.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.showSegmentDetails(e);
|
||||
|
@ -254,7 +268,8 @@ public final class CommitsPanelProvider {
|
|||
attrRB.setSelected(false);
|
||||
attrRB.setEnabled(false);
|
||||
attrRB.setOpaque(false);
|
||||
attrRB.addMouseListener(new MouseAdapter() {
|
||||
attrRB.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.showSegmentDetails(e);
|
||||
|
@ -267,7 +282,8 @@ public final class CommitsPanelProvider {
|
|||
codecRB.setSelected(false);
|
||||
codecRB.setEnabled(false);
|
||||
codecRB.setOpaque(false);
|
||||
codecRB.addMouseListener(new MouseAdapter() {
|
||||
codecRB.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.showSegmentDetails(e);
|
||||
|
@ -296,7 +312,10 @@ public final class CommitsPanelProvider {
|
|||
segDetailList.setModel(new DefaultListModel<>());
|
||||
|
||||
long commitGen = (long) commitGenCombo.getSelectedItem();
|
||||
commitsModel.getCommit(commitGen).ifPresent(commit -> {
|
||||
commitsModel
|
||||
.getCommit(commitGen)
|
||||
.ifPresent(
|
||||
commit -> {
|
||||
deletedLbl.setText(String.valueOf(commit.isDeleted()));
|
||||
segCntLbl.setText(String.valueOf(commit.getSegCount()));
|
||||
userDataTA.setText(commit.getUserData());
|
||||
|
@ -304,22 +323,44 @@ public final class CommitsPanelProvider {
|
|||
|
||||
filesTable.setModel(new FilesTableModel(commitsModel.getFiles(commitGen)));
|
||||
filesTable.setShowGrid(true);
|
||||
filesTable.getColumnModel().getColumn(FilesTableModel.Column.FILENAME.getIndex()).setPreferredWidth(FilesTableModel.Column.FILENAME.getColumnWidth());
|
||||
filesTable
|
||||
.getColumnModel()
|
||||
.getColumn(FilesTableModel.Column.FILENAME.getIndex())
|
||||
.setPreferredWidth(FilesTableModel.Column.FILENAME.getColumnWidth());
|
||||
|
||||
segmentsTable.setModel(new SegmentsTableModel(commitsModel.getSegments(commitGen)));
|
||||
segmentsTable.setShowGrid(true);
|
||||
segmentsTable.getColumnModel().getColumn(SegmentsTableModel.Column.NAME.getIndex()).setPreferredWidth(SegmentsTableModel.Column.NAME.getColumnWidth());
|
||||
segmentsTable.getColumnModel().getColumn(SegmentsTableModel.Column.MAXDOCS.getIndex()).setPreferredWidth(SegmentsTableModel.Column.MAXDOCS.getColumnWidth());
|
||||
segmentsTable.getColumnModel().getColumn(SegmentsTableModel.Column.DELS.getIndex()).setPreferredWidth(SegmentsTableModel.Column.DELS.getColumnWidth());
|
||||
segmentsTable.getColumnModel().getColumn(SegmentsTableModel.Column.DELGEN.getIndex()).setPreferredWidth(SegmentsTableModel.Column.DELGEN.getColumnWidth());
|
||||
segmentsTable.getColumnModel().getColumn(SegmentsTableModel.Column.VERSION.getIndex()).setPreferredWidth(SegmentsTableModel.Column.VERSION.getColumnWidth());
|
||||
segmentsTable.getColumnModel().getColumn(SegmentsTableModel.Column.CODEC.getIndex()).setPreferredWidth(SegmentsTableModel.Column.CODEC.getColumnWidth());
|
||||
segmentsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SegmentsTableModel.Column.NAME.getIndex())
|
||||
.setPreferredWidth(SegmentsTableModel.Column.NAME.getColumnWidth());
|
||||
segmentsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SegmentsTableModel.Column.MAXDOCS.getIndex())
|
||||
.setPreferredWidth(SegmentsTableModel.Column.MAXDOCS.getColumnWidth());
|
||||
segmentsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SegmentsTableModel.Column.DELS.getIndex())
|
||||
.setPreferredWidth(SegmentsTableModel.Column.DELS.getColumnWidth());
|
||||
segmentsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SegmentsTableModel.Column.DELGEN.getIndex())
|
||||
.setPreferredWidth(SegmentsTableModel.Column.DELGEN.getColumnWidth());
|
||||
segmentsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SegmentsTableModel.Column.VERSION.getIndex())
|
||||
.setPreferredWidth(SegmentsTableModel.Column.VERSION.getColumnWidth());
|
||||
segmentsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SegmentsTableModel.Column.CODEC.getIndex())
|
||||
.setPreferredWidth(SegmentsTableModel.Column.CODEC.getColumnWidth());
|
||||
}
|
||||
|
||||
private void showSegmentDetails() {
|
||||
int selectedRow = segmentsTable.getSelectedRow();
|
||||
if (commitGenCombo.getSelectedItem() == null ||
|
||||
selectedRow < 0 || selectedRow >= segmentsTable.getRowCount()) {
|
||||
if (commitGenCombo.getSelectedItem() == null
|
||||
|| selectedRow < 0
|
||||
|| selectedRow >= segmentsTable.getRowCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -328,7 +369,8 @@ public final class CommitsPanelProvider {
|
|||
codecRB.setEnabled(true);
|
||||
|
||||
long commitGen = (long) commitGenCombo.getSelectedItem();
|
||||
String segName = (String) segmentsTable.getValueAt(selectedRow, SegmentsTableModel.Column.NAME.getIndex());
|
||||
String segName =
|
||||
(String) segmentsTable.getValueAt(selectedRow, SegmentsTableModel.Column.NAME.getIndex());
|
||||
ActionCommand command = ActionCommand.valueOf(rbGroup.getSelection().getActionCommand());
|
||||
|
||||
final DefaultListModel<String> detailsModel = new DefaultListModel<>();
|
||||
|
@ -344,7 +386,10 @@ public final class CommitsPanelProvider {
|
|||
.forEach(detailsModel::addElement);
|
||||
break;
|
||||
case CODEC:
|
||||
commitsModel.getSegmentCodec(commitGen, segName).ifPresent(codec -> {
|
||||
commitsModel
|
||||
.getSegmentCodec(commitGen, segName)
|
||||
.ifPresent(
|
||||
codec -> {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("Codec name", codec.getName());
|
||||
map.put("Codec class name", codec.getClass().getName());
|
||||
|
@ -359,12 +404,12 @@ public final class CommitsPanelProvider {
|
|||
map.put("StoredFields format", codec.storedFieldsFormat().getClass().getName());
|
||||
map.put("TermVectors format", codec.termVectorsFormat().getClass().getName());
|
||||
map.entrySet().stream()
|
||||
.map(entry -> entry.getKey() + " = " + entry.getValue()).forEach(detailsModel::addElement);
|
||||
.map(entry -> entry.getKey() + " = " + entry.getValue())
|
||||
.forEach(detailsModel::addElement);
|
||||
});
|
||||
break;
|
||||
}
|
||||
segDetailList.setModel(detailsModel);
|
||||
|
||||
}
|
||||
|
||||
private class ListenerFunctions {
|
||||
|
@ -376,7 +421,6 @@ public final class CommitsPanelProvider {
|
|||
void showSegmentDetails(MouseEvent e) {
|
||||
CommitsPanelProvider.this.showSegmentDetails();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver, DirectoryObserver {
|
||||
|
@ -425,8 +469,17 @@ public final class CommitsPanelProvider {
|
|||
deletedLbl.setText("");
|
||||
segCntLbl.setText("");
|
||||
userDataTA.setText("");
|
||||
TableUtils.setupTable(filesTable, ListSelectionModel.SINGLE_SELECTION, new FilesTableModel(), null, FilesTableModel.Column.FILENAME.getColumnWidth());
|
||||
TableUtils.setupTable(segmentsTable, ListSelectionModel.SINGLE_SELECTION, new SegmentsTableModel(), null,
|
||||
TableUtils.setupTable(
|
||||
filesTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new FilesTableModel(),
|
||||
null,
|
||||
FilesTableModel.Column.FILENAME.getColumnWidth());
|
||||
TableUtils.setupTable(
|
||||
segmentsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new SegmentsTableModel(),
|
||||
null,
|
||||
SegmentsTableModel.Column.NAME.getColumnWidth(),
|
||||
SegmentsTableModel.Column.MAXDOCS.getColumnWidth(),
|
||||
SegmentsTableModel.Column.DELS.getColumnWidth(),
|
||||
|
@ -441,13 +494,14 @@ public final class CommitsPanelProvider {
|
|||
}
|
||||
|
||||
enum ActionCommand {
|
||||
DIAGNOSTICS, ATTRIBUTES, CODEC;
|
||||
DIAGNOSTICS,
|
||||
ATTRIBUTES,
|
||||
CODEC;
|
||||
}
|
||||
|
||||
static final class FilesTableModel extends TableModelBase<FilesTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
|
||||
FILENAME("Filename", 0, String.class, 200),
|
||||
SIZE("Size", 1, String.class, Integer.MAX_VALUE);
|
||||
|
||||
|
@ -506,7 +560,6 @@ public final class CommitsPanelProvider {
|
|||
static final class SegmentsTableModel extends TableModelBase<SegmentsTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
|
||||
NAME("Name", 0, String.class, 60),
|
||||
MAXDOCS("Max docs", 1, Integer.class, 60),
|
||||
DELS("Dels", 2, Integer.class, 60),
|
||||
|
@ -572,4 +625,3 @@ public final class CommitsPanelProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,5 @@ public class ComponentOperatorRegistry {
|
|||
}
|
||||
|
||||
/** marker interface for operators */
|
||||
public interface ComponentOperator {
|
||||
}
|
||||
|
||||
public interface ComponentOperator {}
|
||||
}
|
||||
|
|
|
@ -17,26 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SpinnerModel;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
|
@ -57,7 +37,26 @@ import java.math.BigInteger;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSpinner;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.SpinnerModel;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.apache.lucene.index.DocValuesType;
|
||||
import org.apache.lucene.index.IndexOptions;
|
||||
import org.apache.lucene.index.Term;
|
||||
|
@ -152,16 +151,20 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
this.dvDialogFactory = DocValuesDialogFactory.getInstance();
|
||||
this.valueDialogFactory = StoredValueDialogFactory.getInstance();
|
||||
HelpDialogFactory helpDialogFactory = HelpDialogFactory.getInstance();
|
||||
this.tableHeaderRenderer = new HelpHeaderRenderer(
|
||||
"About Flags", "Format: IdfpoNPSB#txxVDtxxxxTx/x",
|
||||
createFlagsHelpDialog(), helpDialogFactory);
|
||||
this.tableHeaderRenderer =
|
||||
new HelpHeaderRenderer(
|
||||
"About Flags",
|
||||
"Format: IdfpoNPSB#txxVDtxxxxTx/x",
|
||||
createFlagsHelpDialog(),
|
||||
helpDialogFactory);
|
||||
|
||||
IndexHandler.getInstance().addObserver(new Observer());
|
||||
operatorRegistry.register(DocumentsTabOperator.class, this);
|
||||
}
|
||||
|
||||
private JComponent createFlagsHelpDialog() {
|
||||
String[] values = new String[]{
|
||||
String[] values =
|
||||
new String[] {
|
||||
"I - index options(docs, frequencies, positions, offsets)",
|
||||
"N - norms",
|
||||
"P - payloads",
|
||||
|
@ -181,7 +184,8 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
splitPane.setOpaque(false);
|
||||
splitPane.setDividerLocation(0.4);
|
||||
panel.add(splitPane);
|
||||
|
@ -238,7 +242,9 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
c.gridwidth = 2;
|
||||
center.add(fieldsCombo, c);
|
||||
|
||||
firstTermBtn.setText(FontUtils.elegantIconHtml("8", MessageUtils.getLocalizedMessage("documents.button.first_term")));
|
||||
firstTermBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"8", MessageUtils.getLocalizedMessage("documents.button.first_term")));
|
||||
firstTermBtn.setMaximumSize(new Dimension(80, 30));
|
||||
firstTermBtn.addActionListener(listeners::showFirstTerm);
|
||||
c.gridx = 0;
|
||||
|
@ -272,7 +278,8 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.LEADING, 20, 5));
|
||||
footer.setOpaque(false);
|
||||
JLabel hintLbl = new JLabel(MessageUtils.getLocalizedMessage("documents.label.browse_terms_hint"));
|
||||
JLabel hintLbl =
|
||||
new JLabel(MessageUtils.getLocalizedMessage("documents.label.browse_terms_hint"));
|
||||
footer.add(hintLbl);
|
||||
panel.add(footer, BorderLayout.PAGE_END);
|
||||
|
||||
|
@ -289,7 +296,8 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.fill = GridBagConstraints.BOTH;
|
||||
|
||||
JLabel label = new JLabel(MessageUtils.getLocalizedMessage("documents.label.browse_doc_by_term"));
|
||||
JLabel label =
|
||||
new JLabel(MessageUtils.getLocalizedMessage("documents.label.browse_doc_by_term"));
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
c.weightx = 0.0;
|
||||
|
@ -308,7 +316,9 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
c.insets = new Insets(5, 5, 5, 5);
|
||||
center.add(selectedTermTF, c);
|
||||
|
||||
firstTermDocBtn.setText(FontUtils.elegantIconHtml("8", MessageUtils.getLocalizedMessage("documents.button.first_termdoc")));
|
||||
firstTermDocBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"8", MessageUtils.getLocalizedMessage("documents.button.first_termdoc")));
|
||||
firstTermDocBtn.addActionListener(listeners::showFirstTermDoc);
|
||||
c.gridx = 0;
|
||||
c.gridy = 2;
|
||||
|
@ -343,8 +353,14 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
c.insets = new Insets(5, 5, 5, 5);
|
||||
center.add(termDocsNumLbl, c);
|
||||
|
||||
TableUtils.setupTable(posTable, ListSelectionModel.SINGLE_SELECTION, new PosTableModel(), null,
|
||||
PosTableModel.Column.POSITION.getColumnWidth(), PosTableModel.Column.OFFSETS.getColumnWidth(), PosTableModel.Column.PAYLOAD.getColumnWidth());
|
||||
TableUtils.setupTable(
|
||||
posTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new PosTableModel(),
|
||||
null,
|
||||
PosTableModel.Column.POSITION.getColumnWidth(),
|
||||
PosTableModel.Column.OFFSETS.getColumnWidth(),
|
||||
PosTableModel.Column.PAYLOAD.getColumnWidth());
|
||||
JScrollPane scrollPane = new JScrollPane(posTable);
|
||||
scrollPane.setMinimumSize(new Dimension(100, 100));
|
||||
c.gridx = 0;
|
||||
|
@ -370,17 +386,23 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
JPanel browseDocsNote1 = new JPanel(new FlowLayout(FlowLayout.LEADING));
|
||||
browseDocsNote1.setOpaque(false);
|
||||
browseDocsNote1.add(new JLabel(MessageUtils.getLocalizedMessage("documents.label.doc_table_note1")));
|
||||
browseDocsNote1.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("documents.label.doc_table_note1")));
|
||||
browseDocsPanel.add(browseDocsNote1);
|
||||
|
||||
JPanel browseDocsNote2 = new JPanel(new FlowLayout(FlowLayout.LEADING));
|
||||
browseDocsNote2.setOpaque(false);
|
||||
browseDocsNote2.add(new JLabel(MessageUtils.getLocalizedMessage("documents.label.doc_table_note2")));
|
||||
browseDocsNote2.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("documents.label.doc_table_note2")));
|
||||
browseDocsPanel.add(browseDocsNote2);
|
||||
|
||||
panel.add(browseDocsPanel, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(documentTable, ListSelectionModel.MULTIPLE_INTERVAL_SELECTION, new DocumentsTableModel(), new MouseAdapter() {
|
||||
TableUtils.setupTable(
|
||||
documentTable,
|
||||
ListSelectionModel.MULTIPLE_INTERVAL_SELECTION,
|
||||
new DocumentsTableModel(),
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.showDocumentContextMenu(e);
|
||||
|
@ -394,7 +416,10 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
flagsHeader.setOpaque(false);
|
||||
flagsHeader.add(new JLabel("Flags"));
|
||||
flagsHeader.add(new JLabel("Help"));
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setHeaderValue(flagsHeader);
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.FLAGS.getIndex())
|
||||
.setHeaderValue(flagsHeader);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane(documentTable);
|
||||
scrollPane.getHorizontalScrollBar().setAutoscrolls(false);
|
||||
|
@ -410,7 +435,10 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
JPanel left = new JPanel(new FlowLayout(FlowLayout.LEADING, 10, 2));
|
||||
left.setOpaque(false);
|
||||
JLabel label = new JLabel(FontUtils.elegantIconHtml("h", MessageUtils.getLocalizedMessage("documents.label.browse_doc_by_idx")));
|
||||
JLabel label =
|
||||
new JLabel(
|
||||
FontUtils.elegantIconHtml(
|
||||
"h", MessageUtils.getLocalizedMessage("documents.label.browse_doc_by_idx")));
|
||||
label.setHorizontalTextPosition(JLabel.LEFT);
|
||||
left.add(label);
|
||||
docNumSpnr.setPreferredSize(new Dimension(100, 25));
|
||||
|
@ -422,15 +450,21 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
JPanel right = new JPanel(new FlowLayout(FlowLayout.TRAILING));
|
||||
right.setOpaque(false);
|
||||
copyDocValuesBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("documents.buttont.copy_values")));
|
||||
copyDocValuesBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("documents.buttont.copy_values")));
|
||||
copyDocValuesBtn.setMargin(new Insets(5, 0, 5, 0));
|
||||
copyDocValuesBtn.addActionListener(listeners::copySelectedOrAllStoredValues);
|
||||
right.add(copyDocValuesBtn);
|
||||
mltBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("documents.button.mlt")));
|
||||
mltBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("documents.button.mlt")));
|
||||
mltBtn.setMargin(new Insets(5, 0, 5, 0));
|
||||
mltBtn.addActionListener(listeners::mltSearch);
|
||||
right.add(mltBtn);
|
||||
addDocBtn.setText(FontUtils.elegantIconHtml("Y", MessageUtils.getLocalizedMessage("documents.button.add")));
|
||||
addDocBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"Y", MessageUtils.getLocalizedMessage("documents.button.add")));
|
||||
addDocBtn.setMargin(new Insets(5, 0, 5, 0));
|
||||
addDocBtn.addActionListener(listeners::showAddDocumentDialog);
|
||||
right.add(addDocBtn);
|
||||
|
@ -441,22 +475,26 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
private void setUpDocumentContextMenu() {
|
||||
// show term vector
|
||||
JMenuItem item1 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item1"));
|
||||
JMenuItem item1 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item1"));
|
||||
item1.addActionListener(listeners::showTermVectorDialog);
|
||||
documentContextMenu.add(item1);
|
||||
|
||||
// show doc values
|
||||
JMenuItem item2 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item2"));
|
||||
JMenuItem item2 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item2"));
|
||||
item2.addActionListener(listeners::showDocValuesDialog);
|
||||
documentContextMenu.add(item2);
|
||||
|
||||
// show stored value
|
||||
JMenuItem item3 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item3"));
|
||||
JMenuItem item3 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item3"));
|
||||
item3.addActionListener(listeners::showStoredValueDialog);
|
||||
documentContextMenu.add(item3);
|
||||
|
||||
// copy stored value to clipboard
|
||||
JMenuItem item4 = new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item4"));
|
||||
JMenuItem item4 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("documents.doctable.menu.item4"));
|
||||
item4.addActionListener(listeners::copyStoredValue);
|
||||
documentContextMenu.add(item4);
|
||||
}
|
||||
|
@ -466,7 +504,8 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
private void showFirstTerm() {
|
||||
String fieldName = (String) fieldsCombo.getSelectedItem();
|
||||
if (fieldName == null || fieldName.length() == 0) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.field.message.not_selected"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("documents.field.message.not_selected"));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -542,9 +581,12 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
messageBroker.clearStatusMessage();
|
||||
}
|
||||
|
||||
|
||||
private void clearPosTable() {
|
||||
TableUtils.setupTable(posTable, ListSelectionModel.SINGLE_SELECTION, new PosTableModel(), null,
|
||||
TableUtils.setupTable(
|
||||
posTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new PosTableModel(),
|
||||
null,
|
||||
PosTableModel.Column.POSITION.getColumnWidth(),
|
||||
PosTableModel.Column.OFFSETS.getColumnWidth(),
|
||||
PosTableModel.Column.PAYLOAD.getColumnWidth());
|
||||
|
@ -555,7 +597,8 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
int docid = documentsModel.firstTermDoc().orElse(-1);
|
||||
if (docid < 0) {
|
||||
nextTermDocBtn.setEnabled(false);
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available"));
|
||||
return;
|
||||
}
|
||||
termDocIdxTF.setText(String.valueOf(1));
|
||||
|
@ -563,9 +606,18 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
List<TermPosting> postings = documentsModel.getTermPositions();
|
||||
posTable.setModel(new PosTableModel(postings));
|
||||
posTable.getColumnModel().getColumn(PosTableModel.Column.POSITION.getIndex()).setPreferredWidth(PosTableModel.Column.POSITION.getColumnWidth());
|
||||
posTable.getColumnModel().getColumn(PosTableModel.Column.OFFSETS.getIndex()).setPreferredWidth(PosTableModel.Column.OFFSETS.getColumnWidth());
|
||||
posTable.getColumnModel().getColumn(PosTableModel.Column.PAYLOAD.getIndex()).setPreferredWidth(PosTableModel.Column.PAYLOAD.getColumnWidth());
|
||||
posTable
|
||||
.getColumnModel()
|
||||
.getColumn(PosTableModel.Column.POSITION.getIndex())
|
||||
.setPreferredWidth(PosTableModel.Column.POSITION.getColumnWidth());
|
||||
posTable
|
||||
.getColumnModel()
|
||||
.getColumn(PosTableModel.Column.OFFSETS.getIndex())
|
||||
.setPreferredWidth(PosTableModel.Column.OFFSETS.getColumnWidth());
|
||||
posTable
|
||||
.getColumnModel()
|
||||
.getColumn(PosTableModel.Column.PAYLOAD.getIndex())
|
||||
.setPreferredWidth(PosTableModel.Column.PAYLOAD.getColumnWidth());
|
||||
|
||||
nextTermDocBtn.setEnabled(true);
|
||||
messageBroker.clearStatusMessage();
|
||||
|
@ -575,7 +627,8 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
int docid = documentsModel.nextTermDoc().orElse(-1);
|
||||
if (docid < 0) {
|
||||
nextTermDocBtn.setEnabled(false);
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("documents.termdocs.message.not_available"));
|
||||
return;
|
||||
}
|
||||
int curIdx = Integer.parseInt(termDocIdxTF.getText());
|
||||
|
@ -596,29 +649,40 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
private void mltSearch() {
|
||||
int docNum = (int) docNumSpnr.getValue();
|
||||
operatorRegistry.get(SearchTabOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(SearchTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.mltSearch(docNum);
|
||||
tabSwitcher.switchTab(TabbedPaneProvider.Tab.SEARCH);
|
||||
});
|
||||
}
|
||||
|
||||
private void showAddDocumentDialog() {
|
||||
new DialogOpener<>(addDocDialogFactory).open("Add document", 600, 500,
|
||||
(factory) -> {
|
||||
});
|
||||
new DialogOpener<>(addDocDialogFactory).open("Add document", 600, 500, (factory) -> {});
|
||||
}
|
||||
|
||||
private void showTermVectorDialog() {
|
||||
int docid = (Integer) docNumSpnr.getValue();
|
||||
String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
String field =
|
||||
(String)
|
||||
documentTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
List<TermVectorEntry> tvEntries = documentsModel.getTermVectors(docid, field);
|
||||
if (tvEntries.isEmpty()) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.termvector.message.not_available", field, docid));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage(
|
||||
"documents.termvector.message.not_available", field, docid));
|
||||
return;
|
||||
}
|
||||
|
||||
new DialogOpener<>(tvDialogFactory).open(
|
||||
"Term Vector", 600, 400,
|
||||
new DialogOpener<>(tvDialogFactory)
|
||||
.open(
|
||||
"Term Vector",
|
||||
600,
|
||||
400,
|
||||
(factory) -> {
|
||||
factory.setField(field);
|
||||
factory.setTvEntries(tvEntries);
|
||||
|
@ -628,30 +692,54 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
private void showDocValuesDialog() {
|
||||
int docid = (Integer) docNumSpnr.getValue();
|
||||
String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
String field =
|
||||
(String)
|
||||
documentTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
Optional<DocValues> docValues = documentsModel.getDocValues(docid, field);
|
||||
if (docValues.isPresent()) {
|
||||
new DialogOpener<>(dvDialogFactory).open(
|
||||
"Doc Values", 400, 300,
|
||||
new DialogOpener<>(dvDialogFactory)
|
||||
.open(
|
||||
"Doc Values",
|
||||
400,
|
||||
300,
|
||||
(factory) -> {
|
||||
factory.setValue(field, docValues.get());
|
||||
});
|
||||
messageBroker.clearStatusMessage();
|
||||
} else {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.docvalues.message.not_available", field, docid));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage(
|
||||
"documents.docvalues.message.not_available", field, docid));
|
||||
}
|
||||
}
|
||||
|
||||
private void showStoredValueDialog() {
|
||||
int docid = (Integer) docNumSpnr.getValue();
|
||||
String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
String value = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.VALUE.getIndex());
|
||||
String field =
|
||||
(String)
|
||||
documentTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
String value =
|
||||
(String)
|
||||
documentTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
documentTable.getSelectedRow(), DocumentsTableModel.Column.VALUE.getIndex());
|
||||
if (Objects.isNull(value)) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid));
|
||||
return;
|
||||
}
|
||||
new DialogOpener<>(valueDialogFactory).open(
|
||||
"Stored Value", 400, 300,
|
||||
new DialogOpener<>(valueDialogFactory)
|
||||
.open(
|
||||
"Stored Value",
|
||||
400,
|
||||
300,
|
||||
(factory) -> {
|
||||
factory.setField(field);
|
||||
factory.setValue(value);
|
||||
|
@ -661,10 +749,21 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
|
||||
private void copyStoredValue() {
|
||||
int docid = (Integer) docNumSpnr.getValue();
|
||||
String field = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
String value = (String) documentTable.getModel().getValueAt(documentTable.getSelectedRow(), DocumentsTableModel.Column.VALUE.getIndex());
|
||||
String field =
|
||||
(String)
|
||||
documentTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
documentTable.getSelectedRow(), DocumentsTableModel.Column.FIELD.getIndex());
|
||||
String value =
|
||||
(String)
|
||||
documentTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
documentTable.getSelectedRow(), DocumentsTableModel.Column.VALUE.getIndex());
|
||||
if (Objects.isNull(value)) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("documents.stored.message.not_availabe", field, docid));
|
||||
return;
|
||||
}
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
|
@ -688,7 +787,9 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
private StringSelection copyAllValues() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < documentTable.getRowCount(); i++) {
|
||||
String value = (String) documentTable.getModel().getValueAt(i, DocumentsTableModel.Column.VALUE.getIndex());
|
||||
String value =
|
||||
(String)
|
||||
documentTable.getModel().getValueAt(i, DocumentsTableModel.Column.VALUE.getIndex());
|
||||
if (Objects.nonNull(value)) {
|
||||
sb.append((i == 0) ? value : System.lineSeparator() + value);
|
||||
}
|
||||
|
@ -700,7 +801,11 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
StringBuilder sb = new StringBuilder();
|
||||
boolean isFirst = true;
|
||||
for (int rowIndex : documentTable.getSelectedRows()) {
|
||||
String value = (String) documentTable.getModel().getValueAt(rowIndex, DocumentsTableModel.Column.VALUE.getIndex());
|
||||
String value =
|
||||
(String)
|
||||
documentTable
|
||||
.getModel()
|
||||
.getValueAt(rowIndex, DocumentsTableModel.Column.VALUE.getIndex());
|
||||
if (Objects.nonNull(value)) {
|
||||
sb.append(isFirst ? value : System.lineSeparator() + value);
|
||||
isFirst = false;
|
||||
|
@ -727,7 +832,6 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
public void displayDoc(int docid) {
|
||||
showDoc(docid);
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
private void showDoc(int docid) {
|
||||
|
@ -736,13 +840,34 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
List<DocumentField> doc = documentsModel.getDocumentFields(docid);
|
||||
documentTable.setModel(new DocumentsTableModel(doc));
|
||||
documentTable.setFont(StyleConstants.FONT_MONOSPACE_LARGE);
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FIELD.getIndex()).setPreferredWidth(DocumentsTableModel.Column.FIELD.getColumnWidth());
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setMinWidth(DocumentsTableModel.Column.FLAGS.getColumnWidth());
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setMaxWidth(DocumentsTableModel.Column.FIELD.getColumnWidth());
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.NORM.getIndex()).setMinWidth(DocumentsTableModel.Column.NORM.getColumnWidth());
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.NORM.getIndex()).setMaxWidth(DocumentsTableModel.Column.NORM.getColumnWidth());
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.VALUE.getIndex()).setPreferredWidth(DocumentsTableModel.Column.VALUE.getColumnWidth());
|
||||
documentTable.getColumnModel().getColumn(DocumentsTableModel.Column.FLAGS.getIndex()).setHeaderRenderer(tableHeaderRenderer);
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.FIELD.getIndex())
|
||||
.setPreferredWidth(DocumentsTableModel.Column.FIELD.getColumnWidth());
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.FLAGS.getIndex())
|
||||
.setMinWidth(DocumentsTableModel.Column.FLAGS.getColumnWidth());
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.FLAGS.getIndex())
|
||||
.setMaxWidth(DocumentsTableModel.Column.FIELD.getColumnWidth());
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.NORM.getIndex())
|
||||
.setMinWidth(DocumentsTableModel.Column.NORM.getColumnWidth());
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.NORM.getIndex())
|
||||
.setMaxWidth(DocumentsTableModel.Column.NORM.getColumnWidth());
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.VALUE.getIndex())
|
||||
.setPreferredWidth(DocumentsTableModel.Column.VALUE.getColumnWidth());
|
||||
documentTable
|
||||
.getColumnModel()
|
||||
.getColumn(DocumentsTableModel.Column.FLAGS.getIndex())
|
||||
.setHeaderRenderer(tableHeaderRenderer);
|
||||
|
||||
messageBroker.clearStatusMessage();
|
||||
}
|
||||
|
@ -810,7 +935,6 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
void copySelectedOrAllStoredValues(ActionEvent e) {
|
||||
DocumentsPanelProvider.this.copySelectedOrAllStoredValues();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver {
|
||||
|
@ -854,7 +978,6 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
static final class PosTableModel extends TableModelBase<PosTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
|
||||
POSITION("Position", 0, Integer.class, 80),
|
||||
OFFSETS("Offsets", 1, String.class, 120),
|
||||
PAYLOAD("Payload", 2, String.class, 300);
|
||||
|
@ -1110,6 +1233,4 @@ public final class DocumentsPanelProvider implements DocumentsTabOperator {
|
|||
return Column.values();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
|
||||
import org.apache.lucene.luke.app.desktop.LukeMain;
|
||||
import org.apache.lucene.luke.app.desktop.util.MessageUtils;
|
||||
|
||||
|
@ -54,5 +53,4 @@ public final class LogsPanelProvider {
|
|||
panel.add(new JScrollPane(logTextArea), BorderLayout.CENTER);
|
||||
return panel;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,14 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.WindowConstants;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
|
@ -33,7 +25,14 @@ import java.awt.GridBagConstraints;
|
|||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.WindowConstants;
|
||||
import org.apache.lucene.luke.app.DirectoryHandler;
|
||||
import org.apache.lucene.luke.app.DirectoryObserver;
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
|
@ -51,7 +50,8 @@ import org.apache.lucene.util.Version;
|
|||
/** Provider of the root window */
|
||||
public final class LukeWindowProvider implements LukeWindowOperator {
|
||||
|
||||
private static final String WINDOW_TITLE = MessageUtils.getLocalizedMessage("window.title") + " - v" + Version.LATEST.toString();
|
||||
private static final String WINDOW_TITLE =
|
||||
MessageUtils.getLocalizedMessage("window.title") + " - v" + Version.LATEST.toString();
|
||||
|
||||
private final Preferences prefs;
|
||||
|
||||
|
@ -149,7 +149,6 @@ public final class LukeWindowProvider implements LukeWindowOperator {
|
|||
multiIcon.setVisible(false);
|
||||
iconPanel.add(multiIcon);
|
||||
|
||||
|
||||
readOnlyIcon.setText(FontUtils.elegantIconHtml(""));
|
||||
readOnlyIcon.setToolTipText(MessageUtils.getLocalizedMessage("tooltip.read_only"));
|
||||
readOnlyIcon.setVisible(false);
|
||||
|
@ -206,9 +205,11 @@ public final class LukeWindowProvider implements LukeWindowOperator {
|
|||
noReaderIcon.setVisible(false);
|
||||
|
||||
if (state.readOnly()) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("message.index_opened_ro"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("message.index_opened_ro"));
|
||||
} else if (!state.hasDirectoryReader()) {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("message.index_opened_multi"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("message.index_opened_multi"));
|
||||
} else {
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("message.index_opened"));
|
||||
}
|
||||
|
@ -222,7 +223,6 @@ public final class LukeWindowProvider implements LukeWindowOperator {
|
|||
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("message.index_closed"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class MessageReceiverImpl implements MessageBroker.MessageReceiver {
|
||||
|
@ -242,9 +242,6 @@ public final class LukeWindowProvider implements LukeWindowOperator {
|
|||
messageLbl.setText("");
|
||||
}
|
||||
|
||||
private MessageReceiverImpl() {
|
||||
private MessageReceiverImpl() {}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,12 +17,11 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import javax.swing.JMenu;
|
||||
import javax.swing.JMenuBar;
|
||||
import javax.swing.JMenuItem;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.luke.app.DirectoryHandler;
|
||||
import org.apache.lucene.luke.app.DirectoryObserver;
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
|
@ -135,7 +134,6 @@ public final class MenuBarProvider {
|
|||
createIndexMItem.addActionListener(listeners::showCreateIndexDialog);
|
||||
fileMenu.add(createIndexMItem);
|
||||
|
||||
|
||||
closeIndexMItem.setText(MessageUtils.getLocalizedMessage("menu.item.close_index"));
|
||||
closeIndexMItem.setEnabled(false);
|
||||
closeIndexMItem.addActionListener(listeners::closeIndex);
|
||||
|
@ -197,12 +195,20 @@ public final class MenuBarProvider {
|
|||
private class ListenerFunctions {
|
||||
|
||||
void showOpenIndexDialog(ActionEvent e) {
|
||||
new DialogOpener<>(openIndexDialogFactory).open(MessageUtils.getLocalizedMessage("openindex.dialog.title"), 600, 420,
|
||||
new DialogOpener<>(openIndexDialogFactory)
|
||||
.open(
|
||||
MessageUtils.getLocalizedMessage("openindex.dialog.title"),
|
||||
600,
|
||||
420,
|
||||
(factory) -> {});
|
||||
}
|
||||
|
||||
void showCreateIndexDialog(ActionEvent e) {
|
||||
new DialogOpener<>(createIndexDialogFactory).open(MessageUtils.getLocalizedMessage("createindex.dialog.title"), 600, 360,
|
||||
new DialogOpener<>(createIndexDialogFactory)
|
||||
.open(
|
||||
MessageUtils.getLocalizedMessage("createindex.dialog.title"),
|
||||
600,
|
||||
360,
|
||||
(factory) -> {});
|
||||
}
|
||||
|
||||
|
@ -233,7 +239,9 @@ public final class MenuBarProvider {
|
|||
private void changeTheme(Preferences.ColorTheme theme) {
|
||||
try {
|
||||
prefs.setColorTheme(theme);
|
||||
operatorRegistry.get(LukeWindowOperator.class).ifPresent(operator -> operator.setColorTheme(theme));
|
||||
operatorRegistry
|
||||
.get(LukeWindowOperator.class)
|
||||
.ifPresent(operator -> operator.setColorTheme(theme));
|
||||
} catch (IOException e) {
|
||||
throw new LukeException("Failed to set color theme : " + theme.name(), e);
|
||||
}
|
||||
|
@ -250,30 +258,22 @@ public final class MenuBarProvider {
|
|||
}
|
||||
|
||||
void showOptimizeIndexDialog(ActionEvent e) {
|
||||
new DialogOpener<>(optimizeIndexDialogFactory).open("Optimize index", 600, 600,
|
||||
factory -> {
|
||||
});
|
||||
new DialogOpener<>(optimizeIndexDialogFactory)
|
||||
.open("Optimize index", 600, 600, factory -> {});
|
||||
}
|
||||
|
||||
void showCheckIndexDialog(ActionEvent e) {
|
||||
new DialogOpener<>(checkIndexDialogFactory).open("Check index", 600, 600,
|
||||
factory -> {
|
||||
});
|
||||
new DialogOpener<>(checkIndexDialogFactory).open("Check index", 600, 600, factory -> {});
|
||||
}
|
||||
|
||||
void showAboutDialog(ActionEvent e) {
|
||||
final String title = "About Luke v" + Version.LATEST.toString();
|
||||
new DialogOpener<>(aboutDialogFactory).open(title, 800, 480,
|
||||
factory -> {
|
||||
});
|
||||
new DialogOpener<>(aboutDialogFactory).open(title, 800, 480, factory -> {});
|
||||
}
|
||||
|
||||
void showExportTermsDialog(ActionEvent e) {
|
||||
new DialogOpener<>(exportTermsDialogFactory).open("Export terms", 600, 450,
|
||||
factory -> {
|
||||
});
|
||||
new DialogOpener<>(exportTermsDialogFactory).open("Export terms", 600, 450, factory -> {});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver, DirectoryObserver {
|
||||
|
@ -317,6 +317,5 @@ public final class MenuBarProvider {
|
|||
checkIndexMItem.setEnabled(false);
|
||||
exportTermsMItem.setEnabled(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,20 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
|
@ -33,21 +47,6 @@ import javax.swing.ListSelectionModel;
|
|||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
import org.apache.lucene.luke.app.IndexObserver;
|
||||
import org.apache.lucene.luke.app.LukeState;
|
||||
|
@ -127,7 +126,8 @@ public final class OverviewPanelProvider {
|
|||
panel.setLayout(new GridLayout(1, 1));
|
||||
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
splitPane.setDividerLocation(0.4);
|
||||
splitPane.setOpaque(false);
|
||||
panel.add(splitPane);
|
||||
|
@ -149,7 +149,8 @@ public final class OverviewPanelProvider {
|
|||
|
||||
c.gridx = GRIDX_DESC;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.index_path"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.index_path"), JLabel.RIGHT), c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -159,7 +160,8 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.num_fields"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.num_fields"), JLabel.RIGHT), c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -169,7 +171,8 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.num_docs"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.num_docs"), JLabel.RIGHT), c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -179,7 +182,8 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.num_terms"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.num_terms"), JLabel.RIGHT), c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -189,7 +193,8 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.del_opt"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.del_opt"), JLabel.RIGHT), c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -199,7 +204,9 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.index_version"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.index_version"), JLabel.RIGHT),
|
||||
c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -209,7 +216,9 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.index_format"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.index_format"), JLabel.RIGHT),
|
||||
c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -219,7 +228,8 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.dir_impl"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.dir_impl"), JLabel.RIGHT), c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -229,7 +239,9 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.commit_point"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("overview.label.commit_point"), JLabel.RIGHT),
|
||||
c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -239,7 +251,10 @@ public final class OverviewPanelProvider {
|
|||
c.gridx = GRIDX_DESC;
|
||||
c.gridy += 1;
|
||||
c.weightx = WEIGHTX_DESC;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("overview.label.commit_userdata"), JLabel.RIGHT), c);
|
||||
panel.add(
|
||||
new JLabel(
|
||||
MessageUtils.getLocalizedMessage("overview.label.commit_userdata"), JLabel.RIGHT),
|
||||
c);
|
||||
|
||||
c.gridx = GRIDX_VAL;
|
||||
c.weightx = WEIGHTX_VAL;
|
||||
|
@ -257,7 +272,8 @@ public final class OverviewPanelProvider {
|
|||
label.setBorder(BorderFactory.createEmptyBorder(5, 10, 5, 10));
|
||||
panel.add(label, BorderLayout.PAGE_START);
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, initTermCountsPanel(), initTopTermsPanel());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, initTermCountsPanel(), initTopTermsPanel());
|
||||
splitPane.setOpaque(false);
|
||||
splitPane.setDividerLocation(320);
|
||||
splitPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
|
@ -274,13 +290,18 @@ public final class OverviewPanelProvider {
|
|||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
|
||||
panel.add(label, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(termCountsTable, ListSelectionModel.SINGLE_SELECTION, new TermCountsTableModel(),
|
||||
TableUtils.setupTable(
|
||||
termCountsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new TermCountsTableModel(),
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.selectField(e);
|
||||
}
|
||||
}, TermCountsTableModel.Column.NAME.getColumnWidth(), TermCountsTableModel.Column.TERM_COUNT.getColumnWidth());
|
||||
},
|
||||
TermCountsTableModel.Column.NAME.getColumnWidth(),
|
||||
TermCountsTableModel.Column.TERM_COUNT.getColumnWidth());
|
||||
JScrollPane scrollPane = new JScrollPane(termCountsTable);
|
||||
panel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
|
@ -344,13 +365,18 @@ public final class OverviewPanelProvider {
|
|||
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0));
|
||||
termsPanel.add(label, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(topTermsTable, ListSelectionModel.SINGLE_SELECTION, new TopTermsTableModel(),
|
||||
TableUtils.setupTable(
|
||||
topTermsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new TopTermsTableModel(),
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.showTopTermsContextMenu(e);
|
||||
}
|
||||
}, TopTermsTableModel.Column.RANK.getColumnWidth(), TopTermsTableModel.Column.FREQ.getColumnWidth());
|
||||
},
|
||||
TopTermsTableModel.Column.RANK.getColumnWidth(),
|
||||
TopTermsTableModel.Column.FREQ.getColumnWidth());
|
||||
JScrollPane scrollPane = new JScrollPane(topTermsTable);
|
||||
termsPanel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
|
@ -364,11 +390,13 @@ public final class OverviewPanelProvider {
|
|||
}
|
||||
|
||||
private void setUpTopTermsContextMenu() {
|
||||
JMenuItem item1 = new JMenuItem(MessageUtils.getLocalizedMessage("overview.toptermtable.menu.item1"));
|
||||
JMenuItem item1 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("overview.toptermtable.menu.item1"));
|
||||
item1.addActionListener(listeners::browseByTerm);
|
||||
topTermsContextMenu.add(item1);
|
||||
|
||||
JMenuItem item2 = new JMenuItem(MessageUtils.getLocalizedMessage("overview.toptermtable.menu.item2"));
|
||||
JMenuItem item2 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("overview.toptermtable.menu.item2"));
|
||||
item2.addActionListener(listeners::searchByTerm);
|
||||
topTermsContextMenu.add(item2);
|
||||
}
|
||||
|
@ -388,15 +416,24 @@ public final class OverviewPanelProvider {
|
|||
|
||||
// update top terms table
|
||||
topTermsTable.setModel(new TopTermsTableModel(termStats, numTerms));
|
||||
topTermsTable.getColumnModel().getColumn(TopTermsTableModel.Column.RANK.getIndex()).setMaxWidth(TopTermsTableModel.Column.RANK.getColumnWidth());
|
||||
topTermsTable.getColumnModel().getColumn(TopTermsTableModel.Column.FREQ.getIndex()).setMaxWidth(TopTermsTableModel.Column.FREQ.getColumnWidth());
|
||||
topTermsTable
|
||||
.getColumnModel()
|
||||
.getColumn(TopTermsTableModel.Column.RANK.getIndex())
|
||||
.setMaxWidth(TopTermsTableModel.Column.RANK.getColumnWidth());
|
||||
topTermsTable
|
||||
.getColumnModel()
|
||||
.getColumn(TopTermsTableModel.Column.FREQ.getIndex())
|
||||
.setMaxWidth(TopTermsTableModel.Column.FREQ.getColumnWidth());
|
||||
messageBroker.clearStatusMessage();
|
||||
}
|
||||
|
||||
private void browseByTerm() {
|
||||
String field = getSelectedField();
|
||||
String term = getSelectedTerm();
|
||||
operatorRegistry.get(DocumentsTabOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(DocumentsTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.browseTerm(field, term);
|
||||
tabSwitcher.switchTab(TabbedPaneProvider.Tab.DOCUMENTS);
|
||||
});
|
||||
|
@ -405,7 +442,10 @@ public final class OverviewPanelProvider {
|
|||
private void searchByTerm() {
|
||||
String field = getSelectedField();
|
||||
String term = getSelectedTerm();
|
||||
operatorRegistry.get(SearchTabOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(SearchTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.searchByTerm(field, term);
|
||||
tabSwitcher.switchTab(TabbedPaneProvider.Tab.SEARCH);
|
||||
});
|
||||
|
@ -419,7 +459,8 @@ public final class OverviewPanelProvider {
|
|||
if (row < 0 || row >= termCountsTable.getRowCount()) {
|
||||
throw new IllegalStateException("Field is not selected.");
|
||||
}
|
||||
return (String) termCountsTable.getModel().getValueAt(row, TermCountsTableModel.Column.NAME.getIndex());
|
||||
return (String)
|
||||
termCountsTable.getModel().getValueAt(row, TermCountsTableModel.Column.NAME.getIndex());
|
||||
}
|
||||
|
||||
private String getSelectedTerm() {
|
||||
|
@ -427,7 +468,8 @@ public final class OverviewPanelProvider {
|
|||
if (rowTerm < 0 || rowTerm >= topTermsTable.getRowCount()) {
|
||||
throw new IllegalStateException("Term is not selected.");
|
||||
}
|
||||
return (String) topTermsTable.getModel().getValueAt(rowTerm, TopTermsTableModel.Column.TEXT.getIndex());
|
||||
return (String)
|
||||
topTermsTable.getModel().getValueAt(rowTerm, TopTermsTableModel.Column.TEXT.getIndex());
|
||||
}
|
||||
|
||||
private class ListenerFunctions {
|
||||
|
@ -457,7 +499,6 @@ public final class OverviewPanelProvider {
|
|||
void searchByTerm(ActionEvent e) {
|
||||
OverviewPanelProvider.this.searchByTerm();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver {
|
||||
|
@ -471,7 +512,10 @@ public final class OverviewPanelProvider {
|
|||
numFieldsLbl.setText(Integer.toString(overviewModel.getNumFields()));
|
||||
numDocsLbl.setText(Integer.toString(overviewModel.getNumDocuments()));
|
||||
numTermsLbl.setText(Long.toString(overviewModel.getNumTerms()));
|
||||
String del = overviewModel.hasDeletions() ? String.format(Locale.ENGLISH, "Yes (%d)", overviewModel.getNumDeletedDocs()) : "No";
|
||||
String del =
|
||||
overviewModel.hasDeletions()
|
||||
? String.format(Locale.ENGLISH, "Yes (%d)", overviewModel.getNumDeletedDocs())
|
||||
: "No";
|
||||
String opt = overviewModel.isOptimized().map(b -> b ? "Yes" : "No").orElse("?");
|
||||
delOptLbl.setText(del + " / " + opt);
|
||||
indexVerLbl.setText(overviewModel.getIndexVersion().map(v -> Long.toString(v)).orElse("?"));
|
||||
|
@ -485,16 +529,31 @@ public final class OverviewPanelProvider {
|
|||
long numTerms = overviewModel.getNumTerms();
|
||||
termCountsTable.setModel(new TermCountsTableModel(numTerms, termCounts));
|
||||
termCountsTable.setRowSorter(new TableRowSorter<>(termCountsTable.getModel()));
|
||||
termCountsTable.getColumnModel().getColumn(TermCountsTableModel.Column.NAME.getIndex()).setMaxWidth(TermCountsTableModel.Column.NAME.getColumnWidth());
|
||||
termCountsTable.getColumnModel().getColumn(TermCountsTableModel.Column.TERM_COUNT.getIndex()).setMaxWidth(TermCountsTableModel.Column.TERM_COUNT.getColumnWidth());
|
||||
termCountsTable
|
||||
.getColumnModel()
|
||||
.getColumn(TermCountsTableModel.Column.NAME.getIndex())
|
||||
.setMaxWidth(TermCountsTableModel.Column.NAME.getColumnWidth());
|
||||
termCountsTable
|
||||
.getColumnModel()
|
||||
.getColumn(TermCountsTableModel.Column.TERM_COUNT.getIndex())
|
||||
.setMaxWidth(TermCountsTableModel.Column.TERM_COUNT.getColumnWidth());
|
||||
DefaultTableCellRenderer rightRenderer = new DefaultTableCellRenderer();
|
||||
rightRenderer.setHorizontalAlignment(JLabel.RIGHT);
|
||||
termCountsTable.getColumnModel().getColumn(TermCountsTableModel.Column.RATIO.getIndex()).setCellRenderer(rightRenderer);
|
||||
termCountsTable
|
||||
.getColumnModel()
|
||||
.getColumn(TermCountsTableModel.Column.RATIO.getIndex())
|
||||
.setCellRenderer(rightRenderer);
|
||||
|
||||
// top terms table
|
||||
topTermsTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
topTermsTable.getColumnModel().getColumn(TopTermsTableModel.Column.RANK.getIndex()).setMaxWidth(TopTermsTableModel.Column.RANK.getColumnWidth());
|
||||
topTermsTable.getColumnModel().getColumn(TopTermsTableModel.Column.FREQ.getIndex()).setMaxWidth(TopTermsTableModel.Column.FREQ.getColumnWidth());
|
||||
topTermsTable
|
||||
.getColumnModel()
|
||||
.getColumn(TopTermsTableModel.Column.RANK.getIndex())
|
||||
.setMaxWidth(TopTermsTableModel.Column.RANK.getColumnWidth());
|
||||
topTermsTable
|
||||
.getColumnModel()
|
||||
.getColumn(TopTermsTableModel.Column.FREQ.getIndex())
|
||||
.setMaxWidth(TopTermsTableModel.Column.FREQ.getColumnWidth());
|
||||
topTermsTable.getColumnModel().setColumnMargin(StyleConstants.TABLE_COLUMN_MARGIN_DEFAULT);
|
||||
}
|
||||
|
||||
|
@ -518,13 +577,11 @@ public final class OverviewPanelProvider {
|
|||
termCountsTable.setModel(new TermCountsTableModel());
|
||||
topTermsTable.setModel(new TopTermsTableModel());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static final class TermCountsTableModel extends TableModelBase<TermCountsTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
|
||||
NAME("Name", 0, String.class, 150),
|
||||
TERM_COUNT("Term count", 1, Long.class, 100),
|
||||
RATIO("%", 2, String.class, Integer.MAX_VALUE);
|
||||
|
@ -572,7 +629,10 @@ public final class OverviewPanelProvider {
|
|||
for (Map.Entry<String, Long> e : termCounts.entrySet()) {
|
||||
String term = e.getKey();
|
||||
Long count = e.getValue();
|
||||
data[i++] = new Object[]{term, count, String.format(Locale.ENGLISH, "%.2f %%", count / numTerms * 100)};
|
||||
data[i++] =
|
||||
new Object[] {
|
||||
term, count, String.format(Locale.ENGLISH, "%.2f %%", count / numTerms * 100)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,21 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
|
@ -51,7 +36,21 @@ import java.util.Locale;
|
|||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JFormattedTextField;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JPopupMenu;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
import org.apache.lucene.index.Term;
|
||||
|
@ -192,7 +191,8 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
panel.setOpaque(false);
|
||||
panel.setBorder(BorderFactory.createLineBorder(Color.gray));
|
||||
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.VERTICAL_SPLIT, initUpperPanel(), initLowerPanel());
|
||||
splitPane.setOpaque(false);
|
||||
splitPane.setDividerLocation(350);
|
||||
panel.add(splitPane);
|
||||
|
@ -201,7 +201,8 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
}
|
||||
|
||||
private JSplitPane initUpperPanel() {
|
||||
JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, initQuerySettingsPane(), initQueryPane());
|
||||
JSplitPane splitPane =
|
||||
new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, initQuerySettingsPane(), initQueryPane());
|
||||
splitPane.setOpaque(false);
|
||||
splitPane.setDividerLocation(570);
|
||||
return splitPane;
|
||||
|
@ -263,7 +264,12 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
c.gridwidth = 3;
|
||||
c.weightx = 0.0;
|
||||
c.insets = new Insets(2, 0, 2, 2);
|
||||
panel.add(new JScrollPane(queryStringTA, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER), c);
|
||||
panel.add(
|
||||
new JScrollPane(
|
||||
queryStringTA,
|
||||
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
|
||||
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER),
|
||||
c);
|
||||
|
||||
JLabel labelPQ = new JLabel(MessageUtils.getLocalizedMessage("search.label.parsed"));
|
||||
c.gridx = 0;
|
||||
|
@ -283,7 +289,9 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
c.insets = new Insets(2, 0, 2, 2);
|
||||
panel.add(new JScrollPane(parsedQueryTA), c);
|
||||
|
||||
parseBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("search.button.parse")));
|
||||
parseBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("search.button.parse")));
|
||||
parseBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
parseBtn.setMargin(new Insets(3, 0, 3, 0));
|
||||
parseBtn.addActionListener(listeners::execParse);
|
||||
|
@ -303,7 +311,9 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
c.insets = new Insets(5, 0, 0, 2);
|
||||
panel.add(rewriteCB, c);
|
||||
|
||||
searchBtn.setText(FontUtils.elegantIconHtml("U", MessageUtils.getLocalizedMessage("search.button.search")));
|
||||
searchBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"U", MessageUtils.getLocalizedMessage("search.button.search")));
|
||||
searchBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
searchBtn.setMargin(new Insets(3, 0, 3, 0));
|
||||
searchBtn.addActionListener(listeners::execSearch);
|
||||
|
@ -323,7 +333,9 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
c.insets = new Insets(5, 0, 0, 2);
|
||||
panel.add(exactHitsCntCB, c);
|
||||
|
||||
mltBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("search.button.mlt")));
|
||||
mltBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("search.button.mlt")));
|
||||
mltBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
mltBtn.setMargin(new Insets(3, 0, 3, 0));
|
||||
mltBtn.addActionListener(listeners::execMLTSearch);
|
||||
|
@ -366,7 +378,10 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
JPanel panel = new JPanel(new GridLayout(1, 2));
|
||||
panel.setOpaque(false);
|
||||
|
||||
JLabel label = new JLabel(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("search.label.results")));
|
||||
JLabel label =
|
||||
new JLabel(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("search.label.results")));
|
||||
label.setHorizontalTextPosition(JLabel.LEFT);
|
||||
label.setBorder(BorderFactory.createEmptyBorder(2, 0, 2, 0));
|
||||
panel.add(label);
|
||||
|
@ -407,7 +422,9 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
sep.setPreferredSize(new Dimension(5, 1));
|
||||
resultsInfo.add(sep);
|
||||
|
||||
delBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("search.button.del_all")));
|
||||
delBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("search.button.del_all")));
|
||||
delBtn.setMargin(new Insets(5, 0, 5, 0));
|
||||
delBtn.setEnabled(false);
|
||||
delBtn.addActionListener(listeners::confirmDeletion);
|
||||
|
@ -427,7 +444,10 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
note.add(new JLabel(MessageUtils.getLocalizedMessage("search.label.results.note")));
|
||||
panel.add(note, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(resultsTable, ListSelectionModel.SINGLE_SELECTION, new SearchResultsTableModel(),
|
||||
TableUtils.setupTable(
|
||||
resultsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new SearchResultsTableModel(),
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
|
@ -456,10 +476,10 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
tabbedPane.setEnabledAt(Tab.QPARSER.index(), false);
|
||||
tabbedPane.setEnabledAt(Tab.ANALYZER.index(), false);
|
||||
tabbedPane.setEnabledAt(Tab.SIMILARITY.index(), false);
|
||||
if (tabbedPane.getSelectedIndex() == Tab.QPARSER.index() ||
|
||||
tabbedPane.getSelectedIndex() == Tab.ANALYZER.index() ||
|
||||
tabbedPane.getSelectedIndex() == Tab.SIMILARITY.index() ||
|
||||
tabbedPane.getSelectedIndex() == Tab.MLT.index()) {
|
||||
if (tabbedPane.getSelectedIndex() == Tab.QPARSER.index()
|
||||
|| tabbedPane.getSelectedIndex() == Tab.ANALYZER.index()
|
||||
|| tabbedPane.getSelectedIndex() == Tab.SIMILARITY.index()
|
||||
|| tabbedPane.getSelectedIndex() == Tab.MLT.index()) {
|
||||
tabbedPane.setSelectedIndex(Tab.SORT.index());
|
||||
}
|
||||
parseBtn.setEnabled(false);
|
||||
|
@ -489,24 +509,34 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
}
|
||||
String[] tmp = queryStringTA.getText().split(":");
|
||||
if (tmp.length < 2) {
|
||||
throw new LukeException(String.format(Locale.ENGLISH, "Invalid query [ %s ]", queryStringTA.getText()));
|
||||
throw new LukeException(
|
||||
String.format(Locale.ENGLISH, "Invalid query [ %s ]", queryStringTA.getText()));
|
||||
}
|
||||
query = new TermQuery(new Term(tmp[0].trim(), tmp[1].trim()));
|
||||
} else {
|
||||
query = parse(false);
|
||||
}
|
||||
SimilarityConfig simConfig = operatorRegistry.get(SimilarityTabOperator.class)
|
||||
SimilarityConfig simConfig =
|
||||
operatorRegistry
|
||||
.get(SimilarityTabOperator.class)
|
||||
.map(SimilarityTabOperator::getConfig)
|
||||
.orElse(new SimilarityConfig.Builder().build());
|
||||
Sort sort = operatorRegistry.get(SortTabOperator.class)
|
||||
.map(SortTabOperator::getSort)
|
||||
.orElse(null);
|
||||
Set<String> fieldsToLoad = operatorRegistry.get(FieldValuesTabOperator.class)
|
||||
Sort sort =
|
||||
operatorRegistry.get(SortTabOperator.class).map(SortTabOperator::getSort).orElse(null);
|
||||
Set<String> fieldsToLoad =
|
||||
operatorRegistry
|
||||
.get(FieldValuesTabOperator.class)
|
||||
.map(FieldValuesTabOperator::getFieldsToLoad)
|
||||
.orElse(Collections.emptySet());
|
||||
SearchResults results = searchModel.search(query, simConfig, sort, fieldsToLoad, DEFAULT_PAGE_SIZE, exactHitsCntCB.isSelected());
|
||||
SearchResults results =
|
||||
searchModel.search(
|
||||
query, simConfig, sort, fieldsToLoad, DEFAULT_PAGE_SIZE, exactHitsCntCB.isSelected());
|
||||
|
||||
TableUtils.setupTable(resultsTable, ListSelectionModel.SINGLE_SELECTION, new SearchResultsTableModel(), null,
|
||||
TableUtils.setupTable(
|
||||
resultsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new SearchResultsTableModel(),
|
||||
null,
|
||||
SearchResultsTableModel.Column.DOCID.getColumnWidth(),
|
||||
SearchResultsTableModel.Column.SCORE.getColumnWidth());
|
||||
populateResults(results);
|
||||
|
@ -529,19 +559,31 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
throw new LukeException("Doc num is not set.");
|
||||
}
|
||||
int docNum = (int) mltDocFTF.getValue();
|
||||
MLTConfig mltConfig = operatorRegistry.get(MLTTabOperator.class)
|
||||
MLTConfig mltConfig =
|
||||
operatorRegistry
|
||||
.get(MLTTabOperator.class)
|
||||
.map(MLTTabOperator::getConfig)
|
||||
.orElse(new MLTConfig.Builder().build());
|
||||
Analyzer analyzer = operatorRegistry.get(AnalysisTabOperator.class)
|
||||
Analyzer analyzer =
|
||||
operatorRegistry
|
||||
.get(AnalysisTabOperator.class)
|
||||
.map(AnalysisTabOperator::getCurrentAnalyzer)
|
||||
.orElse(new StandardAnalyzer());
|
||||
Query query = searchModel.mltQuery(docNum, mltConfig, analyzer);
|
||||
Set<String> fieldsToLoad = operatorRegistry.get(FieldValuesTabOperator.class)
|
||||
Set<String> fieldsToLoad =
|
||||
operatorRegistry
|
||||
.get(FieldValuesTabOperator.class)
|
||||
.map(FieldValuesTabOperator::getFieldsToLoad)
|
||||
.orElse(Collections.emptySet());
|
||||
SearchResults results = searchModel.search(query, new SimilarityConfig.Builder().build(), fieldsToLoad, DEFAULT_PAGE_SIZE, false);
|
||||
SearchResults results =
|
||||
searchModel.search(
|
||||
query, new SimilarityConfig.Builder().build(), fieldsToLoad, DEFAULT_PAGE_SIZE, false);
|
||||
|
||||
TableUtils.setupTable(resultsTable, ListSelectionModel.SINGLE_SELECTION, new SearchResultsTableModel(), null,
|
||||
TableUtils.setupTable(
|
||||
resultsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new SearchResultsTableModel(),
|
||||
null,
|
||||
SearchResultsTableModel.Column.DOCID.getColumnWidth(),
|
||||
SearchResultsTableModel.Column.SCORE.getColumnWidth());
|
||||
populateResults(results);
|
||||
|
@ -550,14 +592,21 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
}
|
||||
|
||||
private Query parse(boolean rewrite) {
|
||||
String expr = StringUtils.isNullOrEmpty(queryStringTA.getText()) ? "*:*" : queryStringTA.getText();
|
||||
String df = operatorRegistry.get(QueryParserTabOperator.class)
|
||||
String expr =
|
||||
StringUtils.isNullOrEmpty(queryStringTA.getText()) ? "*:*" : queryStringTA.getText();
|
||||
String df =
|
||||
operatorRegistry
|
||||
.get(QueryParserTabOperator.class)
|
||||
.map(QueryParserTabOperator::getDefaultField)
|
||||
.orElse("");
|
||||
QueryParserConfig config = operatorRegistry.get(QueryParserTabOperator.class)
|
||||
QueryParserConfig config =
|
||||
operatorRegistry
|
||||
.get(QueryParserTabOperator.class)
|
||||
.map(QueryParserTabOperator::getConfig)
|
||||
.orElse(new QueryParserConfig.Builder().build());
|
||||
Analyzer analyzer = operatorRegistry.get(AnalysisTabOperator.class)
|
||||
Analyzer analyzer =
|
||||
operatorRegistry
|
||||
.get(AnalysisTabOperator.class)
|
||||
.map(AnalysisTabOperator::getCurrentAnalyzer)
|
||||
.orElse(new StandardAnalyzer());
|
||||
return searchModel.parseQuery(expr, df, analyzer, config, rewrite);
|
||||
|
@ -570,16 +619,27 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
endLbl.setText(String.valueOf(res.getOffset() + res.size()));
|
||||
|
||||
prevBtn.setEnabled(res.getOffset() > 0);
|
||||
nextBtn.setEnabled(res.getTotalHits().relation == TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO || res.getTotalHits().value > res.getOffset() + res.size());
|
||||
nextBtn.setEnabled(
|
||||
res.getTotalHits().relation == TotalHits.Relation.GREATER_THAN_OR_EQUAL_TO
|
||||
|| res.getTotalHits().value > res.getOffset() + res.size());
|
||||
|
||||
if (!indexHandler.getState().readOnly() && indexHandler.getState().hasDirectoryReader()) {
|
||||
delBtn.setEnabled(true);
|
||||
}
|
||||
|
||||
resultsTable.setModel(new SearchResultsTableModel(res));
|
||||
resultsTable.getColumnModel().getColumn(SearchResultsTableModel.Column.DOCID.getIndex()).setPreferredWidth(SearchResultsTableModel.Column.DOCID.getColumnWidth());
|
||||
resultsTable.getColumnModel().getColumn(SearchResultsTableModel.Column.SCORE.getIndex()).setPreferredWidth(SearchResultsTableModel.Column.SCORE.getColumnWidth());
|
||||
resultsTable.getColumnModel().getColumn(SearchResultsTableModel.Column.VALUE.getIndex()).setPreferredWidth(SearchResultsTableModel.Column.VALUE.getColumnWidth());
|
||||
resultsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SearchResultsTableModel.Column.DOCID.getIndex())
|
||||
.setPreferredWidth(SearchResultsTableModel.Column.DOCID.getColumnWidth());
|
||||
resultsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SearchResultsTableModel.Column.SCORE.getIndex())
|
||||
.setPreferredWidth(SearchResultsTableModel.Column.SCORE.getColumnWidth());
|
||||
resultsTable
|
||||
.getColumnModel()
|
||||
.getColumn(SearchResultsTableModel.Column.VALUE.getIndex())
|
||||
.setPreferredWidth(SearchResultsTableModel.Column.VALUE.getColumnWidth());
|
||||
} else {
|
||||
startLbl.setText("0");
|
||||
endLbl.setText("0");
|
||||
|
@ -590,7 +650,12 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
}
|
||||
|
||||
private void confirmDeletion() {
|
||||
new DialogOpener<>(confirmDialogFactory).open("Confirm Deletion", 400, 200, (factory) -> {
|
||||
new DialogOpener<>(confirmDialogFactory)
|
||||
.open(
|
||||
"Confirm Deletion",
|
||||
400,
|
||||
200,
|
||||
(factory) -> {
|
||||
factory.setMessage(MessageUtils.getLocalizedMessage("search.message.delete_confirm"));
|
||||
factory.setCallback(this::deleteDocs);
|
||||
});
|
||||
|
@ -601,7 +666,8 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
if (query != null) {
|
||||
toolsModel.deleteDocuments(query);
|
||||
indexHandler.reOpen();
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("search.message.delete_success", query.toString()));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("search.message.delete_success", query.toString()));
|
||||
}
|
||||
delBtn.setEnabled(false);
|
||||
}
|
||||
|
@ -610,11 +676,23 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
JPopupMenu popup = new JPopupMenu();
|
||||
|
||||
// show explanation
|
||||
JMenuItem item1 = new JMenuItem(MessageUtils.getLocalizedMessage("search.results.menu.explain"));
|
||||
item1.addActionListener(e -> {
|
||||
int docid = (int) resultsTable.getModel().getValueAt(resultsTable.getSelectedRow(), SearchResultsTableModel.Column.DOCID.getIndex());
|
||||
JMenuItem item1 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("search.results.menu.explain"));
|
||||
item1.addActionListener(
|
||||
e -> {
|
||||
int docid =
|
||||
(int)
|
||||
resultsTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
resultsTable.getSelectedRow(),
|
||||
SearchResultsTableModel.Column.DOCID.getIndex());
|
||||
Explanation explanation = searchModel.explain(parse(false), docid);
|
||||
new DialogOpener<>(explainDialogProvider).open("Explanation", 600, 400,
|
||||
new DialogOpener<>(explainDialogProvider)
|
||||
.open(
|
||||
"Explanation",
|
||||
600,
|
||||
400,
|
||||
(factory) -> {
|
||||
factory.setDocid(docid);
|
||||
factory.setExplanation(explanation);
|
||||
|
@ -623,10 +701,20 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
popup.add(item1);
|
||||
|
||||
// show all fields
|
||||
JMenuItem item2 = new JMenuItem(MessageUtils.getLocalizedMessage("search.results.menu.showdoc"));
|
||||
item2.addActionListener(e -> {
|
||||
int docid = (int) resultsTable.getModel().getValueAt(resultsTable.getSelectedRow(), SearchResultsTableModel.Column.DOCID.getIndex());
|
||||
operatorRegistry.get(DocumentsTabOperator.class).ifPresent(operator -> operator.displayDoc(docid));
|
||||
JMenuItem item2 =
|
||||
new JMenuItem(MessageUtils.getLocalizedMessage("search.results.menu.showdoc"));
|
||||
item2.addActionListener(
|
||||
e -> {
|
||||
int docid =
|
||||
(int)
|
||||
resultsTable
|
||||
.getModel()
|
||||
.getValueAt(
|
||||
resultsTable.getSelectedRow(),
|
||||
SearchResultsTableModel.Column.DOCID.getIndex());
|
||||
operatorRegistry
|
||||
.get(DocumentsTabOperator.class)
|
||||
.ifPresent(operator -> operator.displayDoc(docid));
|
||||
tabSwitcher.switchTab(TabbedPaneProvider.Tab.DOCUMENTS);
|
||||
});
|
||||
popup.add(item2);
|
||||
|
@ -691,11 +779,12 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
|
||||
void showContextMenuInResultsTable(MouseEvent e) {
|
||||
if (e.getClickCount() == 2 && !e.isConsumed()) {
|
||||
SearchPanelProvider.this.setupResultsContextMenuPopup().show(e.getComponent(), e.getX(), e.getY());
|
||||
SearchPanelProvider.this
|
||||
.setupResultsContextMenuPopup()
|
||||
.show(e.getComponent(), e.getX(), e.getY());
|
||||
setupResultsContextMenuPopup().show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver {
|
||||
|
@ -703,19 +792,33 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
@Override
|
||||
public void openIndex(LukeState state) {
|
||||
searchModel = searchFactory.newInstance(state.getIndexReader());
|
||||
toolsModel = toolsFactory.newInstance(state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
operatorRegistry.get(QueryParserTabOperator.class).ifPresent(operator -> {
|
||||
toolsModel =
|
||||
toolsFactory.newInstance(
|
||||
state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
operatorRegistry
|
||||
.get(QueryParserTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setSearchableFields(searchModel.getSearchableFieldNames());
|
||||
operator.setRangeSearchableFields(searchModel.getRangeSearchableFieldNames());
|
||||
});
|
||||
operatorRegistry.get(SortTabOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(SortTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setSearchModel(searchModel);
|
||||
operator.setSortableFields(searchModel.getSortableFieldNames());
|
||||
});
|
||||
operatorRegistry.get(FieldValuesTabOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(FieldValuesTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setFields(searchModel.getFieldNames());
|
||||
});
|
||||
operatorRegistry.get(MLTTabOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(MLTTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setFields(searchModel.getFieldNames());
|
||||
});
|
||||
|
||||
|
@ -742,16 +845,24 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
nextBtn.setEnabled(false);
|
||||
prevBtn.setEnabled(false);
|
||||
delBtn.setEnabled(false);
|
||||
TableUtils.setupTable(resultsTable, ListSelectionModel.SINGLE_SELECTION, new SearchResultsTableModel(), null,
|
||||
TableUtils.setupTable(
|
||||
resultsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new SearchResultsTableModel(),
|
||||
null,
|
||||
SearchResultsTableModel.Column.DOCID.getColumnWidth(),
|
||||
SearchResultsTableModel.Column.SCORE.getColumnWidth());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** tabs in the Search panel */
|
||||
public enum Tab {
|
||||
QPARSER(0), ANALYZER(1), SIMILARITY(2), SORT(3), VALUES(4), MLT(5);
|
||||
QPARSER(0),
|
||||
ANALYZER(1),
|
||||
SIMILARITY(2),
|
||||
SORT(3),
|
||||
VALUES(4),
|
||||
MLT(5);
|
||||
|
||||
private int tabIdx;
|
||||
|
||||
|
@ -764,7 +875,8 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
}
|
||||
}
|
||||
|
||||
static final class SearchResultsTableModel extends TableModelBase<SearchResultsTableModel.Column> {
|
||||
static final class SearchResultsTableModel
|
||||
extends TableModelBase<SearchResultsTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
DOCID("Doc ID", 0, Integer.class, 50),
|
||||
|
@ -818,10 +930,14 @@ public final class SearchPanelProvider implements SearchTabOperator {
|
|||
} else {
|
||||
data[i][Column.SCORE.getIndex()] = 1.0f;
|
||||
}
|
||||
List<String> concatValues = doc.getFieldValues().entrySet().stream().map(e -> {
|
||||
List<String> concatValues =
|
||||
doc.getFieldValues().entrySet().stream()
|
||||
.map(
|
||||
e -> {
|
||||
String v = String.join(",", Arrays.asList(e.getValue()));
|
||||
return e.getKey() + "=" + v + ";";
|
||||
}).collect(Collectors.toList());
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
data[i][Column.VALUE.getIndex()] = String.join(" ", concatValues);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -45,5 +45,4 @@ public class TabSwitcherProxy {
|
|||
public interface TabSwitcher {
|
||||
void switchTab(TabbedPaneProvider.Tab tab);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,11 +17,10 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import java.io.IOException;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTabbedPane;
|
||||
import javax.swing.JTextArea;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.luke.app.DirectoryHandler;
|
||||
import org.apache.lucene.luke.app.DirectoryObserver;
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
|
@ -121,7 +120,11 @@ public final class TabbedPaneProvider implements TabSwitcherProxy.TabSwitcher {
|
|||
|
||||
/** tabs in the main frame */
|
||||
public enum Tab {
|
||||
OVERVIEW(0), DOCUMENTS(1), SEARCH(2), ANALYZER(3), COMMITS(4);
|
||||
OVERVIEW(0),
|
||||
DOCUMENTS(1),
|
||||
SEARCH(2),
|
||||
ANALYZER(3),
|
||||
COMMITS(4);
|
||||
|
||||
private int tabIdx;
|
||||
|
||||
|
@ -133,5 +136,4 @@ public final class TabbedPaneProvider implements TabSwitcherProxy.TabSwitcher {
|
|||
return tabIdx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,5 +29,4 @@ public interface TableColumnInfo {
|
|||
default int getColumnWidth() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,12 +17,14 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.apache.lucene.luke.app.desktop.util.TableUtils;
|
||||
|
||||
/** Base table model that stores table's meta data and content. This also provides some default implementation of the {@link javax.swing.table.TableModel} interface. */
|
||||
/**
|
||||
* Base table model that stores table's meta data and content. This also provides some default
|
||||
* implementation of the {@link javax.swing.table.TableModel} interface.
|
||||
*/
|
||||
public abstract class TableModelBase<T extends TableColumnInfo> extends AbstractTableModel {
|
||||
|
||||
private final Map<Integer, T> columnMap = TableUtils.columnMap(columnInfos());
|
||||
|
@ -67,7 +69,6 @@ public abstract class TableModelBase<T extends TableColumnInfo> extends Abstract
|
|||
return Object.class;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object getValueAt(int rowIndex, int columnIndex) {
|
||||
return data[rowIndex][columnIndex];
|
||||
|
|
|
@ -17,11 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dialog;
|
||||
|
@ -31,7 +26,11 @@ import java.awt.Font;
|
|||
import java.awt.GridLayout;
|
||||
import java.awt.Window;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.util.DialogOpener;
|
||||
|
@ -52,7 +51,7 @@ public final class ConfirmDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
private Callable callback;
|
||||
|
||||
public synchronized static ConfirmDialogFactory getInstance() throws IOException {
|
||||
public static synchronized ConfirmDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new ConfirmDialogFactory();
|
||||
}
|
||||
|
@ -103,7 +102,8 @@ public final class ConfirmDialogFactory implements DialogOpener.DialogFactory {
|
|||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.TRAILING));
|
||||
footer.setOpaque(false);
|
||||
JButton okBtn = new JButton(MessageUtils.getLocalizedMessage("button.ok"));
|
||||
okBtn.addActionListener(e -> {
|
||||
okBtn.addActionListener(
|
||||
e -> {
|
||||
callback.call();
|
||||
dialog.dispose();
|
||||
});
|
||||
|
@ -115,5 +115,4 @@ public final class ConfirmDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
return panel;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,12 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -30,7 +24,12 @@ import java.awt.FlowLayout;
|
|||
import java.awt.GridLayout;
|
||||
import java.awt.Window;
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.util.DialogOpener;
|
||||
|
@ -49,7 +48,7 @@ public final class HelpDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
private JComponent helpContent;
|
||||
|
||||
public synchronized static HelpDialogFactory getInstance() throws IOException {
|
||||
public static synchronized HelpDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new HelpDialogFactory();
|
||||
}
|
||||
|
|
|
@ -17,14 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.analysis;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dialog;
|
||||
|
@ -35,11 +27,18 @@ import java.awt.GridBagLayout;
|
|||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.analysis.custom.CustomAnalyzer;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextField;
|
||||
import org.apache.lucene.analysis.CharFilterFactory;
|
||||
import org.apache.lucene.analysis.TokenFilterFactory;
|
||||
import org.apache.lucene.analysis.TokenizerFactory;
|
||||
import org.apache.lucene.analysis.custom.CustomAnalyzer;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.util.DialogOpener;
|
||||
|
@ -56,7 +55,7 @@ public class AnalysisChainDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
private CustomAnalyzer analyzer;
|
||||
|
||||
public synchronized static AnalysisChainDialogFactory getInstance() throws IOException {
|
||||
public static synchronized AnalysisChainDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new AnalysisChainDialogFactory();
|
||||
}
|
||||
|
@ -110,11 +109,16 @@ public class AnalysisChainDialogFactory implements DialogOpener.DialogFactory {
|
|||
c.gridy = 0;
|
||||
c.weightx = 0.1;
|
||||
c.weighty = 0.5;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("analysis.dialog.chain.label.charfilters")), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis.dialog.chain.label.charfilters")), c);
|
||||
|
||||
String[] charFilters = analyzer.getCharFilterFactories().stream().map(f -> CharFilterFactory.findSPIName(f.getClass())).toArray(String[]::new);
|
||||
String[] charFilters =
|
||||
analyzer.getCharFilterFactories().stream()
|
||||
.map(f -> CharFilterFactory.findSPIName(f.getClass()))
|
||||
.toArray(String[]::new);
|
||||
JList<String> charFilterList = new JList<>(charFilters);
|
||||
charFilterList.setVisibleRowCount(charFilters.length == 0 ? 1 : Math.min(charFilters.length, 5));
|
||||
charFilterList.setVisibleRowCount(
|
||||
charFilters.length == 0 ? 1 : Math.min(charFilters.length, 5));
|
||||
c.gridx = 1;
|
||||
c.gridy = 0;
|
||||
c.weightx = 0.5;
|
||||
|
@ -125,7 +129,8 @@ public class AnalysisChainDialogFactory implements DialogOpener.DialogFactory {
|
|||
c.gridy = 1;
|
||||
c.weightx = 0.1;
|
||||
c.weighty = 0.1;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("analysis.dialog.chain.label.tokenizer")), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis.dialog.chain.label.tokenizer")), c);
|
||||
|
||||
String tokenizer = TokenizerFactory.findSPIName(analyzer.getTokenizerFactory().getClass());
|
||||
JTextField tokenizerTF = new JTextField(tokenizer);
|
||||
|
@ -143,11 +148,17 @@ public class AnalysisChainDialogFactory implements DialogOpener.DialogFactory {
|
|||
c.gridy = 2;
|
||||
c.weightx = 0.1;
|
||||
c.weighty = 0.5;
|
||||
panel.add(new JLabel(MessageUtils.getLocalizedMessage("analysis.dialog.chain.label.tokenfilters")), c);
|
||||
panel.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis.dialog.chain.label.tokenfilters")),
|
||||
c);
|
||||
|
||||
String[] tokenFilters = analyzer.getTokenFilterFactories().stream().map(f -> TokenFilterFactory.findSPIName(f.getClass())).toArray(String[]::new);
|
||||
String[] tokenFilters =
|
||||
analyzer.getTokenFilterFactories().stream()
|
||||
.map(f -> TokenFilterFactory.findSPIName(f.getClass()))
|
||||
.toArray(String[]::new);
|
||||
JList<String> tokenFilterList = new JList<>(tokenFilters);
|
||||
tokenFilterList.setVisibleRowCount(tokenFilters.length == 0 ? 1 : Math.min(tokenFilters.length, 5));
|
||||
tokenFilterList.setVisibleRowCount(
|
||||
tokenFilters.length == 0 ? 1 : Math.min(tokenFilters.length, 5));
|
||||
tokenFilterList.setMinimumSize(new Dimension(300, 25));
|
||||
c.gridx = 1;
|
||||
c.gridy = 2;
|
||||
|
@ -157,5 +168,4 @@ public class AnalysisChainDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
return panel;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,15 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.analysis;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Dialog;
|
||||
|
@ -39,7 +30,15 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
|
@ -78,7 +77,7 @@ public final class EditFiltersDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private EditFiltersMode mode;
|
||||
|
||||
public synchronized static EditFiltersDialogFactory getInstance() throws IOException {
|
||||
public static synchronized EditFiltersDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new EditFiltersDialogFactory();
|
||||
}
|
||||
|
@ -124,25 +123,37 @@ public final class EditFiltersDialogFactory implements DialogOpener.DialogFactor
|
|||
header.add(targetLbl);
|
||||
panel.add(header, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(filtersTable, ListSelectionModel.SINGLE_SELECTION, new FiltersTableModel(selectedFilters), tableListener,
|
||||
TableUtils.setupTable(
|
||||
filtersTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new FiltersTableModel(selectedFilters),
|
||||
tableListener,
|
||||
FiltersTableModel.Column.DELETE.getColumnWidth(),
|
||||
FiltersTableModel.Column.ORDER.getColumnWidth());
|
||||
filtersTable.setShowGrid(true);
|
||||
filtersTable.getColumnModel().getColumn(FiltersTableModel.Column.TYPE.getIndex()).setCellRenderer(new TypeCellRenderer());
|
||||
filtersTable
|
||||
.getColumnModel()
|
||||
.getColumn(FiltersTableModel.Column.TYPE.getIndex())
|
||||
.setCellRenderer(new TypeCellRenderer());
|
||||
panel.add(new JScrollPane(filtersTable), BorderLayout.CENTER);
|
||||
|
||||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.TRAILING, 10, 5));
|
||||
footer.setOpaque(false);
|
||||
JButton okBtn = new JButton(MessageUtils.getLocalizedMessage("button.ok"));
|
||||
okBtn.addActionListener(e -> {
|
||||
okBtn.addActionListener(
|
||||
e -> {
|
||||
List<Integer> deletedIndexes = new ArrayList<>();
|
||||
for (int i = 0; i < filtersTable.getRowCount(); i++) {
|
||||
boolean deleted = (boolean) filtersTable.getValueAt(i, FiltersTableModel.Column.DELETE.getIndex());
|
||||
boolean deleted =
|
||||
(boolean) filtersTable.getValueAt(i, FiltersTableModel.Column.DELETE.getIndex());
|
||||
if (deleted) {
|
||||
deletedIndexes.add(i);
|
||||
}
|
||||
}
|
||||
operatorRegistry.get(CustomAnalyzerPanelOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(CustomAnalyzerPanelOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
switch (mode) {
|
||||
case CHARFILTER:
|
||||
operator.updateCharFilters(deletedIndexes);
|
||||
|
@ -188,9 +199,19 @@ public final class EditFiltersDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private void showEditParamsCharFilterDialog(int selectedIndex) {
|
||||
int targetIndex = filtersTable.getSelectedRow();
|
||||
String selectedItem = (String) filtersTable.getValueAt(selectedIndex, FiltersTableModel.Column.TYPE.getIndex());
|
||||
Map<String, String> params = operatorRegistry.get(CustomAnalyzerPanelOperator.class).map(operator -> operator.getCharFilterParams(targetIndex)).orElse(Collections.emptyMap());
|
||||
new DialogOpener<>(editParamsDialogFactory).open(dialog, MessageUtils.getLocalizedMessage("analysis.dialog.title.char_filter_params"), 400, 300,
|
||||
String selectedItem =
|
||||
(String) filtersTable.getValueAt(selectedIndex, FiltersTableModel.Column.TYPE.getIndex());
|
||||
Map<String, String> params =
|
||||
operatorRegistry
|
||||
.get(CustomAnalyzerPanelOperator.class)
|
||||
.map(operator -> operator.getCharFilterParams(targetIndex))
|
||||
.orElse(Collections.emptyMap());
|
||||
new DialogOpener<>(editParamsDialogFactory)
|
||||
.open(
|
||||
dialog,
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.char_filter_params"),
|
||||
400,
|
||||
300,
|
||||
factory -> {
|
||||
factory.setMode(EditParamsMode.CHARFILTER);
|
||||
factory.setTargetIndex(targetIndex);
|
||||
|
@ -201,9 +222,19 @@ public final class EditFiltersDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private void showEditParamsTokenFilterDialog(int selectedIndex) {
|
||||
int targetIndex = filtersTable.getSelectedRow();
|
||||
String selectedItem = (String) filtersTable.getValueAt(selectedIndex, FiltersTableModel.Column.TYPE.getIndex());
|
||||
Map<String, String> params = operatorRegistry.get(CustomAnalyzerPanelOperator.class).map(operator -> operator.getTokenFilterParams(targetIndex)).orElse(Collections.emptyMap());
|
||||
new DialogOpener<>(editParamsDialogFactory).open(dialog, MessageUtils.getLocalizedMessage("analysis.dialog.title.char_filter_params"), 400, 300,
|
||||
String selectedItem =
|
||||
(String) filtersTable.getValueAt(selectedIndex, FiltersTableModel.Column.TYPE.getIndex());
|
||||
Map<String, String> params =
|
||||
operatorRegistry
|
||||
.get(CustomAnalyzerPanelOperator.class)
|
||||
.map(operator -> operator.getTokenFilterParams(targetIndex))
|
||||
.orElse(Collections.emptyMap());
|
||||
new DialogOpener<>(editParamsDialogFactory)
|
||||
.open(
|
||||
dialog,
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.char_filter_params"),
|
||||
400,
|
||||
300,
|
||||
factory -> {
|
||||
factory.setMode(EditParamsMode.TOKENFILTER);
|
||||
factory.setTargetIndex(targetIndex);
|
||||
|
@ -292,12 +323,11 @@ public final class EditFiltersDialogFactory implements DialogOpener.DialogFactor
|
|||
static final class TypeCellRenderer implements TableCellRenderer {
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
String[] tmp = ((String) value).split("\\.");
|
||||
String type = tmp[tmp.length - 1];
|
||||
return new JLabel(type);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,5 +19,6 @@ package org.apache.lucene.luke.app.desktop.components.dialog.analysis;
|
|||
|
||||
/** Edit filters mode */
|
||||
public enum EditFiltersMode {
|
||||
CHARFILTER, TOKENFILTER;
|
||||
CHARFILTER,
|
||||
TOKENFILTER;
|
||||
}
|
||||
|
|
|
@ -17,14 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.analysis;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -36,7 +28,14 @@ import java.util.HashMap;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
|
@ -71,7 +70,7 @@ public final class EditParamsDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
private Callable callback;
|
||||
|
||||
public synchronized static EditParamsDialogFactory getInstance() throws IOException {
|
||||
public static synchronized EditParamsDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new EditParamsDialogFactory();
|
||||
}
|
||||
|
@ -126,7 +125,11 @@ public final class EditParamsDialogFactory implements DialogOpener.DialogFactory
|
|||
header.add(targetLbl);
|
||||
panel.add(header, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(paramsTable, ListSelectionModel.SINGLE_SELECTION, new ParamsTableModel(params), null,
|
||||
TableUtils.setupTable(
|
||||
paramsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new ParamsTableModel(params),
|
||||
null,
|
||||
ParamsTableModel.Column.DELETE.getColumnWidth(),
|
||||
ParamsTableModel.Column.NAME.getColumnWidth());
|
||||
paramsTable.setShowGrid(true);
|
||||
|
@ -135,13 +138,21 @@ public final class EditParamsDialogFactory implements DialogOpener.DialogFactory
|
|||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.TRAILING, 10, 5));
|
||||
footer.setOpaque(false);
|
||||
JButton okBtn = new JButton(MessageUtils.getLocalizedMessage("button.ok"));
|
||||
okBtn.addActionListener(e -> {
|
||||
okBtn.addActionListener(
|
||||
e -> {
|
||||
Map<String, String> params = new HashMap<>();
|
||||
for (int i = 0; i < paramsTable.getRowCount(); i++) {
|
||||
boolean deleted = (boolean) paramsTable.getValueAt(i, ParamsTableModel.Column.DELETE.getIndex());
|
||||
String name = (String) paramsTable.getValueAt(i, ParamsTableModel.Column.NAME.getIndex());
|
||||
String value = (String) paramsTable.getValueAt(i, ParamsTableModel.Column.VALUE.getIndex());
|
||||
if (deleted || Objects.isNull(name) || name.equals("") || Objects.isNull(value) || value.equals("")) {
|
||||
boolean deleted =
|
||||
(boolean) paramsTable.getValueAt(i, ParamsTableModel.Column.DELETE.getIndex());
|
||||
String name =
|
||||
(String) paramsTable.getValueAt(i, ParamsTableModel.Column.NAME.getIndex());
|
||||
String value =
|
||||
(String) paramsTable.getValueAt(i, ParamsTableModel.Column.VALUE.getIndex());
|
||||
if (deleted
|
||||
|| Objects.isNull(name)
|
||||
|| name.equals("")
|
||||
|| Objects.isNull(value)
|
||||
|| value.equals("")) {
|
||||
continue;
|
||||
}
|
||||
params.put(name, value);
|
||||
|
@ -153,7 +164,8 @@ public final class EditParamsDialogFactory implements DialogOpener.DialogFactory
|
|||
});
|
||||
footer.add(okBtn);
|
||||
JButton cancelBtn = new JButton(MessageUtils.getLocalizedMessage("button.cancel"));
|
||||
cancelBtn.addActionListener(e -> {
|
||||
cancelBtn.addActionListener(
|
||||
e -> {
|
||||
this.params.clear();
|
||||
dialog.dispose();
|
||||
});
|
||||
|
@ -164,7 +176,10 @@ public final class EditParamsDialogFactory implements DialogOpener.DialogFactory
|
|||
}
|
||||
|
||||
private void updateTargetParams(Map<String, String> params) {
|
||||
operatorRegistry.get(CustomAnalyzerPanelOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(CustomAnalyzerPanelOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
switch (mode) {
|
||||
case CHARFILTER:
|
||||
operator.updateCharFilterParams(targetIndex, params);
|
||||
|
@ -217,7 +232,6 @@ public final class EditParamsDialogFactory implements DialogOpener.DialogFactory
|
|||
public int getColumnWidth() {
|
||||
return width;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final int PARAM_SIZE = 20;
|
||||
|
@ -249,6 +263,4 @@ public final class EditParamsDialogFactory implements DialogOpener.DialogFactory
|
|||
return Column.values();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -19,5 +19,7 @@ package org.apache.lucene.luke.app.desktop.components.dialog.analysis;
|
|||
|
||||
/** Edit parameters mode */
|
||||
public enum EditParamsMode {
|
||||
CHARFILTER, TOKENIZER, TOKENFILTER;
|
||||
CHARFILTER,
|
||||
TOKENIZER,
|
||||
TOKENFILTER;
|
||||
}
|
||||
|
|
|
@ -17,14 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.analysis;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -33,7 +25,14 @@ import java.awt.Window;
|
|||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.components.TableColumnInfo;
|
||||
|
@ -58,7 +57,7 @@ public final class TokenAttributeDialogFactory implements DialogOpener.DialogFac
|
|||
|
||||
private List<Analysis.TokenAttribute> attributes;
|
||||
|
||||
public synchronized static TokenAttributeDialogFactory getInstance() throws IOException {
|
||||
public static synchronized TokenAttributeDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new TokenAttributeDialogFactory();
|
||||
}
|
||||
|
@ -98,10 +97,18 @@ public final class TokenAttributeDialogFactory implements DialogOpener.DialogFac
|
|||
header.add(new JLabel(term));
|
||||
panel.add(header, BorderLayout.PAGE_START);
|
||||
|
||||
List<TokenAttValue> attrValues = attributes.stream()
|
||||
.flatMap(att -> att.getAttValues().entrySet().stream().map(e -> TokenAttValue.of(att.getAttClass(), e.getKey(), e.getValue())))
|
||||
List<TokenAttValue> attrValues =
|
||||
attributes.stream()
|
||||
.flatMap(
|
||||
att ->
|
||||
att.getAttValues().entrySet().stream()
|
||||
.map(e -> TokenAttValue.of(att.getAttClass(), e.getKey(), e.getValue())))
|
||||
.collect(Collectors.toList());
|
||||
TableUtils.setupTable(attributesTable, ListSelectionModel.SINGLE_SELECTION, new AttributeTableModel(attrValues), null);
|
||||
TableUtils.setupTable(
|
||||
attributesTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new AttributeTableModel(attrValues),
|
||||
null);
|
||||
panel.add(new JScrollPane(attributesTable), BorderLayout.CENTER);
|
||||
|
||||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.TRAILING));
|
||||
|
@ -117,7 +124,6 @@ public final class TokenAttributeDialogFactory implements DialogOpener.DialogFac
|
|||
static final class AttributeTableModel extends TableModelBase<AttributeTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
|
||||
ATTR("Attribute", 0, String.class),
|
||||
NAME("Name", 1, String.class),
|
||||
VALUE("Value", 2, String.class);
|
||||
|
@ -177,8 +183,7 @@ public final class TokenAttributeDialogFactory implements DialogOpener.DialogFac
|
|||
return attValue;
|
||||
}
|
||||
|
||||
private TokenAttValue() {
|
||||
}
|
||||
private TokenAttValue() {}
|
||||
|
||||
String getAttClass() {
|
||||
return attClass;
|
||||
|
@ -192,5 +197,4 @@ public final class TokenAttributeDialogFactory implements DialogOpener.DialogFac
|
|||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,22 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.documents;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
|
@ -51,7 +35,22 @@ import java.lang.reflect.Constructor;
|
|||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultCellEditor;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.UIManager;
|
||||
import javax.swing.table.JTableHeader;
|
||||
import javax.swing.table.TableCellRenderer;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.analysis.standard.StandardAnalyzer;
|
||||
|
@ -98,13 +97,14 @@ import org.apache.lucene.luke.util.LoggerFactory;
|
|||
import org.apache.lucene.util.BytesRef;
|
||||
|
||||
/** Factory of add document dialog */
|
||||
public final class AddDocumentDialogFactory implements DialogOpener.DialogFactory, AddDocumentDialogOperator {
|
||||
public final class AddDocumentDialogFactory
|
||||
implements DialogOpener.DialogFactory, AddDocumentDialogOperator {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
||||
private static AddDocumentDialogFactory instance;
|
||||
|
||||
private final static int ROW_COUNT = 50;
|
||||
private static final int ROW_COUNT = 50;
|
||||
|
||||
private final Preferences prefs;
|
||||
|
||||
|
@ -136,7 +136,7 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private JDialog dialog;
|
||||
|
||||
public synchronized static AddDocumentDialogFactory getInstance() throws IOException {
|
||||
public static synchronized AddDocumentDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new AddDocumentDialogFactory();
|
||||
}
|
||||
|
@ -150,7 +150,10 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
this.operatorRegistry = ComponentOperatorRegistry.getInstance();
|
||||
this.indexOptionsDialogFactory = IndexOptionsDialogFactory.getInstance();
|
||||
this.helpDialogFactory = HelpDialogFactory.getInstance();
|
||||
this.newFieldList = IntStream.range(0, ROW_COUNT).mapToObj(i -> NewField.newInstance()).collect(Collectors.toList());
|
||||
this.newFieldList =
|
||||
IntStream.range(0, ROW_COUNT)
|
||||
.mapToObj(i -> NewField.newInstance())
|
||||
.collect(Collectors.toList());
|
||||
|
||||
operatorRegistry.register(AddDocumentDialogOperator.class, this);
|
||||
indexHandler.addObserver(new Observer());
|
||||
|
@ -204,8 +207,10 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
analyzerHeader.setOpaque(false);
|
||||
analyzerHeader.add(new JLabel(MessageUtils.getLocalizedMessage("add_document.label.analyzer")));
|
||||
analyzerHeader.add(analyzerNameLbl);
|
||||
JLabel changeLbl = new JLabel(MessageUtils.getLocalizedMessage("add_document.hyperlink.change"));
|
||||
changeLbl.addMouseListener(new MouseAdapter() {
|
||||
JLabel changeLbl =
|
||||
new JLabel(MessageUtils.getLocalizedMessage("add_document.hyperlink.change"));
|
||||
changeLbl.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
dialog.dispose();
|
||||
|
@ -245,20 +250,46 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private JTable fieldsTable() {
|
||||
JTable fieldsTable = new JTable();
|
||||
TableUtils.setupTable(fieldsTable, ListSelectionModel.SINGLE_SELECTION, new FieldsTableModel(newFieldList), null, 30, 150, 120, 80);
|
||||
TableUtils.setupTable(
|
||||
fieldsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new FieldsTableModel(newFieldList),
|
||||
null,
|
||||
30,
|
||||
150,
|
||||
120,
|
||||
80);
|
||||
fieldsTable.setShowGrid(true);
|
||||
JComboBox<Class<? extends IndexableField>> typesCombo = new JComboBox<>(presetFieldClasses);
|
||||
typesCombo.setRenderer((list, value, index, isSelected, cellHasFocus) -> new JLabel(value.getSimpleName()));
|
||||
fieldsTable.getColumnModel().getColumn(FieldsTableModel.Column.TYPE.getIndex()).setCellEditor(new DefaultCellEditor(typesCombo));
|
||||
typesCombo.setRenderer(
|
||||
(list, value, index, isSelected, cellHasFocus) -> new JLabel(value.getSimpleName()));
|
||||
fieldsTable
|
||||
.getColumnModel()
|
||||
.getColumn(FieldsTableModel.Column.TYPE.getIndex())
|
||||
.setCellEditor(new DefaultCellEditor(typesCombo));
|
||||
for (int i = 0; i < fieldsTable.getModel().getRowCount(); i++) {
|
||||
fieldsTable.getModel().setValueAt(TextField.class, i, FieldsTableModel.Column.TYPE.getIndex());
|
||||
fieldsTable
|
||||
.getModel()
|
||||
.setValueAt(TextField.class, i, FieldsTableModel.Column.TYPE.getIndex());
|
||||
}
|
||||
fieldsTable.getColumnModel().getColumn(FieldsTableModel.Column.TYPE.getIndex()).setHeaderRenderer(
|
||||
fieldsTable
|
||||
.getColumnModel()
|
||||
.getColumn(FieldsTableModel.Column.TYPE.getIndex())
|
||||
.setHeaderRenderer(
|
||||
new HelpHeaderRenderer(
|
||||
"About Type", "Select Field Class:",
|
||||
createTypeHelpDialog(), helpDialogFactory, dialog));
|
||||
fieldsTable.getColumnModel().getColumn(FieldsTableModel.Column.TYPE.getIndex()).setCellRenderer(new TypeCellRenderer());
|
||||
fieldsTable.getColumnModel().getColumn(FieldsTableModel.Column.OPTIONS.getIndex()).setCellRenderer(new OptionsCellRenderer(dialog, indexOptionsDialogFactory, newFieldList));
|
||||
"About Type",
|
||||
"Select Field Class:",
|
||||
createTypeHelpDialog(),
|
||||
helpDialogFactory,
|
||||
dialog));
|
||||
fieldsTable
|
||||
.getColumnModel()
|
||||
.getColumn(FieldsTableModel.Column.TYPE.getIndex())
|
||||
.setCellRenderer(new TypeCellRenderer());
|
||||
fieldsTable
|
||||
.getColumnModel()
|
||||
.getColumn(FieldsTableModel.Column.OPTIONS.getIndex())
|
||||
.setCellRenderer(new OptionsCellRenderer(dialog, indexOptionsDialogFactory, newFieldList));
|
||||
return fieldsTable;
|
||||
}
|
||||
|
||||
|
@ -271,7 +302,8 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
JPanel header = new JPanel();
|
||||
header.setOpaque(false);
|
||||
header.setLayout(new BoxLayout(header, BoxLayout.PAGE_AXIS));
|
||||
String[] typeList = new String[]{
|
||||
String[] typeList =
|
||||
new String[] {
|
||||
"TextField",
|
||||
"StringField",
|
||||
"IntPoint",
|
||||
|
@ -289,7 +321,8 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
wrapper1.setOpaque(false);
|
||||
JComboBox<String> typeCombo = new JComboBox<>(typeList);
|
||||
typeCombo.setSelectedItem(typeList[0]);
|
||||
typeCombo.addActionListener(e -> {
|
||||
typeCombo.addActionListener(
|
||||
e -> {
|
||||
String selected = (String) typeCombo.getSelectedItem();
|
||||
descTA.setText(MessageUtils.getLocalizedMessage("help.fieldtype." + selected));
|
||||
});
|
||||
|
@ -325,12 +358,20 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private final Class<? extends IndexableField>[] presetFieldClasses = new Class[]{
|
||||
TextField.class, StringField.class,
|
||||
IntPoint.class, LongPoint.class, FloatPoint.class, DoublePoint.class,
|
||||
SortedDocValuesField.class, SortedSetDocValuesField.class,
|
||||
NumericDocValuesField.class, SortedNumericDocValuesField.class,
|
||||
StoredField.class, Field.class
|
||||
private final Class<? extends IndexableField>[] presetFieldClasses =
|
||||
new Class[] {
|
||||
TextField.class,
|
||||
StringField.class,
|
||||
IntPoint.class,
|
||||
LongPoint.class,
|
||||
FloatPoint.class,
|
||||
DoublePoint.class,
|
||||
SortedDocValuesField.class,
|
||||
SortedSetDocValuesField.class,
|
||||
NumericDocValuesField.class,
|
||||
SortedNumericDocValuesField.class,
|
||||
StoredField.class,
|
||||
Field.class
|
||||
};
|
||||
|
||||
@Override
|
||||
|
@ -341,7 +382,8 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
private class ListenerFunctions {
|
||||
|
||||
void addDocument(ActionEvent e) {
|
||||
List<NewField> validFields = newFieldList.stream()
|
||||
List<NewField> validFields =
|
||||
newFieldList.stream()
|
||||
.filter(nf -> !nf.isDeleted())
|
||||
.filter(nf -> !StringUtils.isNullOrEmpty(nf.getName()))
|
||||
.filter(nf -> !StringUtils.isNullOrEmpty(nf.getValue()))
|
||||
|
@ -391,12 +433,12 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
constr = nf.getType().getConstructor(String.class, double[].class);
|
||||
double[] values = NumericUtils.convertToDoubleArray(nf.getValue(), false);
|
||||
return constr.newInstance(nf.getName(), values);
|
||||
} else if (nf.getType().equals(SortedDocValuesField.class) ||
|
||||
nf.getType().equals(SortedSetDocValuesField.class)) {
|
||||
} else if (nf.getType().equals(SortedDocValuesField.class)
|
||||
|| nf.getType().equals(SortedSetDocValuesField.class)) {
|
||||
constr = nf.getType().getConstructor(String.class, BytesRef.class);
|
||||
return constr.newInstance(nf.getName(), new BytesRef(nf.getValue()));
|
||||
} else if (nf.getType().equals(NumericDocValuesField.class) ||
|
||||
nf.getType().equals(SortedNumericDocValuesField.class)) {
|
||||
} else if (nf.getType().equals(NumericDocValuesField.class)
|
||||
|| nf.getType().equals(SortedNumericDocValuesField.class)) {
|
||||
constr = nf.getType().getConstructor(String.class, long.class);
|
||||
long value = NumericUtils.tryConvertToLongValue(nf.getValue());
|
||||
return constr.newInstance(nf.getName(), value);
|
||||
|
@ -414,12 +456,16 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private void addDocument(Document doc) {
|
||||
try {
|
||||
Analyzer analyzer = operatorRegistry.get(AnalysisTabOperator.class)
|
||||
Analyzer analyzer =
|
||||
operatorRegistry
|
||||
.get(AnalysisTabOperator.class)
|
||||
.map(AnalysisTabOperator::getCurrentAnalyzer)
|
||||
.orElse(new StandardAnalyzer());
|
||||
toolsModel.addDocument(doc, analyzer);
|
||||
indexHandler.reOpen();
|
||||
operatorRegistry.get(DocumentsTabOperator.class).ifPresent(DocumentsTabOperator::displayLatestDoc);
|
||||
operatorRegistry
|
||||
.get(DocumentsTabOperator.class)
|
||||
.ifPresent(DocumentsTabOperator::displayLatestDoc);
|
||||
tabSwitcher.switchTab(TabbedPaneProvider.Tab.DOCUMENTS);
|
||||
infoTA.setText(MessageUtils.getLocalizedMessage("add_document.message.success"));
|
||||
addBtn.setEnabled(false);
|
||||
|
@ -432,14 +478,15 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
throw new LukeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver {
|
||||
|
||||
@Override
|
||||
public void openIndex(LukeState state) {
|
||||
toolsModel = toolsFactory.newInstance(state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
toolsModel =
|
||||
toolsFactory.newInstance(
|
||||
state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -481,7 +528,6 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private final List<NewField> newFieldList;
|
||||
|
@ -533,7 +579,8 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
String simpleName = ((Class<? extends IndexableField>) value).getSimpleName();
|
||||
return new JLabel(simpleName);
|
||||
}
|
||||
|
@ -551,7 +598,10 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private JTable table;
|
||||
|
||||
public OptionsCellRenderer(JDialog dialog, IndexOptionsDialogFactory indexOptionsDialogFactory, List<NewField> newFieldList) {
|
||||
public OptionsCellRenderer(
|
||||
JDialog dialog,
|
||||
IndexOptionsDialogFactory indexOptionsDialogFactory,
|
||||
List<NewField> newFieldList) {
|
||||
this.dialog = dialog;
|
||||
this.indexOptionsDialogFactory = indexOptionsDialogFactory;
|
||||
this.newFieldList = newFieldList;
|
||||
|
@ -559,7 +609,8 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
public Component getTableCellRendererComponent(
|
||||
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
|
||||
if (table != null && this.table != table) {
|
||||
this.table = table;
|
||||
final JTableHeader header = table.getTableHeader();
|
||||
|
@ -569,14 +620,20 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
panel.add(new JLabel(value.toString()));
|
||||
|
||||
JLabel optionsLbl = new JLabel("options");
|
||||
table.addMouseListener(new MouseAdapter() {
|
||||
table.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
int row = table.rowAtPoint(e.getPoint());
|
||||
int col = table.columnAtPoint(e.getPoint());
|
||||
if (row >= 0 && col == FieldsTableModel.Column.OPTIONS.getIndex()) {
|
||||
String title = "Index options for:";
|
||||
new DialogOpener<>(indexOptionsDialogFactory).open(dialog, title, 500, 500,
|
||||
new DialogOpener<>(indexOptionsDialogFactory)
|
||||
.open(
|
||||
dialog,
|
||||
title,
|
||||
500,
|
||||
500,
|
||||
(factory) -> {
|
||||
factory.setNewField(newFieldList.get(row));
|
||||
});
|
||||
|
@ -588,6 +645,5 @@ public final class AddDocumentDialogFactory implements DialogOpener.DialogFactor
|
|||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,4 +24,3 @@ import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
|||
public interface AddDocumentDialogOperator extends ComponentOperatorRegistry.ComponentOperator {
|
||||
void setAnalyzer(Analyzer analyzer);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,18 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.documents;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -44,7 +32,18 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.DefaultListModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.util.DialogOpener;
|
||||
|
@ -73,7 +72,7 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
private DocValues docValues;
|
||||
|
||||
public synchronized static DocValuesDialogFactory getInstance() throws IOException {
|
||||
public static synchronized DocValuesDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new DocValuesDialogFactory();
|
||||
}
|
||||
|
@ -91,14 +90,10 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
DefaultListModel<String> values = new DefaultListModel<>();
|
||||
if (docValues.getValues().size() > 0) {
|
||||
decodersCombo.setEnabled(false);
|
||||
docValues.getValues().stream()
|
||||
.map(BytesRefUtils::decode)
|
||||
.forEach(values::addElement);
|
||||
docValues.getValues().stream().map(BytesRefUtils::decode).forEach(values::addElement);
|
||||
} else if (docValues.getNumericValues().size() > 0) {
|
||||
decodersCombo.setEnabled(true);
|
||||
docValues.getNumericValues().stream()
|
||||
.map(String::valueOf)
|
||||
.forEach(values::addElement);
|
||||
docValues.getNumericValues().stream().map(String::valueOf).forEach(values::addElement);
|
||||
}
|
||||
|
||||
valueList.setModel(values);
|
||||
|
@ -138,7 +133,8 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
JPanel fieldHeader = new JPanel(new FlowLayout(FlowLayout.LEADING, 3, 3));
|
||||
fieldHeader.setOpaque(false);
|
||||
fieldHeader.add(new JLabel(MessageUtils.getLocalizedMessage("documents.docvalues.label.doc_values")));
|
||||
fieldHeader.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("documents.docvalues.label.doc_values")));
|
||||
fieldHeader.add(new JLabel(field));
|
||||
header.add(fieldHeader);
|
||||
|
||||
|
@ -151,7 +147,8 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
JPanel decodeHeader = new JPanel(new FlowLayout(FlowLayout.TRAILING, 3, 3));
|
||||
decodeHeader.setOpaque(false);
|
||||
decodeHeader.add(new JLabel("decoded as"));
|
||||
String[] decoders = Arrays.stream(Decoder.values()).map(Decoder::toString).toArray(String[]::new);
|
||||
String[] decoders =
|
||||
Arrays.stream(Decoder.values()).map(Decoder::toString).toArray(String[]::new);
|
||||
decodersCombo.setModel(new DefaultComboBoxModel<>(decoders));
|
||||
decodersCombo.setSelectedItem(Decoder.LONG.toString());
|
||||
decodersCombo.addActionListener(listeners::selectDecoder);
|
||||
|
@ -171,13 +168,9 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
DefaultListModel<String> values = new DefaultListModel<>();
|
||||
if (docValues.getValues().size() > 0) {
|
||||
docValues.getValues().stream()
|
||||
.map(BytesRefUtils::decode)
|
||||
.forEach(values::addElement);
|
||||
docValues.getValues().stream().map(BytesRefUtils::decode).forEach(values::addElement);
|
||||
} else {
|
||||
docValues.getNumericValues().stream()
|
||||
.map(String::valueOf)
|
||||
.forEach(values::addElement);
|
||||
docValues.getNumericValues().stream().map(String::valueOf).forEach(values::addElement);
|
||||
}
|
||||
valueList.setModel(values);
|
||||
|
||||
|
@ -188,7 +181,9 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.TRAILING, 5, 5));
|
||||
footer.setOpaque(false);
|
||||
|
||||
JButton copyBtn = new JButton(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("button.copy")));
|
||||
JButton copyBtn =
|
||||
new JButton(
|
||||
FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("button.copy")));
|
||||
copyBtn.setMargin(new Insets(3, 0, 3, 0));
|
||||
copyBtn.addActionListener(listeners::copyValues);
|
||||
footer.add(copyBtn);
|
||||
|
@ -214,9 +209,7 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
DefaultListModel<String> values = new DefaultListModel<>();
|
||||
switch (decoder) {
|
||||
case LONG:
|
||||
docValues.getNumericValues().stream()
|
||||
.map(String::valueOf)
|
||||
.forEach(values::addElement);
|
||||
docValues.getNumericValues().stream().map(String::valueOf).forEach(values::addElement);
|
||||
break;
|
||||
case FLOAT:
|
||||
docValues.getNumericValues().stream()
|
||||
|
@ -266,11 +259,11 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/** doc value decoders */
|
||||
public enum Decoder {
|
||||
|
||||
LONG("long"), FLOAT("float"), DOUBLE("double");
|
||||
LONG("long"),
|
||||
FLOAT("float"),
|
||||
DOUBLE("double");
|
||||
|
||||
private final String label;
|
||||
|
||||
|
@ -292,5 +285,4 @@ public final class DocValuesDialogFactory implements DialogOpener.DialogFactory
|
|||
throw new IllegalArgumentException("No such decoder: " + label);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,13 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.documents;
|
||||
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
|
@ -27,14 +34,6 @@ import javax.swing.JLabel;
|
|||
import javax.swing.JPanel;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.document.FieldType;
|
||||
import org.apache.lucene.document.StringField;
|
||||
|
@ -80,7 +79,7 @@ public final class IndexOptionsDialogFactory implements DialogOpener.DialogFacto
|
|||
|
||||
private NewField nf;
|
||||
|
||||
public synchronized static IndexOptionsDialogFactory getInstance() throws IOException {
|
||||
public static synchronized IndexOptionsDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new IndexOptionsDialogFactory();
|
||||
}
|
||||
|
@ -155,7 +154,8 @@ public final class IndexOptionsDialogFactory implements DialogOpener.DialogFacto
|
|||
|
||||
JPanel inner2 = new JPanel(new FlowLayout(FlowLayout.LEADING, 10, 1));
|
||||
inner2.setOpaque(false);
|
||||
JLabel idxOptLbl = new JLabel(MessageUtils.getLocalizedMessage("idx_options.label.index_options"));
|
||||
JLabel idxOptLbl =
|
||||
new JLabel(MessageUtils.getLocalizedMessage("idx_options.label.index_options"));
|
||||
inner2.add(idxOptLbl);
|
||||
inner2.add(idxOptCombo);
|
||||
panel.add(inner2);
|
||||
|
@ -249,9 +249,9 @@ public final class IndexOptionsDialogFactory implements DialogOpener.DialogFacto
|
|||
dimCountTF.setText(String.valueOf(fieldType.pointDimensionCount()));
|
||||
dimNumBytesTF.setText(String.valueOf(fieldType.pointNumBytes()));
|
||||
|
||||
if (nf.getType().equals(org.apache.lucene.document.TextField.class) ||
|
||||
nf.getType().equals(StringField.class) ||
|
||||
nf.getType().equals(Field.class)) {
|
||||
if (nf.getType().equals(org.apache.lucene.document.TextField.class)
|
||||
|| nf.getType().equals(StringField.class)
|
||||
|| nf.getType().equals(Field.class)) {
|
||||
storedCB.setEnabled(true);
|
||||
} else {
|
||||
storedCB.setEnabled(false);
|
||||
|
@ -304,5 +304,4 @@ public final class IndexOptionsDialogFactory implements DialogOpener.DialogFacto
|
|||
private static String[] availableDocValuesType() {
|
||||
return Arrays.stream(DocValuesType.values()).map(DocValuesType::name).toArray(String[]::new);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,13 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.documents;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dialog;
|
||||
|
@ -36,7 +29,13 @@ import java.awt.datatransfer.Clipboard;
|
|||
import java.awt.datatransfer.StringSelection;
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTextArea;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.util.DialogOpener;
|
||||
|
@ -56,7 +55,7 @@ public final class StoredValueDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private String value;
|
||||
|
||||
public synchronized static StoredValueDialogFactory getInstance() throws IOException {
|
||||
public static synchronized StoredValueDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new StoredValueDialogFactory();
|
||||
}
|
||||
|
@ -110,9 +109,12 @@ public final class StoredValueDialogFactory implements DialogOpener.DialogFactor
|
|||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.TRAILING, 5, 5));
|
||||
footer.setOpaque(false);
|
||||
|
||||
JButton copyBtn = new JButton(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("button.copy")));
|
||||
JButton copyBtn =
|
||||
new JButton(
|
||||
FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("button.copy")));
|
||||
copyBtn.setMargin(new Insets(3, 3, 3, 3));
|
||||
copyBtn.addActionListener(e -> {
|
||||
copyBtn.addActionListener(
|
||||
e -> {
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
StringSelection selection = new StringSelection(value);
|
||||
clipboard.setContents(selection, null);
|
||||
|
@ -127,6 +129,4 @@ public final class StoredValueDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
return panel;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,14 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.documents;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -35,7 +27,14 @@ import java.io.IOException;
|
|||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.components.TableColumnInfo;
|
||||
|
@ -58,7 +57,7 @@ public final class TermVectorDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
private List<TermVectorEntry> tvEntries;
|
||||
|
||||
public synchronized static TermVectorDialogFactory getInstance() throws IOException {
|
||||
public static synchronized TermVectorDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new TermVectorDialogFactory();
|
||||
}
|
||||
|
@ -98,12 +97,20 @@ public final class TermVectorDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
JPanel header = new JPanel(new FlowLayout(FlowLayout.LEADING, 5, 5));
|
||||
header.setOpaque(false);
|
||||
header.add(new JLabel(MessageUtils.getLocalizedMessage("documents.termvector.label.term_vector")));
|
||||
header.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("documents.termvector.label.term_vector")));
|
||||
header.add(new JLabel(field));
|
||||
panel.add(header, BorderLayout.PAGE_START);
|
||||
|
||||
JTable tvTable = new JTable();
|
||||
TableUtils.setupTable(tvTable, ListSelectionModel.SINGLE_SELECTION, new TermVectorTableModel(tvEntries), null, 100, 50, 100);
|
||||
TableUtils.setupTable(
|
||||
tvTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new TermVectorTableModel(tvEntries),
|
||||
null,
|
||||
100,
|
||||
50,
|
||||
100);
|
||||
JScrollPane scrollPane = new JScrollPane(tvTable);
|
||||
panel.add(scrollPane, BorderLayout.CENTER);
|
||||
|
||||
|
@ -121,7 +128,6 @@ public final class TermVectorDialogFactory implements DialogOpener.DialogFactory
|
|||
static final class TermVectorTableModel extends TableModelBase<TermVectorTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
|
||||
TERM("Term", 0, String.class),
|
||||
FREQ("Freq", 1, Long.class),
|
||||
POSITIONS("Positions", 2, String.class),
|
||||
|
@ -165,20 +171,27 @@ public final class TermVectorDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
String termText = entry.getTermText();
|
||||
long freq = tvEntries.get(i).getFreq();
|
||||
String positions = String.join(",",
|
||||
String positions =
|
||||
String.join(
|
||||
",",
|
||||
entry.getPositions().stream()
|
||||
.map(pos -> Integer.toString(pos.getPosition()))
|
||||
.collect(Collectors.toList()));
|
||||
String offsets = String.join(",",
|
||||
String offsets =
|
||||
String.join(
|
||||
",",
|
||||
entry.getPositions().stream()
|
||||
.filter(pos -> pos.getStartOffset().isPresent() && pos.getEndOffset().isPresent())
|
||||
.map(pos -> Integer.toString(pos.getStartOffset().orElse(-1)) + "-" + Integer.toString(pos.getEndOffset().orElse(-1)))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
.filter(
|
||||
pos -> pos.getStartOffset().isPresent() && pos.getEndOffset().isPresent())
|
||||
.map(
|
||||
pos ->
|
||||
Integer.toString(pos.getStartOffset().orElse(-1))
|
||||
+ "-"
|
||||
+ Integer.toString(pos.getEndOffset().orElse(-1)))
|
||||
.collect(Collectors.toList()));
|
||||
|
||||
data[i] = new Object[] {termText, freq, positions, offsets};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,18 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.menubar;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JEditorPane;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Desktop;
|
||||
|
@ -42,7 +30,18 @@ import java.awt.Window;
|
|||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.Objects;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JEditorPane;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.ScrollPaneConstants;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.event.HyperlinkEvent;
|
||||
import javax.swing.event.HyperlinkListener;
|
||||
import org.apache.lucene.LucenePackage;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
|
@ -62,7 +61,7 @@ public final class AboutDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
private JDialog dialog;
|
||||
|
||||
public synchronized static AboutDialogFactory getInstance() throws IOException {
|
||||
public static synchronized AboutDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new AboutDialogFactory();
|
||||
}
|
||||
|
@ -143,9 +142,14 @@ public final class AboutDialogFactory implements DialogOpener.DialogFactory {
|
|||
editorPane.setText(LICENSE_NOTICE);
|
||||
editorPane.setEditable(false);
|
||||
editorPane.addHyperlinkListener(hyperlinkListener);
|
||||
JScrollPane scrollPane = new JScrollPane(editorPane, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
JScrollPane scrollPane =
|
||||
new JScrollPane(
|
||||
editorPane,
|
||||
ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
|
||||
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
|
||||
scrollPane.setBorder(BorderFactory.createLineBorder(Color.gray));
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
SwingUtilities.invokeLater(
|
||||
() -> {
|
||||
// Set the scroll bar position to top
|
||||
scrollPane.getVerticalScrollBar().setValue(0);
|
||||
});
|
||||
|
@ -164,28 +168,30 @@ public final class AboutDialogFactory implements DialogOpener.DialogFactory {
|
|||
return panel;
|
||||
}
|
||||
|
||||
private static final String LUCENE_IMPLEMENTATION_VERSION = LucenePackage.get().getImplementationVersion();
|
||||
private static final String LUCENE_IMPLEMENTATION_VERSION =
|
||||
LucenePackage.get().getImplementationVersion();
|
||||
|
||||
private static final String LICENSE_NOTICE =
|
||||
"<p>[Implementation Version]</p>" +
|
||||
"<p>" + (Objects.nonNull(LUCENE_IMPLEMENTATION_VERSION) ? LUCENE_IMPLEMENTATION_VERSION : "") + "</p>" +
|
||||
"<p>[License]</p>" +
|
||||
"<p>Luke is distributed under <a href=\"http://www.apache.org/licenses/LICENSE-2.0\">Apache License Version 2.0</a> (http://www.apache.org/licenses/LICENSE-2.0) " +
|
||||
"and includes <a href=\"https://www.elegantthemes.com/blog/resources/elegant-icon-font\">The Elegant Icon Font</a> (https://www.elegantthemes.com/blog/resources/elegant-icon-font) " +
|
||||
"licensed under <a href=\"https://opensource.org/licenses/MIT\">MIT</a> (https://opensource.org/licenses/MIT)</p>" +
|
||||
"<p>[Brief history]</p>" +
|
||||
"<ul>" +
|
||||
"<li>The original author is Andrzej Bialecki</li>" +
|
||||
"<li>The project has been mavenized by Neil Ireson</li>" +
|
||||
"<li>The project has been ported to Lucene trunk (marked as 5.0 at the time) by Dmitry Kan\n</li>" +
|
||||
"<li>The project has been back-ported to Lucene 4.3 by sonarname</li>" +
|
||||
"<li>There are updates to the (non-mavenized) project done by tarzanek</li>" +
|
||||
"<li>The UI and core components has been re-implemented on top of Swing by Tomoko Uchida</li>" +
|
||||
"</ul>"
|
||||
;
|
||||
"<p>[Implementation Version]</p>"
|
||||
+ "<p>"
|
||||
+ (Objects.nonNull(LUCENE_IMPLEMENTATION_VERSION) ? LUCENE_IMPLEMENTATION_VERSION : "")
|
||||
+ "</p>"
|
||||
+ "<p>[License]</p>"
|
||||
+ "<p>Luke is distributed under <a href=\"http://www.apache.org/licenses/LICENSE-2.0\">Apache License Version 2.0</a> (http://www.apache.org/licenses/LICENSE-2.0) "
|
||||
+ "and includes <a href=\"https://www.elegantthemes.com/blog/resources/elegant-icon-font\">The Elegant Icon Font</a> (https://www.elegantthemes.com/blog/resources/elegant-icon-font) "
|
||||
+ "licensed under <a href=\"https://opensource.org/licenses/MIT\">MIT</a> (https://opensource.org/licenses/MIT)</p>"
|
||||
+ "<p>[Brief history]</p>"
|
||||
+ "<ul>"
|
||||
+ "<li>The original author is Andrzej Bialecki</li>"
|
||||
+ "<li>The project has been mavenized by Neil Ireson</li>"
|
||||
+ "<li>The project has been ported to Lucene trunk (marked as 5.0 at the time) by Dmitry Kan\n</li>"
|
||||
+ "<li>The project has been back-ported to Lucene 4.3 by sonarname</li>"
|
||||
+ "<li>There are updates to the (non-mavenized) project done by tarzanek</li>"
|
||||
+ "<li>The UI and core components has been re-implemented on top of Swing by Tomoko Uchida</li>"
|
||||
+ "</ul>";
|
||||
|
||||
|
||||
private static final HyperlinkListener hyperlinkListener = e -> {
|
||||
private static final HyperlinkListener hyperlinkListener =
|
||||
e -> {
|
||||
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED)
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
try {
|
||||
|
@ -195,6 +201,4 @@ public final class AboutDialogFactory implements DialogOpener.DialogFactory {
|
|||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -17,16 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.menubar;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingWorker;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -39,7 +29,16 @@ import java.io.IOException;
|
|||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.index.CheckIndex;
|
||||
import org.apache.lucene.luke.app.DirectoryHandler;
|
||||
|
@ -95,7 +94,7 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
private final ListenerFunctions listeners = new ListenerFunctions();
|
||||
|
||||
public synchronized static CheckIndexDialogFactory getInstance() throws IOException {
|
||||
public static synchronized CheckIndexDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new CheckIndexDialogFactory();
|
||||
}
|
||||
|
@ -115,7 +114,9 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
}
|
||||
|
||||
private void initialize() {
|
||||
repairBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("checkidx.button.fix")));
|
||||
repairBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("checkidx.button.fix")));
|
||||
repairBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
repairBtn.setMargin(new Insets(3, 3, 3, 3));
|
||||
repairBtn.setEnabled(false);
|
||||
|
@ -126,7 +127,6 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
logArea.setEditable(false);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public JDialog create(Window owner, String title, int width, int height) {
|
||||
dialog = new JDialog(owner, title, Dialog.ModalityType.APPLICATION_MODAL);
|
||||
|
@ -171,7 +171,10 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
JPanel execButtons = new JPanel(new FlowLayout(FlowLayout.TRAILING));
|
||||
execButtons.setOpaque(false);
|
||||
JButton checkBtn = new JButton(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("checkidx.button.check")));
|
||||
JButton checkBtn =
|
||||
new JButton(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("checkidx.button.check")));
|
||||
checkBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
checkBtn.setMargin(new Insets(3, 0, 3, 0));
|
||||
checkBtn.addActionListener(listeners::checkIndex);
|
||||
|
@ -199,7 +202,8 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
repair.setOpaque(false);
|
||||
repair.add(repairBtn);
|
||||
|
||||
JTextArea warnArea = new JTextArea(MessageUtils.getLocalizedMessage("checkidx.label.warn"), 3, 30);
|
||||
JTextArea warnArea =
|
||||
new JTextArea(MessageUtils.getLocalizedMessage("checkidx.label.warn"), 3, 30);
|
||||
warnArea.setLineWrap(true);
|
||||
warnArea.setEditable(false);
|
||||
warnArea.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
|
@ -234,7 +238,9 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
@Override
|
||||
public void openIndex(LukeState state) {
|
||||
lukeState = state;
|
||||
toolsModel = indexToolsFactory.newInstance(state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
toolsModel =
|
||||
indexToolsFactory.newInstance(
|
||||
state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -261,9 +267,11 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
private class ListenerFunctions {
|
||||
|
||||
void checkIndex(ActionEvent e) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("check-index-dialog-check"));
|
||||
ExecutorService executor =
|
||||
Executors.newFixedThreadPool(1, new NamedThreadFactory("check-index-dialog-check"));
|
||||
|
||||
SwingWorker<CheckIndex.Status, Void> task = new SwingWorker<CheckIndex.Status, Void>() {
|
||||
SwingWorker<CheckIndex.Status, Void> task =
|
||||
new SwingWorker<CheckIndex.Status, Void>() {
|
||||
|
||||
@Override
|
||||
protected CheckIndex.Status doInBackground() {
|
||||
|
@ -337,9 +345,11 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
return;
|
||||
}
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("check-index-dialog-repair"));
|
||||
ExecutorService executor =
|
||||
Executors.newFixedThreadPool(1, new NamedThreadFactory("check-index-dialog-repair"));
|
||||
|
||||
SwingWorker<CheckIndex.Status, Void> task = new SwingWorker<CheckIndex.Status, Void>() {
|
||||
SwingWorker<CheckIndex.Status, Void> task =
|
||||
new SwingWorker<CheckIndex.Status, Void>() {
|
||||
|
||||
@Override
|
||||
protected CheckIndex.Status doInBackground() {
|
||||
|
@ -376,5 +386,4 @@ public final class CheckIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
executor.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,18 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.menubar;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingWorker;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -47,7 +35,18 @@ import java.nio.file.SimpleFileVisitor;
|
|||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
|
@ -99,7 +98,7 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
private JDialog dialog;
|
||||
|
||||
public synchronized static CreateIndexDialogFactory getInstance() throws IOException {
|
||||
public static synchronized CreateIndexDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new CreateIndexDialogFactory();
|
||||
}
|
||||
|
@ -117,7 +116,8 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
locationTF.setText(System.getProperty("user.home"));
|
||||
locationTF.setEditable(false);
|
||||
|
||||
browseBtn.setText(FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("button.browse")));
|
||||
browseBtn.setText(
|
||||
FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("button.browse")));
|
||||
browseBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
browseBtn.setPreferredSize(new Dimension(120, 30));
|
||||
browseBtn.addActionListener(listeners::browseLocationDirectory);
|
||||
|
@ -131,7 +131,8 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
clearBtn.setPreferredSize(new Dimension(70, 30));
|
||||
clearBtn.addActionListener(listeners::clearDataDir);
|
||||
|
||||
dataBrowseBtn.setText(FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("button.browse")));
|
||||
dataBrowseBtn.setText(
|
||||
FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("button.browse")));
|
||||
dataBrowseBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
dataBrowseBtn.setPreferredSize(new Dimension(100, 30));
|
||||
dataBrowseBtn.addActionListener(listeners::browseDataDirectory);
|
||||
|
@ -205,7 +206,8 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
name.add(nameLbl);
|
||||
description.add(name);
|
||||
|
||||
JTextArea descTA1 = new JTextArea(MessageUtils.getLocalizedMessage("createindex.textarea.data_help1"));
|
||||
JTextArea descTA1 =
|
||||
new JTextArea(MessageUtils.getLocalizedMessage("createindex.textarea.data_help1"));
|
||||
descTA1.setPreferredSize(new Dimension(550, 20));
|
||||
descTA1.setBorder(BorderFactory.createEmptyBorder(2, 10, 10, 5));
|
||||
descTA1.setOpaque(false);
|
||||
|
@ -215,11 +217,14 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
JPanel link = new JPanel(new FlowLayout(FlowLayout.LEADING, 10, 1));
|
||||
link.setOpaque(false);
|
||||
JLabel linkLbl = FontUtils.toLinkText(new URLLabel(MessageUtils.getLocalizedMessage("createindex.label.data_link")));
|
||||
JLabel linkLbl =
|
||||
FontUtils.toLinkText(
|
||||
new URLLabel(MessageUtils.getLocalizedMessage("createindex.label.data_link")));
|
||||
link.add(linkLbl);
|
||||
description.add(link);
|
||||
|
||||
JTextArea descTA2 = new JTextArea(MessageUtils.getLocalizedMessage("createindex.textarea.data_help2"));
|
||||
JTextArea descTA2 =
|
||||
new JTextArea(MessageUtils.getLocalizedMessage("createindex.textarea.data_help2"));
|
||||
descTA2.setPreferredSize(new Dimension(550, 50));
|
||||
descTA2.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 5));
|
||||
descTA2.setOpaque(false);
|
||||
|
@ -279,12 +284,15 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
Path path = Paths.get(locationTF.getText(), dirnameTF.getText());
|
||||
if (Files.exists(path)) {
|
||||
String message = "The directory " + path.toAbsolutePath().toString() + " already exists.";
|
||||
JOptionPane.showMessageDialog(dialog, message, "Empty index path", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(
|
||||
dialog, message, "Empty index path", JOptionPane.ERROR_MESSAGE);
|
||||
} else {
|
||||
// create new index asynchronously
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("create-index-dialog"));
|
||||
ExecutorService executor =
|
||||
Executors.newFixedThreadPool(1, new NamedThreadFactory("create-index-dialog"));
|
||||
|
||||
SwingWorker<Void, Void> task = new SwingWorker<Void, Void>() {
|
||||
SwingWorker<Void, Void> task =
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
|
@ -313,9 +321,12 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
} catch (Exception ex) {
|
||||
// cleanup
|
||||
try {
|
||||
Files.walkFileTree(path, new SimpleFileVisitor<Path>() {
|
||||
Files.walkFileTree(
|
||||
path,
|
||||
new SimpleFileVisitor<Path>() {
|
||||
@Override
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
|
||||
throws IOException {
|
||||
Files.delete(file);
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
@ -326,7 +337,8 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
log.error("Cannot create index", ex);
|
||||
String message = "See Logs tab or log file for more details.";
|
||||
JOptionPane.showMessageDialog(dialog, message, "Cannot create index", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(
|
||||
dialog, message, "Cannot create index", JOptionPane.ERROR_MESSAGE);
|
||||
} finally {
|
||||
setProgress(100);
|
||||
}
|
||||
|
|
|
@ -17,16 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.menubar;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingWorker;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -42,7 +32,16 @@ import java.util.Arrays;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.SwingWorker;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
import org.apache.lucene.luke.app.IndexObserver;
|
||||
|
@ -61,9 +60,7 @@ import org.apache.lucene.luke.util.LoggerFactory;
|
|||
import org.apache.lucene.util.NamedThreadFactory;
|
||||
import org.apache.lucene.util.SuppressForbidden;
|
||||
|
||||
/**
|
||||
* Factory of export terms dialog
|
||||
*/
|
||||
/** Factory of export terms dialog */
|
||||
public final class ExportTermsDialogFactory implements DialogOpener.DialogFactory {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
|
||||
|
@ -94,7 +91,7 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
|
||||
private String selectedDelimiter;
|
||||
|
||||
public synchronized static ExportTermsDialogFactory getInstance() throws IOException {
|
||||
public static synchronized ExportTermsDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new ExportTermsDialogFactory();
|
||||
}
|
||||
|
@ -105,7 +102,8 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
this.prefs = PreferencesFactory.getInstance();
|
||||
this.indexHandler = IndexHandler.getInstance();
|
||||
indexHandler.addObserver(new Observer());
|
||||
Stream.of(Delimiter.values()).forEachOrdered(delimiterVal -> delimiterCombo.addItem(delimiterVal.getDescription()));
|
||||
Stream.of(Delimiter.values())
|
||||
.forEachOrdered(delimiterVal -> delimiterCombo.addItem(delimiterVal.getDescription()));
|
||||
delimiterCombo.setSelectedItem(Delimiter.COMMA.getDescription()); // Set default delimiter
|
||||
}
|
||||
|
||||
|
@ -230,9 +228,11 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
}
|
||||
|
||||
void export(ActionEvent e) {
|
||||
ExecutorService executor = Executors.newSingleThreadExecutor(new NamedThreadFactory("export-terms-dialog"));
|
||||
ExecutorService executor =
|
||||
Executors.newSingleThreadExecutor(new NamedThreadFactory("export-terms-dialog"));
|
||||
|
||||
SwingWorker<Void, Void> task = new SwingWorker<Void, Void>() {
|
||||
SwingWorker<Void, Void> task =
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
String filename;
|
||||
|
||||
|
@ -242,14 +242,16 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
statusLbl.setText("Exporting...");
|
||||
indicatorLbl.setVisible(true);
|
||||
String field = (String) fieldCombo.getSelectedItem();
|
||||
selectedDelimiter = Delimiter.getSelectedDelimiterValue((String) delimiterCombo.getSelectedItem());
|
||||
selectedDelimiter =
|
||||
Delimiter.getSelectedDelimiterValue((String) delimiterCombo.getSelectedItem());
|
||||
|
||||
String directory = destDir.getText();
|
||||
try {
|
||||
filename = toolsModel.exportTerms(directory, field, selectedDelimiter);
|
||||
} catch (LukeException e) {
|
||||
log.error("Error while exporting terms from field {}", field, e);
|
||||
statusLbl.setText(MessageUtils.getLocalizedMessage("export.terms.label.error", e.getMessage()));
|
||||
statusLbl.setText(
|
||||
MessageUtils.getLocalizedMessage("export.terms.label.error", e.getMessage()));
|
||||
} catch (Exception e) {
|
||||
log.error("Error while exporting terms from field {}", field, e);
|
||||
statusLbl.setText(MessageUtils.getLocalizedMessage("message.error.unknown"));
|
||||
|
@ -264,7 +266,11 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
protected void done() {
|
||||
indicatorLbl.setVisible(false);
|
||||
if (filename != null) {
|
||||
statusLbl.setText(MessageUtils.getLocalizedMessage("export.terms.label.success", filename, "[term]" + selectedDelimiter + "[doc frequency]"));
|
||||
statusLbl.setText(
|
||||
MessageUtils.getLocalizedMessage(
|
||||
"export.terms.label.success",
|
||||
filename,
|
||||
"[term]" + selectedDelimiter + "[doc frequency]"));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -272,15 +278,18 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
executor.submit(task);
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver {
|
||||
|
||||
@Override
|
||||
public void openIndex(LukeState state) {
|
||||
toolsModel = indexToolsFactory.newInstance(state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
IndexUtils.getFieldNames(state.getIndexReader()).stream().sorted().forEach(fieldCombo::addItem);
|
||||
toolsModel =
|
||||
indexToolsFactory.newInstance(
|
||||
state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
IndexUtils.getFieldNames(state.getIndexReader()).stream()
|
||||
.sorted()
|
||||
.forEach(fieldCombo::addItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -288,14 +297,13 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
fieldCombo.removeAllItems();
|
||||
toolsModel = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Delimiters that can be selected
|
||||
*/
|
||||
/** Delimiters that can be selected */
|
||||
private enum Delimiter {
|
||||
COMMA("Comma", ","), WHITESPACE("Whitespace", " "), TAB("Tab", "\t");
|
||||
COMMA("Comma", ","),
|
||||
WHITESPACE("Whitespace", " "),
|
||||
TAB("Tab", "\t");
|
||||
|
||||
private final String description;
|
||||
private final String separator;
|
||||
|
@ -321,5 +329,4 @@ public final class ExportTermsDialogFactory implements DialogOpener.DialogFactor
|
|||
.getSeparator();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,19 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.menubar;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JSeparator;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
|
@ -48,7 +35,19 @@ import java.util.Set;
|
|||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.ButtonGroup;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JRadioButton;
|
||||
import javax.swing.JSeparator;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.luke.app.DirectoryHandler;
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
|
@ -98,7 +97,7 @@ public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
|
||||
private JDialog dialog;
|
||||
|
||||
public synchronized static OpenIndexDialogFactory getInstance() throws IOException {
|
||||
public static synchronized OpenIndexDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new OpenIndexDialogFactory();
|
||||
}
|
||||
|
@ -115,7 +114,8 @@ public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
private void initialize() {
|
||||
idxPathCombo.setPreferredSize(new Dimension(360, 40));
|
||||
|
||||
browseBtn.setText(FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("button.browse")));
|
||||
browseBtn.setText(
|
||||
FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("button.browse")));
|
||||
browseBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
browseBtn.setPreferredSize(new Dimension(120, 40));
|
||||
browseBtn.addActionListener(listeners::browseDirectory);
|
||||
|
@ -126,8 +126,10 @@ public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
readOnlyCB.setOpaque(false);
|
||||
|
||||
// Scanning all Directory types will take time...
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(1, new NamedThreadFactory("load-directory-types"));
|
||||
executorService.execute(() -> {
|
||||
ExecutorService executorService =
|
||||
Executors.newFixedThreadPool(1, new NamedThreadFactory("load-directory-types"));
|
||||
executorService.execute(
|
||||
() -> {
|
||||
for (String clazzName : supportedDirImpls()) {
|
||||
dirImplCombo.addItem(clazzName);
|
||||
}
|
||||
|
@ -144,14 +146,14 @@ public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
useCompoundCB.setSelected(prefs.isUseCompound());
|
||||
useCompoundCB.setOpaque(false);
|
||||
|
||||
keepLastCommitRB.setText(MessageUtils.getLocalizedMessage("openindex.radio.keep_only_last_commit"));
|
||||
keepLastCommitRB.setText(
|
||||
MessageUtils.getLocalizedMessage("openindex.radio.keep_only_last_commit"));
|
||||
keepLastCommitRB.setSelected(!prefs.isKeepAllCommits());
|
||||
keepLastCommitRB.setOpaque(false);
|
||||
|
||||
keepAllCommitsRB.setText(MessageUtils.getLocalizedMessage("openindex.radio.keep_all_commits"));
|
||||
keepAllCommitsRB.setSelected(prefs.isKeepAllCommits());
|
||||
keepAllCommitsRB.setOpaque(false);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -334,24 +336,32 @@ public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
String selectedPath = (String) idxPathCombo.getSelectedItem();
|
||||
String dirImplClazz = (String) dirImplCombo.getSelectedItem();
|
||||
if (selectedPath == null || selectedPath.length() == 0) {
|
||||
String message = MessageUtils.getLocalizedMessage("openindex.message.index_path_not_selected");
|
||||
JOptionPane.showMessageDialog(dialog, message, "Empty index path", JOptionPane.ERROR_MESSAGE);
|
||||
String message =
|
||||
MessageUtils.getLocalizedMessage("openindex.message.index_path_not_selected");
|
||||
JOptionPane.showMessageDialog(
|
||||
dialog, message, "Empty index path", JOptionPane.ERROR_MESSAGE);
|
||||
} else if (isNoReader()) {
|
||||
directoryHandler.open(selectedPath, dirImplClazz);
|
||||
addHistory(selectedPath);
|
||||
} else {
|
||||
indexHandler.open(selectedPath, dirImplClazz, isReadOnly(), useCompound(), keepAllCommits());
|
||||
indexHandler.open(
|
||||
selectedPath, dirImplClazz, isReadOnly(), useCompound(), keepAllCommits());
|
||||
addHistory(selectedPath);
|
||||
}
|
||||
prefs.setIndexOpenerPrefs(
|
||||
isReadOnly(), dirImplClazz,
|
||||
isNoReader(), useCompound(), keepAllCommits());
|
||||
isReadOnly(), dirImplClazz, isNoReader(), useCompound(), keepAllCommits());
|
||||
closeDialog();
|
||||
} catch (LukeException ex) {
|
||||
String message = ex.getMessage() + System.lineSeparator() + "See Logs tab or log file for more details.";
|
||||
JOptionPane.showMessageDialog(dialog, message, "Invalid index path", JOptionPane.ERROR_MESSAGE);
|
||||
String message =
|
||||
ex.getMessage() + System.lineSeparator() + "See Logs tab or log file for more details.";
|
||||
JOptionPane.showMessageDialog(
|
||||
dialog, message, "Invalid index path", JOptionPane.ERROR_MESSAGE);
|
||||
} catch (Throwable cause) {
|
||||
JOptionPane.showMessageDialog(dialog, MessageUtils.getLocalizedMessage("message.error.unknown"), "Unknown Error", JOptionPane.ERROR_MESSAGE);
|
||||
JOptionPane.showMessageDialog(
|
||||
dialog,
|
||||
MessageUtils.getLocalizedMessage("message.error.unknown"),
|
||||
"Unknown Error",
|
||||
JOptionPane.ERROR_MESSAGE);
|
||||
log.error("Error opening index or directory", cause);
|
||||
}
|
||||
}
|
||||
|
@ -379,7 +389,5 @@ public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory
|
|||
private void addHistory(String indexPath) throws IOException {
|
||||
prefs.addHistory(indexPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.menubar;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
|
@ -30,19 +42,6 @@ import javax.swing.JSpinner;
|
|||
import javax.swing.JTextArea;
|
||||
import javax.swing.SpinnerNumberModel;
|
||||
import javax.swing.SwingWorker;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Insets;
|
||||
import java.awt.Window;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.IOException;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.lucene.luke.app.IndexHandler;
|
||||
import org.apache.lucene.luke.app.IndexObserver;
|
||||
|
@ -89,7 +88,7 @@ public final class OptimizeIndexDialogFactory implements DialogOpener.DialogFact
|
|||
|
||||
private IndexTools toolsModel;
|
||||
|
||||
public synchronized static OptimizeIndexDialogFactory getInstance() throws IOException {
|
||||
public static synchronized OptimizeIndexDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new OptimizeIndexDialogFactory();
|
||||
}
|
||||
|
@ -165,7 +164,10 @@ public final class OptimizeIndexDialogFactory implements DialogOpener.DialogFact
|
|||
|
||||
JPanel execButtons = new JPanel(new FlowLayout(FlowLayout.TRAILING));
|
||||
execButtons.setOpaque(false);
|
||||
JButton optimizeBtn = new JButton(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("optimize.button.optimize")));
|
||||
JButton optimizeBtn =
|
||||
new JButton(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("optimize.button.optimize")));
|
||||
optimizeBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
optimizeBtn.setMargin(new Insets(3, 0, 3, 0));
|
||||
optimizeBtn.addActionListener(listeners::optimize);
|
||||
|
@ -206,9 +208,11 @@ public final class OptimizeIndexDialogFactory implements DialogOpener.DialogFact
|
|||
private class ListenerFunctions {
|
||||
|
||||
void optimize(ActionEvent e) {
|
||||
ExecutorService executor = Executors.newFixedThreadPool(1, new NamedThreadFactory("optimize-index-dialog"));
|
||||
ExecutorService executor =
|
||||
Executors.newFixedThreadPool(1, new NamedThreadFactory("optimize-index-dialog"));
|
||||
|
||||
SwingWorker<Void, Void> task = new SwingWorker<Void, Void>() {
|
||||
SwingWorker<Void, Void> task =
|
||||
new SwingWorker<Void, Void>() {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
|
@ -240,21 +244,20 @@ public final class OptimizeIndexDialogFactory implements DialogOpener.DialogFact
|
|||
executor.submit(task);
|
||||
executor.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class Observer implements IndexObserver {
|
||||
|
||||
@Override
|
||||
public void openIndex(LukeState state) {
|
||||
toolsModel = indexToolsFactory.newInstance(state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
toolsModel =
|
||||
indexToolsFactory.newInstance(
|
||||
state.getIndexReader(), state.useCompound(), state.keepAllCommits());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeIndex() {
|
||||
toolsModel = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,15 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.dialog.search;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dialog;
|
||||
import java.awt.Dimension;
|
||||
|
@ -39,7 +30,15 @@ import java.awt.datatransfer.StringSelection;
|
|||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JDialog;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTree;
|
||||
import javax.swing.tree.DefaultMutableTreeNode;
|
||||
import javax.swing.tree.DefaultTreeCellRenderer;
|
||||
import org.apache.lucene.luke.app.desktop.Preferences;
|
||||
import org.apache.lucene.luke.app.desktop.PreferencesFactory;
|
||||
import org.apache.lucene.luke.app.desktop.util.DialogOpener;
|
||||
|
@ -60,7 +59,7 @@ public final class ExplainDialogFactory implements DialogOpener.DialogFactory {
|
|||
|
||||
private Explanation explanation;
|
||||
|
||||
public synchronized static ExplainDialogFactory getInstance() throws IOException {
|
||||
public static synchronized ExplainDialogFactory getInstance() throws IOException {
|
||||
if (instance == null) {
|
||||
instance = new ExplainDialogFactory();
|
||||
}
|
||||
|
@ -112,9 +111,12 @@ public final class ExplainDialogFactory implements DialogOpener.DialogFactory {
|
|||
JPanel footer = new JPanel(new FlowLayout(FlowLayout.TRAILING, 5, 5));
|
||||
footer.setOpaque(false);
|
||||
|
||||
JButton copyBtn = new JButton(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("button.copy")));
|
||||
JButton copyBtn =
|
||||
new JButton(
|
||||
FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("button.copy")));
|
||||
copyBtn.setMargin(new Insets(3, 3, 3, 3));
|
||||
copyBtn.addActionListener(e -> {
|
||||
copyBtn.addActionListener(
|
||||
e -> {
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
StringSelection selection = new StringSelection(explanationToString());
|
||||
clipboard.setContents(selection, null);
|
||||
|
|
|
@ -19,7 +19,6 @@ package org.apache.lucene.luke.app.desktop.components.fragments.analysis;
|
|||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
import org.apache.lucene.luke.models.analysis.Analysis;
|
||||
|
||||
|
|
|
@ -17,17 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.fragments.analysis;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextField;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
|
@ -52,7 +41,17 @@ import java.util.Map;
|
|||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFileChooser;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JTextField;
|
||||
import org.apache.lucene.luke.app.desktop.MessageBroker;
|
||||
import org.apache.lucene.luke.app.desktop.components.AnalysisTabOperator;
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
|
@ -156,18 +155,23 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
confDirTF.setColumns(30);
|
||||
confDirTF.setPreferredSize(new Dimension(200, 30));
|
||||
panel.add(confDirTF);
|
||||
confDirBtn.setText(FontUtils.elegantIconHtml("n", MessageUtils.getLocalizedMessage("analysis.button.browse")));
|
||||
confDirBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"n", MessageUtils.getLocalizedMessage("analysis.button.browse")));
|
||||
confDirBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
confDirBtn.setMargin(new Insets(3, 3, 3, 3));
|
||||
confDirBtn.addActionListener(listeners::chooseConfigDir);
|
||||
panel.add(confDirBtn);
|
||||
buildBtn.setText(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("analysis.button.build_analyzser")));
|
||||
buildBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("analysis.button.build_analyzser")));
|
||||
buildBtn.setFont(StyleConstants.FONT_BUTTON_LARGE);
|
||||
buildBtn.setMargin(new Insets(3, 3, 3, 3));
|
||||
buildBtn.addActionListener(listeners::buildAnalyzer);
|
||||
panel.add(buildBtn);
|
||||
loadJarLbl.setText(MessageUtils.getLocalizedMessage("analysis.hyperlink.load_jars"));
|
||||
loadJarLbl.addMouseListener(new MouseAdapter() {
|
||||
loadJarLbl.addMouseListener(
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
listeners.loadExternalJars(e);
|
||||
|
@ -202,7 +206,8 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
sepc.gridwidth = GridBagConstraints.REMAINDER;
|
||||
|
||||
// char filters
|
||||
JLabel cfLbl = new JLabel(MessageUtils.getLocalizedMessage("analysis_custom.label.charfilters"));
|
||||
JLabel cfLbl =
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis_custom.label.charfilters"));
|
||||
cfLbl.setBorder(BorderFactory.createEmptyBorder(3, 10, 3, 3));
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
|
@ -234,7 +239,9 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
c.anchor = GridBagConstraints.LINE_END;
|
||||
panel.add(selectedPanel, c);
|
||||
|
||||
cfEditBtn.setText(FontUtils.elegantIconHtml("j", MessageUtils.getLocalizedMessage("analysis_custom.label.edit")));
|
||||
cfEditBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"j", MessageUtils.getLocalizedMessage("analysis_custom.label.edit")));
|
||||
cfEditBtn.setMargin(new Insets(2, 4, 2, 4));
|
||||
cfEditBtn.setEnabled(false);
|
||||
cfEditBtn.addActionListener(listeners::editCharFilters);
|
||||
|
@ -248,7 +255,10 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
c.anchor = GridBagConstraints.CENTER;
|
||||
panel.add(cfEditBtn, c);
|
||||
|
||||
JLabel cfAddLabel = new JLabel(FontUtils.elegantIconHtml("L", MessageUtils.getLocalizedMessage("analysis_custom.label.add")));
|
||||
JLabel cfAddLabel =
|
||||
new JLabel(
|
||||
FontUtils.elegantIconHtml(
|
||||
"L", MessageUtils.getLocalizedMessage("analysis_custom.label.add")));
|
||||
cfAddLabel.setHorizontalAlignment(JLabel.LEFT);
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 1;
|
||||
|
@ -276,7 +286,8 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
panel.add(new JSeparator(JSeparator.HORIZONTAL), sepc);
|
||||
|
||||
// tokenizer
|
||||
JLabel tokLabel = new JLabel(MessageUtils.getLocalizedMessage("analysis_custom.label.tokenizer"));
|
||||
JLabel tokLabel =
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis_custom.label.tokenizer"));
|
||||
tokLabel.setBorder(BorderFactory.createEmptyBorder(3, 10, 3, 3));
|
||||
c.gridx = 0;
|
||||
c.gridy = 4;
|
||||
|
@ -310,7 +321,9 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
c.anchor = GridBagConstraints.LINE_END;
|
||||
panel.add(selectedTokTF, c);
|
||||
|
||||
tokEditBtn.setText(FontUtils.elegantIconHtml("j", MessageUtils.getLocalizedMessage("analysis_custom.label.edit")));
|
||||
tokEditBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"j", MessageUtils.getLocalizedMessage("analysis_custom.label.edit")));
|
||||
tokEditBtn.setMargin(new Insets(2, 4, 2, 4));
|
||||
tokEditBtn.addActionListener(listeners::editTokenizer);
|
||||
c.fill = GridBagConstraints.NONE;
|
||||
|
@ -323,7 +336,10 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
c.anchor = GridBagConstraints.CENTER;
|
||||
panel.add(tokEditBtn, c);
|
||||
|
||||
JLabel setTokLabel = new JLabel(FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("analysis_custom.label.set")));
|
||||
JLabel setTokLabel =
|
||||
new JLabel(
|
||||
FontUtils.elegantIconHtml(
|
||||
"", MessageUtils.getLocalizedMessage("analysis_custom.label.set")));
|
||||
setTokLabel.setHorizontalAlignment(JLabel.LEFT);
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 1;
|
||||
|
@ -351,7 +367,8 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
panel.add(new JSeparator(JSeparator.HORIZONTAL), sepc);
|
||||
|
||||
// token filters
|
||||
JLabel tfLbl = new JLabel(MessageUtils.getLocalizedMessage("analysis_custom.label.tokenfilters"));
|
||||
JLabel tfLbl =
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis_custom.label.tokenfilters"));
|
||||
tfLbl.setBorder(BorderFactory.createEmptyBorder(3, 10, 3, 3));
|
||||
c.gridx = 0;
|
||||
c.gridy = 8;
|
||||
|
@ -383,7 +400,9 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
c.anchor = GridBagConstraints.LINE_END;
|
||||
panel.add(selectedTfPanel, c);
|
||||
|
||||
tfEditBtn.setText(FontUtils.elegantIconHtml("j", MessageUtils.getLocalizedMessage("analysis_custom.label.edit")));
|
||||
tfEditBtn.setText(
|
||||
FontUtils.elegantIconHtml(
|
||||
"j", MessageUtils.getLocalizedMessage("analysis_custom.label.edit")));
|
||||
tfEditBtn.setMargin(new Insets(2, 4, 2, 4));
|
||||
tfEditBtn.setEnabled(false);
|
||||
tfEditBtn.addActionListener(listeners::editTokenFilters);
|
||||
|
@ -397,7 +416,10 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
c.anchor = GridBagConstraints.CENTER;
|
||||
panel.add(tfEditBtn, c);
|
||||
|
||||
JLabel tfAddLabel = new JLabel(FontUtils.elegantIconHtml("L", MessageUtils.getLocalizedMessage("analysis_custom.label.add")));
|
||||
JLabel tfAddLabel =
|
||||
new JLabel(
|
||||
FontUtils.elegantIconHtml(
|
||||
"L", MessageUtils.getLocalizedMessage("analysis_custom.label.add")));
|
||||
tfAddLabel.setHorizontalAlignment(JLabel.LEFT);
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.gridx = 1;
|
||||
|
@ -442,15 +464,15 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
int ret = fileChooser.showOpenDialog(containerPanel);
|
||||
if (ret == JFileChooser.APPROVE_OPTION) {
|
||||
File[] files = fileChooser.getSelectedFiles();
|
||||
analysisModel.addExternalJars(Arrays.stream(files).map(File::getAbsolutePath).collect(Collectors.toList()));
|
||||
operatorRegistry.get(CustomAnalyzerPanelOperator.class).ifPresent(operator ->
|
||||
operator.resetAnalysisComponents()
|
||||
);
|
||||
analysisModel.addExternalJars(
|
||||
Arrays.stream(files).map(File::getAbsolutePath).collect(Collectors.toList()));
|
||||
operatorRegistry
|
||||
.get(CustomAnalyzerPanelOperator.class)
|
||||
.ifPresent(operator -> operator.resetAnalysisComponents());
|
||||
messageBroker.showStatusMessage("External jars were added.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void buildAnalyzer() {
|
||||
List<String> charFilters = ListUtils.getAllItems(selectedCfList);
|
||||
assert charFilters.size() == cfParamsList.size();
|
||||
|
@ -461,24 +483,26 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
String tokenizerName = selectedTokTF.getText();
|
||||
CustomAnalyzerConfig.Builder builder =
|
||||
new CustomAnalyzerConfig.Builder(tokenizerName, tokParams).configDir(confDirTF.getText());
|
||||
IntStream.range(0, charFilters.size()).forEach(i ->
|
||||
builder.addCharFilterConfig(charFilters.get(i), cfParamsList.get(i))
|
||||
);
|
||||
IntStream.range(0, tokenFilters.size()).forEach(i ->
|
||||
builder.addTokenFilterConfig(tokenFilters.get(i), tfParamsList.get(i))
|
||||
);
|
||||
IntStream.range(0, charFilters.size())
|
||||
.forEach(i -> builder.addCharFilterConfig(charFilters.get(i), cfParamsList.get(i)));
|
||||
IntStream.range(0, tokenFilters.size())
|
||||
.forEach(i -> builder.addTokenFilterConfig(tokenFilters.get(i), tfParamsList.get(i)));
|
||||
CustomAnalyzerConfig config = builder.build();
|
||||
|
||||
operatorRegistry.get(AnalysisTabOperator.class).ifPresent(operator -> {
|
||||
operatorRegistry
|
||||
.get(AnalysisTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> {
|
||||
operator.setAnalyzerByCustomConfiguration(config);
|
||||
messageBroker.showStatusMessage(MessageUtils.getLocalizedMessage("analysis.message.build_success"));
|
||||
messageBroker.showStatusMessage(
|
||||
MessageUtils.getLocalizedMessage("analysis.message.build_success"));
|
||||
buildBtn.setEnabled(false);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void addCharFilter() {
|
||||
if (Objects.isNull(cfFactoryCombo.getSelectedItem()) || cfFactoryCombo.getSelectedItem() == "") {
|
||||
if (Objects.isNull(cfFactoryCombo.getSelectedItem())
|
||||
|| cfFactoryCombo.getSelectedItem() == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -490,8 +514,12 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
|
||||
assert selectedCfList.getModel().getSize() == cfParamsList.size();
|
||||
|
||||
showEditParamsDialog(MessageUtils.getLocalizedMessage("analysis.dialog.title.char_filter_params"),
|
||||
EditParamsMode.CHARFILTER, targetIndex, selectedItem, cfParamsList.get(cfParamsList.size() - 1),
|
||||
showEditParamsDialog(
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.char_filter_params"),
|
||||
EditParamsMode.CHARFILTER,
|
||||
targetIndex,
|
||||
selectedItem,
|
||||
cfParamsList.get(cfParamsList.size() - 1),
|
||||
() -> {
|
||||
selectedCfList.setModel(new DefaultComboBoxModel<>(updatedList.toArray(new String[0])));
|
||||
cfFactoryCombo.setSelectedItem("");
|
||||
|
@ -501,13 +529,18 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
}
|
||||
|
||||
private void setTokenizer() {
|
||||
if (Objects.isNull(tokFactoryCombo.getSelectedItem()) || tokFactoryCombo.getSelectedItem() == "") {
|
||||
if (Objects.isNull(tokFactoryCombo.getSelectedItem())
|
||||
|| tokFactoryCombo.getSelectedItem() == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
String selectedItem = (String) tokFactoryCombo.getSelectedItem();
|
||||
showEditParamsDialog(MessageUtils.getLocalizedMessage("analysis.dialog.title.tokenizer_params"),
|
||||
EditParamsMode.TOKENIZER, -1, selectedItem, Collections.emptyMap(),
|
||||
showEditParamsDialog(
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.tokenizer_params"),
|
||||
EditParamsMode.TOKENIZER,
|
||||
-1,
|
||||
selectedItem,
|
||||
Collections.emptyMap(),
|
||||
() -> {
|
||||
selectedTokTF.setText(selectedItem);
|
||||
tokFactoryCombo.setSelectedItem("");
|
||||
|
@ -516,7 +549,8 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
}
|
||||
|
||||
private void addTokenFilter() {
|
||||
if (Objects.isNull(tfFactoryCombo.getSelectedItem()) || tfFactoryCombo.getSelectedItem() == "") {
|
||||
if (Objects.isNull(tfFactoryCombo.getSelectedItem())
|
||||
|| tfFactoryCombo.getSelectedItem() == "") {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -528,18 +562,33 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
|
||||
assert selectedTfList.getModel().getSize() == tfParamsList.size();
|
||||
|
||||
showEditParamsDialog(MessageUtils.getLocalizedMessage("analysis.dialog.title.token_filter_params"),
|
||||
EditParamsMode.TOKENFILTER, targetIndex, selectedItem, tfParamsList.get(tfParamsList.size() - 1),
|
||||
showEditParamsDialog(
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.token_filter_params"),
|
||||
EditParamsMode.TOKENFILTER,
|
||||
targetIndex,
|
||||
selectedItem,
|
||||
tfParamsList.get(tfParamsList.size() - 1),
|
||||
() -> {
|
||||
selectedTfList.setModel(new DefaultComboBoxModel<>(updatedList.toArray(new String[updatedList.size()])));
|
||||
selectedTfList.setModel(
|
||||
new DefaultComboBoxModel<>(updatedList.toArray(new String[updatedList.size()])));
|
||||
tfFactoryCombo.setSelectedItem("");
|
||||
tfEditBtn.setEnabled(true);
|
||||
buildBtn.setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
private void showEditParamsDialog(String title, EditParamsMode mode, int targetIndex, String selectedItem, Map<String, String> params, Callable callback) {
|
||||
new DialogOpener<>(editParamsDialogFactory).open(title, 400, 300,
|
||||
private void showEditParamsDialog(
|
||||
String title,
|
||||
EditParamsMode mode,
|
||||
int targetIndex,
|
||||
String selectedItem,
|
||||
Map<String, String> params,
|
||||
Callable callback) {
|
||||
new DialogOpener<>(editParamsDialogFactory)
|
||||
.open(
|
||||
title,
|
||||
400,
|
||||
300,
|
||||
(factory) -> {
|
||||
factory.setMode(mode);
|
||||
factory.setTargetIndex(targetIndex);
|
||||
|
@ -551,7 +600,9 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
|
||||
private void editCharFilters() {
|
||||
List<String> filters = ListUtils.getAllItems(selectedCfList);
|
||||
showEditFiltersDialog(EditFiltersMode.CHARFILTER, filters,
|
||||
showEditFiltersDialog(
|
||||
EditFiltersMode.CHARFILTER,
|
||||
filters,
|
||||
() -> {
|
||||
cfEditBtn.setEnabled(selectedCfList.getModel().getSize() > 0);
|
||||
buildBtn.setEnabled(true);
|
||||
|
@ -560,26 +611,39 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
|
||||
private void editTokenizer() {
|
||||
String selectedItem = selectedTokTF.getText();
|
||||
showEditParamsDialog(MessageUtils.getLocalizedMessage("analysis.dialog.title.tokenizer_params"),
|
||||
EditParamsMode.TOKENIZER, -1, selectedItem, tokParams, () -> {
|
||||
showEditParamsDialog(
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.tokenizer_params"),
|
||||
EditParamsMode.TOKENIZER,
|
||||
-1,
|
||||
selectedItem,
|
||||
tokParams,
|
||||
() -> {
|
||||
buildBtn.setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
private void editTokenFilters() {
|
||||
List<String> filters = ListUtils.getAllItems(selectedTfList);
|
||||
showEditFiltersDialog(EditFiltersMode.TOKENFILTER, filters,
|
||||
showEditFiltersDialog(
|
||||
EditFiltersMode.TOKENFILTER,
|
||||
filters,
|
||||
() -> {
|
||||
tfEditBtn.setEnabled(selectedTfList.getModel().getSize() > 0);
|
||||
buildBtn.setEnabled(true);
|
||||
});
|
||||
}
|
||||
|
||||
private void showEditFiltersDialog(EditFiltersMode mode, List<String> selectedFilters, Callable callback) {
|
||||
String title = (mode == EditFiltersMode.CHARFILTER) ?
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.selected_char_filter") :
|
||||
MessageUtils.getLocalizedMessage("analysis.dialog.title.selected_token_filter");
|
||||
new DialogOpener<>(editFiltersDialogFactory).open(title, 400, 300,
|
||||
private void showEditFiltersDialog(
|
||||
EditFiltersMode mode, List<String> selectedFilters, Callable callback) {
|
||||
String title =
|
||||
(mode == EditFiltersMode.CHARFILTER)
|
||||
? MessageUtils.getLocalizedMessage("analysis.dialog.title.selected_char_filter")
|
||||
: MessageUtils.getLocalizedMessage("analysis.dialog.title.selected_token_filter");
|
||||
new DialogOpener<>(editFiltersDialogFactory)
|
||||
.open(
|
||||
title,
|
||||
400,
|
||||
300,
|
||||
(factory) -> {
|
||||
factory.setMode(mode);
|
||||
factory.setSelectedFilters(selectedFilters);
|
||||
|
@ -620,7 +684,8 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
Collection<String> tokenFilters = analysisModel.getAvailableTokenFilters();
|
||||
String[] tokenFilterNames = new String[tokenFilters.size() + 1];
|
||||
tokenFilterNames[0] = "";
|
||||
System.arraycopy(tokenFilters.toArray(new String[0]), 0, tokenFilterNames, 1, tokenFilters.size());
|
||||
System.arraycopy(
|
||||
tokenFilters.toArray(new String[0]), 0, tokenFilterNames, 1, tokenFilters.size());
|
||||
tfFactoryCombo.setModel(new DefaultComboBoxModel<>(tokenFilterNames));
|
||||
}
|
||||
|
||||
|
@ -628,13 +693,15 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
public void updateCharFilters(List<Integer> deletedIndexes) {
|
||||
// update filters
|
||||
List<String> filters = ListUtils.getAllItems(selectedCfList);
|
||||
String[] updatedFilters = IntStream.range(0, filters.size())
|
||||
String[] updatedFilters =
|
||||
IntStream.range(0, filters.size())
|
||||
.filter(i -> !deletedIndexes.contains(i))
|
||||
.mapToObj(filters::get)
|
||||
.toArray(String[]::new);
|
||||
selectedCfList.setModel(new DefaultComboBoxModel<>(updatedFilters));
|
||||
// update parameters map for each filter
|
||||
List<Map<String, String>> updatedParamList = IntStream.range(0, cfParamsList.size())
|
||||
List<Map<String, String>> updatedParamList =
|
||||
IntStream.range(0, cfParamsList.size())
|
||||
.filter(i -> !deletedIndexes.contains(i))
|
||||
.mapToObj(cfParamsList::get)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -647,13 +714,15 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
public void updateTokenFilters(List<Integer> deletedIndexes) {
|
||||
// update filters
|
||||
List<String> filters = ListUtils.getAllItems(selectedTfList);
|
||||
String[] updatedFilters = IntStream.range(0, filters.size())
|
||||
String[] updatedFilters =
|
||||
IntStream.range(0, filters.size())
|
||||
.filter(i -> !deletedIndexes.contains(i))
|
||||
.mapToObj(filters::get)
|
||||
.toArray(String[]::new);
|
||||
selectedTfList.setModel(new DefaultComboBoxModel<>(updatedFilters));
|
||||
// update parameters map for each filter
|
||||
List<Map<String, String>> updatedParamList = IntStream.range(0, tfParamsList.size())
|
||||
List<Map<String, String>> updatedParamList =
|
||||
IntStream.range(0, tfParamsList.size())
|
||||
.filter(i -> !deletedIndexes.contains(i))
|
||||
.mapToObj(tfParamsList::get)
|
||||
.collect(Collectors.toList());
|
||||
|
@ -745,7 +814,5 @@ public final class CustomAnalyzerPanelProvider implements CustomAnalyzerPanelOpe
|
|||
void editTokenFilters(ActionEvent e) {
|
||||
CustomAnalyzerPanelProvider.this.editTokenFilters();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package org.apache.lucene.luke.app.desktop.components.fragments.analysis;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
|
||||
|
|
|
@ -17,18 +17,17 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.fragments.analysis;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Collection;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.ComboBoxModel;
|
||||
import javax.swing.DefaultComboBoxModel;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.luke.app.desktop.components.AnalysisTabOperator;
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
|
@ -86,11 +85,10 @@ public final class PresetAnalyzerPanelProvider implements PresetAnalyzerPanelOpe
|
|||
private class ListenerFunctions {
|
||||
|
||||
void setAnalyzer(ActionEvent e) {
|
||||
operatorRegistry.get(AnalysisTabOperator.class).ifPresent(operator ->
|
||||
operator.setAnalyzerByType((String) analyzersCB.getSelectedItem())
|
||||
);
|
||||
operatorRegistry
|
||||
.get(AnalysisTabOperator.class)
|
||||
.ifPresent(
|
||||
operator -> operator.setAnalyzerByType((String) analyzersCB.getSelectedItem()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.fragments.analysis;
|
||||
|
||||
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
import org.apache.lucene.luke.models.analysis.Analysis;
|
||||
|
||||
/** Operator of the simple analyze result panel */
|
||||
public interface SimpleAnalyzeResultPanelOperator extends ComponentOperatorRegistry.ComponentOperator {
|
||||
public interface SimpleAnalyzeResultPanelOperator
|
||||
extends ComponentOperatorRegistry.ComponentOperator {
|
||||
|
||||
void setAnalysisModel(Analysis analysisModel);
|
||||
|
||||
|
|
|
@ -17,19 +17,17 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.fragments.analysis;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
import org.apache.lucene.luke.app.desktop.components.TableColumnInfo;
|
||||
import org.apache.lucene.luke.app.desktop.components.TableModelBase;
|
||||
|
@ -69,7 +67,10 @@ public class SimpleAnalyzeResultPanelProvider implements SimpleAnalyzeResultPane
|
|||
hint.add(new JLabel(MessageUtils.getLocalizedMessage("analysis.hint.show_attributes")));
|
||||
panel.add(hint, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(tokensTable, ListSelectionModel.SINGLE_SELECTION, new TokensTableModel(),
|
||||
TableUtils.setupTable(
|
||||
tokensTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new TokensTableModel(),
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
@ -93,15 +94,22 @@ public class SimpleAnalyzeResultPanelProvider implements SimpleAnalyzeResultPane
|
|||
tokens = analysisModel.analyze(text);
|
||||
tokensTable.setModel(new TokensTableModel(tokens));
|
||||
tokensTable.setShowGrid(true);
|
||||
tokensTable.getColumnModel().getColumn(TokensTableModel.Column.TERM.getIndex())
|
||||
tokensTable
|
||||
.getColumnModel()
|
||||
.getColumn(TokensTableModel.Column.TERM.getIndex())
|
||||
.setPreferredWidth(TokensTableModel.Column.TERM.getColumnWidth());
|
||||
tokensTable.getColumnModel().getColumn(TokensTableModel.Column.ATTR.getIndex())
|
||||
tokensTable
|
||||
.getColumnModel()
|
||||
.getColumn(TokensTableModel.Column.ATTR.getIndex())
|
||||
.setPreferredWidth(TokensTableModel.Column.ATTR.getColumnWidth());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTable() {
|
||||
TableUtils.setupTable(tokensTable, ListSelectionModel.SINGLE_SELECTION, new TokensTableModel(),
|
||||
TableUtils.setupTable(
|
||||
tokensTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new TokensTableModel(),
|
||||
null,
|
||||
TokensTableModel.Column.TERM.getColumnWidth(),
|
||||
TokensTableModel.Column.ATTR.getColumnWidth());
|
||||
|
@ -110,7 +118,11 @@ public class SimpleAnalyzeResultPanelProvider implements SimpleAnalyzeResultPane
|
|||
private void showAttributeValues(int selectedIndex) {
|
||||
String term = tokens.get(selectedIndex).getTerm();
|
||||
List<Analysis.TokenAttribute> attributes = tokens.get(selectedIndex).getAttributes();
|
||||
new DialogOpener<>(tokenAttrDialogFactory).open("Token Attributes", 650, 400,
|
||||
new DialogOpener<>(tokenAttrDialogFactory)
|
||||
.open(
|
||||
"Token Attributes",
|
||||
650,
|
||||
400,
|
||||
factory -> {
|
||||
factory.setTerm(term);
|
||||
factory.setAttributes(attributes);
|
||||
|
@ -180,8 +192,11 @@ public class SimpleAnalyzeResultPanelProvider implements SimpleAnalyzeResultPane
|
|||
for (int i = 0; i < tokens.size(); i++) {
|
||||
Analysis.Token token = tokens.get(i);
|
||||
data[i][Column.TERM.getIndex()] = token.getTerm();
|
||||
List<String> attValues = token.getAttributes().stream()
|
||||
.flatMap(att -> att.getAttValues().entrySet().stream()
|
||||
List<String> attValues =
|
||||
token.getAttributes().stream()
|
||||
.flatMap(
|
||||
att ->
|
||||
att.getAttValues().entrySet().stream()
|
||||
.map(e -> e.getKey() + "=" + e.getValue()))
|
||||
.collect(Collectors.toList());
|
||||
data[i][Column.ATTR.getIndex()] = String.join(",", attValues);
|
||||
|
|
|
@ -21,7 +21,8 @@ import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
|||
import org.apache.lucene.luke.models.analysis.Analysis;
|
||||
|
||||
/** Operator of the Step by step analyze result panel */
|
||||
public interface StepByStepAnalyzeResultPanelOperator extends ComponentOperatorRegistry.ComponentOperator {
|
||||
public interface StepByStepAnalyzeResultPanelOperator
|
||||
extends ComponentOperatorRegistry.ComponentOperator {
|
||||
|
||||
void setAnalysisModel(Analysis analysisModel);
|
||||
|
||||
|
|
|
@ -17,14 +17,6 @@
|
|||
|
||||
package org.apache.lucene.luke.app.desktop.components.fragments.analysis;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
|
@ -33,7 +25,13 @@ import java.awt.event.MouseEvent;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.JSplitPane;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.ListSelectionModel;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import org.apache.lucene.luke.app.desktop.components.ComponentOperatorRegistry;
|
||||
import org.apache.lucene.luke.app.desktop.components.TableColumnInfo;
|
||||
import org.apache.lucene.luke.app.desktop.components.TableModelBase;
|
||||
|
@ -76,17 +74,27 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
|
||||
JPanel hint = new JPanel(new FlowLayout(FlowLayout.LEADING));
|
||||
hint.setOpaque(false);
|
||||
hint.add(new JLabel(MessageUtils.getLocalizedMessage("analysis.hint.show_attributes_step_by_step")));
|
||||
hint.add(
|
||||
new JLabel(MessageUtils.getLocalizedMessage("analysis.hint.show_attributes_step_by_step")));
|
||||
panel.add(hint, BorderLayout.PAGE_START);
|
||||
|
||||
TableUtils.setupTable(charfilterTextsRowHeader, ListSelectionModel.SINGLE_SELECTION, new RowHeaderTableModel(),
|
||||
TableUtils.setupTable(
|
||||
charfilterTextsRowHeader,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new RowHeaderTableModel(),
|
||||
null);
|
||||
TableUtils.setupTable(charfilterTextsTable, ListSelectionModel.SINGLE_SELECTION, new CharfilterTextTableModel(),
|
||||
TableUtils.setupTable(
|
||||
charfilterTextsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new CharfilterTextTableModel(),
|
||||
null);
|
||||
|
||||
TableUtils.setupTable(namedTokensRowHeader, ListSelectionModel.SINGLE_SELECTION, new RowHeaderTableModel(),
|
||||
null);
|
||||
TableUtils.setupTable(namedTokensTable, ListSelectionModel.SINGLE_SELECTION, new NamedTokensTableModel(),
|
||||
TableUtils.setupTable(
|
||||
namedTokensRowHeader, ListSelectionModel.SINGLE_SELECTION, new RowHeaderTableModel(), null);
|
||||
TableUtils.setupTable(
|
||||
namedTokensTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new NamedTokensTableModel(),
|
||||
new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
|
@ -94,7 +102,11 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
}
|
||||
});
|
||||
namedTokensTable.setColumnSelectionAllowed(true);
|
||||
JSplitPane inner = new JSplitPane(JSplitPane.VERTICAL_SPLIT, initResultScroll(charfilterTextsTable, charfilterTextsRowHeader), initResultScroll(namedTokensTable, namedTokensRowHeader));
|
||||
JSplitPane inner =
|
||||
new JSplitPane(
|
||||
JSplitPane.VERTICAL_SPLIT,
|
||||
initResultScroll(charfilterTextsTable, charfilterTextsRowHeader),
|
||||
initResultScroll(namedTokensTable, namedTokensRowHeader));
|
||||
inner.setDividerLocation(60);
|
||||
|
||||
panel.add(inner, BorderLayout.CENTER);
|
||||
|
@ -110,7 +122,6 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
return scroll;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setAnalysisModel(Analysis analysisModel) {
|
||||
this.analysisModel = analysisModel;
|
||||
|
@ -119,11 +130,13 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
@Override
|
||||
public void executeAnalysisStepByStep(String text) {
|
||||
result = analysisModel.analyzeStepByStep(text);
|
||||
RowHeaderTableModel charfilterTextsHeaderModel = new RowHeaderTableModel(result.getCharfilteredTexts());
|
||||
RowHeaderTableModel charfilterTextsHeaderModel =
|
||||
new RowHeaderTableModel(result.getCharfilteredTexts());
|
||||
charfilterTextsRowHeader.setModel(charfilterTextsHeaderModel);
|
||||
charfilterTextsRowHeader.setShowGrid(true);
|
||||
|
||||
CharfilterTextTableModel charfilterTextTableModel = new CharfilterTextTableModel(result.getCharfilteredTexts());
|
||||
CharfilterTextTableModel charfilterTextTableModel =
|
||||
new CharfilterTextTableModel(result.getCharfilteredTexts());
|
||||
charfilterTextsTable.setModel(charfilterTextTableModel);
|
||||
charfilterTextsTable.setShowGrid(true);
|
||||
|
||||
|
@ -135,32 +148,44 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
namedTokensTable.setModel(tableModel);
|
||||
namedTokensTable.setShowGrid(true);
|
||||
for (int i = 0; i < tableModel.getColumnCount(); i++) {
|
||||
namedTokensTable.getColumnModel().getColumn(i).setPreferredWidth(tableModel.getColumnWidth(i));
|
||||
namedTokensTable
|
||||
.getColumnModel()
|
||||
.getColumn(i)
|
||||
.setPreferredWidth(tableModel.getColumnWidth(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTable() {
|
||||
TableUtils.setupTable(charfilterTextsRowHeader, ListSelectionModel.SINGLE_SELECTION, new RowHeaderTableModel(),
|
||||
TableUtils.setupTable(
|
||||
charfilterTextsRowHeader,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new RowHeaderTableModel(),
|
||||
null);
|
||||
TableUtils.setupTable(charfilterTextsTable, ListSelectionModel.SINGLE_SELECTION, new CharfilterTextTableModel(),
|
||||
TableUtils.setupTable(
|
||||
charfilterTextsTable,
|
||||
ListSelectionModel.SINGLE_SELECTION,
|
||||
new CharfilterTextTableModel(),
|
||||
null);
|
||||
|
||||
TableUtils.setupTable(namedTokensRowHeader, ListSelectionModel.SINGLE_SELECTION, new RowHeaderTableModel(),
|
||||
null);
|
||||
TableUtils.setupTable(namedTokensTable, ListSelectionModel.SINGLE_SELECTION, new NamedTokensTableModel(),
|
||||
null);
|
||||
TableUtils.setupTable(
|
||||
namedTokensRowHeader, ListSelectionModel.SINGLE_SELECTION, new RowHeaderTableModel(), null);
|
||||
TableUtils.setupTable(
|
||||
namedTokensTable, ListSelectionModel.SINGLE_SELECTION, new NamedTokensTableModel(), null);
|
||||
}
|
||||
|
||||
private void showAttributeValues(int rowIndex, int columnIndex) {
|
||||
Analysis.NamedTokens namedTokens =
|
||||
this.result.getNamedTokens().get(rowIndex);
|
||||
Analysis.NamedTokens namedTokens = this.result.getNamedTokens().get(rowIndex);
|
||||
List<Analysis.Token> tokens = namedTokens.getTokens();
|
||||
|
||||
if (rowIndex <= tokens.size()) {
|
||||
String term = "\"" + tokens.get(columnIndex).getTerm() + "\" BY " + namedTokens.getName();
|
||||
List<Analysis.TokenAttribute> attributes = tokens.get(columnIndex).getAttributes();
|
||||
new DialogOpener<>(tokenAttrDialogFactory).open("Token Attributes", 650, 400,
|
||||
new DialogOpener<>(tokenAttrDialogFactory)
|
||||
.open(
|
||||
"Token Attributes",
|
||||
650,
|
||||
400,
|
||||
factory -> {
|
||||
factory.setTerm(term);
|
||||
factory.setAttributes(attributes);
|
||||
|
@ -241,7 +266,8 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
}
|
||||
|
||||
/** Table model for charfilter result */
|
||||
private static class CharfilterTextTableModel extends TableModelBase<CharfilterTextTableModel.Column> {
|
||||
private static class CharfilterTextTableModel
|
||||
extends TableModelBase<CharfilterTextTableModel.Column> {
|
||||
|
||||
enum Column implements TableColumnInfo {
|
||||
TEXT("Text", 0, String.class, 1000);
|
||||
|
@ -338,7 +364,6 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
|
||||
private final Object[][] data;
|
||||
|
||||
|
||||
NamedTokensTableModel() {
|
||||
this.data = new Object[0][0];
|
||||
}
|
||||
|
@ -381,7 +406,6 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
return columnMap.size();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getColumnName(int colIndex) {
|
||||
if (columnMap.containsKey(colIndex)) {
|
||||
|
@ -411,5 +435,4 @@ public class StepByStepAnalyzeResultPanelProvider implements StepByStepAnalyzeRe
|
|||
private static String shortenName(String name) {
|
||||
return name.substring(name.lastIndexOf('.') + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue