Index: src/java/org/apache/nutch/analysis/NutchAnalysis.jj
===================================================================
--- src/java/org/apache/nutch/analysis/NutchAnalysis.jj	(revision 8)
+++ src/java/org/apache/nutch/analysis/NutchAnalysis.jj	(working copy)
@@ -84,7 +84,11 @@
           queryString, (analyzer != null) ? analyzer : new NutchDocumentAnalyzer(conf));
     parser.queryString = queryString;
     parser.queryFilters = new QueryFilters(conf);
-    return parser.parse(conf);
+    try {
+      return parser.parse(conf);
+    } catch (ParseException pe) {
+      throw new IOException("Parse exception: " + pe);
+    }
   }
 
   /** For debugging. */
@@ -112,8 +116,9 @@
 
 TOKEN : {					  // token regular expressions
 
+<QOR: "OR" >
   // basic word -- lowercase it
-<WORD: ((<LETTER>|<DIGIT>|<WORD_PUNCT>)+ | <IRREGULAR_WORD>)>
+| <WORD: ((<LETTER>|<DIGIT>|<WORD_PUNCT>)+ | <IRREGULAR_WORD>)>
   { matchedToken.image = matchedToken.image.toLowerCase(); }
 
   // special handling for acronyms: U.S.A., I.B.M., etc: dots are removed
@@ -203,20 +208,23 @@
   String field;
   boolean stop;
   boolean prohibited;
+  boolean required;
 
 }
 {
   nonOpOrTerm()                                   // skip noise
   (
-    { stop=true; prohibited=false; field = Clause.DEFAULT_FIELD; }
+    { stop=true; prohibited=false; required=true; field = Clause.DEFAULT_FIELD; }
 
                                                   // optional + or - operator
-    ( <PLUS> {stop=false;} | (<MINUS> { stop=false;prohibited=true; } ))?
+    (<PLUS> {stop=false;required=true;} | (<MINUS> { stop=false;prohibited=true; } ))?
 
                                                   // optional field spec.
     ( LOOKAHEAD(<WORD><COLON>(phrase(field)|compound(field)))
       token=<WORD> <COLON> { field = token.image; } )?
-
+                                                  // optional disjunction
+    (<QOR> {required=false;})?
+    
     ( terms=phrase(field) {stop=false;} |         // quoted terms or
       terms=compound(field))                      // single or compound term
 
@@ -233,8 +241,10 @@
       } else {
         if (prohibited)
           query.addProhibitedPhrase(array, field);
+        else if (required)
+          query.addRequiredPhrase(array, field);
         else
-          query.addRequiredPhrase(array, field);
+          query.addNormalPhrase(array, field);
       }
     }
   )*
@@ -243,7 +253,7 @@
 
 }
 
-/** Parse an explcitly quoted phrase query.  Note that this may return a single
+/** Parse an explicitly quoted phrase query.  Note that this may return a single
  * term, a trivial phrase.*/
 ArrayList phrase(String field) :
 {
@@ -277,7 +287,7 @@
 
 /** Parse a compound term that is interpreted as an implicit phrase query.
  * Compounds are a sequence of terms separated by infix characters.  Note that
- * htis may return a single term, a trivial compound. */
+ * this may return a single term, a trivial compound. */
 ArrayList compound(String field) :
 {
   int start;
@@ -348,13 +358,19 @@
   <WHITE> | infix()
 }
 
+void disjunction() :
+{}
+{
+  <QOR> nonOpOrTerm()
+}
+
 void nonTermOrEOF() :
 {}
 {
   nonTerm() | <EOF>
 }
 
-/** Parse anything but a term or an operator (plur or minus or quote). */
+/** Parse anything but a term or an operator (plus or minus or quote). */
 void nonOpOrTerm() :
 {}
 {
Index: src/java/org/apache/nutch/searcher/Query.java
===================================================================
--- src/java/org/apache/nutch/searcher/Query.java	(revision 8)
+++ src/java/org/apache/nutch/searcher/Query.java	(working copy)
@@ -134,9 +134,9 @@
 
     public String toString() {
       StringBuffer buffer = new StringBuffer();
-//       if (isRequired)
-//         buffer.append("+");
-//       else
+       if (isRequired)
+         buffer.append("+");
+       else
       if (isProhibited)
         buffer.append ("-");
 
@@ -318,6 +318,16 @@
     clauses.add(new Clause(new Term(term), field, true, false, this.conf));
   }
 
+  /** Add a normal term in the default field. */
+  public void addNormalTerm(String term) {
+    addNormalTerm(term, Clause.DEFAULT_FIELD);
+  }
+
+  /** Add a required term in a specified field. */
+  public void addNormalTerm(String term, String field) {
+    clauses.add(new Clause(new Term(term), field, false, false, this.conf));
+  }
+
   /** Add a prohibited term in the default field. */
   public void addProhibitedTerm(String term) {
     addProhibitedTerm(term, Clause.DEFAULT_FIELD);
@@ -343,6 +353,21 @@
     }
   }
 
+  /** Add a normal phrase in the default field. */
+  public void addNormalPhrase(String[] terms) {
+    addNormalPhrase(terms, Clause.DEFAULT_FIELD);
+  }
+
+  /** Add a normal phrase in the specified field. */
+  public void addNormalPhrase(String[] terms, String field) {
+    if (terms.length == 0) {                      // ignore empty phrase
+    } else if (terms.length == 1) {
+      addNormalTerm(terms[0], field);           // optimize to term query
+    } else {
+      clauses.add(new Clause(new Phrase(terms), field, false, false, this.conf));
+    }
+  }
+
   /** Add a prohibited phrase in the default field. */
   public void addProhibitedPhrase(String[] terms) {
     addProhibitedPhrase(terms, Clause.DEFAULT_FIELD);
