package lux; import lux.exception.LuxException; import lux.index.FieldRole; import lux.solr.MissingStringLastComparatorSource; import net.sf.saxon.om.NodeInfo; import net.sf.saxon.om.SequenceIterator; import org.apache.lucene.search.Sort; import org.apache.lucene.search.SortField; public abstract class SearchIteratorBase implements SequenceIterator<NodeInfo> { protected final Evaluator eval; protected final QueryStats stats; protected final String [] sortCriteria; protected final int start; protected NodeInfo current = null; protected int position = 0; public static final MissingStringLastComparatorSource MISSING_LAST = new MissingStringLastComparatorSource(); public SearchIteratorBase (Evaluator eval, String[] sortCriteria, int start1) { this.eval = eval; this.stats = eval.getQueryStats(); this.sortCriteria = sortCriteria; this.start = start1 - 1; } protected Sort makeSortFromCriteria(String [] criteria) { SortField[] sortFields = new SortField [criteria.length]; for (int i = 0; i < criteria.length; i++) { SortField.Type type = SortField.Type.STRING; String [] tokens = criteria[i].split("\\s+"); String field = tokens[0]; Boolean reverse = null; Boolean emptyGreatest = null; for (int j = 1; j < tokens.length; j++) { if (tokens[j].equals("descending")) { reverse = setBooleanOnce (reverse, true, criteria[i]); } else if (tokens[j].equals("ascending")) { reverse = setBooleanOnce (reverse, false, criteria[i]); } else if (tokens[j].equals("empty")) { if (j == tokens.length-1) { throw new LuxException ("missing keyword after 'empty' in: " + criteria[i]); } j = j + 1; if (tokens[j].equals("least")) { emptyGreatest = setBooleanOnce(emptyGreatest, false, criteria[i]); } else if (tokens[j].equals("greatest")) { emptyGreatest = setBooleanOnce(emptyGreatest, true, criteria[i]); } else { throw new LuxException ("missing or invalid keyword after 'empty' in: " + criteria[i]); } } else if (tokens[j].equals("int")) { type = SortField.Type.INT; } else if (tokens[j].equals("long")) { type = SortField.Type.LONG; } else if (tokens[j].equals("string")) { type = SortField.Type.STRING; } else { throw new LuxException ("invalid keyword '" + tokens[j] + "' in: " + criteria[i]); } } if (field.equals(FieldRole.LUX_SCORE)) { if (reverse == Boolean.FALSE) { throw new LuxException ("not countenanced: attempt to sort by irrelevance"); } sortFields[i] = SortField.FIELD_SCORE; } else if (field.equals(FieldRole.LUX_DOCID)) { if (reverse == Boolean.FALSE) { throw new LuxException ("not countenanced: attempt to sort by descending docid"); } if (criteria.length == 1) { return null; } sortFields[i] = SortField.FIELD_DOC; } else if (emptyGreatest == Boolean.TRUE) { if (type == SortField.Type.STRING) { sortFields[i] = new SortField(field, MISSING_LAST, reverse == Boolean.TRUE); } else { sortFields[i] = new SortField(field, type, reverse == Boolean.TRUE); switch (type) { case INT: sortFields[i].setMissingValue(reverse == Boolean.TRUE ? 0 : Integer.MAX_VALUE); break; case LONG: sortFields[i].setMissingValue(reverse == Boolean.TRUE ? 0 : Long.MAX_VALUE); break; default: throw new LuxException ("unsupported combination of empty greatest and sort field type: " + type); } } } else { sortFields[i] = new SortField(field, type, reverse == Boolean.TRUE); } } return new Sort(sortFields); } final Boolean setBooleanOnce (Boolean current, boolean value, String sortCriteria) { if (current != null) { throw new LuxException ("invalid ordering keyword in: " + sortCriteria); } return value; } /** * @return the current result. This is the last result returned by next(), and will be null if there * are no more results. */ @Override public NodeInfo current() { return current; } /** * @return the (0-based) index of the next result: this will be 0 before any calls to next(), and -1 after the last * result has been retrieved. */ @Override public int position() { return position; } /** * does nothing */ @Override public void close() { // Saxon doesn't call this reliably } /** * This iterator has no special properties * @return 0 */ @Override public int getProperties() { return 0; } }