You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by sa...@apache.org on 2017/05/14 15:38:24 UTC

[1/3] lucene-solr:master: LUCENE-7821: The classic and flexible query parsers, as well as Solr's 'lucene'/standard query parser, should require ' TO ' in range queries, and accept 'TO' as endpoints in range queries.

Repository: lucene-solr
Updated Branches:
  refs/heads/branch_6_6 4b93c62d2 -> b172ceb14
  refs/heads/branch_6x ee70e1e4c -> 8633f49dc
  refs/heads/master 9fa04ecc9 -> e11bc0309


LUCENE-7821: The classic and flexible query parsers, as well as Solr's 'lucene'/standard query parser, should require ' TO ' in range queries, and accept 'TO' as endpoints in range queries.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/e11bc030
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/e11bc030
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/e11bc030

Branch: refs/heads/master
Commit: e11bc030987b33faadf42f0b2fb21d521077f361
Parents: 9fa04ec
Author: Steve Rowe <sa...@apache.org>
Authored: Sun May 14 11:31:22 2017 -0400
Committer: Steve Rowe <sa...@apache.org>
Committed: Sun May 14 11:31:22 2017 -0400

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |  4 ++
 .../lucene/queryparser/classic/QueryParser.java | 47 +++++++++---------
 .../lucene/queryparser/classic/QueryParser.jj   |  6 +--
 .../standard/parser/StandardSyntaxParser.java   | 43 ++++++++---------
 .../standard/parser/StandardSyntaxParser.jj     |  6 +--
 .../queryparser/util/QueryParserTestBase.java   | 51 ++++++++++++++++++++
 solr/CHANGES.txt                                |  3 ++
 .../org/apache/solr/parser/QueryParser.java     | 47 +++++++++---------
 .../java/org/apache/solr/parser/QueryParser.jj  |  6 +--
 .../org/apache/solr/search/TestRangeQuery.java  | 42 ++++++++++++++++
 10 files changed, 176 insertions(+), 79 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index a57d67b..10c46cf 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -126,6 +126,10 @@ Bug Fixes
 * LUCENE-5365, LUCENE-7818: Fix incorrect condition in queryparser's
   QueryNodeOperation#logicalAnd().  (Olivier Binda, Amrit Sarkar,
   AppChecker via Uwe Schindler)
+  
+* LUCENE-7821: The classic and flexible query parsers, as well as Solr's
+ "lucene"/standard query parser, should require " TO " in range queries,
+  and accept "TO" as endpoints in range queries. (hossman, Steve Rowe)
 
 Improvements
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
index 5e5d57b..134ddfa 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
@@ -490,19 +490,15 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[16] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[17] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -510,8 +506,11 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[18] = jj_gen;
+        jj_la1[17] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -524,7 +523,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[19] = jj_gen;
+        jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -534,7 +533,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         ;
       }
       boolean startOpen=false;
@@ -566,7 +565,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
                                                         fuzzy=true;
             break;
           default:
-            jj_la1[21] = jj_gen;
+            jj_la1[20] = jj_gen;
             ;
           }
           break;
@@ -579,24 +578,24 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
             boost = jj_consume_token(NUMBER);
             break;
           default:
-            jj_la1[22] = jj_gen;
+            jj_la1[21] = jj_gen;
             ;
           }
           break;
         default:
-          jj_la1[23] = jj_gen;
+          jj_la1[22] = jj_gen;
           jj_consume_token(-1);
           throw new ParseException();
         }
         break;
       default:
-        jj_la1[24] = jj_gen;
+        jj_la1[23] = jj_gen;
         ;
       }
       q = handleQuotedTerm(field, term, fuzzySlop);
       break;
     default:
-      jj_la1[25] = jj_gen;
+      jj_la1[24] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -732,7 +731,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
   private boolean jj_lookingAhead = false;
   private boolean jj_semLA;
   private int jj_gen;
-  final private int[] jj_la1 = new int[26];
+  final private int[] jj_la1 = new int[25];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -740,10 +739,10 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x300,0x300,0x1c00,0x1c00,0xfda7c00,0xfda7f00,0xfda7f00,0x120000,0x40000,0xfda6000,0x9d22000,0x200000,0x40000,0x240000,0x240000,0x6000000,0x80000000,0x10000000,0x80000000,0x60000000,0x40000,0x200000,0x40000,0x240000,0x240000,0xfda2000,};
+      jj_la1_0 = new int[] {0x300,0x300,0x1c00,0x1c00,0xfda7c00,0xfda7f00,0xfda7f00,0x120000,0x40000,0xfda6000,0x9d22000,0x200000,0x40000,0x240000,0x240000,0x6000000,0x90000000,0x90000000,0x60000000,0x40000,0x200000,0x40000,0x240000,0x240000,0xfda2000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[3];
   private boolean jj_rescan = false;
@@ -755,7 +754,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -766,7 +765,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     jj_ntk = -1;
     jj_lookingAhead = false;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -776,7 +775,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -786,7 +785,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -903,7 +902,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 26; i++) {
+    for (int i = 0; i < 25; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
index 35cbe1a..2d280d1 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
@@ -341,9 +341,9 @@ Query Term(String field) : {
     { q = handleBareTokenQuery(field, term, fuzzySlop, prefix, wildcard, fuzzy, regexp); }
 
   | ( <RANGEIN_START> { startInc = true; } | <RANGEEX_START> )
-    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> )
-    [ <RANGE_TO> ]
-    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> )
+    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> | goop1=<RANGE_TO> )
+    ( <RANGE_TO> )
+    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> | goop2=<RANGE_TO> )
     ( <RANGEIN_END> { endInc = true; } | <RANGEEX_END> )
     [ <CARAT> boost=<NUMBER> ]
     {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
index 8ba34a6..6008bcc 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
@@ -577,19 +577,15 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[19] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -597,8 +593,11 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -611,7 +610,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[21] = jj_gen;
+        jj_la1[20] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -621,7 +620,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[22] = jj_gen;
+        jj_la1[21] = jj_gen;
         ;
       }
           if (goop1.kind == RANGE_QUOTED) {
@@ -645,7 +644,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         fuzzySlop = jj_consume_token(FUZZY_SLOP);
         break;
       default:
-        jj_la1[23] = jj_gen;
+        jj_la1[22] = jj_gen;
         ;
       }
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
@@ -654,7 +653,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[24] = jj_gen;
+        jj_la1[23] = jj_gen;
         ;
       }
          int phraseSlop = 0;
@@ -672,7 +671,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
          }
       break;
     default:
-      jj_la1[25] = jj_gen;
+      jj_la1[24] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -831,7 +830,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
   private Token jj_scanpos, jj_lastpos;
   private int jj_la;
   private int jj_gen;
