blob: 9dfd65b4daf6a099a1b3a0cb369967d181e42ebe [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.lucene.queryparser.flexible.core.nodes;
import java.util.List;
import org.apache.lucene.queryparser.flexible.messages.MessageImpl;
import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax;
import org.apache.lucene.queryparser.flexible.core.QueryNodeError;
import org.apache.lucene.queryparser.flexible.core.messages.QueryParserMessages;
/**
* A {@link ProximityQueryNode} represents a query where the terms should meet
* specific distance conditions. (a b c) WITHIN [SENTENCE|PARAGRAPH|NUMBER]
* [INORDER] ("a" "b" "c") WITHIN [SENTENCE|PARAGRAPH|NUMBER] [INORDER]
*
* TODO: Add this to the future standard Lucene parser/processor/builder
*/
public class ProximityQueryNode extends BooleanQueryNode {
/**
* Distance condition: PARAGRAPH, SENTENCE, or NUMBER
*/
public enum Type {
PARAGRAPH {
@Override
CharSequence toQueryString() { return "WITHIN PARAGRAPH"; }
},
SENTENCE {
@Override
CharSequence toQueryString() { return "WITHIN SENTENCE"; }
},
NUMBER {
@Override
CharSequence toQueryString() { return "WITHIN"; }
};
abstract CharSequence toQueryString();
}
/** utility class containing the distance condition and number */
static public class ProximityType {
int pDistance = 0;
Type pType = null;
public ProximityType(Type type) {
this(type, 0);
}
public ProximityType(Type type, int distance) {
this.pType = type;
this.pDistance = distance;
}
}
private Type proximityType = Type.SENTENCE;
private int distance = -1;
private boolean inorder = false;
private CharSequence field = null;
/**
* @param clauses
* - QueryNode children
* @param field
* - field name
* @param type
* - type of proximity query
* @param distance
* - positive integer that specifies the distance
* @param inorder
* - true, if the tokens should be matched in the order of the
* clauses
*/
public ProximityQueryNode(List<QueryNode> clauses, CharSequence field,
Type type, int distance, boolean inorder) {
super(clauses);
setLeaf(false);
this.proximityType = type;
this.inorder = inorder;
this.field = field;
if (type == Type.NUMBER) {
if (distance <= 0) {
throw new QueryNodeError(new MessageImpl(
QueryParserMessages.PARAMETER_VALUE_NOT_SUPPORTED, "distance",
distance));
} else {
this.distance = distance;
}
}
clearFields(clauses, field);
}
/**
* @param clauses
* - QueryNode children
* @param field
* - field name
* @param type
* - type of proximity query
* @param inorder
* - true, if the tokens should be matched in the order of the
* clauses
*/
public ProximityQueryNode(List<QueryNode> clauses, CharSequence field,
Type type, boolean inorder) {
this(clauses, field, type, -1, inorder);
}
static private void clearFields(List<QueryNode> nodes, CharSequence field) {
if (nodes == null || nodes.size() == 0)
return;
for (QueryNode clause : nodes) {
if (clause instanceof FieldQueryNode) {
((FieldQueryNode) clause).toQueryStringIgnoreFields = true;
((FieldQueryNode) clause).setField(field);
}
}
}
public Type getProximityType() {
return this.proximityType;
}
@Override
public String toString() {
String distanceSTR = ((this.distance == -1) ? ("")
: (" distance='" + this.distance) + "'");
if (getChildren() == null || getChildren().size() == 0)
return "<proximity field='" + this.field + "' inorder='" + this.inorder
+ "' type='" + this.proximityType.toString() + "'" + distanceSTR
+ "/>";
StringBuilder sb = new StringBuilder();
sb.append("<proximity field='").append(this.field).append("' inorder='").append(this.inorder).append("' type='").append(this.proximityType.toString()).append("'").append(distanceSTR).append(">");
for (QueryNode child : getChildren()) {
sb.append("\n");
sb.append(child.toString());
}
sb.append("\n</proximity>");
return sb.toString();
}
@Override
public CharSequence toQueryString(EscapeQuerySyntax escapeSyntaxParser) {
String withinSTR = this.proximityType.toQueryString()
+ ((this.distance == -1) ? ("") : (" " + this.distance))
+ ((this.inorder) ? (" INORDER") : (""));
StringBuilder sb = new StringBuilder();
if (getChildren() == null || getChildren().size() == 0) {
// no children case
} else {
String filler = "";
for (QueryNode child : getChildren()) {
sb.append(filler).append(child.toQueryString(escapeSyntaxParser));
filler = " ";
}
}
if (isDefaultField(this.field)) {
return "( " + sb.toString() + " ) " + withinSTR;
} else {
return this.field + ":(( " + sb.toString() + " ) " + withinSTR + ")";
}
}
@Override
public QueryNode cloneTree() throws CloneNotSupportedException {
ProximityQueryNode clone = (ProximityQueryNode) super.cloneTree();
clone.proximityType = this.proximityType;
clone.distance = this.distance;
clone.field = this.field;
return clone;
}
/**
* @return the distance
*/
public int getDistance() {
return this.distance;
}
/**
* returns null if the field was not specified in the query string
*
* @return the field
*/
public CharSequence getField() {
return this.field;
}
/**
* returns null if the field was not specified in the query string
*
* @return the field
*/
public String getFieldAsString() {
if (this.field == null)
return null;
else
return this.field.toString();
}
/**
* @param field
* the field to set
*/
public void setField(CharSequence field) {
this.field = field;
}
/**
* @return terms must be matched in the specified order
*/
public boolean isInOrder() {
return this.inorder;
}
}