lucene-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Chris Hostetter <>
Subject Re: "Advanced" query language
Date Wed, 04 Jan 2006 09:29:06 GMT

: I'd still like to keep the parser core reasonably generic (ie
: java.lang.Object rather than Query or Filter) because I can see it being
: used for instantiating many different types of objects eg requests for
: GroupBy , highlighting,  indexing, testing etc.
: As for your type-safety requirement, one approach I considered which
: supports an extensible list of types with type safety was to use a
: reflection-based API like this:

Interesting approach.  I'm generally leary of reflection, but it's just my
prejudice, i have no legitimate objection to the idea.

: However, I'm not sure that that really buys us a lot more than the
: existing approach where java.lang.Object is used in ObjectConsumer and
: the calling ObjectBuilder has to explicitly cast a java.lang.Object to
: the required type. We still don't find out until runtime that something
: doesn't work.

well, ultimately what we're doing is parsing text supplied by the user,
there's really no way to know for certain if it's going to work untill
runtime anyway -- my main concern was in eliminating ClassCastExceptions,
without requiring every ObjectBuilder to do their own little
instanceof/exception/cast dance...

      Object o = parser.processChild(...);
      if (! o instanceof FooBar) {
         throw new ParseException("child element is not a FooBar", ...);
      FooBar f = (FooBar)obj;
      o = parser.processChild(...);
      if (! o instanceof Query) {
         throw new ParseException("child element is not a Query", ...);
      Query q = (Query)obj

That said, I think it's possible to eliminate some suprises at compile
time using a variation of the approach i described before (where a
seperate interface/register/process method is used for each type).  If the
ObjectBuilder's have init methods that take in the specific interface of
the parser they expect to deal with, then at compile time you'll at least
know if you're trying to deal with a parser that can only handle Query and
Filter objects and an ObjectHandler for building test indexes that's
expecting it's child nodes to be Documents.

Unfortunately I can't really picture in my mind how it could work without
having a buttload of marker interfaces and methods on the parsers.

Perhaps some logic inversion is in order? ...

          For the record: it's very late, and i'm totally
          winging this off the cuff, so forgive me if it makes
          no sense what so ever.

...what if instead of registering ObjectBuilders with the parser, the
ObjectBuilders are explicitly registered with eachother in a parent child
relationship -- possibly with wild carded names for "any child"; and the
"parser" just reads the InputStream and produced an intermediate
representation (either generic SAX like events or generic dom like
objects) and the user then interacts with the ObjectBuilder of the
outermost type they expect.

The interfaces the ObjectBuilders impliment can be organized in a
hierarchy that mirrors the hierarchy of the obejcts they produce, and
generic ObjectBuilder classes for high level interfaces can be made to
"shortcut" common sets of Builders (ie: all supported Query types)

Something like...

public interface FilterObjectBuilder extends ObjectBuilder {
   public Filter process(Node n);
public class FOB impliments FilterObjectBuilder {
   public FOB(/* no children allowed */) { ... }
   public Filter process(Node n) { ... }
public interface QueryObjectBuilder extends ObjectBuilder {
   public Query process(Node n) throws UnexpectedNodeNameException;
public interface TermQueryObjectBuilder extends QueryObjectBuilder;
public class TQOB impliments TermQueryObjectBuilder {
   public TQOB(/* no children allowed */) { ... }
   public Query process(Node n) { ... }
public interface FilterQueryObjectBuilder extends QueryObjectBuilder;
public class FQOB impliments FilterQueryObjectBuilder {
   public FQOB(FilterObjectBuilder fkid, QueryObjectBuilder qkid) { ... }
   public Query process(Node n) { ... }
public interface BooleanQueryObjectBuilder extends QueryObjectBuilder;
public class BQOB impliments BooleanQueryObjectBuilder {
   public BQOB(BooleanClauseObjectBuilder childBuilder) { ... }
   public Query process(Node n) { ... }
public interfae BooleanClauseObjectBuilder extends ObjectBuilder {
   public BooleanClause process(Node n);
public class BCOB impliments BooleanClauseObjectBuilder {
   public BCOB(QueryObjectBuilder childBuilder) { ... }
   public BooleanClause process(Node n) { ... }
public class QOB impliments QueryObjectBuilder {
   public QOB() { ... }
   /** adds a node name => QueryObjectBuilder that can be delegated to */
   public void addDelegate(String name, QueryObjectBuilder builder) {...}
   /** throws exception if n.getName() isn't a known delegate */
   public Query process(Node n) throws UnexpectedNodeNameException { ... }
   FilterObjectBuilder f = new FOB();
   QueryObjectBuilder q = new QOB();
   q.addDelegate(new BQOB(new BCOB(q)));
   q.addDelegate(new FQOB(f, q));
   q.addDelegate(new TQOB());
   /* how people get a Query object from some XML... */
   Node n = parser.parse(myXmlInputStream);
   Query myQuery = q.process(n);
   /* how people get a Filter object from some XML... */
   Node n = parser.parse(myXmlInputStream);
   Filter myFilter = f.process(n);

...even better, people could subclass the Parser, and put those
constructer/addDelegate calls in their parser's constructor, and
expose new type specific "parse" methods.

(NOTE: I've glossed over the other issues i mentioned in my earlier email
about passing up/down state, and being able "decorate" existing
ObjectBuilder's so things like <BooleanClause> could be
replaced with clause="required" in any sub tag of a <BooleanQuery> ... but
i *think* all of those things could still be done with an approach like


To unsubscribe, e-mail:
For additional commands, e-mail:

View raw message