-  final private int[] jj_la1 = new int[26];
+  final private int[] jj_la1 = new int[25];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -839,10 +838,10 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x1c00,0x1c00,0x1ec03c00,0x200,0x100,0x18000,0x1e0000,0x10c00000,0x1f8000,0x18000,0x200000,0x1ec02000,0x1ec02000,0x12800000,0x1000000,0x1000000,0x200000,0xc000000,0x0,0x20000000,0x0,0xc0000000,0x200000,0x1000000,0x200000,0x1ec00000,};
+      jj_la1_0 = new int[] {0x1c00,0x1c00,0x1ec03c00,0x200,0x100,0x18000,0x1e0000,0x10c00000,0x1f8000,0x18000,0x200000,0x1ec02000,0x1ec02000,0x12800000,0x1000000,0x1000000,0x200000,0xc000000,0x20000000,0x20000000,0xc0000000,0x200000,0x1000000,0x200000,0x1ec00000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[2];
   private boolean jj_rescan = false;
@@ -854,7 +853,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -864,7 +863,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -874,7 +873,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -884,7 +883,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -1001,7 +1000,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 26; i++) {
+    for (int i = 0; i < 25; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
index b53bab3..8803618 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
@@ -445,9 +445,9 @@ QueryNode Term(CharSequence field) : {
        }
      }
      | ( ( <RANGEIN_START> {startInc=true;} | <RANGEEX_START> )
-         ( goop1=<RANGE_GOOP>|goop1=<RANGE_QUOTED> )
-         [ <RANGE_TO> ]
-         ( goop2=<RANGE_GOOP>|goop2=<RANGE_QUOTED> )
+         ( goop1=<RANGE_GOOP>|goop1=<RANGE_QUOTED>|goop1=<RANGE_TO> )
+         ( <RANGE_TO> )
+         ( goop2=<RANGE_GOOP>|goop2=<RANGE_QUOTED>|goop2=<RANGE_TO> )
          ( <RANGEIN_END> {endInc=true;} | <RANGEEX_END>))
        [ <CARAT> boost=<NUMBER> ]
         {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
index 1b8ee96..b49f576 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
@@ -41,6 +41,7 @@ import org.apache.lucene.index.Term;
 import org.apache.lucene.queryparser.classic.QueryParser;
 import org.apache.lucene.queryparser.classic.QueryParserBase;
 //import org.apache.lucene.queryparser.classic.QueryParserTokenManager;
+import org.apache.lucene.queryparser.classic.TestQueryParser;
 import org.apache.lucene.queryparser.flexible.standard.CommonQueryParserConfiguration;
 import org.apache.lucene.search.*;
 import org.apache.lucene.search.BooleanClause.Occur;
@@ -594,6 +595,56 @@ public abstract class QueryParserTestBase extends LuceneTestCase {
     assertQueryEquals("[\"*\" TO *]",null,"[\\* TO *]");
   }
 
+  public void testRangeQueryEndpointTO() throws Exception {
+    Analyzer a = new MockAnalyzer(random());
+    assertQueryEquals("[to TO to]", a, "[to TO to]");
+    assertQueryEquals("[to TO TO]", a, "[to TO to]");
+    assertQueryEquals("[TO TO to]", a, "[to TO to]");
+    assertQueryEquals("[TO TO TO]", a, "[to TO to]");
+
+    assertQueryEquals("[\"TO\" TO \"TO\"]", a, "[to TO to]");
+    assertQueryEquals("[\"TO\" TO TO]", a, "[to TO to]");
+    assertQueryEquals("[TO TO \"TO\"]", a, "[to TO to]");
+
+    assertQueryEquals("[to TO xx]", a, "[to TO xx]");
+    assertQueryEquals("[\"TO\" TO xx]", a, "[to TO xx]");
+    assertQueryEquals("[TO TO xx]", a, "[to TO xx]");
+
+    assertQueryEquals("[xx TO to]", a, "[xx TO to]");
+    assertQueryEquals("[xx TO \"TO\"]", a, "[xx TO to]");
+    assertQueryEquals("[xx TO TO]", a, "[xx TO to]");
+  }
+
+  public void testRangeQueryRequiresTO() throws Exception {
+    Analyzer a = new MockAnalyzer(random());
+
+    assertQueryEquals("{A TO B}", a, "{a TO b}");
+    assertQueryEquals("[A TO B}", a, "[a TO b}");
+    assertQueryEquals("{A TO B]", a, "{a TO b]");
+    assertQueryEquals("[A TO B]", a, "[a TO b]");
+
+    // " TO " is required between range endpoints
+
+    Class<? extends Throwable> exceptionClass = this instanceof TestQueryParser 
+        ? org.apache.lucene.queryparser.classic.ParseException.class
+        : org.apache.lucene.queryparser.flexible.standard.parser.ParseException.class;
+    
+    expectThrows(exceptionClass, () -> getQuery("{A B}"));
+    expectThrows(exceptionClass, () -> getQuery("[A B}"));
+    expectThrows(exceptionClass, () -> getQuery("{A B]"));
+    expectThrows(exceptionClass, () -> getQuery("[A B]"));
+
+    expectThrows(exceptionClass, () -> getQuery("{TO B}"));
+    expectThrows(exceptionClass, () -> getQuery("[TO B}"));
+    expectThrows(exceptionClass, () -> getQuery("{TO B]"));
+    expectThrows(exceptionClass, () -> getQuery("[TO B]"));
+
+    expectThrows(exceptionClass, () -> getQuery("{A TO}"));
+    expectThrows(exceptionClass, () -> getQuery("[A TO}"));
+    expectThrows(exceptionClass, () -> getQuery("{A TO]"));
+    expectThrows(exceptionClass, () -> getQuery("[A TO]"));
+  }
+
   private String escapeDateString(String s) {
     if (s.indexOf(" ") > -1) {
       return "\"" + s + "\"";

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index c5c41c2..a3b5515 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -370,6 +370,9 @@ Bug Fixes
 * SOLR-9527: Improve distribution of replicas when restoring a collection
   (Hrishikesh Gadre, Stephen Lewis, Rohit, Varun Thacker)
 
+* LUCENE-7821: The classic and flexible query parsers, as well as Solr's
+ "lucene"/standard query parser, should require " TO " in range queries,
+  and accept "TO" as endpoints in range queries. (hossman, Steve Rowe)
 
 Other Changes
 ----------------------

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/solr/core/src/java/org/apache/solr/parser/QueryParser.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/QueryParser.java b/solr/core/src/java/org/apache/solr/parser/QueryParser.java
index 39ec673..42e982f 100644
--- a/solr/core/src/java/org/apache/solr/parser/QueryParser.java
+++ b/solr/core/src/java/org/apache/solr/parser/QueryParser.java
@@ -441,19 +441,15 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[19] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -461,8 +457,11 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -475,7 +474,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[21] = jj_gen;
+        jj_la1[20] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -485,7 +484,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[22] = jj_gen;
+        jj_la1[21] = jj_gen;
         ;
       }
       boolean startOpen=false;
@@ -519,7 +518,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
                                                         fuzzy=true;
             break;
           default:
-            jj_la1[23] = jj_gen;
+            jj_la1[22] = jj_gen;
             ;
           }
           break;
@@ -532,24 +531,24 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
             boost = jj_consume_token(NUMBER);
             break;
           default:
-            jj_la1[24] = jj_gen;
+            jj_la1[23] = jj_gen;
             ;
           }
           break;
         default:
-          jj_la1[25] = jj_gen;
+          jj_la1[24] = jj_gen;
           jj_consume_token(-1);
           throw new ParseException();
         }
         break;
       default:
-        jj_la1[26] = jj_gen;
+        jj_la1[25] = jj_gen;
         ;
       }
       q = handleQuotedTerm(getField(field), term, fuzzySlop);
       break;
     default:
-      jj_la1[27] = jj_gen;
+      jj_la1[26] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -685,7 +684,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
   private boolean jj_lookingAhead = false;
   private boolean jj_semLA;
   private int jj_gen;
