This is an automated email from the ASF dual-hosted git repository. ronny pushed a commit to branch nouveau4win in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 8f31978842919a196bb0b4b593f952cb1a187835 Author: Robert Newson <[email protected]> AuthorDate: Thu Jul 20 22:39:47 2023 +0100 allow locale setting for number detection --- .../org/apache/couchdb/nouveau/api/SearchRequest.java | 16 ++++++++++++++-- .../apache/couchdb/nouveau/lucene9/Lucene9Index.java | 2 +- .../couchdb/nouveau/lucene9/NouveauQueryParser.java | 17 ++++++++++++----- .../nouveau/lucene9/NouveauQueryParserTest.java | 15 ++++++++++++++- src/docs/src/api/ddoc/nouveau.rst | 3 +++ src/nouveau/src/nouveau_httpd.erl | 6 ++++++ test/elixir/test/config/nouveau.elixir | 1 + test/elixir/test/nouveau_test.exs | 19 +++++++++++++++++++ 8 files changed, 70 insertions(+), 9 deletions(-) diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java index 09f2818aa..2fc9e1f69 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/api/SearchRequest.java @@ -22,6 +22,7 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import java.util.List; +import java.util.Locale; import java.util.Map; import org.apache.couchdb.nouveau.core.ser.PrimitiveWrapper; @@ -31,6 +32,8 @@ public class SearchRequest { @NotNull private String query; + private Locale locale; + private String partition; @Positive @@ -61,6 +64,15 @@ public class SearchRequest { return query; } + public void setLocale(final Locale locale) { + this.locale = locale; + } + + @JsonProperty + public Locale getLocale() { + return locale; + } + public void setPartition(final String partition) { this.partition = partition; } @@ -142,7 +154,7 @@ public class SearchRequest { @Override public String toString() { - return "SearchRequest [query=" + query + ", sort=" + sort + ", limit=" + limit + ", after=" + after - + ", counts=" + counts + ", ranges=" + ranges + "]"; + return "SearchRequest [query=" + query + ", locale=" + locale + ", sort=" + sort + ", limit=" + limit + + ", after=" + after + ", counts=" + counts + ", ranges=" + ranges + "]"; } } diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java index 0de610f84..69e6b1264 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/Lucene9Index.java @@ -489,7 +489,7 @@ public class Lucene9Index extends Index { } private Query parse(final SearchRequest request) { - var queryParser = new NouveauQueryParser(analyzer); + var queryParser = new NouveauQueryParser(analyzer, request.getLocale()); Query result; try { result = queryParser.parse(request.getQuery(), "default"); diff --git a/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParser.java b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParser.java index 6516efc2c..f1cf60f0f 100644 --- a/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParser.java +++ b/nouveau/src/main/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParser.java @@ -16,6 +16,7 @@ package org.apache.couchdb.nouveau.lucene9; import java.text.NumberFormat; import java.text.ParseException; import java.util.List; +import java.util.Locale; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.queryparser.flexible.core.QueryNodeException; import org.apache.lucene.queryparser.flexible.core.QueryParserHelper; @@ -55,11 +56,11 @@ import org.apache.lucene.search.Query; public final class NouveauQueryParser extends QueryParserHelper { - public NouveauQueryParser(final Analyzer analyzer) { + public NouveauQueryParser(final Analyzer analyzer, final Locale locale) { super( new StandardQueryConfigHandler(), new StandardSyntaxParser(), - new NouveauQueryNodeProcessorPipeline(), + new NouveauQueryNodeProcessorPipeline(locale), new StandardQueryTreeBuilder()); getQueryConfigHandler().set(ConfigurationKeys.ENABLE_POSITION_INCREMENTS, true); getQueryConfigHandler().set(ConfigurationKeys.ANALYZER, analyzer); @@ -77,7 +78,7 @@ public final class NouveauQueryParser extends QueryParserHelper { */ public static class NouveauQueryNodeProcessorPipeline extends QueryNodeProcessorPipeline { - public NouveauQueryNodeProcessorPipeline() { + public NouveauQueryNodeProcessorPipeline(final Locale locale) { super(null); add(new WildcardQueryNodeProcessor()); add(new MultiFieldQueryNodeProcessor()); @@ -85,7 +86,7 @@ public final class NouveauQueryParser extends QueryParserHelper { add(new RegexpQueryNodeProcessor()); add(new MatchAllDocsQueryNodeProcessor()); add(new OpenRangeQueryNodeProcessor()); - add(new NouveauPointProcessor()); + add(new NouveauPointProcessor(locale)); add(new TermRangeQueryNodeProcessor()); add(new AllowLeadingWildcardProcessor()); add(new AnalyzerQueryNodeProcessor()); @@ -107,9 +108,15 @@ public final class NouveauQueryParser extends QueryParserHelper { */ public static class NouveauPointProcessor extends QueryNodeProcessorImpl { + private final Locale locale; + + NouveauPointProcessor(final Locale locale) { + this.locale = locale != null ? locale : Locale.getDefault(); + } + @Override protected QueryNode postProcessNode(final QueryNode node) throws QueryNodeException { - final var numberFormat = NumberFormat.getInstance(); + final var numberFormat = NumberFormat.getInstance(locale); final var pointsConfig = new PointsConfig(numberFormat, Double.class); if (node instanceof FieldQueryNode && !(node.getParent() instanceof RangeQueryNode)) { diff --git a/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParserTest.java b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParserTest.java index 3b41f4397..6dbcceeb8 100644 --- a/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParserTest.java +++ b/nouveau/src/test/java/org/apache/couchdb/nouveau/lucene9/NouveauQueryParserTest.java @@ -15,6 +15,7 @@ package org.apache.couchdb.nouveau.lucene9; import static org.assertj.core.api.Assertions.assertThat; +import java.util.Locale; import org.apache.lucene.analysis.standard.StandardAnalyzer; import org.apache.lucene.document.DoublePoint; import org.apache.lucene.index.Term; @@ -34,7 +35,7 @@ public class NouveauQueryParserTest { @BeforeAll public static void setup() { - qp = new NouveauQueryParser(new StandardAnalyzer()); + qp = new NouveauQueryParser(new StandardAnalyzer(), Locale.US); } @Test @@ -100,4 +101,16 @@ public class NouveauQueryParserTest { assertThat(qp.parse("foo:[1.0 TO Infinity]", DEFAULT_FIELD)) .isEqualTo(DoublePoint.newRangeQuery("foo", new double[] {1}, new double[] {Double.POSITIVE_INFINITY})); } + + @Test + public void testLocales() throws Exception { + var us = new NouveauQueryParser(new StandardAnalyzer(), Locale.US); + var de = new NouveauQueryParser(new StandardAnalyzer(), Locale.GERMAN); + + assertThat(us.parse("foo:[10.0 TO 20.0]", DEFAULT_FIELD)) + .isEqualTo(DoublePoint.newRangeQuery("foo", new double[] {10}, new double[] {20})); + + assertThat(de.parse("foo:[10.0 TO 20.0]", DEFAULT_FIELD)) + .isEqualTo(DoublePoint.newRangeQuery("foo", new double[] {100}, new double[] {200})); + } } diff --git a/src/docs/src/api/ddoc/nouveau.rst b/src/docs/src/api/ddoc/nouveau.rst index fb6731a5b..0cd40bb5d 100644 --- a/src/docs/src/api/ddoc/nouveau.rst +++ b/src/docs/src/api/ddoc/nouveau.rst @@ -46,6 +46,9 @@ name among the documents that match the search query. :query boolean include_docs: Include the full content of the documents in the response. + :query string locale: The (Java) locale used to parse numbers in range queries. + Defaults to the JDK default locale if not specified. Some examples are ``de`` + , ``us``, ``gb``. :query number limit: Limit the number of the returned documents to the specified number. For a grouped search, this parameter limits the number of documents per group. diff --git a/src/nouveau/src/nouveau_httpd.erl b/src/nouveau/src/nouveau_httpd.erl index e60d7e440..10917b3d4 100644 --- a/src/nouveau/src/nouveau_httpd.erl +++ b/src/nouveau/src/nouveau_httpd.erl @@ -65,6 +65,7 @@ handle_search_req_int(#httpd{method = 'GET', path_parts = [_, _, _, _, IndexName DbName = couch_db:name(Db), QueryArgs = validate_query_args(#{ query => chttpd:qs_value(Req, "q"), + locale => chttpd:qs_value(Req, "locale"), partition => chttpd:qs_value(Req, "partition"), limit => chttpd:qs_value(Req, "limit"), sort => chttpd:qs_value(Req, "sort"), @@ -83,6 +84,7 @@ handle_search_req_int( ReqBody = chttpd:json_body(Req, [return_maps]), QueryArgs = validate_query_args(#{ query => maps:get(<<"q">>, ReqBody, undefined), + locale => maps:get(<<"locale">>, ReqBody, undefined), partition => chttpd:qs_value(Req, "partition"), limit => maps:get(<<"limit">>, ReqBody, undefined), sort => json_or_undefined(<<"sort">>, ReqBody), @@ -177,6 +179,10 @@ validate_query_arg(query, undefined) -> throw({query_parse_error, <<"q parameter is mandatory">>}); validate_query_arg(query, Val) when is_list(Val); is_binary(Val) -> couch_util:to_binary(Val); +validate_query_arg(locale, undefined) -> + null; +validate_query_arg(locale, Val) when is_list(Val); is_binary(Val) -> + couch_util:to_binary(Val); validate_query_arg(partition, undefined) -> null; validate_query_arg(partition, Val) when is_list(Val); is_binary(Val) -> diff --git a/test/elixir/test/config/nouveau.elixir b/test/elixir/test/config/nouveau.elixir index a580581e6..733aa9ddb 100644 --- a/test/elixir/test/config/nouveau.elixir +++ b/test/elixir/test/config/nouveau.elixir @@ -6,6 +6,7 @@ "search returns all items for POST", "search returns all items (paginated)", "search for foo:bar", + "search for numeric ranges with locales", "sort by string field (asc)", "sort by string field (desc)", "sort by numeric field (asc)", diff --git a/test/elixir/test/nouveau_test.exs b/test/elixir/test/nouveau_test.exs index 2a8bc513f..28cc27378 100644 --- a/test/elixir/test/nouveau_test.exs +++ b/test/elixir/test/nouveau_test.exs @@ -182,6 +182,25 @@ defmodule NouveauTest do assert ids == ["doc3"] end + @tag :with_db + test "search for numeric ranges with locales", context do + db_name = context[:db_name] + create_search_docs(db_name) + create_ddoc(db_name) + + url = "/#{db_name}/_design/foo/_nouveau/bar" + resp = Couch.post(url, body: %{q: "bar:[10.0 TO 20.0]", locale: "us", include_docs: true}) + assert_status_code(resp, 200) + ids = get_ids(resp) + assert ids == ["doc3"] + + url = "/#{db_name}/_design/foo/_nouveau/bar" + resp = Couch.post(url, body: %{q: "bar:[10.0 TO 20.0]", locale: "de", include_docs: true}) + assert_status_code(resp, 200) + ids = get_ids(resp) + assert ids == ["doc2"] + end + @tag :with_db test "sort by string field (asc)", context do db_name = context[:db_name]
