Field Types¶
Forage provides several field types that map to Lucene field types. Choosing the right field type is crucial for both search functionality and performance.
Field Types Overview¶
| Field Type | Purpose | Searchable | Sortable | Range Queries | Analysis |
|---|---|---|---|---|---|
TextField |
Full-text search | Yes | No | No | Tokenized |
StringField |
Exact matching | Yes | Yes | No | Not analyzed |
IntField |
Integer values | Yes | Yes | Yes | Not analyzed |
FloatField |
Decimal values | Yes | Yes | Yes | Not analyzed |
TextField¶
Use TextField for content that needs full-text search with tokenization.
new TextField("title", "The Quick Brown Fox")
How It Works¶
The text is analyzed (tokenized, lowercased) before indexing:
Input: "The Quick Brown Fox"
↓ (analysis)
Tokens: ["the", "quick", "brown", "fox"]
Search Behavior¶
// Matches "The Quick Brown Fox"
QueryBuilder.matchQuery("title", "quick") // ✓ Match
QueryBuilder.matchQuery("title", "QUICK") // ✓ Match (case-insensitive)
QueryBuilder.matchQuery("title", "fox brown") // ✓ Match (order doesn't matter)
QueryBuilder.matchQuery("title", "slowly") // ✗ No match
When to Use¶
- Article titles and content
- Product descriptions
- User-generated text
- Any content requiring partial matching
Example¶
fields.add(new TextField("title", book.getTitle()));
fields.add(new TextField("description", book.getDescription()));
fields.add(new TextField("author", book.getAuthor()));
StringField¶
Use StringField for exact-match scenarios where the entire value should be treated as a single token.
new StringField("isbn", "978-0134685991")
How It Works¶
The text is stored as-is without analysis:
Input: "978-0134685991"
↓ (no analysis)
Token: ["978-0134685991"]
Search Behavior¶
// Only exact matches work
QueryBuilder.matchQuery("isbn", "978-0134685991") // ✓ Match
QueryBuilder.matchQuery("isbn", "978") // ✗ No match
QueryBuilder.matchQuery("isbn", "0134685991") // ✗ No match
When to Use¶
- IDs and codes (ISBN, SKU, product codes)
- Categories and tags (exact values)
- Status fields ("ACTIVE", "PENDING")
- Email addresses (for exact lookup)
Example¶
fields.add(new StringField("isbn", book.getIsbn()));
fields.add(new StringField("category", book.getCategory()));
fields.add(new StringField("status", book.getStatus().name()));
IntField¶
Use IntField for integer numeric values.
new IntField("pages", new int[]{350})
Features¶
- Range queries: Find documents within a numeric range
- Sorting: Sort results by numeric value
- DocValues: Automatically stored for function scoring
Search Behavior¶
// Range query
QueryBuilder.intRangeQuery("pages", 200, 500).buildForageQuery()
// Can be used in function scoring
QueryBuilder.functionScoreQuery()
.baseQuery(...)
.fieldValueFactor("pages")
.buildForageQuery()
Multi-Value Support¶
// A book might have multiple editions with different page counts
new IntField("pages", new int[]{350, 400, 425})
When to Use¶
- Page counts, quantities
- Year, age
- Counts and totals
- Any whole number value
Example¶
fields.add(new IntField("pages", new int[]{book.getPages()}));
fields.add(new IntField("year", new int[]{book.getPublicationYear()}));
fields.add(new IntField("quantity", new int[]{book.getStockQuantity()}));
FloatField¶
Use FloatField for decimal numeric values.
new FloatField("rating", new float[]{4.7f})
Features¶
- Range queries: Find documents within a decimal range
- Sorting: Sort by floating-point values
- DocValues: Available for function scoring and decay functions
Search Behavior¶
// Range query
QueryBuilder.floatRangeQuery("rating", 4.0f, 5.0f).buildForageQuery()
// Function scoring with field value
QueryBuilder.functionScoreQuery()
.baseQuery(...)
.fieldValueFactor("rating", 1.2f) // Multiply field value by factor
.buildForageQuery()
// Decay function
QueryBuilder.functionScoreQuery()
.baseQuery(...)
.scoreFunction(new DecayFunction(4.5, 0.5, 0.0, 0.5, DecayType.GAUSS, "rating"))
.buildForageQuery()
When to Use¶
- Ratings and scores
- Prices
- Percentages
- Coordinates (latitude/longitude)
- Any decimal value
Example¶
fields.add(new FloatField("rating", new float[]{book.getRating()}));
fields.add(new FloatField("price", new float[]{book.getPrice()}));
fields.add(new FloatField("discount", new float[]{book.getDiscountPercent()}));
Field Type Decision Tree¶
graph TD
A[What type of data?] --> B{Text?}
B -->|Yes| C{Need partial matching?}
C -->|Yes| D[TextField]
C -->|No| E[StringField]
B -->|No| F{Numeric?}
F -->|Integer| G[IntField]
F -->|Decimal| H[FloatField]
Combining Field Types¶
A typical document uses multiple field types:
new ForageDocument(book.getId(), Arrays.asList(
// Full-text searchable
new TextField("title", book.getTitle()),
new TextField("description", book.getDescription()),
// Exact match
new StringField("isbn", book.getIsbn()),
new StringField("category", book.getCategory()),
// Numeric - for ranges and sorting
new IntField("pages", new int[]{book.getPages()}),
new IntField("year", new int[]{book.getPublicationYear()}),
// Numeric - for scoring and sorting
new FloatField("rating", new float[]{book.getRating()}),
new FloatField("price", new float[]{book.getPrice()})
));
Performance Considerations¶
| Aspect | TextField | StringField | IntField | FloatField |
|---|---|---|---|---|
| Index Size | Large | Medium | Small | Small |
| Query Speed | Medium | Fast | Fast | Fast |
| Memory Usage | High | Low | Low | Low |
| Analysis Cost | High | None | None | None |
Optimization
- Use
StringFieldinstead ofTextFieldwhen you don't need tokenization - Limit the number of
TextFieldfields per document - Numeric fields are very efficient - use them for filtering and sorting
Next Steps¶
- Data Store - Implement the Store interface
- Bootstrapping - Feed documents into Forage
- Query Types - Search your indexed fields