-  final private int[] jj_la1 = new int[28];
+  final private int[] jj_la1 = new int[27];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -693,10 +692,10 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x6000,0x6000,0x38000,0x38000,0xfb4f8000,0xfb4fe000,0xfb4fe000,0x2400000,0x800000,0x800000,0x800000,0xfb4c0000,0x3a440000,0x4000000,0x800000,0x4800000,0x4800000,0xc0000000,0x0,0x0,0x0,0x0,0x800000,0x4000000,0x800000,0x4800000,0x4800000,0xfb440000,};
+      jj_la1_0 = new int[] {0x6000,0x6000,0x38000,0x38000,0xfb4f8000,0xfb4fe000,0xfb4fe000,0x2400000,0x800000,0x800000,0x800000,0xfb4c0000,0x3a440000,0x4000000,0x800000,0x4800000,0x4800000,0xc0000000,0x0,0x0,0x0,0x800000,0x4000000,0x800000,0x4800000,0x4800000,0xfb440000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x7,0x7,0x7,0x0,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0xc0,0x8,0xc0,0x30,0x0,0x0,0x0,0x0,0x0,0x4,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x7,0x7,0x7,0x0,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0xc8,0xc8,0x30,0x0,0x0,0x0,0x0,0x0,0x4,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[3];
   private boolean jj_rescan = false;
@@ -708,7 +707,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -719,7 +718,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     jj_ntk = -1;
     jj_lookingAhead = false;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -729,7 +728,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -739,7 +738,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -856,7 +855,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 28; i++) {
+    for (int i = 0; i < 27; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/QueryParser.jj b/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
index 1dfdfcb..58adcd9 100644
--- a/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
+++ b/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
@@ -282,9 +282,9 @@ Query Term(String field) throws SyntaxError : {
     { q = handleBareTokenQuery(getField(field), term, fuzzySlop, prefix, wildcard, fuzzy, regexp); }
 
   | ( <RANGEIN_START> { startInc = true; } | <RANGEEX_START> )
-    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> )
-    [ <RANGE_TO> ]
-    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> )
+    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> | goop1=<RANGE_TO> )
+    ( <RANGE_TO> )
+    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> | goop2=<RANGE_TO> )
     ( <RANGEIN_END> { endInc = true; } | <RANGEEX_END> )
     [ <CARAT> boost=<NUMBER> ]
     {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/e11bc030/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
index d2244b1..0ba716d 100644
--- a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
+++ b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
@@ -342,6 +342,48 @@ public class TestRangeQuery extends SolrTestCaseJ4 {
       }
     }
   }
+  
+  public void testRangeQueryEndpointTO() throws Exception {
+    assertEquals("[to TO to]", QParser.getParser("[to TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[to TO TO]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO TO]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[to TO to]", QParser.getParser("[\"TO\" TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[\"TO\" TO TO]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[to TO xx]", QParser.getParser("[to TO xx]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO xx]", QParser.getParser("[\"TO\" TO xx]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO xx]", QParser.getParser("[TO TO xx]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO TO]", req("df", "text")).getQuery().toString("text"));
+  }
+
+  public void testRangeQueryRequiresTO() throws Exception {
+    assertEquals("{a TO b}", QParser.getParser("{A TO B}", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[a TO b}", QParser.getParser("[A TO B}", req("df", "text")).getQuery().toString("text"));
+    assertEquals("{a TO b]", QParser.getParser("{A TO B]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[a TO b]", QParser.getParser("[A TO B]", req("df", "text")).getQuery().toString("text"));
+
+    // " TO " is required between range endpoints
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A B]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A B]", req("df", "text")).getQuery());
+
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{TO B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[TO B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{TO B]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[TO B]", req("df", "text")).getQuery());
+
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A TO}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A TO}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A TO]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A TO]", req("df", "text")).getQuery());
+  }
 
   static boolean sameDocs(String msg, DocSet a, DocSet b) {
     DocIterator i = a.iterator();


[2/3] lucene-solr:branch_6x: LUCENE-7821: The classic and flexible query parsers, as well as Solr's 'lucene'/standard query parser, should require ' TO ' in range queries, and accept 'TO' as endpoints in range queries.

Posted by sa...@apache.org.
LUCENE-7821: The classic and flexible query parsers, as well as Solr's 'lucene'/standard query parser, should require ' TO ' in range queries, and accept 'TO' as endpoints in range queries.

 Conflicts:
	solr/CHANGES.txt


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/8633f49d
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/8633f49d
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/8633f49d

Branch: refs/heads/branch_6x
Commit: 8633f49dcd1c0237fe0d6e65ea8cacd193950038
Parents: ee70e1e
Author: Steve Rowe <sa...@apache.org>
Authored: Sun May 14 11:31:22 2017 -0400
Committer: Steve Rowe <sa...@apache.org>
Committed: Sun May 14 11:36:52 2017 -0400

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |  4 ++
 .../lucene/queryparser/classic/QueryParser.java | 47 +++++++++---------
 .../lucene/queryparser/classic/QueryParser.jj   |  6 +--
 .../standard/parser/StandardSyntaxParser.java   | 43 ++++++++---------
 .../standard/parser/StandardSyntaxParser.jj     |  6 +--
 .../queryparser/util/QueryParserTestBase.java   | 51 ++++++++++++++++++++
 solr/CHANGES.txt                                |  4 ++
 .../org/apache/solr/parser/QueryParser.java     | 47 +++++++++---------
 .../java/org/apache/solr/parser/QueryParser.jj  |  6 +--
 .../org/apache/solr/search/TestRangeQuery.java  | 42 ++++++++++++++++
 10 files changed, 177 insertions(+), 79 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index bbb082e..fea75a0 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -37,6 +37,10 @@ Bug Fixes
 * LUCENE-5365, LUCENE-7818: Fix incorrect condition in queryparser's
   QueryNodeOperation#logicalAnd().  (Olivier Binda, Amrit Sarkar,
   AppChecker via Uwe Schindler)
+  
+* LUCENE-7821: The classic and flexible query parsers, as well as Solr's
+ "lucene"/standard query parser, should require " TO " in range queries,
+  and accept "TO" as endpoints in range queries. (hossman, Steve Rowe)
 
 Improvements
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
index 2410cb0..db6291a 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
@@ -490,19 +490,15 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[16] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[17] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -510,8 +506,11 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[18] = jj_gen;
+        jj_la1[17] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -524,7 +523,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[19] = jj_gen;
+        jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -534,7 +533,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         ;
       }
       boolean startOpen=false;
@@ -566,7 +565,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
                                                         fuzzy=true;
             break;
           default:
-            jj_la1[21] = jj_gen;
+            jj_la1[20] = jj_gen;
             ;
           }
           break;
@@ -579,24 +578,24 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
             boost = jj_consume_token(NUMBER);
             break;
           default:
-            jj_la1[22] = jj_gen;
+            jj_la1[21] = jj_gen;
             ;
           }
           break;
         default:
-          jj_la1[23] = jj_gen;
+          jj_la1[22] = jj_gen;
           jj_consume_token(-1);
           throw new ParseException();
         }
         break;
       default:
-        jj_la1[24] = jj_gen;
+        jj_la1[23] = jj_gen;
         ;
       }
       q = handleQuotedTerm(field, term, fuzzySlop);
       break;
     default:
-      jj_la1[25] = jj_gen;
+      jj_la1[24] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -732,7 +731,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
   private boolean jj_lookingAhead = false;
   private boolean jj_semLA;
   private int jj_gen;
