1
package org.apache.lucene.search.spans;
4
* Licensed to the Apache Software Foundation (ASF) under one or more
5
* contributor license agreements. See the NOTICE file distributed with
6
* this work for additional information regarding copyright ownership.
7
* The ASF licenses this file to You under the Apache License, Version 2.0
8
* (the "License"); you may not use this file except in compliance with
9
* the License. You may obtain a copy of the License at
11
* http://www.apache.org/licenses/LICENSE-2.0
13
* Unless required by applicable law or agreed to in writing, software
14
* distributed under the License is distributed on an "AS IS" BASIS,
15
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
* See the License for the specific language governing permissions and
17
* limitations under the License.
20
import org.apache.lucene.index.IndexReader;
21
import org.apache.lucene.index.Term;
22
import org.apache.lucene.search.Query;
23
import org.apache.lucene.util.ToStringUtils;
25
import java.io.IOException;
26
import java.util.ArrayList;
27
import java.util.Collection;
30
/** Removes matches which overlap with another SpanQuery. */
31
public class SpanNotQuery extends SpanQuery implements Cloneable {
32
private SpanQuery include;
33
private SpanQuery exclude;
35
/** Construct a SpanNotQuery matching spans from <code>include</code> which
36
* have no overlap with spans from <code>exclude</code>.*/
37
public SpanNotQuery(SpanQuery include, SpanQuery exclude) {
38
this.include = include;
39
this.exclude = exclude;
41
if (!include.getField().equals(exclude.getField()))
42
throw new IllegalArgumentException("Clauses must have same field.");
45
/** Return the SpanQuery whose matches are filtered. */
46
public SpanQuery getInclude() { return include; }
48
/** Return the SpanQuery whose matches must not overlap those returned. */
49
public SpanQuery getExclude() { return exclude; }
52
public String getField() { return include.getField(); }
55
public void extractTerms(Set<Term> terms) { include.extractTerms(terms); }
58
public String toString(String field) {
59
StringBuilder buffer = new StringBuilder();
60
buffer.append("spanNot(");
61
buffer.append(include.toString(field));
63
buffer.append(exclude.toString(field));
65
buffer.append(ToStringUtils.boost(getBoost()));
66
return buffer.toString();
70
public Object clone() {
71
SpanNotQuery spanNotQuery = new SpanNotQuery((SpanQuery)include.clone(),(SpanQuery) exclude.clone());
72
spanNotQuery.setBoost(getBoost());
77
public Spans getSpans(final IndexReader reader) throws IOException {
79
private Spans includeSpans = include.getSpans(reader);
80
private boolean moreInclude = true;
82
private Spans excludeSpans = exclude.getSpans(reader);
83
private boolean moreExclude = excludeSpans.next();
86
public boolean next() throws IOException {
87
if (moreInclude) // move to next include
88
moreInclude = includeSpans.next();
90
while (moreInclude && moreExclude) {
92
if (includeSpans.doc() > excludeSpans.doc()) // skip exclude
93
moreExclude = excludeSpans.skipTo(includeSpans.doc());
95
while (moreExclude // while exclude is before
96
&& includeSpans.doc() == excludeSpans.doc()
97
&& excludeSpans.end() <= includeSpans.start()) {
98
moreExclude = excludeSpans.next(); // increment exclude
101
if (!moreExclude // if no intersection
102
|| includeSpans.doc() != excludeSpans.doc()
103
|| includeSpans.end() <= excludeSpans.start())
104
break; // we found a match
106
moreInclude = includeSpans.next(); // intersected: keep scanning
112
public boolean skipTo(int target) throws IOException {
113
if (moreInclude) // skip include
114
moreInclude = includeSpans.skipTo(target);
119
if (moreExclude // skip exclude
120
&& includeSpans.doc() > excludeSpans.doc())
121
moreExclude = excludeSpans.skipTo(includeSpans.doc());
123
while (moreExclude // while exclude is before
124
&& includeSpans.doc() == excludeSpans.doc()
125
&& excludeSpans.end() <= includeSpans.start()) {
126
moreExclude = excludeSpans.next(); // increment exclude
129
if (!moreExclude // if no intersection
130
|| includeSpans.doc() != excludeSpans.doc()
131
|| includeSpans.end() <= excludeSpans.start())
132
return true; // we found a match
134
return next(); // scan to next match
138
public int doc() { return includeSpans.doc(); }
140
public int start() { return includeSpans.start(); }
142
public int end() { return includeSpans.end(); }
144
// TODO: Remove warning after API has been finalized
146
public Collection<byte[]> getPayload() throws IOException {
147
ArrayList<byte[]> result = null;
148
if (includeSpans.isPayloadAvailable()) {
149
result = new ArrayList<byte[]>(includeSpans.getPayload());
154
// TODO: Remove warning after API has been finalized
156
public boolean isPayloadAvailable() {
157
return includeSpans.isPayloadAvailable();
161
public String toString() {
162
return "spans(" + SpanNotQuery.this.toString() + ")";
169
public Query rewrite(IndexReader reader) throws IOException {
170
SpanNotQuery clone = null;
172
SpanQuery rewrittenInclude = (SpanQuery) include.rewrite(reader);
173
if (rewrittenInclude != include) {
174
clone = (SpanNotQuery) this.clone();
175
clone.include = rewrittenInclude;
177
SpanQuery rewrittenExclude = (SpanQuery) exclude.rewrite(reader);
178
if (rewrittenExclude != exclude) {
179
if (clone == null) clone = (SpanNotQuery) this.clone();
180
clone.exclude = rewrittenExclude;
184
return clone; // some clauses rewrote
186
return this; // no clauses rewrote
190
/** Returns true iff <code>o</code> is equal to this. */
192
public boolean equals(Object o) {
193
if (this == o) return true;
194
if (!(o instanceof SpanNotQuery)) return false;
196
SpanNotQuery other = (SpanNotQuery)o;
197
return this.include.equals(other.include)
198
&& this.exclude.equals(other.exclude)
199
&& this.getBoost() == other.getBoost();
203
public int hashCode() {
204
int h = include.hashCode();
205
h = (h<<1) | (h >>> 31); // rotate left
206
h ^= exclude.hashCode();
207
h = (h<<1) | (h >>> 31); // rotate left
208
h ^= Float.floatToRawIntBits(getBoost());