org.apache.lucene.monitor.HighlightsMatch Maven / Gradle / Ivy
Show all versions of lucene-monitor Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.monitor;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.Matches;
import org.apache.lucene.search.MatchesIterator;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.search.Weight;
/**
* QueryMatch object that contains the hit positions of a matching Query
*
* If the Query does not support interval iteration (eg, if it gets re-written to a Filter), then
* no hits will be reported, but an IntervalsQueryMatch will still be returned from an
* IntervalsMatcher to indicate a match.
*/
public class HighlightsMatch extends QueryMatch {
public static final MatcherFactory MATCHER =
searcher ->
new CandidateMatcher(searcher) {
@Override
protected void matchQuery(
String queryId, Query matchQuery, Map metadata) throws IOException {
Weight w =
searcher.createWeight(
searcher.rewrite(matchQuery), ScoreMode.COMPLETE_NO_SCORES, 1);
for (LeafReaderContext ctx : searcher.getIndexReader().leaves()) {
for (int i = 0; i < ctx.reader().maxDoc(); i++) {
Matches matches = w.matches(ctx, i);
if (matches != null) {
addMatch(buildMatch(matches, queryId), i);
}
}
}
}
@Override
public HighlightsMatch resolve(HighlightsMatch match1, HighlightsMatch match2) {
return HighlightsMatch.merge(match1.getQueryId(), match1, match2);
}
private HighlightsMatch buildMatch(Matches matches, String queryId) throws IOException {
HighlightsMatch m = new HighlightsMatch(queryId);
for (String field : matches) {
MatchesIterator mi = matches.getMatches(field);
while (mi.next()) {
MatchesIterator sub = mi.getSubMatches();
if (sub != null) {
while (sub.next()) {
m.addHit(
field,
sub.startPosition(),
sub.endPosition(),
sub.startOffset(),
sub.endOffset());
}
} else {
m.addHit(
field,
mi.startPosition(),
mi.endPosition(),
mi.startOffset(),
mi.endOffset());
}
}
}
return m;
}
};
private final Map> hits;
HighlightsMatch(String queryId) {
super(queryId);
this.hits = new TreeMap<>();
}
/**
* @return a map of hits per field
*/
public Map> getHits() {
return Collections.unmodifiableMap(this.hits);
}
/**
* @return the fields in which matches have been found
*/
public Set getFields() {
return Collections.unmodifiableSet(hits.keySet());
}
/**
* Get the hits for a specific field
*
* @param field the field
* @return the Hits found in this field
*/
public Collection getHits(String field) {
Collection found = hits.get(field);
if (found != null) return Collections.unmodifiableCollection(found);
return Collections.emptyList();
}
/**
* @return the total number of hits for the query
*/
public int getHitCount() {
int c = 0;
for (Set fieldhits : hits.values()) {
c += fieldhits.size();
}
return c;
}
static HighlightsMatch merge(String queryId, HighlightsMatch... matches) {
HighlightsMatch newMatch = new HighlightsMatch(queryId);
for (HighlightsMatch match : matches) {
for (String field : match.getFields()) {
Set hitSet = newMatch.hits.computeIfAbsent(field, f -> new TreeSet<>());
hitSet.addAll(match.getHits(field));
}
}
return newMatch;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof HighlightsMatch)) return false;
if (!super.equals(o)) return false;
HighlightsMatch that = (HighlightsMatch) o;
if (hits != null ? !hits.equals(that.hits) : that.hits != null) return false;
return true;
}
@Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (hits != null ? hits.hashCode() : 0);
return result;
}
@Override
public String toString() {
return super.toString() + "{hits=" + hits + "}";
}
void addHit(String field, int startPos, int endPos, int startOffset, int endOffset) {
Set hitSet = hits.computeIfAbsent(field, f -> new TreeSet<>());
hitSet.add(new Hit(startPos, startOffset, endPos, endOffset));
}
/** Represents an individual hit */
public static class Hit implements Comparable {
/** The start position */
public final int startPosition;
/** The start offset */
public final int startOffset;
/** The end positions */
public final int endPosition;
/** The end offset */
public final int endOffset;
public Hit(int startPosition, int startOffset, int endPosition, int endOffset) {
this.startPosition = startPosition;
this.startOffset = startOffset;
this.endPosition = endPosition;
this.endOffset = endOffset;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Hit)) return false;
Hit other = (Hit) obj;
return this.startOffset == other.startOffset
&& this.endOffset == other.endOffset
&& this.startPosition == other.startPosition
&& this.endPosition == other.endPosition;
}
@Override
public int hashCode() {
int result = startPosition;
result = 31 * result + startOffset;
result = 31 * result + endPosition;
result = 31 * result + endOffset;
return result;
}
@Override
public String toString() {
return String.format(
Locale.ROOT, "%d(%d)->%d(%d)", startPosition, startOffset, endPosition, endOffset);
}
@Override
public int compareTo(Hit other) {
if (this.startPosition != other.startPosition)
return Integer.compare(this.startPosition, other.startPosition);
return Integer.compare(this.endPosition, other.endPosition);
}
}
}