package lux.query; import java.util.ArrayList; import lux.index.IndexConfiguration; import lux.xml.QName; import lux.xpath.AbstractExpression; import lux.xpath.LiteralExpression; import lux.xpath.Sequence; import lux.xquery.AttributeConstructor; import lux.xquery.ElementConstructor; import org.apache.lucene.search.BooleanClause.Occur; public class BooleanPQuery extends ParseableQuery { public static final QName BOOLEAN_QUERY_QNAME = new QName("BooleanQuery"); public static final QName CLAUSE_QNAME = new QName("Clause"); public static final LiteralExpression OCCURS_ATT_NAME = new LiteralExpression("occurs"); public static final AttributeConstructor MUST_OCCUR_ATT = new AttributeConstructor (OCCURS_ATT_NAME, new LiteralExpression ("must")); public static final AttributeConstructor SHOULD_OCCUR_ATT = new AttributeConstructor (OCCURS_ATT_NAME, new LiteralExpression ("should")); public static final AttributeConstructor MUST_NOT_OCCUR_ATT = new AttributeConstructor (OCCURS_ATT_NAME, new LiteralExpression ("mustNot")); private Clause clauses[]; public BooleanPQuery (Clause ... clauses) { setClauses (clauses); } public BooleanPQuery (Occur occur, ParseableQuery ... queries) { Clause[] cl= new Clause[queries.length]; int i = 0; for (ParseableQuery query : queries) { cl[i++] = new Clause(query, occur); } setClauses (cl); } private void setClauses (Clause[] clauses) { if (clauses.length == 0) { this.clauses = clauses; return; } Occur oc = clauses[0].getOccur(); // We assume all the clauses have the same occur // otherwise possibly merge the clauses if some of them are BooleanPQuery ArrayList<Clause> cl = new ArrayList<Clause> (); RangePQuery rangeQuery = null; for (Clause clause : clauses) { ParseableQuery query = clause.getQuery(); if (query instanceof BooleanPQuery) { BooleanPQuery bq = (BooleanPQuery) query; if (bq.getOccur() == oc) { // same occur; let's merge for (Clause subclause : bq.getClauses()) { cl.add (subclause); } continue; } } else if (query instanceof RangePQuery) { RangePQuery rquery = (RangePQuery) query; if (rangeQuery == null) { rangeQuery = rquery; } else { if (rangeQuery.intersect (rquery)) { continue; } else { // assume similar fields are adjacent? rangeQuery = rquery; } } } else if (query instanceof MatchAllPQuery && oc == Occur.MUST) { continue; } // no merging possible cl.add (clause); } this.clauses = cl.toArray(new Clause[cl.size()]); } public Occur getOccur () { return clauses.length > 0 ? clauses[0].occur : Occur.SHOULD; } public Clause[] getClauses() { return clauses; } @Override public ElementConstructor toXmlNode(String field, IndexConfiguration config) { if (clauses.length == 1 && clauses[0].occur == Occur.MUST) { return clauses[0].getQuery().toXmlNode(field, config); } AbstractExpression[] clauseExprs = new AbstractExpression[clauses.length]; int i = 0; for (Clause clause : clauses) { AbstractExpression q = clause.getQuery().toXmlNode(field, config); AttributeConstructor occurAtt = null; if (clause.occur == Occur.MUST) { occurAtt = MUST_OCCUR_ATT; } else if (clause.occur == Occur.SHOULD) { occurAtt = SHOULD_OCCUR_ATT; } else if (clause.occur == Occur.MUST_NOT) { occurAtt = MUST_NOT_OCCUR_ATT; } clauseExprs[i++] = new ElementConstructor(CLAUSE_QNAME, q, occurAtt); } return new ElementConstructor(BOOLEAN_QUERY_QNAME, new Sequence(clauseExprs)); } public static class Clause { private final Occur occur; private final ParseableQuery query; public Clause (ParseableQuery query, Occur occur) { this.occur = occur; this.query = query; } public Occur getOccur () { return occur; } public String getOccurAttribute() { switch (occur) { case MUST: return "must"; case SHOULD: return "should"; case MUST_NOT: return "mustNot"; default: return ""; } } public ParseableQuery getQuery() { return query; } } // derived from BooleanQuery.toString() @Override public String toQueryString(String field, IndexConfiguration config) { StringBuilder buf = new StringBuilder(); for (int i = 0 ; i < clauses.length; i++) { Clause c = clauses[i]; if (c.occur == Occur.MUST_NOT) { buf.append("-"); } else if (c.occur == Occur.MUST) { buf.append("+"); } ParseableQuery subq = c.getQuery(); if (subq != null) { if (subq instanceof BooleanPQuery) { buf.append('(').append(subq.toQueryString(field, config)).append(')'); } else { buf.append(subq.toQueryString(field, config)); } } if (i < clauses.length-1) { buf.append(' '); } } return buf.toString(); } @Override public boolean equals(ParseableQuery other) { if (! (other instanceof BooleanPQuery)) { return false; } BooleanPQuery oq = (BooleanPQuery) other; if (clauses.length != oq.clauses.length) { return false; } for (int i = 0; i < clauses.length; i++) { if (! (clauses[i].occur == oq.clauses[i].occur && clauses[i].query.equals(oq.clauses[i].query))) { return false; } } return true; } }