-  final private int[] jj_la1 = new int[26];
+  final private int[] jj_la1 = new int[25];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -740,10 +739,10 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x300,0x300,0x1c00,0x1c00,0xfda7c00,0xfda7f00,0xfda7f00,0x120000,0x40000,0xfda6000,0x9d22000,0x200000,0x40000,0x240000,0x240000,0x6000000,0x80000000,0x10000000,0x80000000,0x60000000,0x40000,0x200000,0x40000,0x240000,0x240000,0xfda2000,};
+      jj_la1_0 = new int[] {0x300,0x300,0x1c00,0x1c00,0xfda7c00,0xfda7f00,0xfda7f00,0x120000,0x40000,0xfda6000,0x9d22000,0x200000,0x40000,0x240000,0x240000,0x6000000,0x90000000,0x90000000,0x60000000,0x40000,0x200000,0x40000,0x240000,0x240000,0xfda2000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[3];
   private boolean jj_rescan = false;
@@ -755,7 +754,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -766,7 +765,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     jj_ntk = -1;
     jj_lookingAhead = false;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -776,7 +775,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -786,7 +785,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -903,7 +902,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 26; i++) {
+    for (int i = 0; i < 25; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
index 970a055..13a6d75 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
@@ -341,9 +341,9 @@ Query Term(String field) : {
     { q = handleBareTokenQuery(field, term, fuzzySlop, prefix, wildcard, fuzzy, regexp); }
 
   | ( <RANGEIN_START> { startInc = true; } | <RANGEEX_START> )
-    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> )
-    [ <RANGE_TO> ]
-    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> )
+    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> | goop1=<RANGE_TO> )
+    ( <RANGE_TO> )
+    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> | goop2=<RANGE_TO> )
     ( <RANGEIN_END> { endInc = true; } | <RANGEEX_END> )
     [ <CARAT> boost=<NUMBER> ]
     {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
index 8ba34a6..6008bcc 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
@@ -577,19 +577,15 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[19] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -597,8 +593,11 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -611,7 +610,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[21] = jj_gen;
+        jj_la1[20] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -621,7 +620,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[22] = jj_gen;
+        jj_la1[21] = jj_gen;
         ;
       }
           if (goop1.kind == RANGE_QUOTED) {
@@ -645,7 +644,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         fuzzySlop = jj_consume_token(FUZZY_SLOP);
         break;
       default:
-        jj_la1[23] = jj_gen;
+        jj_la1[22] = jj_gen;
         ;
       }
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
@@ -654,7 +653,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[24] = jj_gen;
+        jj_la1[23] = jj_gen;
         ;
       }
          int phraseSlop = 0;
@@ -672,7 +671,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
          }
       break;
     default:
-      jj_la1[25] = jj_gen;
+      jj_la1[24] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -831,7 +830,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
   private Token jj_scanpos, jj_lastpos;
   private int jj_la;
   private int jj_gen;
-  final private int[] jj_la1 = new int[26];
+  final private int[] jj_la1 = new int[25];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -839,10 +838,10 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x1c00,0x1c00,0x1ec03c00,0x200,0x100,0x18000,0x1e0000,0x10c00000,0x1f8000,0x18000,0x200000,0x1ec02000,0x1ec02000,0x12800000,0x1000000,0x1000000,0x200000,0xc000000,0x0,0x20000000,0x0,0xc0000000,0x200000,0x1000000,0x200000,0x1ec00000,};
+      jj_la1_0 = new int[] {0x1c00,0x1c00,0x1ec03c00,0x200,0x100,0x18000,0x1e0000,0x10c00000,0x1f8000,0x18000,0x200000,0x1ec02000,0x1ec02000,0x12800000,0x1000000,0x1000000,0x200000,0xc000000,0x20000000,0x20000000,0xc0000000,0x200000,0x1000000,0x200000,0x1ec00000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[2];
   private boolean jj_rescan = false;
@@ -854,7 +853,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -864,7 +863,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -874,7 +873,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -884,7 +883,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -1001,7 +1000,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 26; i++) {
+    for (int i = 0; i < 25; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
index b53bab3..8803618 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
@@ -445,9 +445,9 @@ QueryNode Term(CharSequence field) : {
        }
      }
      | ( ( <RANGEIN_START> {startInc=true;} | <RANGEEX_START> )
-         ( goop1=<RANGE_GOOP>|goop1=<RANGE_QUOTED> )
-         [ <RANGE_TO> ]
-         ( goop2=<RANGE_GOOP>|goop2=<RANGE_QUOTED> )
+         ( goop1=<RANGE_GOOP>|goop1=<RANGE_QUOTED>|goop1=<RANGE_TO> )
+         ( <RANGE_TO> )
+         ( goop2=<RANGE_GOOP>|goop2=<RANGE_QUOTED>|goop2=<RANGE_TO> )
          ( <RANGEIN_END> {endInc=true;} | <RANGEEX_END>))
        [ <CARAT> boost=<NUMBER> ]
         {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
index bbececb..5ab8fb2 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
@@ -41,6 +41,7 @@ import org.apache.lucene.index.Term;
 import org.apache.lucene.queryparser.classic.QueryParser;
 import org.apache.lucene.queryparser.classic.QueryParserBase;
 //import org.apache.lucene.queryparser.classic.QueryParserTokenManager;
+import org.apache.lucene.queryparser.classic.TestQueryParser;
 import org.apache.lucene.queryparser.flexible.standard.CommonQueryParserConfiguration;
 import org.apache.lucene.search.*;
 import org.apache.lucene.search.BooleanClause.Occur;
@@ -619,6 +620,56 @@ public abstract class QueryParserTestBase extends LuceneTestCase {
     assertQueryEquals("[\"*\" TO *]",null,"[\\* TO *]");
   }
 
+  public void testRangeQueryEndpointTO() throws Exception {
+    Analyzer a = new MockAnalyzer(random());
+    assertQueryEquals("[to TO to]", a, "[to TO to]");
+    assertQueryEquals("[to TO TO]", a, "[to TO to]");
+    assertQueryEquals("[TO TO to]", a, "[to TO to]");
+    assertQueryEquals("[TO TO TO]", a, "[to TO to]");
+
+    assertQueryEquals("[\"TO\" TO \"TO\"]", a, "[to TO to]");
+    assertQueryEquals("[\"TO\" TO TO]", a, "[to TO to]");
+    assertQueryEquals("[TO TO \"TO\"]", a, "[to TO to]");
+
+    assertQueryEquals("[to TO xx]", a, "[to TO xx]");
+    assertQueryEquals("[\"TO\" TO xx]", a, "[to TO xx]");
+    assertQueryEquals("[TO TO xx]", a, "[to TO xx]");
+
+    assertQueryEquals("[xx TO to]", a, "[xx TO to]");
+    assertQueryEquals("[xx TO \"TO\"]", a, "[xx TO to]");
+    assertQueryEquals("[xx TO TO]", a, "[xx TO to]");
+  }
+
+  public void testRangeQueryRequiresTO() throws Exception {
+    Analyzer a = new MockAnalyzer(random());
+
+    assertQueryEquals("{A TO B}", a, "{a TO b}");
+    assertQueryEquals("[A TO B}", a, "[a TO b}");
+    assertQueryEquals("{A TO B]", a, "{a TO b]");
+    assertQueryEquals("[A TO B]", a, "[a TO b]");
+
+    // " TO " is required between range endpoints
+
+    Class<? extends Throwable> exceptionClass = this instanceof TestQueryParser 
+        ? org.apache.lucene.queryparser.classic.ParseException.class
+        : org.apache.lucene.queryparser.flexible.standard.parser.ParseException.class;
+    
+    expectThrows(exceptionClass, () -> getQuery("{A B}"));
+    expectThrows(exceptionClass, () -> getQuery("[A B}"));
+    expectThrows(exceptionClass, () -> getQuery("{A B]"));
+    expectThrows(exceptionClass, () -> getQuery("[A B]"));
+
+    expectThrows(exceptionClass, () -> getQuery("{TO B}"));
+    expectThrows(exceptionClass, () -> getQuery("[TO B}"));
+    expectThrows(exceptionClass, () -> getQuery("{TO B]"));
+    expectThrows(exceptionClass, () -> getQuery("[TO B]"));
+
+    expectThrows(exceptionClass, () -> getQuery("{A TO}"));
+    expectThrows(exceptionClass, () -> getQuery("[A TO}"));
+    expectThrows(exceptionClass, () -> getQuery("{A TO]"));
+    expectThrows(exceptionClass, () -> getQuery("[A TO]"));
+  }
+
   private String escapeDateString(String s) {
     if (s.indexOf(" ") > -1) {
       return "\"" + s + "\"";

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 28b9d89..918e927 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -257,6 +257,10 @@ Bug Fixes
 * SOLR-9527: Improve distribution of replicas when restoring a collection
   (Hrishikesh Gadre, Stephen Lewis, Rohit, Varun Thacker)
 
+* LUCENE-7821: The classic and flexible query parsers, as well as Solr's
+ "lucene"/standard query parser, should require " TO " in range queries,
+  and accept "TO" as endpoints in range queries. (hossman, Steve Rowe)
+
 Other Changes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/solr/core/src/java/org/apache/solr/parser/QueryParser.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/QueryParser.java b/solr/core/src/java/org/apache/solr/parser/QueryParser.java
index 2b64b88..7a21847 100644
--- a/solr/core/src/java/org/apache/solr/parser/QueryParser.java
+++ b/solr/core/src/java/org/apache/solr/parser/QueryParser.java
@@ -441,19 +441,15 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[19] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -461,8 +457,11 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -475,7 +474,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[21] = jj_gen;
+        jj_la1[20] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -485,7 +484,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[22] = jj_gen;
+        jj_la1[21] = jj_gen;
         ;
       }
       boolean startOpen=false;
@@ -519,7 +518,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
                                                         fuzzy=true;
             break;
           default:
-            jj_la1[23] = jj_gen;
+            jj_la1[22] = jj_gen;
             ;
           }
           break;
@@ -532,24 +531,24 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
             boost = jj_consume_token(NUMBER);
             break;
           default:
-            jj_la1[24] = jj_gen;
+            jj_la1[23] = jj_gen;
             ;
           }
           break;
         default:
-          jj_la1[25] = jj_gen;
+          jj_la1[24] = jj_gen;
           jj_consume_token(-1);
           throw new ParseException();
         }
         break;
       default:
