You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by yo...@apache.org on 2006/01/26 06:40:05 UTC

svn commit: r372455 [6/11] - in /incubator/solr/trunk: ./ src/ src/apps/ src/apps/SolarTest/ src/apps/SolarTest/src/ src/java/ src/java/org/ src/java/org/apache/ src/java/org/apache/solr/ src/java/org/apache/solr/analysis/ src/java/org/apache/solr/core...

Added: incubator/solr/trunk/src/java/org/apache/solr/schema/SortableFloatField.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/schema/SortableFloatField.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/schema/SortableFloatField.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/schema/SortableFloatField.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,129 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.function.ValueSource;
+import org.apache.lucene.search.function.FieldCacheSource;
+import org.apache.lucene.search.function.DocValues;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.solr.util.NumberUtils;
+import org.apache.solr.request.XMLWriter;
+
+import java.util.Map;
+import java.io.IOException;
+/**
+ * @author yonik
+ * @version $Id$
+ */
+public class SortableFloatField extends FieldType {
+  protected void init(IndexSchema schema, Map<String,String> args) {
+  }
+
+  public SortField getSortField(SchemaField field,boolean reverse) {
+    return getStringSort(field,reverse);
+  }
+
+  public ValueSource getValueSource(SchemaField field) {
+    return new SortableFloatFieldSource(field.name);
+  }
+
+  public String toInternal(String val) {
+    return NumberUtils.float2sortableStr(val);
+  }
+
+  public String toExternal(Field f) {
+    return indexedToReadable(f.stringValue());
+  }
+
+  public String indexedToReadable(String indexedForm) {
+    return NumberUtils.SortableStr2floatStr(indexedForm);
+  }
+
+  public void write(XMLWriter xmlWriter, String name, Field f) throws IOException {
+    String sval = f.stringValue();
+    xmlWriter.writeFloat(name, NumberUtils.SortableStr2float(sval));
+  }
+}
+
+
+
+
+class SortableFloatFieldSource extends FieldCacheSource {
+  protected float defVal;
+
+  public SortableFloatFieldSource(String field) {
+    this(field, 0.0f);
+  }
+
+  public SortableFloatFieldSource(String field, float defVal) {
+    super(field);
+    this.defVal = defVal;
+  }
+
+    public String description() {
+    return "sfloat(" + field + ')';
+  }
+
+  public DocValues getValues(IndexReader reader) throws IOException {
+    final FieldCache.StringIndex index = cache.getStringIndex(reader, field);
+    final int[] order = index.order;
+    final String[] lookup = index.lookup;
+    final float def = defVal;
+
+    return new DocValues() {
+      public float floatVal(int doc) {
+        int ord=order[doc];
+        return ord==0 ? def  : NumberUtils.SortableStr2float(lookup[ord]);
+      }
+
+      public int intVal(int doc) {
+        return (int)floatVal(doc);
+      }
+
+      public long longVal(int doc) {
+        return (long)floatVal(doc);
+      }
+
+      public double doubleVal(int doc) {
+        return (double)floatVal(doc);
+      }
+
+      public String strVal(int doc) {
+        return Float.toString(floatVal(doc));
+      }
+
+      public String toString(int doc) {
+        return description() + '=' + floatVal(doc);
+      }
+    };
+  }
+
+  public boolean equals(Object o) {
+    return o instanceof SortableFloatFieldSource
+            && super.equals(o)
+            && defVal == ((SortableFloatFieldSource)o).defVal;
+  }
+
+  private static int hcode = SortableFloatFieldSource.class.hashCode();
+  public int hashCode() {
+    return hcode + super.hashCode() + Float.floatToIntBits(defVal);
+  };
+}
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/schema/SortableFloatField.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/schema/SortableIntField.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/schema/SortableIntField.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/schema/SortableIntField.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/schema/SortableIntField.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,132 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.function.ValueSource;
+import org.apache.lucene.search.function.FieldCacheSource;
+import org.apache.lucene.search.function.DocValues;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.solr.util.NumberUtils;
+import org.apache.solr.request.XMLWriter;
+
+import java.util.Map;
+import java.io.IOException;
+/**
+ * @author yonik
+ * @version $Id$
+ */
+public class SortableIntField extends FieldType {
+  protected void init(IndexSchema schema, Map<String,String> args) {
+  }
+
+  public SortField getSortField(SchemaField field,boolean reverse) {
+    return getStringSort(field,reverse);
+  }
+
+  public ValueSource getValueSource(SchemaField field) {
+    return new SortableIntFieldSource(field.name);
+  }
+
+  public String toInternal(String val) {
+    // special case single digits?  years?, etc
+    // stringCache?  general stringCache on a
+    // global field level?
+    return NumberUtils.int2sortableStr(val);
+  }
+
+  public String toExternal(Field f) {
+    return indexedToReadable(f.stringValue());
+  }
+
+  public String indexedToReadable(String indexedForm) {
+    return NumberUtils.SortableStr2int(indexedForm);
+  }
+
+  public void write(XMLWriter xmlWriter, String name, Field f) throws IOException {
+    String sval = f.stringValue();
+    // since writeInt an int instead of a String since that may be more efficient
+    // in the future (saves the construction of one String)
+    xmlWriter.writeInt(name, NumberUtils.SortableStr2int(sval,0,sval.length()));
+  }
+}
+
+
+
+class SortableIntFieldSource extends FieldCacheSource {
+  protected int defVal;
+
+  public SortableIntFieldSource(String field) {
+    this(field, 0);
+  }
+
+  public SortableIntFieldSource(String field, int defVal) {
+    super(field);
+    this.defVal = defVal;
+  }
+
+  public String description() {
+    return "sint(" + field + ')';
+  }
+
+  public DocValues getValues(IndexReader reader) throws IOException {
+    final FieldCache.StringIndex index = cache.getStringIndex(reader, field);
+    final int[] order = index.order;
+    final String[] lookup = index.lookup;
+    final int def = defVal;
+
+    return new DocValues() {
+      public float floatVal(int doc) {
+        return (float)intVal(doc);
+      }
+
+      public int intVal(int doc) {
+        int ord=order[doc];
+        return ord==0 ? def  : NumberUtils.SortableStr2int(lookup[ord],0,3);
+      }
+
+      public long longVal(int doc) {
+        return (long)intVal(doc);
+      }
+
+      public double doubleVal(int doc) {
+        return (double)intVal(doc);
+      }
+
+      public String strVal(int doc) {
+        return Integer.toString(intVal(doc));
+      }
+
+      public String toString(int doc) {
+        return description() + '=' + intVal(doc);
+      }
+    };
+  }
+
+  public boolean equals(Object o) {
+    return o instanceof SortableIntFieldSource
+            && super.equals(o)
+            && defVal == ((SortableIntFieldSource)o).defVal;
+  }
+
+  private static int hcode = SortableIntFieldSource.class.hashCode();
+  public int hashCode() {
+    return hcode + super.hashCode() + defVal;
+  };
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/schema/SortableIntField.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/schema/SortableLongField.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/schema/SortableLongField.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/schema/SortableLongField.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/schema/SortableLongField.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,129 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.search.function.ValueSource;
+import org.apache.lucene.search.function.FieldCacheSource;
+import org.apache.lucene.search.function.DocValues;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.solr.util.NumberUtils;
+import org.apache.solr.request.XMLWriter;
+
+import java.util.Map;
+import java.io.IOException;
+/**
+ * @author yonik
+ * @version $Id$
+ */
+public class SortableLongField extends FieldType {
+  protected void init(IndexSchema schema, Map<String,String> args) {
+  }
+
+  public SortField getSortField(SchemaField field,boolean reverse) {
+    return getStringSort(field,reverse);
+  }
+
+  public ValueSource getValueSource(SchemaField field) {
+    return new SortableLongFieldSource(field.name);
+  }
+
+  public String toInternal(String val) {
+    return NumberUtils.long2sortableStr(val);
+  }
+
+  public String indexedToReadable(String indexedForm) {
+    return NumberUtils.SortableStr2long(indexedForm);
+  }
+
+  public String toExternal(Field f) {
+    return indexedToReadable(f.stringValue());
+  }
+
+  public void write(XMLWriter xmlWriter, String name, Field f) throws IOException {
+    String sval = f.stringValue();
+    xmlWriter.writeLong(name, NumberUtils.SortableStr2long(sval,0,sval.length()));
+  }
+}
+
+
+
+
+
+class SortableLongFieldSource extends FieldCacheSource {
+  protected long defVal;
+
+  public SortableLongFieldSource(String field) {
+    this(field, 0);
+  }
+
+  public SortableLongFieldSource(String field, long defVal) {
+    super(field);
+    this.defVal = defVal;
+  }
+
+  public String description() {
+    return "slong(" + field + ')';
+  }
+
+  public DocValues getValues(IndexReader reader) throws IOException {
+    final FieldCache.StringIndex index = cache.getStringIndex(reader, field);
+    final int[] order = index.order;
+    final String[] lookup = index.lookup;
+    final long def = defVal;
+
+    return new DocValues() {
+      public float floatVal(int doc) {
+        return (float)longVal(doc);
+      }
+
+      public int intVal(int doc) {
+        return (int)longVal(doc);
+      }
+
+      public long longVal(int doc) {
+        int ord=order[doc];
+        return ord==0 ? def  : NumberUtils.SortableStr2long(lookup[ord],0,5);
+      }
+
+      public double doubleVal(int doc) {
+        return (double)longVal(doc);
+      }
+
+      public String strVal(int doc) {
+        return Long.toString(longVal(doc));
+      }
+
+      public String toString(int doc) {
+        return description() + '=' + longVal(doc);
+      }
+    };
+  }
+
+  public boolean equals(Object o) {
+    return o instanceof SortableLongFieldSource
+            && super.equals(o)
+            && defVal == ((SortableLongFieldSource)o).defVal;
+  }
+
+  private static int hcode = SortableLongFieldSource.class.hashCode();
+  public int hashCode() {
+    return hcode + super.hashCode() + (int)defVal;
+  };
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/schema/SortableLongField.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/schema/StrField.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/schema/StrField.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/schema/StrField.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/schema/StrField.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,41 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.document.Field;
+import org.apache.solr.request.XMLWriter;
+
+import java.util.Map;
+import java.io.IOException;
+/**
+ * @author yonik
+ * @version $Id$
+ */
+//TODO: allow specification of max string size?
+public class StrField extends FieldType {
+  protected void init(IndexSchema schema, Map<String,String> args) {
+  }
+
+  public SortField getSortField(SchemaField field,boolean reverse) {
+    return getStringSort(field,reverse);
+  }
+
+  public void write(XMLWriter xmlWriter, String name, Field f) throws IOException {
+    xmlWriter.writeStr(name, f.stringValue());
+  }
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/schema/StrField.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/schema/TextField.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/schema/TextField.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/schema/TextField.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/schema/TextField.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.schema;
+
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.document.Field;
+import org.apache.solr.request.XMLWriter;
+
+import java.util.Map;
+import java.io.IOException;
+
+/** <code>TextField</code> is the basic type for configurable text analysis.
+ * Analyzers for field types using this implementation should be defined in the schema.
+ * @author yonik
+ * @version $Id$
+ */
+public class TextField extends FieldType {
+  protected void init(IndexSchema schema, Map<String,String> args) {
+    properties |= TOKENIZED;
+  }
+
+  public SortField getSortField(SchemaField field, boolean reverse) {
+    return new SortField(field.name,SortField.STRING, reverse);
+  }
+
+  public void write(XMLWriter xmlWriter, String name, Field f) throws IOException {
+    xmlWriter.writeStr(name, f.stringValue());
+  }
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/schema/TextField.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/BitDocSet.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/BitDocSet.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/BitDocSet.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/BitDocSet.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,112 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import java.util.BitSet;
+
+/**
+ * <code>BitDocSet</code> represents an unordered set of Lucene Document Ids
+ * using a BitSet.  A set bit represents inclusion in the set for that document.
+ *
+ * @author yonik
+ * @version $Id: BitDocSet.java,v 1.4 2005/10/27 04:14:49 yonik Exp $
+ * @since solr 0.9
+ */
+public class BitDocSet extends DocSetBase {
+  final BitSet bits;
+  int size;    // number of docs in the set (cached for perf)
+
+  public BitDocSet() {
+    bits = new BitSet();
+  }
+
+  public BitDocSet(BitSet bits) {
+    this.bits = bits;
+    size=-1;
+  }
+
+  public BitDocSet(BitSet bits, int size) {
+    this.bits = bits;
+    this.size = size;
+  }
+
+  public DocIterator iterator() {
+    return new DocIterator() {
+      int pos=bits.nextSetBit(0);
+      public boolean hasNext() {
+        return pos>=0;
+      }
+
+      public Integer next() {
+        return nextDoc();
+      }
+
+      public void remove() {
+        bits.clear(pos);
+      }
+
+      public int nextDoc() {
+        int old=pos;
+        pos=bits.nextSetBit(old+1);
+        return old;
+      }
+
+      public float score() {
+        return 0.0f;
+      }
+    };
+  }
+
+  /**
+   *
+   * @return the <b>internal</b> BitSet that should <b>not</b> be modified.
+   */
+  public BitSet getBits() {
+    return bits;
+  }
+
+  public void add(int doc) {
+    bits.set(doc);
+    size=-1;  // invalidate size
+  }
+
+  public void addUnique(int doc) {
+    size++;
+    bits.set(doc);
+  }
+
+  public int size() {
+    if (size!=-1) return size;
+    return size=bits.cardinality();
+  }
+
+  /**
+   * The number of set bits - size - is cached.  If the bitset is changed externally,
+   * this method should be used to invalidate the previously cached size.
+   */
+  public void invalidateSize() {
+    size=-1;
+  }
+
+  public boolean exists(int doc) {
+    return bits.get(doc);
+  }
+
+  public long memSize() {
+    return (bits.size() >> 3) + 16;
+  }
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/BitDocSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/CacheConfig.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/CacheConfig.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/CacheConfig.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/CacheConfig.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,110 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.Map;
+
+import org.apache.solr.util.DOMUtil;
+import org.apache.solr.core.SolrException;
+import org.apache.solr.core.SolrConfig;
+import org.apache.solr.core.Config;
+
+import javax.xml.xpath.XPathConstants;
+
+/**
+ * Contains the knowledge of how cache config is
+ * stored in the solarconfig.xml file, and implements a
+ * factory to create caches.
+ *
+ * @author yonik
+ * @version $Id: CacheConfig.java,v 1.2 2005/09/07 20:37:57 yonik Exp $
+ */
+class CacheConfig {
+  private String nodeName;
+  private Map args;
+
+  private String cacheImpl;
+  private Class clazz;
+
+  private Object[] persistence = new Object[1];
+
+  private String regenImpl;
+  private CacheRegenerator regenerator;
+
+  public CacheRegenerator getRegenerator() {
+    return regenerator;
+  }
+
+  public void setRegenerator(CacheRegenerator regenerator) {
+    this.regenerator = regenerator;
+  }
+
+  public static CacheConfig[] getMultipleConfigs(String configPath) {
+    NodeList nodes = (NodeList)SolrConfig.config.evaluate(configPath, XPathConstants.NODESET);
+    if (nodes==null || nodes.getLength()==0) return null;
+    CacheConfig[] configs = new CacheConfig[nodes.getLength()];
+    for (int i=0; i<nodes.getLength(); i++) {
+      configs[i] = getConfig(nodes.item(i));
+    }
+    return configs;
+  }
+
+
+  public static CacheConfig getConfig(String xpath) {
+    Node node = (Node)SolrConfig.config.getNode(xpath, false);
+    return getConfig(node);
+  }
+
+
+  public static CacheConfig getConfig(Node node) {
+    if (node==null) return null;
+    CacheConfig config = new CacheConfig();
+    config.nodeName = node.getNodeName();
+    config.args = DOMUtil.toMap(node.getAttributes());
+    String nameAttr = (String)config.args.get("name");  // OPTIONAL
+    if (nameAttr==null) {
+      config.args.put("name",config.nodeName);
+    }
+
+    config.cacheImpl = (String)config.args.get("class");
+    config.regenImpl = (String)config.args.get("regenerator");
+    config.clazz = Config.findClass(config.cacheImpl);
+    if (config.regenImpl != null) {
+      config.regenerator = (CacheRegenerator) Config.newInstance(config.regenImpl);
+    }
+
+
+    return config;
+  }
+
+  public SolrCache newInstance() {
+    try {
+      SolrCache cache = (SolrCache)clazz.newInstance();
+      persistence[0] = cache.init(args, persistence[0], regenerator);
+      return cache;
+    } catch (Exception e) {
+      SolrException.log(SolrCache.log,"Error instantiating cache",e);
+      // we can carry on without a cache... but should we?
+      // in some cases (like an OOM) we probably should try to continue.
+      return null;
+    }
+  }
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/CacheConfig.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/CacheRegenerator.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/CacheRegenerator.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/CacheRegenerator.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/CacheRegenerator.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,43 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import java.io.IOException;
+
+/**
+ * Implementations of <code>CacheRegenerator</code> are used in autowarming to populate a new cache
+ * based on an old cache.  <code>regenerateItem</code> is called for each item that should be inserted into the new cache.
+ * <p>
+ * Implementations should have a noarg constructor and be thread safe (a single instance will be
+ * used for all cache autowarmings).
+ *
+ * @author yonik
+ * @version $Id: CacheRegenerator.java,v 1.2 2005/09/07 20:37:57 yonik Exp $
+ */
+public interface CacheRegenerator {
+  /**
+   * Regenerate an old cache item and insert it into <code>newCache</code>
+   *
+   * @param newSearcher the new searcher who's caches are being autowarmed
+   * @param newCache    where regenerated cache items should be stored. the target of the autowarming
+   * @param oldCache    the old cache being used as a source for autowarming
+   * @param oldKey      the key of the old cache item to regenerate in the new cache
+   * @param oldVal      the old value of the cache item
+   * @return true to continue with autowarming, false to stop
+   */
+  public boolean regenerateItem(SolrIndexSearcher newSearcher, SolrCache newCache, SolrCache oldCache, Object oldKey, Object oldVal) throws IOException;
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/CacheRegenerator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/DocIterator.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/DocIterator.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/DocIterator.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/DocIterator.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import java.util.Iterator;
+
+/**
+ * @author yonik
+ * @version $Id$
+ */
+public interface DocIterator extends Iterator<Integer> {
+  public boolean hasNext();
+
+  /**
+   * returns the next document id if hasNext()==true
+   */
+  public int nextDoc();
+
+  /**
+   * returns the score for the document just returned by nextDoc()
+   */
+  public float score();
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/DocIterator.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/DocList.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/DocList.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/DocList.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/DocList.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,128 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+
+/**
+ * <code>DocList</code> represents the result of a query: an ordered list of document ids with optional score.
+ * This list contains a subset of the complete list of documents actually matched: <code>size()</code>
+ * document ids starting at <code>offset()</code>.
+ *
+ * @author yonik
+ * @version $Id: DocList.java,v 1.6 2005/11/11 21:57:56 yonik Exp $
+ * @since solr 0.9
+ */
+public interface DocList extends DocSet {
+
+  /**
+   * Returns the zero based offset of this list within the total ordered list of matches to the query.
+   */
+  public int offset();
+
+  /**
+   * Returns the number of ids in this list.
+   */
+  public int size();
+
+  /**
+   * Returns the total number of matches for the search
+   * (as opposed to just the number collected according
+   * to <code>offset()</code> and <code>size()</code>).
+   * Hence it's always true that matches() >= size()
+   * @return number of matches for the search(query&filter)
+   */
+  public int matches();
+
+
+  /***
+  public int getDoc(int pos);
+  ***/
+
+  // hmmm, what if a different slice could be generated from an existing DocSet
+  // (and was before)...
+
+  // how to distinguish cached values from logical values?
+  // docSet could represent docs 10-20, but actually contain 0-100
+  // should the big slice be cached independently, and a new class called
+  // DocListSubset be created to refer to a range within the DocList?
+
+  /**
+   * Get a subset of an existing DocList.
+   * Returns null if not possible.
+   */
+  public DocList subset(int offset, int len);
+
+  /** True if scores were retained */
+  public boolean hasScores();
+
+  /** The maximum score for the search... only valid if
+   * scores were retained (if hasScores()==true)
+   */
+  public float maxScore();
+}
+
+
+/****  Maybe do this at a higher level (more efficient)
+
+class SmartDocSet implements DocSet {
+  static int INITIAL_SIZE=10;
+  static int TRANSITION_SIZE=10;
+
+  protected BitSet bits;
+  int size;
+
+  protected int[] arr;     // keep small set as an array, or as a hash?
+  protected int arrsize;
+
+  public SmartDocSet() {
+    if (INITIAL_SIZE>0) {
+      arr=new int[INITIAL_SIZE];
+    } else {
+      bits=new BitSet();
+    }
+  }
+
+
+  public void addUnique(int doc) {
+    size++;
+    if (bits != null) {
+      bits.set(doc);
+    }
+    else {
+      if (arrsize<10) {
+        arr[arrsize++]=doc;
+      } else  {
+        // TODO: transition to bit set
+      }
+    }
+  };
+
+  public int size() {
+    return size;
+  }
+  public boolean exists(int docid) {
+    return false;
+  }
+  public DocSet intersection(DocSet other) {
+    return null;
+
+  }
+  public DocSet union(DocSet other) {
+    return null;
+  }
+}
+***/

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/DocList.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/DocListAndSet.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/DocListAndSet.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/DocListAndSet.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/DocListAndSet.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,37 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+
+/**
+ * A struct who's only purpose is to hold both a DocList and a DocSet so that both
+ * may be returned from a single method.
+ * <p>
+ * The DocList and DocSet returned should <b>not</b> be modified as they may
+ * have been retrieved or inserted into a cache and should be considered shared.
+ * <p>
+ * Oh, if only java had "out" parameters or multiple return args...
+ * <p>
+ *
+ * @author yonik
+ * @version $Id: DocListAndSet.java,v 1.3 2005/04/08 05:38:05 yonik Exp $
+ * @since solr 0.9
+ */
+public final class DocListAndSet {
+  public DocList docList;
+  public DocSet docSet;
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/DocListAndSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/DocSet.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/DocSet.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/DocSet.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/DocSet.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,182 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import org.apache.solr.core.SolrException;
+
+import java.util.BitSet;
+
+/**
+ * <code>DocSet</code> represents an unordered set of Lucene Document Ids.
+ * <p>
+ * WARNING: Any DocSet returned from SolrIndexSearcher should <b>not</b> be modified as it may have been retrieved from
+ * a cache and could be shared.
+ * @author yonik
+ * @version $Id: DocSet.java,v 1.6 2005/05/13 21:20:15 yonik Exp $
+ * @since solr 0.9
+ */
+public interface DocSet /* extends Collection<Integer> */ {
+  public void add(int doc);
+  public void addUnique(int doc);
+
+  /**
+   * @return The number of document ids in the set.
+   */
+  public int size();
+
+  /**
+   *
+   * @param docid
+   * @return
+   * true if the docid is in the set
+   */
+  public boolean exists(int docid);
+
+  /**
+   *
+   * @return an interator that may be used to iterate over all of the documents in the set.
+   */
+  public DocIterator iterator();
+
+  /**
+   * Returns a BitSet view of the DocSet.  Any changes to this BitSet <b>may</b>
+   * be reflected in the DocSet, hence if the DocSet is shared or was returned from
+   * a SolrIndexSearcher method, it's not safe to modify the BitSet.
+   *
+   * @return
+   * A BitSet with the bit number of every docid set in the set.
+   */
+  @Deprecated
+  public BitSet getBits();
+
+  /**
+   * Returns the approximate amount of memory taken by this DocSet.
+   * This is only an approximation and doesn't take into account java object overhead.
+   *
+   * @return
+   * the approximate memory consumption in bytes
+   */
+  public long memSize();
+
+  /**
+   * Returns the intersection of this set with another set.  Neither set is modified - a new DocSet is
+   * created and returned.
+   * @param other
+   * @return a DocSet representing the intersection
+   */
+  public DocSet intersection(DocSet other);
+
+  /**
+   * Returns the number of documents of the intersection of this set with another set.
+   * May be more efficient than actually creating the intersection and then getting it's size.
+   */
+  public int intersectionSize(DocSet other);
+
+  /**
+   * Returns the union of this set with another set.  Neither set is modified - a new DocSet is
+   * created and returned.
+   * @param other
+   * @return a DocSet representing the union
+   */
+  public DocSet union(DocSet other);
+
+  /**
+   * Returns the number of documents of the union of this set with another set.
+   * May be more efficient than actually creating the union and then getting it's size.
+   */
+  public int unionSize(DocSet other);
+
+}
+
+
+abstract class DocSetBase implements DocSet {
+
+  // Not implemented efficiently... for testing purposes only
+  public boolean equals(Object obj) {
+    if (!(obj instanceof DocSet)) return false;
+    DocSet other = (DocSet)obj;
+    if (this.size() != other.size()) return false;
+
+    if (this instanceof DocList && other instanceof DocList) {
+      // compare ordering
+      DocIterator i1=this.iterator();
+      DocIterator i2=this.iterator();
+      while(i1.hasNext() && i2.hasNext()) {
+        if (i1.nextDoc() != i2.nextDoc()) return false;
+      }
+      return true;
+      // don't compare matches
+    }
+
+    // if (this.size() != other.size()) return false;
+    return this.getBits().equals(other.getBits());
+  }
+
+  public void add(int doc) {
+    throw new SolrException(500,"Unsupported Operation");
+  }
+
+  public void addUnique(int doc) {
+    throw new SolrException(500,"Unsupported Operation");
+  }
+
+  // Only the inefficient base implementation.  DocSets based on
+  // BitSets will return the actual BitSet without making a copy.
+  public BitSet getBits() {
+    BitSet bits = new BitSet();
+    for (DocIterator iter = iterator(); iter.hasNext();) {
+      bits.set(iter.nextDoc());
+    }
+    return bits;
+  };
+
+  public DocSet intersection(DocSet other) {
+    // intersection is overloaded in HashDocSet to be more
+    // efficient, so if "other" is a HashDocSet, dispatch off
+    // of it instead.
+    if (other instanceof HashDocSet) {
+      return other.intersection(this);
+    }
+
+    // Default... handle with bitsets.
+    BitSet newbits = (BitSet)(this.getBits().clone());
+    newbits.and(other.getBits());
+    return new BitDocSet(newbits);
+  }
+
+  public DocSet union(DocSet other) {
+    BitSet newbits = (BitSet)(this.getBits().clone());
+    newbits.or(other.getBits());
+    return new BitDocSet(newbits);
+  }
+
+  // TODO: more efficient implementations
+  public int intersectionSize(DocSet other) {
+    return intersection(other).size();
+  }
+
+  // TODO: more efficient implementations
+  public int unionSize(DocSet other) {
+    return union(other).size();
+  }
+
+
+}
+
+
+
+

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/DocSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/DocSlice.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/DocSlice.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/DocSlice.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/DocSlice.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,119 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+/**
+ * <code>DocSlice</code> implements DocList as an array of docids and optional scores.
+ *
+ * @author yonik
+ * @version $Id: DocSlice.java,v 1.9 2005/11/11 21:57:56 yonik Exp $
+ * @since solr 0.9
+ */
+public class DocSlice extends DocSetBase implements DocList {
+  final int offset;    // starting position of the docs (zero based)
+  final int len;       // number of positions used in arrays
+  final int[] docs;    // a slice of documents (docs 0-100 of the query)
+
+  final float[] scores;  // optional score list
+  final int matches;
+  final float maxScore;
+
+  /**
+   *
+   * @param offset  starting offset for this range of docs
+   * @param len     length of results
+   * @param docs    array of docids starting at position 0
+   * @param scores
+   * @param matches total number of matches for the query
+   */
+  public DocSlice(int offset, int len, int[] docs, float[] scores, int matches, float maxScore) {
+    this.offset=offset;
+    this.len=len;
+    this.docs=docs;
+    this.scores=scores;
+    this.matches=matches;
+    this.maxScore=maxScore;
+  }
+
+  public DocList subset(int offset, int len) {
+    if (this.offset == offset && this.len==len) return this;
+
+    // if we didn't store enough (and there was more to store)
+    // then we can't take a subset.
+    int requestedEnd = offset + len;
+    if (requestedEnd > docs.length && this.matches > docs.length) return null;
+    int realEndDoc = Math.min(requestedEnd, docs.length);
+    int realLen = Math.max(realEndDoc-offset,0);
+    if (this.offset == offset && this.len == realLen) return this;
+    return new DocSlice(offset, realLen, docs, scores, matches, maxScore);
+  }
+
+  public boolean hasScores() {
+    return scores!=null;
+  }
+
+  public float maxScore() {
+    return maxScore;
+  }
+
+
+  public int offset()  { return offset; }
+  public int size()    { return len; }
+  public int matches() { return matches; }
+
+
+  public long memSize() {
+    return (docs.length<<2)
+            + (scores==null ? 0 : (scores.length<<2))
+            + 24;
+  }
+
+
+  public boolean exists(int doc) {
+    for (int i: docs) {
+      if (i==doc) return true;
+    }
+    return false;
+  }
+
+  // Hmmm, maybe I could have reused the scorer interface here...
+  // except that it carries Similarity baggage...
+  public DocIterator iterator() {
+    return new DocIterator() {
+      int pos=offset;
+      final int end=offset+len;
+      public boolean hasNext() {
+        return pos < end;
+      }
+
+      public Integer next() {
+        return nextDoc();
+      }
+
+      public void remove() {
+      }
+
+      public int nextDoc() {
+        return docs[pos++];
+      }
+
+      public float score() {
+        return scores[pos-1];
+      }
+    };
+  }
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/DocSlice.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/HashDocSet.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/HashDocSet.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/HashDocSet.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/HashDocSet.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,280 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import org.apache.solr.core.SolrConfig;
+
+
+/**
+ * <code>HashDocSet</code> represents an unordered set of Lucene Document Ids
+ * using a primitive int hash table.  It can be a better choice if there are few docs
+ * in the set because it takes up less memory and is faster to iterate and take
+ * set intersections.
+ *
+ * @author yonik
+ * @version $Id: HashDocSet.java,v 1.7 2005/11/22 17:16:19 yonik Exp $
+ * @since solr 0.9
+ */
+public final class HashDocSet extends DocSetBase {
+  // keep track of the inverse of the Loadfactor  since
+  // multiplication is so much faster than division.
+  final static float inverseLoadfactor = 1.0f / SolrConfig.config.getFloat("//HashDocSet/@loadFactor",0.75f);
+  public final static int MAX_SIZE = SolrConfig.config.getInt("//HashDocSet/@maxSize",-1);
+
+
+  // lucene docs are numbered from 0, so a neg number must be used for missing.
+  // an alternative to having to init the array to EMPTY at the start is
+  //
+  private final static int EMPTY=-1;
+  private final int tablesize;
+  private final int[] table;
+  private final int size;
+
+  private final int mask;
+
+  public HashDocSet(int[] docs, int offset, int len) {
+    int tsize = Math.max(nextHighestPowerOfTwo(len), 1);
+    if (tsize < len * inverseLoadfactor) {
+      tsize <<= 1;
+    }
+    tablesize = tsize;
+    mask=tablesize-1;
+
+    table = new int[tablesize];
+    for (int i=0; i<tablesize; i++) table[i]=EMPTY;
+
+    for (int i=offset; i<len; i++) {
+      put(docs[i]);
+    }
+
+    size = len;
+  }
+
+  static int nextHighestPowerOfTwo(int v) {
+    v--;
+    v |= v >> 1;
+    v |= v >> 2;
+    v |= v >> 4;
+    v |= v >> 8;
+    v |= v >> 16;
+    v++;
+    return v;
+  }
+
+
+  void put(int doc) {
+    table[getSlot(doc)]=doc;
+  }
+
+  private int getSlot(int val) {
+    int s,v;
+    s=val & mask;
+    v=table[s];
+    // check for EMPTY first since that value is more likely
+    if (v==EMPTY || v==val) return s;
+    s=rehash(val);
+    return s;
+  }
+
+
+  // As the size of this int hashtable is expected to be small
+  // (thousands at most), I did not try to keep the rehash function
+  // reversible (important to avoid collisions in large hash tables).
+  private int rehash(int val) {
+    int h,s,v;
+    final int comp=~val;
+
+    // don't left shift too far... the only bits
+    // that count in the answer are the ones on the right.
+    // We want to put more of the bits on the left
+    // into the answer.
+    // Keep small tables in mind.  We may be only using
+    // the first 5 or 6 bits.
+
+    // on the first rehash, use complement instead of val to shift
+    // so we don't end up with 0 again if val==0.
+    h = val ^ (comp>>8);
+    s = h & mask;
+    v = table[s];
+    if (v==EMPTY || v==val) return s;
+
+    h ^= (v << 17) | (comp >>> 16);   // this is reversible
+    s = h & mask;
+    v = table[s];
+    if (v==EMPTY || v==val) return s;
+
+    h ^= (h << 8) | (comp >>> 25);    // this is reversible
+    s = h & mask;
+    v = table[s];
+    if (v==EMPTY || v==val) return s;
+
+    /**********************
+     // Knuth, Thomas Wang, http://www.concentric.net/~Ttwang/tech/inthash.htm
+     // This magic number has no common factors with 2^32, and magic/(2^32) approximates
+     // the golden ratio.
+    private static final int magic = (int)2654435761L;
+
+    h = magic*val;
+    s = h & mask;
+    v=table[s];
+    if (v==EMPTY || v==val) return s;
+
+    // the mult with magic should have thoroughly mixed the bits.
+    // add entropy to the right half from the left half.
+    h ^= h>>>16;
+    s = h & mask;
+    v=table[s];
+    if (v==EMPTY || v==val) return s;
+    *************************/
+
+    // linear scan now... ug.
+    final int start=s;
+    while (++s<tablesize) {
+      v=table[s];
+      if (v==EMPTY || v==val) return s;
+    }
+    s=start;
+    while (--s>=0) {
+      v=table[s];
+      if (v==EMPTY || v==val) return s;
+    }
+    return s;
+  }
+
+
+  /**
+   *
+   * @return The number of document ids in the set.
+   */
+  public int size() {
+    return size;
+  }
+
+  public boolean exists(int docid) {
+    int v = table[docid & mask];
+    if (v==EMPTY) return false;
+    else if (v==docid) return true;
+    else {
+      v = table[rehash(docid)];
+      if (v==docid) return true;
+      else return false;
+    }
+  }
+
+  public DocIterator iterator() {
+    return new DocIterator() {
+      int pos=0;
+      int doc;
+      { goNext(); }
+
+      public boolean hasNext() {
+        return pos < tablesize;
+      }
+
+      public Integer next() {
+        return nextDoc();
+      }
+
+      public void remove() {
+      }
+
+      void goNext() {
+        while (pos<tablesize && table[pos]==EMPTY) pos++;
+      }
+
+      // modify to return -1 at end of iteration?
+      public int nextDoc() {
+        int doc = table[pos];
+        pos++;
+        goNext();
+        return doc;
+      }
+
+      public float score() {
+        return 0.0f;
+      }
+    };
+  }
+
+
+  public long memSize() {
+    return (tablesize<<2) + 20;
+  }
+
+
+  public DocSet intersection(DocSet other) {
+   if (other instanceof HashDocSet) {
+     // set "a" to the smallest doc set for the most efficient
+     // intersection.
+     final HashDocSet a = size()<=other.size() ? this : (HashDocSet)other;
+     final HashDocSet b = size()<=other.size() ? (HashDocSet)other : this;
+
+     int[] result = new int[a.size()];
+     int resultCount=0;
+     for (int i=0; i<a.table.length; i++) {
+       int id=a.table[i];
+       if (id >= 0 && b.exists(id)) {
+         result[resultCount++]=id;
+       }
+     }
+     return new HashDocSet(result,0,resultCount);
+
+   } else {
+
+     int[] result = new int[size()];
+     int resultCount=0;
+     for (int i=0; i<table.length; i++) {
+       int id=table[i];
+       if (id >= 0 && other.exists(id)) {
+         result[resultCount++]=id;
+       }
+     }
+     return new HashDocSet(result,0,resultCount);
+   }
+
+  }
+
+  public int intersectionSize(DocSet other) {
+   if (other instanceof HashDocSet) {
+     // set "a" to the smallest doc set for the most efficient
+     // intersection.
+     final HashDocSet a = size()<=other.size() ? this : (HashDocSet)other;
+     final HashDocSet b = size()<=other.size() ? (HashDocSet)other : this;
+
+     int resultCount=0;
+     for (int i=0; i<a.table.length; i++) {
+       int id=a.table[i];
+       if (id >= 0 && b.exists(id)) {
+         resultCount++;
+       }
+     }
+     return resultCount;
+   } else {
+     int resultCount=0;
+     for (int i=0; i<table.length; i++) {
+       int id=table[i];
+       if (id >= 0 && other.exists(id)) {
+         resultCount++;
+       }
+     }
+     return resultCount;
+   }
+
+  }
+
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/HashDocSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/LRUCache.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/LRUCache.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/LRUCache.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/LRUCache.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,274 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrException;
+import org.apache.solr.util.NamedList;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicLong;
+import java.io.IOException;
+import java.net.URL;
+
+
+/**
+ * @author yonik
+ * @version $Id: LRUCache.java,v 1.12 2005/11/30 06:12:55 yonik Exp $
+ */
+public class LRUCache implements SolrCache {
+
+  /* An instance of this class will be shared across multiple instances
+   * of an LRUCache at the same time.  Make sure everything is thread safe.
+   */
+  private static class CumulativeStats {
+    AtomicLong lookups = new AtomicLong();
+    AtomicLong hits = new AtomicLong();
+    AtomicLong inserts = new AtomicLong();
+    AtomicLong evictions = new AtomicLong();
+  }
+
+  private CumulativeStats stats;
+
+  // per instance stats.  The synchronization used for the map will also be
+  // used for updating these statistics (and hence they are not AtomicLongs
+  private long lookups;
+  private long hits;
+  private long inserts;
+  private long evictions;
+
+  private Map map;
+  private String name;
+  private int autowarmCount;
+  private State state;
+  private CacheRegenerator regenerator;
+
+  public Object init(Map args, Object persistence, CacheRegenerator regenerator) {
+    state=State.CREATED;
+    this.regenerator = regenerator;
+    name = (String)args.get("name");
+    String str = (String)args.get("size");
+    final int limit = str==null ? 1024 : Integer.parseInt(str);
+    str = (String)args.get("initialSize");
+    final int initialSize = Math.min(str==null ? 1024 : Integer.parseInt(str), limit);
+    str = (String)args.get("autowarmCount");
+    autowarmCount = str==null ? 0 : Integer.parseInt(str);
+
+    map = new LinkedHashMap(initialSize, 0.75f, true) {
+        protected boolean removeEldestEntry(Map.Entry eldest) {
+          if (size() > limit) {
+            // increment evictions regardless of state.
+            // this doesn't need to be synchronized because it will
+            // only be called in the context of a higher level synchronized block.
+            evictions++;
+            stats.evictions.incrementAndGet();
+            return true;
+          }
+          return false;
+        }
+      };
+
+    if (persistence==null) {
+      // must be the first time a cache of this type is being created
+      persistence = new CumulativeStats();
+    }
+
+    stats = (CumulativeStats)persistence;
+
+    return persistence;
+  }
+
+  public String name() {
+    return name;
+  }
+
+  public int size() {
+    synchronized(map) {
+      return map.size();
+    }
+  }
+
+  public synchronized Object put(Object key, Object value) {
+    if (state == State.LIVE) {
+      stats.inserts.incrementAndGet();
+    }
+
+    synchronized (map) {
+      // increment local inserts regardless of state???
+      // it does make it more consistent with the current size...
+      inserts++;
+      return map.put(key,value);
+    }
+  }
+
+  public Object get(Object key) {
+    synchronized (map) {
+      Object val = map.get(key);
+      if (state == State.LIVE) {
+        // only increment lookups and hits if we are live.
+        lookups++;
+        stats.lookups.incrementAndGet();
+        if (val!=null) {
+          hits++;
+          stats.hits.incrementAndGet();
+        }
+      }
+      return val;
+    }
+  }
+
+  public void clear() {
+    synchronized(map) {
+      map.clear();
+    }
+  }
+
+  public void setState(State state) {
+    this.state = state;
+  }
+
+  public State getState() {
+    return state;
+  }
+
+  public void warm(SolrIndexSearcher searcher, SolrCache old) throws IOException {
+    if (regenerator==null) return;
+
+    LRUCache other = (LRUCache)old;
+
+    // warm entries
+    if (autowarmCount != 0) {
+      Object[] keys,vals = null;
+
+      // Don't do the autowarming in the synchronized block, just pull out the keys and values.
+      synchronized (other.map) {
+        int sz = other.map.size();
+        if (autowarmCount!=-1) sz = Math.min(sz,autowarmCount);
+        keys = new Object[sz];
+        vals = new Object[sz];
+
+        Iterator iter = other.map.entrySet().iterator();
+
+        // iteration goes from oldest (least recently used) to most recently used,
+        // so we need to skip over the oldest entries.
+        int skip = other.map.size() - sz;
+        for (int i=0; i<skip; i++) iter.next();
+
+
+        for (int i=0; i<sz; i++) {
+          Map.Entry entry = (Map.Entry)iter.next();
+          keys[i]=entry.getKey();
+          vals[i]=entry.getValue();
+        }
+      }
+
+      // autowarm from the oldest to the newest entries so that the ordering will be
+      // correct in the new cache.
+      for (int i=0; i<keys.length; i++) {
+        try {
+          boolean continueRegen = regenerator.regenerateItem(searcher, this, old, keys[i], vals[i]);
+          if (!continueRegen) break;
+        }
+        catch (Throwable e) {
+          SolrException.log(log,"Error during auto-warming of key:" + keys[i], e);
+        }
+      }
+    }
+  }
+
+
+  public void close() {
+  }
+
+
+  //////////////////////// SolrInfoMBeans methods //////////////////////
+
+
+  public String getName() {
+    return LRUCache.class.getName();
+  }
+
+  public String getVersion() {
+    return SolrCore.version;
+  }
+
+  public String getDescription() {
+    return "LRU Cache";
+  }
+
+  public Category getCategory() {
+    return Category.CACHE;
+  }
+
+  public String getCvsId() {
+    return "$Id: LRUCache.java,v 1.12 2005/11/30 06:12:55 yonik Exp $";
+  }
+
+  public String getCvsName() {
+    return "$Name:  $";
+  }
+
+  public String getCvsSource() {
+    return "$Source: /cvs/main/searching/solr/solarcore/src/solr/search/LRUCache.java,v $";
+  }
+
+  public URL[] getDocs() {
+    return null;
+  }
+
+
+  // returns a ratio, not a percent.
+  private static String calcHitRatio(long lookups, long hits) {
+    if (lookups==0) return "0.00";
+    if (lookups==hits) return "1.00";
+    int hundredths = (int)(hits*100/lookups);   // rounded down
+    if (hundredths < 10) return "0.0" + hundredths;
+    return "0." + hundredths;
+
+    /*** code to produce a percent, if we want it...
+    int ones = (int)(hits*100 / lookups);
+    int tenths = (int)(hits*1000 / lookups) - ones*10;
+    return Integer.toString(ones) + '.' + tenths;
+    ***/
+  }
+
+  public NamedList getStatistics() {
+    NamedList lst = new NamedList();
+    synchronized (map) {
+      lst.add("lookups", lookups);
+      lst.add("hits", hits);
+      lst.add("hitratio", calcHitRatio(lookups,hits));
+      lst.add("inserts", inserts);
+      lst.add("evictions", evictions);
+      lst.add("size", map.size());
+    }
+
+    long clookups = stats.lookups.get();
+    long chits = stats.hits.get();
+    lst.add("cumulative_lookups", clookups);
+    lst.add("cumulative_hits", chits);
+    lst.add("cumulative_hitratio", calcHitRatio(clookups,chits));
+    lst.add("cumulative_inserts", stats.inserts.get());
+    lst.add("cumulative_evictions", stats.evictions.get());
+
+    return lst;
+  }
+
+  public String toString() {
+    return name + getStatistics().toString();
+  }
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/LRUCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/LuceneQueryOptimizer.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/LuceneQueryOptimizer.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/LuceneQueryOptimizer.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/LuceneQueryOptimizer.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,116 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+/* Copyright (c) 2003 The Nutch Organization.  All rights reserved.   */
+/* Use subject to the conditions in http://www.nutch.org/LICENSE.txt. */
+
+
+import org.apache.lucene.search.*;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.io.IOException;
+
+/** Utility which converts certain query clauses into {@link QueryFilter}s and
+ * caches these.  Only required {@link TermQuery}s whose boost is zero and
+ * whose term occurs in at least a certain fraction of documents are converted
+ * to cached filters.  This accellerates query constraints like language,
+ * document format, etc., which do not affect ranking but might otherwise slow
+ * search considerably. */
+// Taken from Nutch and modified - YCS
+class LuceneQueryOptimizer {
+  private LinkedHashMap cache;                   // an LRU cache of QueryFilter
+
+  private float threshold;
+
+  /** Construct an optimizer that caches and uses filters for required {@link
+   * TermQuery}s whose boost is zero.
+   * @param cacheSize the number of QueryFilters to cache
+   * @param threshold the fraction of documents which must contain term
+   */
+  public LuceneQueryOptimizer(final int cacheSize, float threshold) {
+    this.cache = new LinkedHashMap(cacheSize, 0.75f, true) {
+        protected boolean removeEldestEntry(Map.Entry eldest) {
+          return size() > cacheSize;              // limit size of cache
+        }
+      };
+    this.threshold = threshold;
+  }
+
+  public TopDocs optimize(BooleanQuery original,
+                          Searcher searcher,
+                          int numHits,
+                          Query[] queryOut,
+                          Filter[] filterOut
+                          )
+    throws IOException {
+
+    BooleanQuery query = new BooleanQuery();
+    BooleanQuery filterQuery = null;
+
+    BooleanClause[] clauses = original.getClauses();
+    for (int i = 0; i < clauses.length; i++) {
+      BooleanClause c = clauses[i];
+
+/***
+System.out.println("required="+c.required);
+System.out.println("boost="+c.query.getBoost());
+System.out.println("isTermQuery="+(c.query instanceof TermQuery));
+if (c.query instanceof TermQuery) {
+ System.out.println("term="+((TermQuery)c.query).getTerm());
+ System.out.println("docFreq="+searcher.docFreq(((TermQuery)c.query).getTerm()));
+}
+***/
+      if (c.required                              // required
+          && c.query.getBoost() == 0.0f           // boost is zero
+          && c.query instanceof TermQuery         // TermQuery
+          && (searcher.docFreq(((TermQuery)c.query).getTerm())
+              / (float)searcher.maxDoc()) >= threshold) { // check threshold
+        if (filterQuery == null)
+          filterQuery = new BooleanQuery();
+        filterQuery.add(c.query, true, false);    // filter it
+//System.out.println("WooHoo... qualified to be hoisted to a filter!");
+      } else {
+        query.add(c);                             // query it
+      }
+    }
+
+    Filter filter = null;
+    if (filterQuery != null) {
+      synchronized (cache) {                      // check cache
+        filter = (Filter)cache.get(filterQuery);
+      }
+      if (filter == null) {                       // miss
+        filter = new QueryFilter(filterQuery);    // construct new entry
+        synchronized (cache) {
+          cache.put(filterQuery, filter);         // cache it
+        }
+      }        
+    }
+
+    // YCS: added code to pass out optimized query and filter
+    // so they can be used with Hits
+    if (queryOut != null && filterOut != null) {
+      queryOut[0] = query; filterOut[0] = filter;
+      return null;
+    } else {
+      return searcher.search(query, filter, numHits);
+    }
+
+  }
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/LuceneQueryOptimizer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,115 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import org.apache.lucene.search.*;
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+
+
+/**
+ * A {@link SortComparatorSource} for strings that orders null values after non-null values.
+ * Based on FieldSortedHitQueue.comparatorString
+ * <p>
+ *
+ * @author Chris Hostetter
+ * @author yonik
+ * @version $Id: MissingStringLastComparatorSource.java,v 1.1 2005/06/02 04:43:06 yonik Exp $
+ *
+ */
+
+// move to apache package and make public if it is accepted as a patch
+class MissingStringLastComparatorSource implements SortComparatorSource {
+
+  public static final String bigString="\uffff\uffff\uffff\uffff\uffff\uffff\uffff\uffffNULL_VAL";
+
+  private final String missingValueProxy;
+
+  public MissingStringLastComparatorSource() {
+    this(bigString);
+  }
+
+  /**
+	 * Returns the value used to sort the given document.  The
+	 * object returned must implement the java.io.Serializable
+	 * interface.  This is used by multisearchers to determine how to collate results from their searchers.
+	 * @see FieldDoc
+	 * @param i Document
+	 * @return Serializable object
+	 */
+
+  /** Creates a {@link SortComparatorSource} that uses <tt>missingValueProxy</tt> as the value to return from ScoreDocComparator.sortValue()
+   * which is only used my multisearchers to determine how to collate results from their searchers.
+   *
+   * @param missingValueProxy   The value returned when sortValue() is called for a document missing the sort field.
+   * This value is *not* normally used for sorting, but used to create
+   */
+  public MissingStringLastComparatorSource(String missingValueProxy) {
+    this.missingValueProxy=missingValueProxy;
+  }
+
+  public ScoreDocComparator newComparator(final IndexReader reader,
+                                          final String fieldname)
+          throws IOException {
+
+    final String field = fieldname.intern();
+    final FieldCache.StringIndex index =
+            FieldCache.DEFAULT.getStringIndex (reader, field);
+
+    // :HACK:
+    // final String lastString =
+    // (index.lookup[index.lookup.length-1]+"X").intern();
+    //
+    // Note: basing lastStringValue on the StringIndex won't work
+    // with a multisearcher.
+
+
+    return new ScoreDocComparator () {
+
+      public final int compare (final ScoreDoc i, final ScoreDoc j) {
+        final int fi = index.order[i.doc];
+        final int fj = index.order[j.doc];
+
+        // 0 is the magic position of null
+
+        /**** alternate logic
+         if (fi < fj && fi != 0) return -1;
+         if (fj < fi && fj != 0) return 1;
+         if (fi==fj) return 0;
+         return fi==0 ? 1 : -1;
+         ****/
+
+        if (fi==fj) return 0;
+        if (fi==0) return 1;
+        if (fj==0) return -1;
+        return fi < fj ? -1 : 1;
+
+      }
+
+      public Comparable sortValue (final ScoreDoc i) {
+        int f = index.order[i.doc];
+        return (0 == f) ? missingValueProxy : index.lookup[f];
+      }
+
+      public int sortType() {
+        return SortField.CUSTOM;
+      }
+    };
+
+  }
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/MissingStringLastComparatorSource.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java
URL: http://svn.apache.org/viewcvs/incubator/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java?rev=372455&view=auto
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java Wed Jan 25 21:37:29 2006
@@ -0,0 +1,479 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.search;
+
+import org.apache.lucene.search.*;
+import org.apache.lucene.search.function.*;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.Term;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrException;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.schema.FieldType;
+
+import java.util.ArrayList;
+import java.util.regex.Pattern;
+import java.util.logging.Level;
+import java.io.IOException;
+
+/**
+ * @author yonik
+ * @version $Id: QueryParsing.java,v 1.10 2005/12/20 21:34:44 yonik Exp $
+ */
+public class QueryParsing {
+
+  public static Query parseQuery(String qs, IndexSchema schema) {
+    try {
+      Query query = new SolrQueryParser(schema).parse(qs);
+
+      if (SolrCore.log.isLoggable(Level.FINEST)) {
+        SolrCore.log.finest("After QueryParser:" + query);
+      }
+
+      return query;
+
+    } catch (ParseException e) {
+      SolrCore.log(e);
+      throw new SolrException(400,"Error parsing Lucene query",e);
+    }
+  }
+
+
+
+  /***
+   * SortSpec encapsulates a Lucene Sort and a count of the number of documents
+   * to return.
+   */
+  public static class SortSpec {
+    private final Sort sort;
+    private final int num;
+
+    SortSpec(Sort sort, int num) {
+      this.sort=sort;
+      this.num=num;
+    }
+
+    /**
+     * Gets the Lucene Sort object, or null for the default sort
+     * by score descending.
+     */
+    public Sort getSort() { return sort; }
+
+    /**
+     * Gets the number of documens to return after sorting.
+     * -1 means there is no cutoff (only do the sort)
+     * @return
+     */
+    public int getCount() { return num; }
+  }
+
+
+  private static Pattern sortSeparator = Pattern.compile("[\\s,]+");
+
+  /**
+   * Returns null if the sortSpec string doesn't look like a sort specification,
+   * or if the sort specification couldn't be converted into a Lucene Sort
+   * (because of a field not being indexed or undefined, etc).
+   *
+   * The form of the sort specification string currently parsed is:
+   * SortSpec ::= SingleSort [, SingleSort]* <number>?
+   * SingleSort ::= <fieldname> SortDirection
+   * SortDirection ::= top | desc | bottom | asc
+   *
+   * Examples:
+   *   top 10                        #take the top 10 by score
+   *   desc 10                       #take the top 10 by score
+   *   score desc 10                 #take the top 10 by score
+   *   weight bottom 10              #sort by weight ascending and take the first 10
+   *   weight desc                   #sort by weight descending
+   *   height desc,weight desc       #sort by height descending, and use weight descending to break any ties
+   *   height desc,weight asc top 20 #sort by height descending, using weight ascending as a tiebreaker
+   *
+   */
+  public static SortSpec parseSort(String sortSpec, IndexSchema schema) {
+    if (sortSpec==null || sortSpec.length()==0) return null;
+
+    // I wonder how fast the regex is??? as least we cache the pattern.
+    String[] parts = sortSeparator.split(sortSpec.trim(),0);
+    if (parts.length == 0) return null;
+
+    ArrayList<SortField> lst = new ArrayList<SortField>();
+    int num=-1;
+
+    int pos=0;
+    String fn;
+    boolean top=true;
+    boolean normalSortOnScore=false;
+
+    while (pos < parts.length) {
+      String str=parts[pos];
+      if ("top".equals(str) || "bottom".equals(str) || "asc".equals(str) || "desc".equals(str)) {
+        // if the field name seems to be missing, default to "score".
+        // note that this will mess up a field name that has the same name
+        // as a sort direction specifier.
+        fn="score";
+      } else {
+        fn=str;
+        pos++;
+      }
+
+      // get the direction of the sort
+      str=parts[pos];
+      if ("top".equals(str) || "desc".equals(str)) {
+        top=true;
+      } else if ("bottom".equals(str) || "asc".equals(str)) {
+        top=false;
+      }  else {
+        return null;  // must not be a sort command
+      }
+
+      // get the field to sort on
+      // hmmm - should there be a fake/pseudo-field named "score" in the schema?
+      if ("score".equals(fn)) {
+        if (top) {
+          normalSortOnScore=true;
+          lst.add(SortField.FIELD_SCORE);
+        } else {
+          lst.add(new SortField(null, SortField.SCORE, true));
+        }
+      } else {
+        // getField could throw an exception if the name isn't found
+        try {
+          SchemaField f = schema.getField(fn);
+          if (f == null || !f.indexed()) return null;
+          lst.add(f.getType().getSortField(f,top));
+        } catch (Exception e) {
+          return null;
+        }
+      }
+      pos++;
+
+      // If there is a leftover part, assume it is a count
+      if (pos+1 == parts.length) {
+        try {
+          num = Integer.parseInt(parts[pos]);
+        } catch (Exception e) {
+          return null;
+        }
+        pos++;
+      }
+    }
+
+    Sort sort;
+    if (normalSortOnScore && lst.size() == 1) {
+      // Normalize the default sort on score descending to sort=null
+      sort=null;
+    } else {
+      sort = new Sort((SortField[]) lst.toArray(new SortField[lst.size()]));
+    }
+    return new SortSpec(sort,num);
+  }
+
+
+  ///////////////////////////
+  ///////////////////////////
+  ///////////////////////////
+
+  static FieldType writeFieldName(String name, IndexSchema schema, Appendable out, int flags) throws IOException {
+    FieldType ft = null;
+    ft = schema.getFieldTypeNoEx(name);
+    out.append(name);
+    if (ft==null) {
+      out.append("(UNKNOWN FIELD "+name+')');
+    }
+    out.append(':');
+    return ft;
+  }
+
+  static void writeFieldVal(String val, FieldType ft, Appendable out, int flags) throws IOException {
+    if (ft!=null) {
+      out.append(ft.toExternal(new Field("",val,true,true,false)));
+    } else {
+      out.append(val);
+    }
+  }
+
+  public static void toString(Query query, IndexSchema schema, Appendable out, int flags) throws IOException {
+    boolean writeBoost=true;
+
+    if (query instanceof TermQuery) {
+      TermQuery q = (TermQuery)query;
+      Term t = q.getTerm();
+      FieldType ft = writeFieldName(t.field(), schema, out, flags);
+      writeFieldVal(t.text(), ft, out, flags);
+    } else if (query instanceof RangeQuery) {
+      RangeQuery q = (RangeQuery)query;
+      String fname = q.getField();
+      FieldType ft = writeFieldName(fname, schema, out, flags);
+      out.append( q.isInclusive() ? '[' : '{' );
+      Term lt = q.getLowerTerm();
+      Term ut = q.getUpperTerm();
+      if (lt==null) {
+        out.append('*');
+      } else {
+        writeFieldVal(lt.text(), ft, out, flags);
+      }
+
+      out.append(" TO ");
+
+      if (ut==null) {
+        out.append('*');
+      } else {
+        writeFieldVal(ut.text(), ft, out, flags);
+      }
+
+      out.append( q.isInclusive() ? ']' : '}' );
+
+    } else if (query instanceof ConstantScoreRangeQuery) {
+      ConstantScoreRangeQuery q = (ConstantScoreRangeQuery)query;
+      String fname = q.getField();
+      FieldType ft = writeFieldName(fname, schema, out, flags);
+      out.append( q.includesLower() ? '[' : '{' );
+      String lt = q.getLowerVal();
+      String ut = q.getUpperVal();
+      if (lt==null) {
+        out.append('*');
+      } else {
+        writeFieldVal(lt, ft, out, flags);
+      }
+
+      out.append(" TO ");
+
+      if (ut==null) {
+        out.append('*');
+      } else {
+        writeFieldVal(ut, ft, out, flags);
+      }
+
+      out.append( q.includesUpper() ? ']' : '}' );
+    } else if (query instanceof BooleanQuery) {
+      BooleanQuery q = (BooleanQuery)query;
+      boolean needParens=false;
+
+      if (q.getBoost() != 1.0 || q.getMinimumNumberShouldMatch() != 0) {
+        needParens=true;
+      }
+      if (needParens) {
+        out.append('(');
+      }
+      BooleanClause[] clauses = q.getClauses();
+      boolean first=true;
+      for (BooleanClause c : clauses) {
+        if (!first) {
+          out.append(' ');
+        } else {
+          first=false;
+        }
+
+        if (c.prohibited) {
+          out.append('-');
+        } else if (c.required) {
+          out.append('+');
+        }
+        Query subQuery = c.query;
+        boolean wrapQuery=false;
+
+        // TODO: may need to put parens around other types
+        // of queries too, depending on future syntax.
+        if (subQuery instanceof BooleanQuery) {
+          wrapQuery=true;
+        }
+
+        if (wrapQuery) {
+          out.append('(');
+        }
+
+        toString(subQuery, schema, out, flags);
+
+        if (wrapQuery) {
+          out.append(')');
+        }
+      }
+
+      if (needParens) {
+        out.append(')');
+      }
+      if (q.getMinimumNumberShouldMatch()>0) {
+        out.append('~');
+        out.append(Integer.toString(q.getMinimumNumberShouldMatch()));
+      }
+
+    } else if (query instanceof PrefixQuery) {
+      PrefixQuery q = (PrefixQuery)query;
+      Term prefix = q.getPrefix();
+      FieldType ft = writeFieldName(prefix.field(), schema, out, flags);
+      out.append(prefix.text());
+      out.append('*');
+    } else if (query instanceof ConstantScorePrefixQuery) {
+      ConstantScorePrefixQuery q = (ConstantScorePrefixQuery)query;
+      Term prefix = q.getPrefix();
+      FieldType ft = writeFieldName(prefix.field(), schema, out, flags);
+      out.append(prefix.text());
+      out.append('*');
+    } else if (query instanceof WildcardQuery) {
+      out.append(query.toString());
+      writeBoost=false;
+    } else if (query instanceof FuzzyQuery) {
+      out.append(query.toString());
+      writeBoost=false;      
+    } else if (query instanceof ConstantScoreQuery) {
+      out.append(query.toString());
+      writeBoost=false;
+    } else {
+      out.append(query.getClass().getSimpleName()
+              + '(' + query.toString() + ')' );
+      writeBoost=false;
+    }
+
+    if (writeBoost && query.getBoost() != 1.0f) {
+      out.append("^");
+      out.append(Float.toString(query.getBoost()));
+    }
+
+  }
+
+  public static String toString(Query query, IndexSchema schema) {
+    try {
+      StringBuilder sb = new StringBuilder();
+      toString(query, schema, sb, 0);
+      return sb.toString();
+    } catch (Exception e) {
+      throw new RuntimeException(e);
+    }
+  }
+
+
+
+
+  // simple class to help with parsing a string
+  private static class StrParser {
+    String val;
+    int pos;
+    int end;
+
+    StrParser(String val) {this.val = val; end=val.length(); }
+
+    void eatws() {
+      while (pos<end && Character.isWhitespace(val.charAt(pos))) pos++;
+    }
+
+    boolean opt(String s) {
+      eatws();
+      int slen=s.length();
+      if (val.regionMatches(pos, s, 0, slen)) {
+        pos+=slen;
+        return true;
+      }
+      return false;
+    }
+
+    void expect(String s) throws ParseException {
+      eatws();
+      int slen=s.length();
+      if (val.regionMatches(pos, s, 0, slen)) {
+        pos+=slen;
+      } else {
+        throw new ParseException("Expected '"+s+"' at position " + pos + " in '"+val+"'");
+      }
+    }
+
+    float getFloat() throws ParseException {
+      eatws();
+      char[] arr = new char[end-pos];
+      int i;
+      for (i=0; i<arr.length; i++) {
+        char ch = val.charAt(pos);
+        if ( (ch>='0' && ch<='9')
+             || ch=='+' || ch=='-'
+             || ch=='.' || ch=='e' || ch=='E'
+        ) {
+          pos++;
+          arr[i]=ch;
+        } else {
+          break;
+        }
+      }
+
+      return Float.parseFloat(new String(arr,0,i));
+    }
+
+    String getId() throws ParseException {
+      eatws();
+      int id_start=pos;
+      while (pos<end && Character.isJavaIdentifierPart(val.charAt(pos))) pos++;
+      return val.substring(id_start, pos);
+    }
+
+    char peek() {
+      eatws();
+      return pos<end ? val.charAt(pos) : 0;
+    }
+
+    public String toString() {
+      return "'" + val + "'" + ", pos=" + pos;
+    }
+
+  }
+
+
+  private static ValueSource parseValSource(StrParser sp, IndexSchema schema) throws ParseException {
+    String id = sp.getId();
+    if (sp.opt("(")) {
+      // a function: could contain a fieldname or another function.
+      ValueSource vs=null;
+      if (id.equals("ord")) {
+        String field = sp.getId();
+        vs = new OrdFieldSource(field);
+      } else if (id.equals("rord")) {
+        String field = sp.getId();
+        vs = new ReverseOrdFieldSource(field);
+      } else if (id.equals("linear")) {
+        ValueSource source = parseValSource(sp, schema);
+        sp.expect(",");
+        float slope = sp.getFloat();
+        sp.expect(",");
+        float intercept = sp.getFloat();
+        vs = new LinearFloatFunction(source,slope,intercept);
+      } else if (id.equals("recip")) {
+        ValueSource source = parseValSource(sp,schema);
+        sp.expect(",");
+        float m = sp.getFloat();
+        sp.expect(",");
+        float a = sp.getFloat();
+        sp.expect(",");
+        float b = sp.getFloat();
+        vs = new ReciprocalFloatFunction(source,m,a,b);
+      } else {
+        throw new ParseException("Unknown function " + id + " in FunctionQuery(" + sp + ")");
+      }
+      sp.expect(")");
+      return vs;
+    }
+
+    SchemaField f = schema.getField(id);
+    return f.getType().getValueSource(f);
+  }
+
+  /** Parse a function, returning a FunctionQuery
+   */
+  public static FunctionQuery parseFunction(String func, IndexSchema schema) throws ParseException {
+    return new FunctionQuery(parseValSource(new StrParser(func), schema));
+  }
+
+}

Propchange: incubator/solr/trunk/src/java/org/apache/solr/search/QueryParsing.java
------------------------------------------------------------------------------
    svn:eol-style = native