-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* grammar changes for adding field:zl:ns and field:zl:tq syntax * generated code changes for adding field:zl:ns and field:zl:tq syntax * refactor out term query and numeric set query code into SetQuery Helper * implement new query syntax for field:zl:ns and field:zl:tq syntax in actual parser * upgrade flapdoodle to 4.7.0 from 3.5.2 * fix spacing in exception * fix exception when field is not set in the directly query (set via default fields) * numeric set syntax tests * add tests for querying fields with names zl and fl * add term query syntax test * add quoted example
- Loading branch information
Showing
16 changed files
with
1,570 additions
and
721 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
110 changes: 110 additions & 0 deletions
110
zulia-query-parser/src/main/java/io/zulia/server/search/queryparser/SetQueryHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package io.zulia.server.search.queryparser; | ||
|
||
import io.zulia.message.ZuliaIndex; | ||
import io.zulia.server.config.IndexFieldInfo; | ||
import io.zulia.server.field.FieldTypeUtil; | ||
import org.apache.lucene.document.DoublePoint; | ||
import org.apache.lucene.document.FloatPoint; | ||
import org.apache.lucene.document.IntPoint; | ||
import org.apache.lucene.document.LongPoint; | ||
import org.apache.lucene.document.SortedNumericDocValuesField; | ||
import org.apache.lucene.search.IndexOrDocValuesQuery; | ||
import org.apache.lucene.search.MultiTermQuery; | ||
import org.apache.lucene.search.Query; | ||
import org.apache.lucene.search.TermInSetQuery; | ||
import org.apache.lucene.util.BytesRef; | ||
import org.apache.lucene.util.NumericUtils; | ||
|
||
import java.util.ArrayList; | ||
import java.util.List; | ||
import java.util.function.Supplier; | ||
|
||
public class SetQueryHelper { | ||
|
||
public static Query getNumericSetQuery(String field, IndexFieldInfo indexFieldInfo, Supplier<List<Integer>> intSupplier, Supplier<List<Long>> longSupplier, | ||
Supplier<List<Float>> floatSupplier, Supplier<List<Double>> doubleSupplier) { | ||
ZuliaIndex.FieldConfig.FieldType fieldType = indexFieldInfo.getFieldType(); | ||
String searchField = indexFieldInfo.getInternalFieldName(); | ||
String sortField = indexFieldInfo.getInternalSortFieldName(); | ||
|
||
if (fieldType == null) { | ||
throw new IllegalArgumentException("Field <" + field + "> is not indexed"); | ||
} | ||
else { | ||
if (FieldTypeUtil.isNumericIntFieldType(fieldType)) { | ||
List<Integer> integerValueList = intSupplier.get(); | ||
if (integerValueList.isEmpty()) { | ||
throw new IllegalArgumentException("No integer values for integer field <" + field + "> for numeric set query"); | ||
} | ||
|
||
Query pointQuery = IntPoint.newSetQuery(searchField, integerValueList); | ||
if (sortField == null) { | ||
return pointQuery; | ||
} | ||
long[] pointsArray = integerValueList.stream().mapToLong(Integer::intValue).toArray(); | ||
return new IndexOrDocValuesQuery(pointQuery, SortedNumericDocValuesField.newSlowSetQuery(sortField, pointsArray)); | ||
} | ||
else if (FieldTypeUtil.isNumericLongFieldType(fieldType)) { | ||
List<Long> longValueList = longSupplier.get(); | ||
if (longValueList.isEmpty()) { | ||
throw new IllegalArgumentException("No long values for long field <" + field + "> for numeric set query"); | ||
} | ||
|
||
Query pointQuery = LongPoint.newSetQuery(searchField, longValueList); | ||
if (sortField == null) { | ||
return pointQuery; | ||
} | ||
long[] pointsArray = longValueList.stream().mapToLong(Long::longValue).toArray(); | ||
return new IndexOrDocValuesQuery(pointQuery, SortedNumericDocValuesField.newSlowSetQuery(sortField, pointsArray)); | ||
} | ||
else if (FieldTypeUtil.isNumericFloatFieldType(fieldType)) { | ||
List<Float> floatValueList = floatSupplier.get(); | ||
if (floatValueList.isEmpty()) { | ||
throw new IllegalArgumentException("No float values for float field <" + field + "> for numeric set query"); | ||
} | ||
|
||
Query pointQuery = FloatPoint.newSetQuery(searchField, floatValueList); | ||
if (sortField == null) { | ||
return pointQuery; | ||
} | ||
long[] pointsArray = floatValueList.stream().mapToLong(NumericUtils::floatToSortableInt).toArray(); | ||
return new IndexOrDocValuesQuery(pointQuery, SortedNumericDocValuesField.newSlowSetQuery(sortField, pointsArray)); | ||
} | ||
else if (FieldTypeUtil.isNumericDoubleFieldType(fieldType)) { | ||
List<Double> doubleValueList = doubleSupplier.get(); | ||
if (doubleValueList.isEmpty()) { | ||
throw new IllegalArgumentException("No double values for double field <" + field + "> for numeric set query"); | ||
} | ||
|
||
Query pointQuery = DoublePoint.newSetQuery(searchField, doubleValueList); | ||
if (sortField == null) { | ||
return pointQuery; | ||
} | ||
long[] pointsArray = doubleValueList.stream().mapToLong(NumericUtils::doubleToSortableLong).toArray(); | ||
return new IndexOrDocValuesQuery(pointQuery, SortedNumericDocValuesField.newSlowSetQuery(sortField, pointsArray)); | ||
} | ||
} | ||
throw new IllegalArgumentException("No field type of <" + fieldType + "> is not supported for numeric set queries"); | ||
} | ||
|
||
public static Query getTermInSetQuery(List<String> terms, String field, IndexFieldInfo indexFieldInfo) { | ||
List<BytesRef> termBytesRef = new ArrayList<>(); | ||
for (String term : terms) { | ||
termBytesRef.add(new BytesRef(term)); | ||
} | ||
|
||
if (FieldTypeUtil.isStringFieldType(indexFieldInfo.getFieldType())) { | ||
String sortField = indexFieldInfo.getInternalSortFieldName(); | ||
|
||
if (sortField != null) { | ||
Query indexQuery = new TermInSetQuery(field, termBytesRef); | ||
Query dvQuery = new TermInSetQuery(MultiTermQuery.DOC_VALUES_REWRITE, sortField, termBytesRef); | ||
return new IndexOrDocValuesQuery(indexQuery, dvQuery); | ||
} | ||
|
||
return new TermInSetQuery(field, termBytesRef); | ||
} | ||
throw new IllegalArgumentException( | ||
"Field type of <" + indexFieldInfo.getFieldType() + "> is not supported for term queries. Only STRING is supported."); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
.../main/java/io/zulia/server/search/queryparser/builder/ZuliaFieldableQueryNodeBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package io.zulia.server.search.queryparser.builder; | ||
|
||
import io.zulia.server.search.queryparser.node.ZuliaFieldableQueryNode; | ||
import org.apache.lucene.queryparser.flexible.core.QueryNodeException; | ||
import org.apache.lucene.queryparser.flexible.core.nodes.QueryNode; | ||
import org.apache.lucene.queryparser.flexible.standard.builders.StandardQueryBuilder; | ||
import org.apache.lucene.search.Query; | ||
|
||
public class ZuliaFieldableQueryNodeBuilder implements StandardQueryBuilder { | ||
|
||
/** | ||
* Constructs a {@link ZuliaFieldableQueryNodeBuilder} object. | ||
*/ | ||
public ZuliaFieldableQueryNodeBuilder() { | ||
// empty constructor | ||
} | ||
|
||
@Override | ||
public Query build(QueryNode queryNode) throws QueryNodeException { | ||
ZuliaFieldableQueryNode fieldableQueryNode = (ZuliaFieldableQueryNode) queryNode; | ||
return fieldableQueryNode.getQuery(); | ||
} | ||
} |
2 changes: 2 additions & 0 deletions
2
...arser/src/main/java/io/zulia/server/search/queryparser/builder/ZuliaQueryTreeBuilder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
package io.zulia.server.search.queryparser.builder; | ||
|
||
import io.zulia.server.search.queryparser.node.ZuliaFieldableQueryNode; | ||
import io.zulia.server.search.queryparser.node.ZuliaPointRangeQueryNode; | ||
import org.apache.lucene.queryparser.flexible.standard.builders.StandardQueryTreeBuilder; | ||
|
||
public class ZuliaQueryTreeBuilder extends StandardQueryTreeBuilder { | ||
|
||
public ZuliaQueryTreeBuilder() { | ||
setBuilder(ZuliaPointRangeQueryNode.class, new ZuliaPointRangeQueryNodeBuilder()); | ||
setBuilder(ZuliaFieldableQueryNode.class, new ZuliaFieldableQueryNodeBuilder()); | ||
} | ||
|
||
} |
31 changes: 31 additions & 0 deletions
31
...parser/src/main/java/io/zulia/server/search/queryparser/node/ZuliaFieldableQueryNode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package io.zulia.server.search.queryparser.node; | ||
|
||
import io.zulia.server.config.IndexFieldInfo; | ||
import org.apache.lucene.queryparser.flexible.core.nodes.FieldableNode; | ||
import org.apache.lucene.queryparser.flexible.core.nodes.QueryNodeImpl; | ||
import org.apache.lucene.search.Query; | ||
|
||
public abstract class ZuliaFieldableQueryNode extends QueryNodeImpl implements FieldableNode { | ||
private CharSequence field; | ||
private IndexFieldInfo indexFieldInfo; | ||
|
||
@Override | ||
public CharSequence getField() { | ||
return field; | ||
} | ||
|
||
@Override | ||
public void setField(CharSequence fieldName) { | ||
this.field = fieldName != null ? fieldName.toString() : null; | ||
} | ||
|
||
public abstract Query getQuery(); | ||
|
||
public void setIndexFieldInfo(IndexFieldInfo indexFieldInfo) { | ||
this.indexFieldInfo = indexFieldInfo; | ||
} | ||
|
||
public IndexFieldInfo getIndexFieldInfo() { | ||
return indexFieldInfo; | ||
} | ||
} |
75 changes: 75 additions & 0 deletions
75
...arser/src/main/java/io/zulia/server/search/queryparser/node/ZuliaNumericSetQueryNode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
* 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 io.zulia.server.search.queryparser.node; | ||
|
||
import io.zulia.server.search.queryparser.SetQueryHelper; | ||
import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax; | ||
import org.apache.lucene.queryparser.flexible.standard.parser.EscapeQuerySyntaxImpl; | ||
import org.apache.lucene.search.Query; | ||
|
||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.Objects; | ||
import java.util.function.Supplier; | ||
import java.util.stream.Collectors; | ||
|
||
public class ZuliaNumericSetQueryNode extends ZuliaFieldableQueryNode { | ||
private final List<CharSequence> terms; | ||
|
||
public ZuliaNumericSetQueryNode(CharSequence field, List<CharSequence> terms) { | ||
setField(field); | ||
this.terms = Objects.requireNonNull(terms); | ||
} | ||
|
||
public Query getQuery() { | ||
Objects.requireNonNull(getField(), "Field must not be null for numeric set queries"); | ||
Objects.requireNonNull(getIndexFieldInfo(), "Field <" + getField() + "> must be indexed for numeric set queries"); | ||
return SetQueryHelper.getNumericSetQuery(getField().toString(), getIndexFieldInfo(), intTerms(), longTerms(), floatTerms(), doubleTerms()); | ||
} | ||
|
||
public Supplier<List<Integer>> intTerms() { | ||
return () -> terms.stream().map(Object::toString).map(Integer::parseInt).collect(Collectors.toList()); | ||
} | ||
|
||
public Supplier<List<Long>> longTerms() { | ||
return () -> terms.stream().map(Object::toString).map(Long::parseLong).collect(Collectors.toList()); | ||
} | ||
|
||
public Supplier<List<Float>> floatTerms() { | ||
return () -> terms.stream().map(Object::toString).map(Float::parseFloat).collect(Collectors.toList()); | ||
} | ||
|
||
public Supplier<List<Double>> doubleTerms() { | ||
return () -> terms.stream().map(Object::toString).map(Double::parseDouble).collect(Collectors.toList()); | ||
} | ||
|
||
@Override | ||
public String toQueryString(EscapeQuerySyntax escapeSyntaxParser) { | ||
return String.format(Locale.ROOT, "%s:numericSet(%s)", getField(), terms); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return toQueryString(new EscapeQuerySyntaxImpl()); | ||
} | ||
|
||
@Override | ||
public ZuliaNumericSetQueryNode cloneTree() { | ||
return new ZuliaNumericSetQueryNode(getField(), terms); | ||
} | ||
|
||
} |
57 changes: 57 additions & 0 deletions
57
...arser/src/main/java/io/zulia/server/search/queryparser/node/ZuliaTermsInSetQueryNode.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/* | ||
* 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 io.zulia.server.search.queryparser.node; | ||
|
||
import io.zulia.server.search.queryparser.SetQueryHelper; | ||
import org.apache.lucene.queryparser.flexible.core.parser.EscapeQuerySyntax; | ||
import org.apache.lucene.queryparser.flexible.standard.parser.EscapeQuerySyntaxImpl; | ||
import org.apache.lucene.search.Query; | ||
|
||
import java.util.List; | ||
import java.util.Locale; | ||
import java.util.Objects; | ||
|
||
public class ZuliaTermsInSetQueryNode extends ZuliaFieldableQueryNode { | ||
private final List<CharSequence> terms; | ||
|
||
public ZuliaTermsInSetQueryNode(CharSequence field, List<CharSequence> terms) { | ||
setField(field); | ||
this.terms = Objects.requireNonNull(terms); | ||
} | ||
|
||
public Query getQuery() { | ||
Objects.requireNonNull(getField(), "Field must not be null for term set queries."); | ||
Objects.requireNonNull(getIndexFieldInfo(), "Field <" + getField() + "> must be indexed for term set queries"); | ||
return SetQueryHelper.getTermInSetQuery(terms.stream().map(Object::toString).toList(), getField().toString(), getIndexFieldInfo()); | ||
} | ||
|
||
@Override | ||
public String toQueryString(EscapeQuerySyntax escapeSyntaxParser) { | ||
return String.format(Locale.ROOT, "%s:termQuery(%s)", getField(), terms); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return toQueryString(new EscapeQuerySyntaxImpl()); | ||
} | ||
|
||
@Override | ||
public ZuliaTermsInSetQueryNode cloneTree() { | ||
return new ZuliaTermsInSetQueryNode(getField(), terms); | ||
} | ||
|
||
} |
Oops, something went wrong.