-        jj_la1[26] = jj_gen;
+        jj_la1[25] = jj_gen;
         ;
       }
       q = handleQuotedTerm(getField(field), term, fuzzySlop);
       break;
     default:
-      jj_la1[27] = jj_gen;
+      jj_la1[26] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -685,7 +684,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
   private boolean jj_lookingAhead = false;
   private boolean jj_semLA;
   private int jj_gen;
-  final private int[] jj_la1 = new int[28];
+  final private int[] jj_la1 = new int[27];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -693,10 +692,10 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x6000,0x6000,0x38000,0x38000,0xfb4f8000,0xfb4fe000,0xfb4fe000,0x2400000,0x800000,0x800000,0x800000,0xfb4c0000,0x3a440000,0x4000000,0x800000,0x4800000,0x4800000,0xc0000000,0x0,0x0,0x0,0x0,0x800000,0x4000000,0x800000,0x4800000,0x4800000,0xfb440000,};
+      jj_la1_0 = new int[] {0x6000,0x6000,0x38000,0x38000,0xfb4f8000,0xfb4fe000,0xfb4fe000,0x2400000,0x800000,0x800000,0x800000,0xfb4c0000,0x3a440000,0x4000000,0x800000,0x4800000,0x4800000,0xc0000000,0x0,0x0,0x0,0x800000,0x4000000,0x800000,0x4800000,0x4800000,0xfb440000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x7,0x7,0x7,0x0,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0xc0,0x8,0xc0,0x30,0x0,0x0,0x0,0x0,0x0,0x4,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x7,0x7,0x7,0x0,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0xc8,0xc8,0x30,0x0,0x0,0x0,0x0,0x0,0x4,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[3];
   private boolean jj_rescan = false;
@@ -708,7 +707,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -719,7 +718,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     jj_ntk = -1;
     jj_lookingAhead = false;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -729,7 +728,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -739,7 +738,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -856,7 +855,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 28; i++) {
+    for (int i = 0; i < 27; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/QueryParser.jj b/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
index c07b28d..9198881 100644
--- a/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
+++ b/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
@@ -282,9 +282,9 @@ Query Term(String field) throws SyntaxError : {
     { q = handleBareTokenQuery(getField(field), term, fuzzySlop, prefix, wildcard, fuzzy, regexp); }
 
   | ( <RANGEIN_START> { startInc = true; } | <RANGEEX_START> )
-    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> )
-    [ <RANGE_TO> ]
-    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> )
+    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> | goop1=<RANGE_TO> )
+    ( <RANGE_TO> )
+    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> | goop2=<RANGE_TO> )
     ( <RANGEIN_END> { endInc = true; } | <RANGEEX_END> )
     [ <CARAT> boost=<NUMBER> ]
     {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/8633f49d/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
index d2244b1..0ba716d 100644
--- a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
+++ b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
@@ -342,6 +342,48 @@ public class TestRangeQuery extends SolrTestCaseJ4 {
       }
     }
   }
