SOLR-1291 SOLR-1322 SOLR-1325 SOLR-1328 : more trie integration

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@800565 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2009-08-03 21:09:05 +00:00
parent 0d3d010633
commit d8027f2de0
16 changed files with 518 additions and 154 deletions

View File

@ -180,10 +180,10 @@ New Features
and lucene query parser (the latter existed as an undocumented
feature in 1.3) (yonik)
40. SOLR-940: Add support for Lucene's Trie Range Queries by providing new FieldTypes in schema for int, float, long,
double and date. Range searches and term queries on such fields will automatically use the corresponding trie
range filter in Lucene contrib-queries and can be dramatically faster than normal range queries.
(Uwe Schindler, shalin)
40. SOLR-940: Add support for Lucene's Trie Range Queries by providing new FieldTypes in
schema for int, float, long, double and date. Single-valued Trie based
fields with a precisionStep will index multiple precisions and enable
faster range queries. (Uwe Schindler, yonik, shalin)
41. SOLR-1038: Enhance CommonsHttpSolrServer to add docs in batch using an iterator API (Noble Paul via shalin)

View File

@ -63,6 +63,7 @@
<!-- The optional sortMissingLast and sortMissingFirst attributes are
currently supported on types that are sorted internally as strings.
This includes "string","boolean","sint","slong","sfloat","sdouble","pdate"
- If sortMissingLast="true", then a sort on this field will cause documents
without the field to come after documents with the field,
regardless of the requested sort order (asc or desc).
@ -74,25 +75,31 @@
field first in an ascending sort and last in a descending sort.
-->
<!-- Default numeric field types. For faster range queries, use the tint/tfloat/tlong/tdouble types.
Note: the statistics component does not yet work with these field types.
-->
<fieldType name="int" class="solr.TrieIntField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="float" class="solr.TrieFloatField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="long" class="solr.TrieLongField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="double" class="solr.TrieDoubleField" precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
<!-- numeric field types that store and index the text
value verbatim (and hence don't support range queries, since the
lexicographic ordering isn't equal to the numeric ordering) -->
<fieldType name="integer" class="solr.IntField" omitNorms="true"/>
<fieldType name="long" class="solr.LongField" omitNorms="true"/>
<fieldType name="float" class="solr.FloatField" omitNorms="true"/>
<fieldType name="double" class="solr.DoubleField" omitNorms="true"/>
<!--
Numeric field types for single-valued fields that index extra tokens with
lower precision to accelerate range queries when the number of values between
the range endpoints is large. See the javadoc for NumericRangeQuery for
internal implementation details.
For single-valued fields, smaller precisionStep values (specified in bits)
will lead to more tokens indexed per value, slightly higher index size, and
faster range queries.
<!-- Numeric field types that manipulate the value into
a string value that isn't human-readable in its internal form,
but with a lexicographic ordering the same as the numeric ordering,
so that range queries work correctly. -->
<fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
Note: precisionStep is disabled for multiValued fields.
Note: faceting does not currently work for these fields.
-->
<fieldType name="tint" class="solr.TrieIntField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tfloat" class="solr.TrieFloatField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tlong" class="solr.TrieLongField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tdouble" class="solr.TrieDoubleField" precisionStep="8" omitNorms="true" positionIncrementGap="0"/>
<!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and
is a more restricted form of the canonical representation of dateTime
@ -114,35 +121,33 @@
Consult the DateField javadocs for more information.
-->
<fieldType name="date" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="date" class="solr.TrieDateField" omitNorms="true" precisionStep="0" positionIncrementGap="0"/>
<!-- A Trie based single-valued date field for faster date range queries and date faceting -->
<fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" precisionStep="8" positionIncrementGap="0"/>
<!-- plain numeric field types that store and index the text
value verbatim (and hence don't support range queries, since the
lexicographic ordering isn't equal to the numeric ordering)
These should only be used for compatibility with existing indexes.
Use Trie based fields instead.
-->
<fieldType name="pint" class="solr.IntField" omitNorms="true"/>
<fieldType name="plong" class="solr.LongField" omitNorms="true"/>
<fieldType name="pfloat" class="solr.FloatField" omitNorms="true"/>
<fieldType name="pdouble" class="solr.DoubleField" omitNorms="true"/>
<fieldType name="pdate" class="solr.DateField" sortMissingLast="true" omitNorms="true"/>
<!--
Numeric field types that manipulate the value into trie encoded strings which are not
human readable in the internal form. Range searches on such fields use the fast Trie Range Queries
which are much faster than range searches on the SortableNumberField types.
For the fast range search to work, trie fields must be indexed.
For each number being added to this field, multiple terms are generated as per the algorithm described in
org.apache.lucene.search.trie package description. The possible number of terms depend on the precisionStep
attribute and increase dramatically with higher precision steps (factor 2**precisionStep). The default
value of precisionStep is 8.
Note that if you use a precisionStep of 32 for int/float and 64 for long/double, then multiple terms
will not be generated, and range search will be no faster than any other number field.
These types should only be used for back compatibility with existing
indexes, or if "sortMissingLast" funcitonallity is needed. Use Trie based fields instead.
-->
<fieldType name="tint" class="solr.TrieField" type="integer" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tfloat" class="solr.TrieField" type="float" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tlong" class="solr.TrieField" type="long" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdouble" class="solr.TrieField" type="double" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdouble4" class="solr.TrieField" type="double" precisionStep="4" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<!--
This date field manipulates the value into a trie encoded strings for fast range searches. They follow the
same format and semantics as the normal DateField and support the date math syntax.
-->
<fieldType name="tdate" class="solr.TrieField" type="date" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="sint" class="solr.SortableIntField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="slong" class="solr.SortableLongField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
<!-- The "RandomSortField" is not used to store or search any
@ -356,17 +361,17 @@
<field name="features" type="text" indexed="true" stored="true" multiValued="true" termVectors="true" termPositions="true" termOffsets="true"/>
<field name="includes" type="text" indexed="true" stored="true"/>
<field name="weight" type="sfloat" indexed="true" stored="true"/>
<field name="price" type="sfloat" indexed="true" stored="true"/>
<field name="weight" type="float" indexed="true" stored="true"/>
<field name="price" type="float" indexed="true" stored="true"/>
<!-- "default" values can be specified for fields, indicating which
value should be used if no value is specified when adding a document.
-->
<field name="popularity" type="sint" indexed="true" stored="true"/>
<field name="popularity" type="int" indexed="true" stored="true"/>
<field name="inStock" type="boolean" indexed="true" stored="true"/>
<!-- Some sample docs exists solely to demonstrate the spellchecker
functionality, this is the only field they contain.
Typically you might build the spellchecker of "catchall" type field
Typically you might build the spellchecker off "catchall" type field
containing all of the text in each document.
-->
<field name="word" type="string" indexed="true" stored="true"/>
@ -389,17 +394,6 @@
<field name="spell" type="textSpell" indexed="true" stored="true" multiValued="true"/>
<!-- Some examples of trie fields -->
<field name="tint" type="tint" indexed="true" stored="false" />
<field name="tfloat" type="tfloat" indexed="true" stored="false" />
<field name="tlong" type="tlong" indexed="true" stored="false" />
<field name="tdouble" type="tdouble" indexed="true" stored="false" />
<!-- A double with a custom precisionStep -->
<field name="tdouble4" type="tdouble4" indexed="true" stored="false" />
<!-- An example for the trie date field -->
<field name="tdate" type="tdate" indexed="true" stored="true" />
<!-- Dynamic field definitions. If a field name is not found, dynamicFields
will be used if the name matches any of the patterns.
@ -408,19 +402,21 @@
EXAMPLE: name="*_i" will match any field ending in _i (like myid_i, z_i)
Longer patterns will be matched first. if equal size patterns
both match, the first appearing in the schema will be used. -->
<dynamicField name="*_i" type="sint" indexed="true" stored="true"/>
<dynamicField name="*_i" type="int" indexed="true" stored="true"/>
<dynamicField name="*_s" type="string" indexed="true" stored="true"/>
<dynamicField name="*_l" type="slong" indexed="true" stored="true"/>
<dynamicField name="*_l" type="long" indexed="true" stored="true"/>
<dynamicField name="*_t" type="text" indexed="true" stored="true"/>
<dynamicField name="*_b" type="boolean" indexed="true" stored="true"/>
<dynamicField name="*_f" type="sfloat" indexed="true" stored="true"/>
<dynamicField name="*_d" type="sdouble" indexed="true" stored="true"/>
<dynamicField name="*_f" type="float" indexed="true" stored="true"/>
<dynamicField name="*_d" type="double" indexed="true" stored="true"/>
<dynamicField name="*_dt" type="date" indexed="true" stored="true"/>
<dynamicField name="ignored_*" type="ignored"/>
<dynamicField name="*_pi" type="pint" indexed="true" stored="true"/>
<dynamicField name="ignored_*" type="ignored" multiValued="true"/>
<dynamicField name="attr_*" type="text" indexed="true" stored="true" multiValued="true"/>
<dynamicField name="random*" type="random" />
<dynamicField name="random_*" type="random" />
<!-- uncomment the following to ignore any fields that don't already match an existing
field name or dynamic field, rather than reporting them as an error.

View File

@ -211,6 +211,11 @@ public class BinaryResponseWriter implements BinaryQueryResponseWriter {
KNOWN_TYPES.add(StrField.class);
KNOWN_TYPES.add(TextField.class);
KNOWN_TYPES.add(TrieField.class);
KNOWN_TYPES.add(TrieIntField.class);
KNOWN_TYPES.add(TrieLongField.class);
KNOWN_TYPES.add(TrieFloatField.class);
KNOWN_TYPES.add(TrieDoubleField.class);
KNOWN_TYPES.add(TrieDateField.class);
KNOWN_TYPES.add(BinaryField.class);
// We do not add UUIDField because UUID object is not a supported type in JavaBinCodec
// and if we write UUIDField.toObject, we wouldn't know how to handle it in the client side

View File

@ -33,11 +33,7 @@ import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.core.SolrCore;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.BoolField;
import org.apache.solr.schema.DateField;
import org.apache.solr.schema.*;
import org.apache.solr.search.*;
import org.apache.solr.util.BoundedTreeSet;
import org.apache.solr.util.DateMathParser;
@ -556,13 +552,13 @@ public class SimpleFacets {
final NamedList resInner = new SimpleOrderedMap();
resOuter.add(key, resInner);
final FieldType trash = schema.getFieldType(f);
if (! (trash instanceof DateField)) {
final SchemaField sf = schema.getField(f);
if (! (sf.getType() instanceof DateField)) {
throw new SolrException
(SolrException.ErrorCode.BAD_REQUEST,
"Can not date facet on a field which is not a DateField: " + f);
}
final DateField ft = (DateField) trash;
final DateField ft = (DateField) sf.getType();
final String startS
= required.getFieldParam(f,FacetParams.FACET_DATE_START);
final Date start;
@ -600,7 +596,9 @@ public class SimpleFacets {
while (low.before(end)) {
dmp.setNow(low);
final String lowI = ft.toInternal(low);
final String label = ft.indexedToReadable(lowI);
// final String label = ft.indexedToReadable(lowI);
String label = ft.toExternal(low);
Date high = dmp.parseMath(gap);
if (end.before(high)) {
if (params.getFieldBool(f,FacetParams.FACET_DATE_HARD_END,false)) {
@ -615,7 +613,8 @@ public class SimpleFacets {
"date facet infinite loop (is gap negative?)");
}
final String highI = ft.toInternal(high);
resInner.add(label, rangeCount(f,lowI,highI,true,true));
// resInner.add(label, rangeCount(sf,lowI,highI,true,true));
resInner.add(label, rangeCount(sf,low,high,true,true));
low = high;
}
} catch (java.text.ParseException e) {
@ -647,15 +646,15 @@ public class SimpleFacets {
if (all || others.contains(FacetDateOther.BEFORE)) {
resInner.add(FacetDateOther.BEFORE.toString(),
rangeCount(f,null,startI,false,false));
rangeCount(sf,null,start,false,false));
}
if (all || others.contains(FacetDateOther.AFTER)) {
resInner.add(FacetDateOther.AFTER.toString(),
rangeCount(f,endI,null,false,false));
rangeCount(sf,end,null,false,false));
}
if (all || others.contains(FacetDateOther.BETWEEN)) {
resInner.add(FacetDateOther.BETWEEN.toString(),
rangeCount(f,startI,endI,true,true));
rangeCount(sf,start,end,true,true));
}
}
}
@ -665,14 +664,20 @@ public class SimpleFacets {
}
/**
* Macro for getting the numDocs of a TermRangeQuery over docs
* Macro for getting the numDocs of range over docs
* @see SolrIndexSearcher#numDocs
* @see TermRangeQuery
*/
protected int rangeCount(String field, String low, String high,
protected int rangeCount(SchemaField sf, String low, String high,
boolean iLow, boolean iHigh) throws IOException {
return searcher.numDocs(new TermRangeQuery(field,low,high,iLow,iHigh),
base);
Query rangeQ = sf.getType().getRangeQuery(null, sf,low,high,iLow,iHigh);
return searcher.numDocs(rangeQ ,base);
}
protected int rangeCount(SchemaField sf, Date low, Date high,
boolean iLow, boolean iHigh) throws IOException {
Query rangeQ = ((DateField)(sf.getType())).getRangeQuery(null, sf,low,high,iLow,iHigh);
return searcher.numDocs(rangeQ ,base);
}
/**

View File

@ -22,6 +22,8 @@ import org.apache.solr.request.XMLWriter;
import org.apache.solr.request.TextResponseWriter;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.index.IndexReader;
import org.apache.solr.search.function.*;
import org.apache.solr.search.QParser;
@ -158,7 +160,8 @@ public class DateField extends FieldType {
if (0 < zz) {
math = val.substring(zz+1);
try {
p.setNow(toObject(val.substring(0,zz)));
// p.setNow(toObject(val.substring(0,zz)));
p.setNow(parseDate(val.substring(0,zz+1)));
} catch (ParseException e) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
"Invalid Date in Date Math String:'"
@ -193,6 +196,7 @@ public class DateField extends FieldType {
public String toExternal(Fieldable f) {
return indexedToReadable(f.stringValue());
}
public Date toObject(String indexedForm) throws java.text.ParseException {
return parseDate(indexedToReadable(indexedForm));
}
@ -243,6 +247,13 @@ public class DateField extends FieldType {
return fmtThreadLocal.get().format(d);
}
/**
* Return the standard human readable form of the date
*/
public String toExternal(Date d) {
return fmtThreadLocal.get().format(d) + 'Z';
}
/**
* Thread safe method that can be used by subclasses to parse a Date
* that is already in the internal representation
@ -336,6 +347,16 @@ public class DateField extends FieldType {
public ValueSource getValueSource(SchemaField field, QParser parser) {
return new DateFieldSource(field.getName(), field.getType());
}
/** DateField specific range query */
public Query getRangeQuery(QParser parser, SchemaField sf, Date part1, Date part2, boolean minInclusive, boolean maxInclusive) {
return new TermRangeQuery(
sf.getName(),
part1 == null ? null : toInternal(part1),
part2 == null ? null : toInternal(part2),
minInclusive, maxInclusive);
}
}

View File

@ -233,7 +233,7 @@ public abstract class FieldType extends FieldProperties {
/**
* Convert an external value (from XML update command or from query string)
* into the internal format.
* into the internal format for both storing and indexing (which can be modified by any analyzers).
* @see #toExternal
*/
public String toInternal(String val) {
@ -281,6 +281,10 @@ public abstract class FieldType extends FieldProperties {
return f.stringValue();
}
/** Given the readable value, return the term value that will match it. */
public String readableToIndexed(String val) {
return toInternal(val);
}
/*********
// default analyzer for non-text fields.
@ -437,7 +441,7 @@ public abstract class FieldType extends FieldProperties {
* handle nulls in part1 and/or part2 as well as unequal minInclusive and maxInclusive parameters gracefully.
*
* @param parser
* @param field the name of the field
* @param field the schema field
* @param part1 the lower boundary of the range, nulls are allowed.
* @param part2 the upper boundary of the range, nulls are allowed
* @param minInclusive whether the minimum of the range is inclusive or not
@ -446,10 +450,10 @@ public abstract class FieldType extends FieldProperties {
*
* @see org.apache.solr.search.SolrQueryParser#getRangeQuery(String, String, String, boolean)
*/
public Query getRangeQuery(QParser parser, String field, String part1, String part2, boolean minInclusive, boolean maxInclusive) {
public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) {
// constant score mode is now enabled per default
return new TermRangeQuery(
field,
field.getName(),
part1 == null ? null : toInternal(part1),
part2 == null ? null : toInternal(part2),
minInclusive, maxInclusive);

View File

@ -17,10 +17,14 @@
package org.apache.solr.schema;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.document.Field;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.FieldCache;
import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.NumericTokenStream;
import org.apache.solr.analysis.*;
import org.apache.solr.common.SolrException;
import org.apache.solr.request.TextResponseWriter;
@ -30,6 +34,7 @@ import org.apache.solr.search.function.*;
import java.io.IOException;
import java.util.Map;
import java.util.Date;
/**
* Provides field types to support for Lucene's Trie Range Queries.
@ -43,7 +48,7 @@ import java.util.Map;
* Trie fields are sortable in numerical order and can be used in function queries.
* <p/>
* Note that if you use a precisionStep of 32 for int/float and 64 for long/double, then multiple terms will not be
* generated, range search will be no faster than any other number field, but sorting will be possible.
* generated, range search will be no faster than any other number field, but sorting will still be possible.
*
* @version $Id$
* @see org.apache.lucene.search.NumericRangeQuery
@ -52,25 +57,27 @@ import java.util.Map;
public class TrieField extends FieldType {
public static final int DEFAULT_PRECISION_STEP = 8;
protected int precisionStep = TrieField.DEFAULT_PRECISION_STEP;
protected int precisionStepArg = TrieField.DEFAULT_PRECISION_STEP; // the one passed in or defaulted
protected int precisionStep; // normalized
protected TrieTypes type;
/**
* Used for handling date types following the same semantics as DateField
*/
private static final DateField dateField = new DateField();
static final DateField dateField = new DateField();
@Override
protected void init(IndexSchema schema, Map<String, String> args) {
String p = args.remove("precisionStep");
if (p != null) {
precisionStep = Integer.parseInt(p);
precisionStepArg = Integer.parseInt(p);
}
// normalize the precisionStep
precisionStep = precisionStepArg;
if (precisionStep<=0 || precisionStep>=64) precisionStep=Integer.MAX_VALUE;
String t = args.remove("type");
if (t == null) {
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
"Invalid type specified in schema.xml for field: " + args.get("name"));
} else {
if (t != null) {
try {
type = TrieTypes.valueOf(t.toUpperCase());
} catch (IllegalArgumentException e) {
@ -79,6 +86,7 @@ public class TrieField extends FieldType {
}
}
CharFilterFactory[] filterFactories = new CharFilterFactory[0];
TokenFilterFactory[] tokenFilterFactories = new TokenFilterFactory[0];
analyzer = new TokenizerChain(filterFactories, new TrieTokenizerFactory(type, precisionStep), tokenFilterFactories);
@ -88,18 +96,19 @@ public class TrieField extends FieldType {
@Override
public Object toObject(Fieldable f) {
String s = f.stringValue();
byte[] arr = f.binaryValue();
if (arr==null) return badFieldString(f);
switch (type) {
case INTEGER:
return Integer.parseInt(s);
return toInt(arr);
case FLOAT:
return Float.parseFloat(s);
return toFloat(arr);
case LONG:
return Long.parseLong(s);
return toLong(arr);
case DOUBLE:
return Double.parseDouble(s);
return toDouble(arr);
case DATE:
return dateField.toObject(f);
return new Date(toLong(arr));
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + f.name());
}
@ -111,7 +120,7 @@ public class TrieField extends FieldType {
return new SortField(field.getName(), FieldCache.NUMERIC_UTILS_INT_PARSER, top);
case FLOAT:
return new SortField(field.getName(), FieldCache.NUMERIC_UTILS_FLOAT_PARSER, top);
case DATE:
case DATE: // fallthrough
case LONG:
return new SortField(field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER, top);
case DOUBLE:
@ -128,6 +137,7 @@ public class TrieField extends FieldType {
case FLOAT:
return new FloatFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_FLOAT_PARSER);
case DATE:
return new TrieDateFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER);
case LONG:
return new LongFieldSource(field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER);
case DOUBLE:
@ -138,11 +148,58 @@ public class TrieField extends FieldType {
}
public void write(XMLWriter xmlWriter, String name, Fieldable f) throws IOException {
xmlWriter.writeVal(name, toObject(f));
byte[] arr = f.binaryValue();
if (arr==null) {
xmlWriter.writeStr(name, badFieldString(f));
return;
}
switch (type) {
case INTEGER:
xmlWriter.writeInt(name,toInt(arr));
break;
case FLOAT:
xmlWriter.writeFloat(name,toFloat(arr));
break;
case LONG:
xmlWriter.writeLong(name,toLong(arr));
break;
case DOUBLE:
xmlWriter.writeDouble(name,toDouble(arr));
break;
case DATE:
xmlWriter.writeDate(name,new Date(toLong(arr)));
break;
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + f.name());
}
}
public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException {
writer.writeVal(name, toObject(f));
byte[] arr = f.binaryValue();
if (arr==null) {
writer.writeStr(name, badFieldString(f),true);
return;
}
switch (type) {
case INTEGER:
writer.writeInt(name,toInt(arr));
break;
case FLOAT:
writer.writeFloat(name,toFloat(arr));
break;
case LONG:
writer.writeLong(name,toLong(arr));
break;
case DOUBLE:
writer.writeDouble(name,toDouble(arr));
break;
case DATE:
writer.writeDate(name,new Date(toLong(arr)));
break;
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + f.name());
}
}
@Override
@ -154,7 +211,7 @@ public class TrieField extends FieldType {
* @return the precisionStep used to index values into the field
*/
public int getPrecisionStep() {
return precisionStep;
return precisionStepArg;
}
/**
@ -165,35 +222,38 @@ public class TrieField extends FieldType {
}
@Override
public Query getRangeQuery(QParser parser, String field, String min, String max, boolean minInclusive, boolean maxInclusive) {
public Query getRangeQuery(QParser parser, SchemaField field, String min, String max, boolean minInclusive, boolean maxInclusive) {
// don't use a precisionStep if the field is multiValued
int ps = field.multiValued() ? Integer.MAX_VALUE : precisionStep;
Query query = null;
switch (type) {
case INTEGER:
query = NumericRangeQuery.newIntRange(field, precisionStep,
query = NumericRangeQuery.newIntRange(field.getName(), ps,
min == null ? null : Integer.parseInt(min),
max == null ? null : Integer.parseInt(max),
minInclusive, maxInclusive);
break;
case FLOAT:
query = NumericRangeQuery.newFloatRange(field, precisionStep,
query = NumericRangeQuery.newFloatRange(field.getName(), ps,
min == null ? null : Float.parseFloat(min),
max == null ? null : Float.parseFloat(max),
minInclusive, maxInclusive);
break;
case LONG:
query = NumericRangeQuery.newLongRange(field, precisionStep,
query = NumericRangeQuery.newLongRange(field.getName(), ps,
min == null ? null : Long.parseLong(min),
max == null ? null : Long.parseLong(max),
minInclusive, maxInclusive);
break;
case DOUBLE:
query = NumericRangeQuery.newDoubleRange(field, precisionStep,
query = NumericRangeQuery.newDoubleRange(field.getName(), ps,
min == null ? null : Double.parseDouble(min),
max == null ? null : Double.parseDouble(max),
minInclusive, maxInclusive);
break;
case DATE:
query = NumericRangeQuery.newLongRange(field, precisionStep,
query = NumericRangeQuery.newLongRange(field.getName(), ps,
min == null ? null : dateField.parseMath(null, min).getTime(),
max == null ? null : dateField.parseMath(null, max).getTime(),
minInclusive, maxInclusive);
@ -205,24 +265,196 @@ public class TrieField extends FieldType {
return query;
}
static int toInt(byte[] arr) {
return (arr[0]<<24) | ((arr[1]&0xff)<<16) | ((arr[2]&0xff)<<8) | (arr[3]&0xff);
}
static long toLong(byte[] arr) {
int high = (arr[0]<<24) | ((arr[1]&0xff)<<16) | ((arr[2]&0xff)<<8) | (arr[3]&0xff);
int low = (arr[4]<<24) | ((arr[5]&0xff)<<16) | ((arr[6]&0xff)<<8) | (arr[7]&0xff);
return (((long)high)<<32) | (low&0x0ffffffffL);
}
static float toFloat(byte[] arr) {
return Float.intBitsToFloat(toInt(arr));
}
static double toDouble(byte[] arr) {
return Double.longBitsToDouble(toLong(arr));
}
static byte[] toArr(int val) {
byte[] arr = new byte[4];
arr[0] = (byte)(val>>>24);
arr[1] = (byte)(val>>>16);
arr[2] = (byte)(val>>>8);
arr[3] = (byte)(val);
return arr;
}
static byte[] toArr(long val) {
byte[] arr = new byte[8];
arr[0] = (byte)(val>>>56);
arr[1] = (byte)(val>>>48);
arr[2] = (byte)(val>>>40);
arr[3] = (byte)(val>>>32);
arr[4] = (byte)(val>>>24);
arr[5] = (byte)(val>>>16);
arr[6] = (byte)(val>>>8);
arr[7] = (byte)(val);
return arr;
}
static byte[] toArr(float val) {
return toArr(Float.floatToRawIntBits(val));
}
static byte[] toArr(double val) {
return toArr(Double.doubleToRawLongBits(val));
}
@Override
public String storedToReadable(Fieldable f) {
return toExternal(f);
}
@Override
public String readableToIndexed(String val) {
switch (type) {
case INTEGER:
return NumericUtils.intToPrefixCoded(Integer.parseInt(val));
case FLOAT:
return NumericUtils.intToPrefixCoded(NumericUtils.floatToSortableInt(Float.parseFloat(val)));
case LONG:
return NumericUtils.longToPrefixCoded(Long.parseLong(val));
case DOUBLE:
return NumericUtils.longToPrefixCoded(NumericUtils.doubleToSortableLong(Double.parseDouble(val)));
case DATE:
return NumericUtils.longToPrefixCoded(dateField.parseMath(null, val).getTime());
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
}
}
@Override
public String toInternal(String val) {
return super.toInternal(val);
return readableToIndexed(val);
}
static String badFieldString(Fieldable f) {
String s = f.stringValue();
return "ERROR:SCHEMA-INDEX-MISMATCH,stringValue="+s;
}
@Override
public String toExternal(Fieldable f) {
return super.toExternal(f);
byte[] arr = f.binaryValue();
if (arr==null) return badFieldString(f);
switch (type) {
case INTEGER:
return Integer.toString(toInt(arr));
case FLOAT:
return Float.toString(toFloat(arr));
case LONG:
return Long.toString(toLong(arr));
case DOUBLE:
return Double.toString(toDouble(arr));
case DATE:
return dateField.formatDate(new Date(toLong(arr)));
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + f.name());
}
}
@Override
public String indexedToReadable(String indexedForm) {
return super.indexedToReadable(indexedForm);
switch (type) {
case INTEGER:
return Integer.toString( NumericUtils.prefixCodedToInt(indexedForm) );
case FLOAT:
return Float.toString( NumericUtils.sortableIntToFloat(NumericUtils.prefixCodedToInt(indexedForm)) );
case LONG:
return Long.toString( NumericUtils.prefixCodedToLong(indexedForm) );
case DOUBLE:
return Double.toString( NumericUtils.sortableLongToDouble(NumericUtils.prefixCodedToLong(indexedForm)) );
case DATE:
return dateField.formatDate( new Date(NumericUtils.prefixCodedToLong(indexedForm)) );
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
}
}
@Override
public String storedToIndexed(Fieldable f) {
return super.storedToIndexed(f);
// TODO: optimize to remove redundant string conversion
return readableToIndexed(storedToReadable(f));
}
@Override
public Field createField(SchemaField field, String externalVal, float boost) {
boolean indexed = field.indexed();
boolean stored = field.stored();
if (!indexed && !stored) {
if (log.isTraceEnabled())
log.trace("Ignoring unindexed/unstored field: " + field);
return null;
}
int ps = field.multiValued() ? Integer.MAX_VALUE : precisionStep;
byte[] arr=null;
TokenStream ts=null;
// String indexedVal = indexed && precisionStep==0 ? readableToIndexed(externalVal) : null;
switch (type) {
case INTEGER:
int i = Integer.parseInt(externalVal);
if (stored) arr = toArr(i);
if (indexed) ts = new NumericTokenStream(ps).setIntValue(i);
break;
case FLOAT:
float f = Float.parseFloat(externalVal);
if (stored) arr = toArr(f);
if (indexed) ts = new NumericTokenStream(ps).setFloatValue(f);
break;
case LONG:
long l = Long.parseLong(externalVal);
if (stored) arr = toArr(l);
if (indexed) ts = new NumericTokenStream(ps).setLongValue(l);
break;
case DOUBLE:
double d = Double.parseDouble(externalVal);
if (stored) arr = toArr(d);
if (indexed) ts = new NumericTokenStream(ps).setDoubleValue(d);
break;
case DATE:
long time = dateField.parseMath(null, externalVal).getTime();
if (stored) arr = toArr(time);
if (indexed) ts = new NumericTokenStream(ps).setLongValue(time);
break;
default:
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Unknown type for trie field: " + type);
}
Field f;
if (stored) {
f = new Field(field.getName(), arr, Field.Store.YES);
if (indexed) f.setTokenStream(ts);
} else {
f = new Field(field.getName(), ts);
}
// term vectors aren't supported
f.setOmitNorms(field.omitNorms());
f.setOmitTermFreqAndPositions(field.omitTf());
f.setBoost(boost);
return f;
}
public enum TrieTypes {
@ -233,3 +465,23 @@ public class TrieField extends FieldType {
DATE
}
}
class TrieDateFieldSource extends LongFieldSource {
public TrieDateFieldSource(String field, FieldCache.LongParser parser) {
super(field, parser);
}
public TrieDateFieldSource(String field) {
super(field);
}
public String description() {
return "date(" + field + ')';
}
@Override
public long externalToLong(String extVal) {
return TrieField.dateField.parseMath(null, extVal).getTime();
}
}

View File

@ -303,7 +303,13 @@ public class QueryParsing {
static void writeFieldVal(String val, FieldType ft, Appendable out, int flags) throws IOException {
if (ft!=null) {
out.append(ft.toExternal(new Field("",val, Field.Store.YES, Field.Index.UN_TOKENIZED)));
try {
out.append(ft.indexedToReadable(val));
} catch (Exception e) {
out.append("EXCEPTION(val=");
out.append(val);
out.append(")");
}
} else {
out.append(val);
}
@ -349,7 +355,7 @@ public class QueryParsing {
if (lt==null) {
out.append('*');
} else {
writeFieldVal(lt.toString(), ft, out, flags);
out.append(lt.toString());
}
out.append(" TO ");
@ -357,7 +363,7 @@ public class QueryParsing {
if (ut==null) {
out.append('*');
} else {
writeFieldVal(ut.toString(), ft, out, flags);
out.append(ut.toString());
}
out.append( q.includesMax() ? ']' : '}' );

View File

@ -26,6 +26,7 @@ import org.apache.solr.common.SolrException;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.schema.TrieField;
import org.apache.solr.schema.SchemaField;
// TODO: implement the analysis of simple fields with
// FieldType.toInternal() instead of going through the
@ -118,8 +119,8 @@ public class SolrQueryParser extends QueryParser {
protected Query getRangeQuery(String field, String part1, String part2, boolean inclusive) throws ParseException {
checkNullField(field);
FieldType ft = schema.getFieldType(field);
return ft.getRangeQuery(parser, field,
SchemaField sf = schema.getField(field);
return sf.getType().getRangeQuery(parser, sf,
"*".equals(part1) ? null : part1,
"*".equals(part2) ? null : part2,
inclusive, inclusive);

View File

@ -47,6 +47,11 @@ public class LongFieldSource extends FieldCacheSource {
return "long(" + field + ')';
}
public long externalToLong(String extVal) {
return Long.parseLong(extVal);
}
public DocValues getValues(IndexReader reader) throws IOException {
final long[] arr = (parser == null) ?
((FieldCache) cache).getLongs(reader, field) :
@ -76,7 +81,6 @@ public class LongFieldSource extends FieldCacheSource {
return description() + '=' + longVal(doc);
}
@Override
public ValueSourceScorer getRangeScorer(IndexReader reader, String lowerVal, String upperVal, boolean includeLower, boolean includeUpper) {
long lower,upper;
@ -86,14 +90,14 @@ public class LongFieldSource extends FieldCacheSource {
if (lowerVal==null) {
lower = Long.MIN_VALUE;
} else {
lower = Long.parseLong(lowerVal);
lower = externalToLong(lowerVal);
if (!includeLower && lower < Long.MAX_VALUE) lower++;
}
if (upperVal==null) {
upper = Long.MAX_VALUE;
} else {
upper = Long.parseLong(upperVal);
upper = externalToLong(upperVal);
if (!includeUpper && upper > Long.MIN_VALUE) upper--;
}
@ -116,7 +120,7 @@ public class LongFieldSource extends FieldCacheSource {
}
public boolean equals(Object o) {
if (o.getClass() != LongFieldSource.class) return false;
if (o.getClass() != this.getClass()) return false;
LongFieldSource other = (LongFieldSource) o;
return super.equals(other)
&& this.parser == null ? other.parser == null :
@ -124,7 +128,7 @@ public class LongFieldSource extends FieldCacheSource {
}
public int hashCode() {
int h = parser == null ? Long.class.hashCode() : parser.getClass().hashCode();
int h = parser == null ? this.getClass().hashCode() : parser.getClass().hashCode();
h += super.hashCode();
return h;
}

View File

@ -180,7 +180,8 @@ public abstract class AbstractSolrTestCase extends TestCase {
String results = h.validateXPath(response, tests);
if (null != results) {
fail(m + "query failed XPath: " + results +
" xml response was: " + response);
"\n xml response was: " + response +
"\n request was: " + req.getParamString());
}
} catch (XPathExpressionException e1) {
throw new RuntimeException("XPath is invalid", e1);

View File

@ -337,13 +337,15 @@ abstract public class SolrExampleTests extends SolrExampleTestBase
server.commit();
assertNumFound( "*:*", 0 ); // make sure it got in
String f = "val_pi";
int i=0; // 0 1 2 3 4 5 6 7 8 9
int[] nums = new int[] { 23, 26, 38, 46, 55, 63, 77, 84, 92, 94 };
for( int num : nums ) {
SolrInputDocument doc = new SolrInputDocument();
doc.setField( "id", "doc"+i++ );
doc.setField( "name", "doc: "+num );
doc.setField( "popularity", num );
doc.setField( f, num );
server.add( doc );
}
server.commit();
@ -351,10 +353,10 @@ abstract public class SolrExampleTests extends SolrExampleTestBase
SolrQuery query = new SolrQuery( "*:*" );
query.setRows( 0 );
query.setGetFieldStatistics( "popularity" );
query.setGetFieldStatistics( f );
QueryResponse rsp = server.query( query );
FieldStatsInfo stats = rsp.getFieldStatsInfo().get( "popularity" );
FieldStatsInfo stats = rsp.getFieldStatsInfo().get( f );
assertNotNull( stats );
assertEquals( 23.0, stats.getMin() );
@ -373,14 +375,14 @@ abstract public class SolrExampleTests extends SolrExampleTestBase
SolrInputDocument doc = new SolrInputDocument();
doc.setField( "id", "doc"+i++ );
doc.setField( "name", "doc: "+num );
doc.setField( "popularity", num );
doc.setField( f, num );
server.add( doc );
}
server.commit();
assertNumFound( "*:*", nums.length ); // make sure they all got in
rsp = server.query( query );
stats = rsp.getFieldStatsInfo().get( "popularity" );
stats = rsp.getFieldStatsInfo().get( f );
assertNotNull( stats );
assertEquals( 5.0, stats.getMin() );
@ -399,7 +401,7 @@ abstract public class SolrExampleTests extends SolrExampleTestBase
SolrInputDocument doc = new SolrInputDocument();
doc.setField( "id", "doc"+i );
doc.setField( "name", "doc: "+num );
doc.setField( "popularity", num );
doc.setField( f, num );
doc.setField( "inStock", i < 5 );
server.add( doc );
}
@ -408,9 +410,9 @@ abstract public class SolrExampleTests extends SolrExampleTestBase
assertNumFound( "inStock:false", 5 ); // make sure they all got in
// facet on 'inStock'
query.addStatsFieldFacets( "popularity", "inStock" );
query.addStatsFieldFacets( f, "inStock" );
rsp = server.query( query );
stats = rsp.getFieldStatsInfo().get( "popularity" );
stats = rsp.getFieldStatsInfo().get( f );
assertNotNull( stats );
List<FieldStatsInfo> facets = stats.getFacets().get( "inStock" );

View File

@ -46,8 +46,65 @@ public class TestQueryTypes extends AbstractSolrTestCase {
assertU(adoc("id","6", "v_f","8983"));
assertU(adoc("id","7", "v_f","1.5"));
assertU(adoc("id","8", "v_ti","5"));
Object[] arr = new Object[] {
"id",999.0
,"v_s","wow dude"
,"v_t","wow"
,"v_ti",-1
,"v_tis",-1
,"v_tl",-1234567891234567890L
,"v_tls",-1234567891234567890L
,"v_tf",-2.0f
,"v_tfs",-2.0f
,"v_td",-2.0
,"v_tds",-2.0
,"v_tdt","2000-05-10T01:01:01Z"
,"v_tdts","2002-08-26T01:01:01Z"
};
String[] sarr = new String[arr.length];
for (int i=0; i<arr.length; i++) {
sarr[i] = arr[i].toString();
}
assertU(adoc(sarr));
assertU(optimize());
// test field queries
for (int i=0; i<arr.length; i+=2) {
String f = arr[i].toString();
String v = arr[i+1].toString();
// normal lucene fielded query
assertQ(req( "q",f+":\""+v+'"')
,"//result[@numFound='1']"
,"//*[@name='id'][.='999.0']"
,"//*[@name='" + f + "'][.='" + v + "']"
);
// System.out.println("#########################################" + f + "=" + v);
// field qparser
assertQ(req( "q", "{!field f="+f+"}"+v)
,"//result[@numFound='1']"
);
// lucene range
assertQ(req( "q", f + ":[\"" + v + "\" TO \"" + v + "\"]" )
,"//result[@numFound='1']"
);
// frange qparser
assertQ(req( "q", "{!frange v="+f+" l='"+v+"' u='"+v+"'}" )
,"//result[@numFound='1']"
);
// function query... just make sure it doesn't throw an exception
assertQ(req( "q", "+id:999 _val_:\"" + f + "\"")
,"//result[@numFound='1']"
);
}
// Some basic tests to ensure that parsing local params is working
assertQ("test prefix query",
@ -85,12 +142,10 @@ public class TestQueryTypes extends AbstractSolrTestCase {
,"//result[@numFound='1']"
);
/** future test
assertQ(
req("q","{!field f=v_ti}5")
,"//result[@numFound='1']"
);
**/
assertQ("test multi term field query on text type",
req("q","{!field f=v_t}Hello DUDE")

View File

@ -94,6 +94,7 @@ public class TestRangeQuery extends AbstractSolrTestCase {
norm_fields.put("foo_ti", ints);
norm_fields.put("foo_tl", longs);
norm_fields.put("foo_td", doubles);
norm_fields.put("foo_tdt", dates);
norm_fields.put("foo_s", strings);
norm_fields.put("foo_dt", dates);
@ -108,6 +109,7 @@ public class TestRangeQuery extends AbstractSolrTestCase {
frange_fields.put("foo_ti", ints);
frange_fields.put("foo_tl", longs);
frange_fields.put("foo_td", doubles);
frange_fields.put("foo_tdt", dates);
frange_fields.put("foo_pi", ints);
frange_fields.put("foo_pl", longs);

View File

@ -88,14 +88,14 @@
<fieldType name="sfloat" class="solr.SortableFloatField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="sdouble" class="solr.SortableDoubleField" sortMissingLast="true" omitNorms="true"/>
<fieldType name="tint" class="solr.TrieField" type="integer" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tfloat" class="solr.TrieField" type="float" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tlong" class="solr.TrieField" type="long" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdouble" class="solr.TrieField" type="double" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tint" class="solr.TrieIntField" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tfloat" class="solr.TrieFloatField" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tlong" class="solr.TrieLongField" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdouble" class="solr.TrieDoubleField" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdouble4" class="solr.TrieField" type="double" precisionStep="4" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdouble4" class="solr.TrieDoubleField" type="double" precisionStep="4" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdate" class="solr.TrieField" type="date" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<!-- The format for this date field is of the form 1995-12-31T23:59:59Z, and

View File

@ -237,15 +237,19 @@
<fieldType name="file" keyField="id" defVal="1" stored="false" indexed="false" class="solr.ExternalFileField" valType="float"/>
<fieldType name="tint" class="solr.TrieField" type="integer" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tfloat" class="solr.TrieField" type="float" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tlong" class="solr.TrieField" type="long" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdouble" class="solr.TrieField" type="double" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tint" class="solr.TrieIntField" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tfloat" class="solr.TrieFloatField" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tlong" class="solr.TrieLongField" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tdouble" class="solr.TrieDoubleField" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tdouble4" class="solr.TrieDoubleField" precisionStep="4" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tdate" class="solr.TrieDateField" omitNorms="true" positionIncrementGap="0"/>
<fieldType name="tdouble4" class="solr.TrieField" type="double" precisionStep="4" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tdate" class="solr.TrieField" type="date" omitNorms="true" positionIncrementGap="0" indexed="true" stored="false" />
<fieldType name="tints" class="solr.TrieIntField" omitNorms="true" positionIncrementGap="0" precisionStep="0" multiValued="true" />
<fieldType name="tfloats" class="solr.TrieFloatField" omitNorms="true" positionIncrementGap="0" precisionStep="0" multiValued="true"/>
<fieldType name="tlongs" class="solr.TrieLongField" omitNorms="true" positionIncrementGap="0" precisionStep="0" multiValued="true"/>
<fieldType name="tdoubles" class="solr.TrieDoubleField" omitNorms="true" positionIncrementGap="0" precisionStep="0" multiValued="true" />
<fieldType name="tdates" class="solr.TrieDateField" omitNorms="true" positionIncrementGap="0" precisionStep="0" multiValued="true" />
</types>
@ -298,6 +302,12 @@
<dynamicField name="*_td" type="tdouble" indexed="true" stored="true"/>
<dynamicField name="*_tdt" type="tdate" indexed="true" stored="true"/>
<dynamicField name="*_tis" type="tints" indexed="true" stored="true"/>
<dynamicField name="*_tls" type="tlongs" indexed="true" stored="true"/>
<dynamicField name="*_tfs" type="tfloats" indexed="true" stored="true"/>
<dynamicField name="*_tds" type="tdoubles" indexed="true" stored="true"/>
<dynamicField name="*_tdts" type="tdates" indexed="true" stored="true"/>
<dynamicField name="*_t" type="text" indexed="true" stored="true"/>
<dynamicField name="*_b" type="boolean" indexed="true" stored="true"/>
<dynamicField name="*_dt" type="date" indexed="true" stored="true"/>