+  
+  public void testRangeQueryEndpointTO() throws Exception {
+    assertEquals("[to TO to]", QParser.getParser("[to TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[to TO TO]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO TO]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[to TO to]", QParser.getParser("[\"TO\" TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[\"TO\" TO TO]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[to TO xx]", QParser.getParser("[to TO xx]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO xx]", QParser.getParser("[\"TO\" TO xx]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO xx]", QParser.getParser("[TO TO xx]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO TO]", req("df", "text")).getQuery().toString("text"));
+  }
+
+  public void testRangeQueryRequiresTO() throws Exception {
+    assertEquals("{a TO b}", QParser.getParser("{A TO B}", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[a TO b}", QParser.getParser("[A TO B}", req("df", "text")).getQuery().toString("text"));
+    assertEquals("{a TO b]", QParser.getParser("{A TO B]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[a TO b]", QParser.getParser("[A TO B]", req("df", "text")).getQuery().toString("text"));
+
+    // " TO " is required between range endpoints
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A B]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A B]", req("df", "text")).getQuery());
+
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{TO B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[TO B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{TO B]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[TO B]", req("df", "text")).getQuery());
+
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A TO}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A TO}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A TO]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A TO]", req("df", "text")).getQuery());
+  }
 
   static boolean sameDocs(String msg, DocSet a, DocSet b) {
     DocIterator i = a.iterator();


[3/3] lucene-solr:branch_6_6: LUCENE-7821: The classic and flexible query parsers, as well as Solr's 'lucene'/standard query parser, should require ' TO ' in range queries, and accept 'TO' as endpoints in range queries.

Posted by sa...@apache.org.
LUCENE-7821: The classic and flexible query parsers, as well as Solr's 'lucene'/standard query parser, should require ' TO ' in range queries, and accept 'TO' as endpoints in range queries.

 Conflicts:
	solr/CHANGES.txt


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/b172ceb1
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/b172ceb1
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/b172ceb1

Branch: refs/heads/branch_6_6
Commit: b172ceb143955bf34a6b16ab3557bf98844ac427
Parents: 4b93c62
Author: Steve Rowe <sa...@apache.org>
Authored: Sun May 14 11:31:22 2017 -0400
Committer: Steve Rowe <sa...@apache.org>
Committed: Sun May 14 11:37:31 2017 -0400

----------------------------------------------------------------------
 lucene/CHANGES.txt                              |  4 ++
 .../lucene/queryparser/classic/QueryParser.java | 47 +++++++++---------
 .../lucene/queryparser/classic/QueryParser.jj   |  6 +--
 .../standard/parser/StandardSyntaxParser.java   | 43 ++++++++---------
 .../standard/parser/StandardSyntaxParser.jj     |  6 +--
 .../queryparser/util/QueryParserTestBase.java   | 51 ++++++++++++++++++++
 solr/CHANGES.txt                                |  4 ++
 .../org/apache/solr/parser/QueryParser.java     | 47 +++++++++---------
 .../java/org/apache/solr/parser/QueryParser.jj  |  6 +--
 .../org/apache/solr/search/TestRangeQuery.java  | 42 ++++++++++++++++
 10 files changed, 177 insertions(+), 79 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 7e7c5ac..8c021eb 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -34,6 +34,10 @@ Bug Fixes
 * LUCENE-5365, LUCENE-7818: Fix incorrect condition in queryparser's
   QueryNodeOperation#logicalAnd().  (Olivier Binda, Amrit Sarkar,
   AppChecker via Uwe Schindler)
+  
+* LUCENE-7821: The classic and flexible query parsers, as well as Solr's
+ "lucene"/standard query parser, should require " TO " in range queries,
+  and accept "TO" as endpoints in range queries. (hossman, Steve Rowe)
 
 Improvements
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
index 2410cb0..db6291a 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.java
@@ -490,19 +490,15 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[16] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[17] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -510,8 +506,11 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[18] = jj_gen;
+        jj_la1[17] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -524,7 +523,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[19] = jj_gen;
+        jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -534,7 +533,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         ;
       }
       boolean startOpen=false;
@@ -566,7 +565,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
                                                         fuzzy=true;
             break;
           default:
-            jj_la1[21] = jj_gen;
+            jj_la1[20] = jj_gen;
             ;
           }
           break;
@@ -579,24 +578,24 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
             boost = jj_consume_token(NUMBER);
             break;
           default:
-            jj_la1[22] = jj_gen;
+            jj_la1[21] = jj_gen;
             ;
           }
           break;
         default:
-          jj_la1[23] = jj_gen;
+          jj_la1[22] = jj_gen;
           jj_consume_token(-1);
           throw new ParseException();
         }
         break;
       default:
-        jj_la1[24] = jj_gen;
+        jj_la1[23] = jj_gen;
         ;
       }
       q = handleQuotedTerm(field, term, fuzzySlop);
       break;
     default:
-      jj_la1[25] = jj_gen;
+      jj_la1[24] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -732,7 +731,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
   private boolean jj_lookingAhead = false;
   private boolean jj_semLA;
   private int jj_gen;
-  final private int[] jj_la1 = new int[26];
+  final private int[] jj_la1 = new int[25];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -740,10 +739,10 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x300,0x300,0x1c00,0x1c00,0xfda7c00,0xfda7f00,0xfda7f00,0x120000,0x40000,0xfda6000,0x9d22000,0x200000,0x40000,0x240000,0x240000,0x6000000,0x80000000,0x10000000,0x80000000,0x60000000,0x40000,0x200000,0x40000,0x240000,0x240000,0xfda2000,};
+      jj_la1_0 = new int[] {0x300,0x300,0x1c00,0x1c00,0xfda7c00,0xfda7f00,0xfda7f00,0x120000,0x40000,0xfda6000,0x9d22000,0x200000,0x40000,0x240000,0x240000,0x6000000,0x90000000,0x90000000,0x60000000,0x40000,0x200000,0x40000,0x240000,0x240000,0xfda2000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1,0x1,0x0,0x0,0x0,0x0,0x0,0x0,0x0,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[3];
   private boolean jj_rescan = false;
@@ -755,7 +754,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -766,7 +765,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     jj_ntk = -1;
     jj_lookingAhead = false;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -776,7 +775,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -786,7 +785,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -903,7 +902,7 @@ public class QueryParser extends QueryParserBase implements QueryParserConstants
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 26; i++) {
+    for (int i = 0; i < 25; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
index 970a055..13a6d75 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/classic/QueryParser.jj
@@ -341,9 +341,9 @@ Query Term(String field) : {
     { q = handleBareTokenQuery(field, term, fuzzySlop, prefix, wildcard, fuzzy, regexp); }
 
   | ( <RANGEIN_START> { startInc = true; } | <RANGEEX_START> )
-    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> )
-    [ <RANGE_TO> ]
-    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> )
+    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> | goop1=<RANGE_TO> )
+    ( <RANGE_TO> )
+    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> | goop2=<RANGE_TO> )
     ( <RANGEIN_END> { endInc = true; } | <RANGEEX_END> )
     [ <CARAT> boost=<NUMBER> ]
     {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
index 8ba34a6..6008bcc 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.java
@@ -577,19 +577,15 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[19] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -597,8 +593,11 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -611,7 +610,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[21] = jj_gen;
+        jj_la1[20] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -621,7 +620,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[22] = jj_gen;
+        jj_la1[21] = jj_gen;
         ;
       }
           if (goop1.kind == RANGE_QUOTED) {
@@ -645,7 +644,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         fuzzySlop = jj_consume_token(FUZZY_SLOP);
         break;
       default:
-        jj_la1[23] = jj_gen;
+        jj_la1[22] = jj_gen;
         ;
       }
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
@@ -654,7 +653,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[24] = jj_gen;
+        jj_la1[23] = jj_gen;
         ;
       }
          int phraseSlop = 0;
@@ -672,7 +671,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
          }
       break;
     default:
-      jj_la1[25] = jj_gen;
+      jj_la1[24] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -831,7 +830,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
   private Token jj_scanpos, jj_lastpos;
   private int jj_la;
   private int jj_gen;
-  final private int[] jj_la1 = new int[26];
+  final private int[] jj_la1 = new int[25];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -839,10 +838,10 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x1c00,0x1c00,0x1ec03c00,0x200,0x100,0x18000,0x1e0000,0x10c00000,0x1f8000,0x18000,0x200000,0x1ec02000,0x1ec02000,0x12800000,0x1000000,0x1000000,0x200000,0xc000000,0x0,0x20000000,0x0,0xc0000000,0x200000,0x1000000,0x200000,0x1ec00000,};
+      jj_la1_0 = new int[] {0x1c00,0x1c00,0x1ec03c00,0x200,0x100,0x18000,0x1e0000,0x10c00000,0x1f8000,0x18000,0x200000,0x1ec02000,0x1ec02000,0x12800000,0x1000000,0x1000000,0x200000,0xc000000,0x20000000,0x20000000,0xc0000000,0x200000,0x1000000,0x200000,0x1ec00000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x0,0x3,0x0,0x0,0x0,0x0,0x0,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x3,0x3,0x0,0x0,0x0,0x0,0x0,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[2];
   private boolean jj_rescan = false;
@@ -854,7 +853,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -864,7 +863,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -874,7 +873,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -884,7 +883,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 26; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 25; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -1001,7 +1000,7 @@ public class StandardSyntaxParser implements SyntaxParser, StandardSyntaxParserC
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 26; i++) {
+    for (int i = 0; i < 25; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
index b53bab3..8803618 100644
--- a/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
+++ b/lucene/queryparser/src/java/org/apache/lucene/queryparser/flexible/standard/parser/StandardSyntaxParser.jj
@@ -445,9 +445,9 @@ QueryNode Term(CharSequence field) : {
        }
      }
      | ( ( <RANGEIN_START> {startInc=true;} | <RANGEEX_START> )
-         ( goop1=<RANGE_GOOP>|goop1=<RANGE_QUOTED> )
-         [ <RANGE_TO> ]
-         ( goop2=<RANGE_GOOP>|goop2=<RANGE_QUOTED> )
+         ( goop1=<RANGE_GOOP>|goop1=<RANGE_QUOTED>|goop1=<RANGE_TO> )
+         ( <RANGE_TO> )
+         ( goop2=<RANGE_GOOP>|goop2=<RANGE_QUOTED>|goop2=<RANGE_TO> )
          ( <RANGEIN_END> {endInc=true;} | <RANGEEX_END>))
        [ <CARAT> boost=<NUMBER> ]
         {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
----------------------------------------------------------------------
diff --git a/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java b/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
index bbececb..5ab8fb2 100644
--- a/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
+++ b/lucene/queryparser/src/test/org/apache/lucene/queryparser/util/QueryParserTestBase.java
@@ -41,6 +41,7 @@ import org.apache.lucene.index.Term;
 import org.apache.lucene.queryparser.classic.QueryParser;
 import org.apache.lucene.queryparser.classic.QueryParserBase;
 //import org.apache.lucene.queryparser.classic.QueryParserTokenManager;
+import org.apache.lucene.queryparser.classic.TestQueryParser;
 import org.apache.lucene.queryparser.flexible.standard.CommonQueryParserConfiguration;
 import org.apache.lucene.search.*;
 import org.apache.lucene.search.BooleanClause.Occur;
@@ -619,6 +620,56 @@ public abstract class QueryParserTestBase extends LuceneTestCase {
     assertQueryEquals("[\"*\" TO *]",null,"[\\* TO *]");
   }
 
+  public void testRangeQueryEndpointTO() throws Exception {
+    Analyzer a = new MockAnalyzer(random());
+    assertQueryEquals("[to TO to]", a, "[to TO to]");
+    assertQueryEquals("[to TO TO]", a, "[to TO to]");
+    assertQueryEquals("[TO TO to]", a, "[to TO to]");
+    assertQueryEquals("[TO TO TO]", a, "[to TO to]");
+
+    assertQueryEquals("[\"TO\" TO \"TO\"]", a, "[to TO to]");
+    assertQueryEquals("[\"TO\" TO TO]", a, "[to TO to]");
+    assertQueryEquals("[TO TO \"TO\"]", a, "[to TO to]");
+
+    assertQueryEquals("[to TO xx]", a, "[to TO xx]");
+    assertQueryEquals("[\"TO\" TO xx]", a, "[to TO xx]");
+    assertQueryEquals("[TO TO xx]", a, "[to TO xx]");
+
+    assertQueryEquals("[xx TO to]", a, "[xx TO to]");
+    assertQueryEquals("[xx TO \"TO\"]", a, "[xx TO to]");
+    assertQueryEquals("[xx TO TO]", a, "[xx TO to]");
+  }
+
+  public void testRangeQueryRequiresTO() throws Exception {
+    Analyzer a = new MockAnalyzer(random());
+
+    assertQueryEquals("{A TO B}", a, "{a TO b}");
+    assertQueryEquals("[A TO B}", a, "[a TO b}");
+    assertQueryEquals("{A TO B]", a, "{a TO b]");
+    assertQueryEquals("[A TO B]", a, "[a TO b]");
+
+    // " TO " is required between range endpoints
+
+    Class<? extends Throwable> exceptionClass = this instanceof TestQueryParser 
+        ? org.apache.lucene.queryparser.classic.ParseException.class
+        : org.apache.lucene.queryparser.flexible.standard.parser.ParseException.class;
+    
+    expectThrows(exceptionClass, () -> getQuery("{A B}"));
+    expectThrows(exceptionClass, () -> getQuery("[A B}"));
+    expectThrows(exceptionClass, () -> getQuery("{A B]"));
+    expectThrows(exceptionClass, () -> getQuery("[A B]"));
+
+    expectThrows(exceptionClass, () -> getQuery("{TO B}"));
+    expectThrows(exceptionClass, () -> getQuery("[TO B}"));
+    expectThrows(exceptionClass, () -> getQuery("{TO B]"));
+    expectThrows(exceptionClass, () -> getQuery("[TO B]"));
+
+    expectThrows(exceptionClass, () -> getQuery("{A TO}"));
+    expectThrows(exceptionClass, () -> getQuery("[A TO}"));
+    expectThrows(exceptionClass, () -> getQuery("{A TO]"));
+    expectThrows(exceptionClass, () -> getQuery("[A TO]"));
+  }
+
   private String escapeDateString(String s) {
     if (s.indexOf(" ") > -1) {
       return "\"" + s + "\"";

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index dee3df5..d61a185 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -232,6 +232,10 @@ Bug Fixes
 * SOLR-9527: Improve distribution of replicas when restoring a collection
   (Hrishikesh Gadre, Stephen Lewis, Rohit, Varun Thacker)
 
+* LUCENE-7821: The classic and flexible query parsers, as well as Solr's
+ "lucene"/standard query parser, should require " TO " in range queries,
+  and accept "TO" as endpoints in range queries. (hossman, Steve Rowe)
+
 Other Changes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/solr/core/src/java/org/apache/solr/parser/QueryParser.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/QueryParser.java b/solr/core/src/java/org/apache/solr/parser/QueryParser.java
index 2b64b88..7a21847 100644
--- a/solr/core/src/java/org/apache/solr/parser/QueryParser.java
+++ b/solr/core/src/java/org/apache/solr/parser/QueryParser.java
@@ -441,19 +441,15 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       case RANGE_QUOTED:
         goop1 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop1 = jj_consume_token(RANGE_TO);
+        break;
       default:
         jj_la1[18] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
-      switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
-      case RANGE_TO:
-        jj_consume_token(RANGE_TO);
-        break;
-      default:
-        jj_la1[19] = jj_gen;
-        ;
-      }
+      jj_consume_token(RANGE_TO);
       switch ((jj_ntk==-1)?jj_ntk():jj_ntk) {
       case RANGE_GOOP:
         goop2 = jj_consume_token(RANGE_GOOP);
@@ -461,8 +457,11 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       case RANGE_QUOTED:
         goop2 = jj_consume_token(RANGE_QUOTED);
         break;
+      case RANGE_TO:
+        goop2 = jj_consume_token(RANGE_TO);
+        break;
       default:
-        jj_la1[20] = jj_gen;
+        jj_la1[19] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -475,7 +474,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
         jj_consume_token(RANGEEX_END);
         break;
       default:
-        jj_la1[21] = jj_gen;
+        jj_la1[20] = jj_gen;
         jj_consume_token(-1);
         throw new ParseException();
       }
@@ -485,7 +484,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
         boost = jj_consume_token(NUMBER);
         break;
       default:
-        jj_la1[22] = jj_gen;
+        jj_la1[21] = jj_gen;
         ;
       }
       boolean startOpen=false;
@@ -519,7 +518,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
                                                         fuzzy=true;
             break;
           default:
-            jj_la1[23] = jj_gen;
+            jj_la1[22] = jj_gen;
             ;
           }
           break;
@@ -532,24 +531,24 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
             boost = jj_consume_token(NUMBER);
             break;
           default:
-            jj_la1[24] = jj_gen;
+            jj_la1[23] = jj_gen;
             ;
           }
           break;
         default:
-          jj_la1[25] = jj_gen;
+          jj_la1[24] = jj_gen;
           jj_consume_token(-1);
           throw new ParseException();
         }
         break;
       default:
-        jj_la1[26] = jj_gen;
+        jj_la1[25] = jj_gen;
         ;
       }
       q = handleQuotedTerm(getField(field), term, fuzzySlop);
       break;
     default:
-      jj_la1[27] = jj_gen;
+      jj_la1[26] = jj_gen;
       jj_consume_token(-1);
       throw new ParseException();
     }
@@ -685,7 +684,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
   private boolean jj_lookingAhead = false;
   private boolean jj_semLA;
   private int jj_gen;
-  final private int[] jj_la1 = new int[28];
+  final private int[] jj_la1 = new int[27];
   static private int[] jj_la1_0;
   static private int[] jj_la1_1;
   static {
@@ -693,10 +692,10 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       jj_la1_init_1();
    }
    private static void jj_la1_init_0() {
-      jj_la1_0 = new int[] {0x6000,0x6000,0x38000,0x38000,0xfb4f8000,0xfb4fe000,0xfb4fe000,0x2400000,0x800000,0x800000,0x800000,0xfb4c0000,0x3a440000,0x4000000,0x800000,0x4800000,0x4800000,0xc0000000,0x0,0x0,0x0,0x0,0x800000,0x4000000,0x800000,0x4800000,0x4800000,0xfb440000,};
+      jj_la1_0 = new int[] {0x6000,0x6000,0x38000,0x38000,0xfb4f8000,0xfb4fe000,0xfb4fe000,0x2400000,0x800000,0x800000,0x800000,0xfb4c0000,0x3a440000,0x4000000,0x800000,0x4800000,0x4800000,0xc0000000,0x0,0x0,0x0,0x800000,0x4000000,0x800000,0x4800000,0x4800000,0xfb440000,};
    }
    private static void jj_la1_init_1() {
-      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x7,0x7,0x7,0x0,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0xc0,0x8,0xc0,0x30,0x0,0x0,0x0,0x0,0x0,0x4,};
+      jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x7,0x7,0x7,0x0,0x0,0x0,0x0,0x7,0x4,0x0,0x0,0x0,0x0,0x0,0xc8,0xc8,0x30,0x0,0x0,0x0,0x0,0x0,0x4,};
    }
   final private JJCalls[] jj_2_rtns = new JJCalls[3];
   private boolean jj_rescan = false;
@@ -708,7 +707,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -719,7 +718,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     jj_ntk = -1;
     jj_lookingAhead = false;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -729,7 +728,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -739,7 +738,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
     token = new Token();
     jj_ntk = -1;
     jj_gen = 0;
-    for (int i = 0; i < 28; i++) jj_la1[i] = -1;
+    for (int i = 0; i < 27; i++) jj_la1[i] = -1;
     for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls();
   }
 
@@ -856,7 +855,7 @@ public class QueryParser extends SolrQueryParserBase implements QueryParserConst
       la1tokens[jj_kind] = true;
       jj_kind = -1;
     }
-    for (int i = 0; i < 28; i++) {
+    for (int i = 0; i < 27; i++) {
       if (jj_la1[i] == jj_gen) {
         for (int j = 0; j < 32; j++) {
           if ((jj_la1_0[i] & (1<<j)) != 0) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/parser/QueryParser.jj b/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
index c07b28d..9198881 100644
--- a/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
+++ b/solr/core/src/java/org/apache/solr/parser/QueryParser.jj
@@ -282,9 +282,9 @@ Query Term(String field) throws SyntaxError : {
     { q = handleBareTokenQuery(getField(field), term, fuzzySlop, prefix, wildcard, fuzzy, regexp); }
 
   | ( <RANGEIN_START> { startInc = true; } | <RANGEEX_START> )
-    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> )
-    [ <RANGE_TO> ]
-    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> )
+    ( goop1=<RANGE_GOOP> | goop1=<RANGE_QUOTED> | goop1=<RANGE_TO> )
+    ( <RANGE_TO> )
+    ( goop2=<RANGE_GOOP> | goop2=<RANGE_QUOTED> | goop2=<RANGE_TO> )
     ( <RANGEIN_END> { endInc = true; } | <RANGEEX_END> )
     [ <CARAT> boost=<NUMBER> ]
     {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b172ceb1/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
index d2244b1..0ba716d 100644
--- a/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
+++ b/solr/core/src/test/org/apache/solr/search/TestRangeQuery.java
@@ -342,6 +342,48 @@ public class TestRangeQuery extends SolrTestCaseJ4 {
       }
     }
   }
+  
+  public void testRangeQueryEndpointTO() throws Exception {
+    assertEquals("[to TO to]", QParser.getParser("[to TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[to TO TO]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO TO]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[to TO to]", QParser.getParser("[\"TO\" TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[\"TO\" TO TO]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO to]", QParser.getParser("[TO TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[to TO xx]", QParser.getParser("[to TO xx]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO xx]", QParser.getParser("[\"TO\" TO xx]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[to TO xx]", QParser.getParser("[TO TO xx]", req("df", "text")).getQuery().toString("text"));
+
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO to]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO \"TO\"]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[xx TO to]", QParser.getParser("[xx TO TO]", req("df", "text")).getQuery().toString("text"));
+  }
+
+  public void testRangeQueryRequiresTO() throws Exception {
+    assertEquals("{a TO b}", QParser.getParser("{A TO B}", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[a TO b}", QParser.getParser("[A TO B}", req("df", "text")).getQuery().toString("text"));
+    assertEquals("{a TO b]", QParser.getParser("{A TO B]", req("df", "text")).getQuery().toString("text"));
+    assertEquals("[a TO b]", QParser.getParser("[A TO B]", req("df", "text")).getQuery().toString("text"));
+
+    // " TO " is required between range endpoints
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A B]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A B]", req("df", "text")).getQuery());
+
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{TO B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[TO B}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{TO B]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[TO B]", req("df", "text")).getQuery());
+
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A TO}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A TO}", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("{A TO]", req("df", "text")).getQuery());
+    expectThrows(SyntaxError.class, () -> QParser.getParser("[A TO]", req("df", "text")).getQuery());
+  }
 
   static boolean sameDocs(String msg, DocSet a, DocSet b) {
     DocIterator i = a.iterator();