This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr-mcp.git
The following commit(s) were added to refs/heads/main by this push:
new 621b25a8 refactor(indexing): remove FieldNameSanitizer; use field
names as given (#235)
621b25a8 is described below
commit 621b25a80eda5a5b3c4e6a46f843d5c2534064a6
Author: Aditya Parikh <[email protected]>
AuthorDate: Tue Oct 6 14:32:27 2026 -0400
refactor(indexing): remove FieldNameSanitizer; use field names as given
(#235)
JSON keys and Markdown front-matter keys were lowercased, stripped of
non-word characters and trimmed of underscores before indexing. Solr
does not require any of that: its reference guide only recommends
alphanumeric/underscore names, and says this is not enforced. The
rewriting also did harm and was inconsistent:
- Lowercasing breaks collections with an explicit schema. Solr field
names are case-sensitive, so a key "Title" or "firstName" was
renamed to a field the schema does not define.
- Solr's own schemaless chain (the _default configset) already
replaces characters outside [\w.-]; testSpecialCharactersInFieldNames
passes unchanged because Solr now does the renaming itself.
- CSV and XML field names already reach Solr as given.
Nested JSON objects are still flattened with underscores
("user": {"name": ...} -> user_name), and the indexing response still
lists the indexed names.
Signed-off-by: Aditya Parikh <[email protected]>
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
AGENTS.md | 1 -
dev-docs/ARCHITECTURE.md | 4 +-
docs/FAQ.md | 3 +-
.../solr/mcp/server/indexing/IndexingService.java | 20 ++--
.../documentcreator/FieldNameSanitizer.java | 124 ---------------------
.../documentcreator/IndexingDocumentCreator.java | 2 -
.../documentcreator/JsonDocumentCreator.java | 15 +--
.../documentcreator/MarkdownDocumentCreator.java | 6 +-
.../documentcreator/SolrDocumentCreator.java | 9 +-
.../indexing/IndexingServiceIntegrationTest.java | 119 +++++---------------
.../mcp/server/indexing/MarkdownIndexingTest.java | 6 +-
.../documentcreator/JsonDocumentCreatorTest.java | 2 +-
12 files changed, 49 insertions(+), 262 deletions(-)
diff --git a/AGENTS.md b/AGENTS.md
index 53fd90c6..cbe4cd91 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -110,7 +110,6 @@ in `indexing/documentcreator/`:
- `SolrDocumentCreator` - Common interface
- `JsonDocumentCreator`, `MarkdownDocumentCreator` - Format implementations
(Jackson, commonmark)
- `IndexingDocumentCreator` - Orchestrator that delegates to the
format-specific creator
-- `FieldNameSanitizer` - Automatic field name validation for Solr compatibility
CSV and XML are forwarded unchanged to Solr's own update handlers (`/update`
with
`text/csv` or `application/xml`); the server does not parse them and field
names
diff --git a/dev-docs/ARCHITECTURE.md b/dev-docs/ARCHITECTURE.md
index f379e152..49a30724 100644
--- a/dev-docs/ARCHITECTURE.md
+++ b/dev-docs/ARCHITECTURE.md
@@ -23,7 +23,6 @@ src/main/java/org/apache/solr/mcp/server/
│ ├── JsonDocumentCreator.java # JSON document parser (implements
SolrDocumentCreator)
│ ├── MarkdownDocumentCreator.java # Markdown document parser
(implements SolrDocumentCreator)
│ ├── SolrDocumentCreator.java # Common interface for document
creators
-│ ├── FieldNameSanitizer.java # Field name sanitization utility
│ └── DocumentProcessingException.java # Indexing exceptions
└── collection/ # Collection management functionality
├── CollectionService.java # MCP tools for collection operations
@@ -56,7 +55,7 @@ Spring Boot configuration using properties files:
Server-side parsing exists only for JSON and Markdown, the formats Solr's
update
handlers do not accept directly:
-- Automatically sanitizes field names to comply with Solr schema requirements
+- Uses field names as given, as the CSV and XML pass-throughs do
- Supports nested JSON structures and multi-valued fields
- Delegation via service composition (IndexingDocumentCreator) to the
appropriate format-specific creator
@@ -114,7 +113,6 @@ Additional Jib benefits:
The document creator pattern allows for:
- **Extensibility**: Easy to add new format parsers
- **Testability**: Each creator can be tested independently
-- **Field Sanitization**: Automatic conversion of field names to
Solr-compatible format
- **Explicit Format Selection**: Separate methods for JSON/CSV/XML (no
automatic detection)
### Error Handling
diff --git a/docs/FAQ.md b/docs/FAQ.md
index 9c38e662..c56939db 100644
--- a/docs/FAQ.md
+++ b/docs/FAQ.md
@@ -32,8 +32,7 @@ re-derive it imperfectly every call:
- **Indexing resilience** — 1000-doc batches, single commit, per-doc retry to
salvage valid docs from a failed batch.
-- **Format hardening** — nested-object flattening and field sanitization for
- JSON and markdown; for XML, an **XXE-hardened `<add>`-only pre-check** so
+- **Format hardening** — nested-object flattening for JSON; for XML, an
**XXE-hardened `<add>`-only pre-check** so
`<delete>`/`<commit>` cannot ride in on an indexing call.
- **Metric aggregation** — `get-collection-stats` folds Luke + Metrics APIs,
normalizes shard names, and degrades gracefully on Solr 10.
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java
b/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java
index e7230ed3..e5ae85f2 100644
--- a/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java
+++ b/src/main/java/org/apache/solr/mcp/server/indexing/IndexingService.java
@@ -72,9 +72,8 @@ import org.springframework.stereotype.Service;
* configurable batch sizes
* <li><strong>Error Resilience</strong>: Individual document fallback when
* batch operations fail
- * <li><strong>Field Sanitization</strong>: Automatic cleanup of JSON and
- * markdown field names for Solr compatibility; CSV and XML field names are
used
- * as given
+ * <li><strong>Field Names</strong>: Used as given in every format; nested JSON
+ * objects are flattened with underscores
* </ul>
*
* <p>
@@ -220,8 +219,7 @@ public class IndexingService {
annotations = @McpTool.McpAnnotations(idempotentHint =
true),
description = "Index documents passed as a JSON array
of objects into Solr collection; one object"
+ " per document, multi-valued fields
as arrays, nested objects flattened with underscores."
- + " Pass the array itself, not a JSON
string. Field names are sanitized for Solr"
- + " compatibility (lowercased, special
characters replaced with underscores); the response"
+ + " Pass the array itself, not a JSON
string. Field names are used as given; the response"
+ " lists the field names as indexed")
public String indexJsonDocuments(@McpToolParam(description = "Solr
collection to index into") String collection,
@McpToolParam(
@@ -339,7 +337,7 @@ public class IndexingService {
*
* <ul>
* <li><strong>YAML Front Matter</strong>: Each entry becomes a
document field
- * with a sanitized name (multi-valued where applicable)
+ * under its own name (multi-valued where applicable)
* <li><strong>title</strong>: From the {@code title} front matter
entry, or the
* first level-1 heading
* <li><strong>headings</strong>: Multi-valued field with the text of
every
@@ -407,11 +405,9 @@ public class IndexingService {
private static final int MAX_REPORTED_FIELDS = 50;
/**
- * Summarizes the field names that were actually indexed. Document
creators
- * sanitize input field names for Solr compatibility (lowercasing,
replacing
- * special characters with underscores), so the indexed names can
differ from
- * the input; reporting them lets MCP clients query the right fields
instead of
- * assuming the input names survived.
+ * Summarizes the field names that were indexed. Nested JSON objects are
+ * flattened with underscores, so the indexed names can differ from the
input;
+ * reporting them lets MCP clients query the right fields.
*
* @param documents
* the documents that were submitted for indexing
@@ -428,7 +424,7 @@ public class IndexingService {
String elided = fieldNames.size() > MAX_REPORTED_FIELDS
? " and " + (fieldNames.size() -
MAX_REPORTED_FIELDS) + " more"
: "";
- return ". Indexed field names (input names are sanitized for
Solr compatibility): " + listed + elided;
+ return ". Indexed field names: " + listed + elided;
}
/**
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/FieldNameSanitizer.java
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/FieldNameSanitizer.java
deleted file mode 100644
index 7b636718..00000000
---
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/FieldNameSanitizer.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.mcp.server.indexing.documentcreator;
-
-import java.util.Locale;
-import java.util.regex.Pattern;
-
-/**
- * Utility class for sanitizing field names to ensure compatibility with Solr's
- * field naming requirements.
- *
- * <p>
- * This class provides shared regex patterns and sanitization logic that can be
- * used across all document creators to ensure consistent field name handling.
- *
- * <p>
- * Solr has specific requirements for field names that must be met to ensure
- * proper indexing and searching functionality. This utility transforms
- * arbitrary field names into Solr-compliant identifiers.
- */
-public final class FieldNameSanitizer {
-
- /**
- * Pattern to match invalid characters in field names. Matches any
character
- * that is not alphanumeric or underscore.
- */
- private static final Pattern INVALID_CHARACTERS_PATTERN =
Pattern.compile("[\\W]");
-
- /**
- * Pattern to match leading and trailing underscores. Uses explicit
grouping to
- * make operator precedence clear.
- */
- private static final Pattern LEADING_TRAILING_UNDERSCORES_PATTERN =
Pattern.compile("(^_+)|(_+$)");
-
- /**
- * Pattern to match multiple consecutive underscores. Matches two or
more
- * consecutive underscores to collapse them into one.
- */
- private static final Pattern MULTIPLE_UNDERSCORES_PATTERN =
Pattern.compile("_{2,}");
-
- // Private constructor to prevent instantiation
- private FieldNameSanitizer() {
- throw new UnsupportedOperationException("Utility class cannot
be instantiated");
- }
-
- /**
- * Sanitizes field names to ensure they are compatible with Solr's
field naming
- * requirements.
- *
- * <p>
- * <strong>Sanitization Rules:</strong>
- *
- * <ul>
- * <li><strong>Case Conversion</strong>: All characters converted to
lowercase
- * <li><strong>Character Replacement</strong>: Non-alphanumeric
characters
- * replaced with underscores
- * <li><strong>Edge Trimming</strong>: Leading and trailing underscores
removed
- * <li><strong>Duplicate Compression</strong>: Multiple consecutive
underscores
- * collapsed to single
- * <li><strong>Numeric Prefix</strong>: Field names starting with
numbers get
- * "field_" prefix
- * </ul>
- *
- * <p>
- * <strong>Example Transformations:</strong>
- *
- * <ul>
- * <li>"User-Name" → "user_name"
- * <li>"product.price" → "product_price"
- * <li>"__field__name__" → "field_name"
- * <li>"Field123@Test" → "field123_test"
- * <li>"123field" → "field_123field"
- * </ul>
- *
- * @param fieldName
- * the original field name to sanitize
- * @return sanitized field name compatible with Solr requirements, or
"field" if
- * the input is empty or sanitizes away to nothing
- * @see <a href=
- *
"https://solr.apache.org/guide/solr/latest/indexing-guide/fields.html">Solr
- * Field Guide</a>
- */
- public static String sanitizeFieldName(String fieldName) {
-
- // No null guard: this package is @NullMarked and NullAway
enforces the
- // non-null contract at compile time. An empty input falls
through to the
- // isEmpty() check below and yields the default name.
-
- // Convert to lowercase and replace invalid characters with
underscores.
- // Locale.ROOT keeps this deterministic - the default locale
would map
- // 'I' to a dotless 'ı' under a Turkish locale and produce a
different field.
- String sanitized =
INVALID_CHARACTERS_PATTERN.matcher(fieldName.toLowerCase(Locale.ROOT)).replaceAll("_");
-
- // Remove leading/trailing underscores and collapse multiple
underscores
- sanitized =
LEADING_TRAILING_UNDERSCORES_PATTERN.matcher(sanitized).replaceAll("");
- sanitized =
MULTIPLE_UNDERSCORES_PATTERN.matcher(sanitized).replaceAll("_");
-
- // If the result is empty after sanitization, provide a default
name
- if (sanitized.isEmpty()) {
- return "field";
- }
-
- // Ensure the field name doesn't start with a number (Solr
requirement)
- if (Character.isDigit(sanitized.charAt(0))) {
- sanitized = "field_" + sanitized;
- }
-
- return sanitized;
- }
-}
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/IndexingDocumentCreator.java
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/IndexingDocumentCreator.java
index 8c12d881..2772b4c2 100644
---
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/IndexingDocumentCreator.java
+++
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/IndexingDocumentCreator.java
@@ -43,8 +43,6 @@ import org.springframework.stereotype.Service;
* documents
* <li><strong>Markdown Processing</strong>: Support for markdown documents
with
* front matter, title, and heading extraction
- * <li><strong>Field Sanitization</strong>: Automatic cleanup of field names
for
- * Solr compatibility
* </ul>
*
* @see SolrInputDocument
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreator.java
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreator.java
index e770826c..ec3ad10f 100644
---
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreator.java
+++
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreator.java
@@ -87,14 +87,6 @@ public class JsonDocumentCreator implements
SolrDocumentCreator {
* </ul>
*
* <p>
- * <strong>Field Name Sanitization:</strong>
- *
- * <p>
- * Field names are automatically sanitized to ensure Solr compatibility
by
- * removing special characters and converting to lowercase with
underscore
- * separators.
- *
- * <p>
* <strong>Example Transformations:</strong>
*
* <pre>{@code
@@ -110,7 +102,6 @@ public class JsonDocumentCreator implements
SolrDocumentCreator {
* is invalid
* @see SolrInputDocument
* @see #addAllFieldsFlat(SolrInputDocument, JsonNode, String)
- * @see FieldNameSanitizer#sanitizeFieldName(String)
*/
public List<SolrInputDocument> create(String json) throws
DocumentProcessingException {
if (json.isBlank()) {
@@ -138,7 +129,7 @@ public class JsonDocumentCreator implements
SolrDocumentCreator {
* <p>
* The maps are converted to a {@link JsonNode} tree and handed to the
same
* {@link #flatten(JsonNode)} walk {@link #create(String)} uses, so
both entry
- * points flatten and sanitize identically by construction.
+ * points flatten identically by construction.
*
* @param documents
* the documents, each a map of field name to value
@@ -219,12 +210,10 @@ public class JsonDocumentCreator implements
SolrDocumentCreator {
* @param prefix
* current field name prefix for nested object flattening
* @see #convertJsonValue(JsonNode)
- * @see FieldNameSanitizer#sanitizeFieldName(String)
*/
private void addAllFieldsFlat(SolrInputDocument doc, JsonNode node,
String prefix) {
Set<Map.Entry<String, JsonNode>> fields = node.properties();
- fields.forEach(field -> processFieldValue(doc, field.getValue(),
- FieldNameSanitizer.sanitizeFieldName(prefix +
field.getKey())));
+ fields.forEach(field -> processFieldValue(doc,
field.getValue(), prefix + field.getKey()));
}
/**
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/MarkdownDocumentCreator.java
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/MarkdownDocumentCreator.java
index 3cd0b3ad..20e008ca 100644
---
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/MarkdownDocumentCreator.java
+++
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/MarkdownDocumentCreator.java
@@ -52,7 +52,7 @@ import org.springframework.stereotype.Component;
*
* <ul>
* <li><strong>YAML Front Matter</strong>: Each front matter entry becomes a
- * document field with a sanitized name. Entries with multiple values become
+ * document field under its own name. Entries with multiple values become
* multi-valued fields.
* <li><strong>id</strong>: Taken from the {@code id} front matter entry when
* present, otherwise derived deterministically from a SHA-256 hash of the
input
@@ -85,7 +85,6 @@ import org.springframework.stereotype.Component;
* }</pre>
*
* @see SolrInputDocument
- * @see FieldNameSanitizer#sanitizeFieldName(String)
*/
@Component
public class MarkdownDocumentCreator implements SolrDocumentCreator {
@@ -179,10 +178,9 @@ public class MarkdownDocumentCreator implements
SolrDocumentCreator {
document.accept(frontMatterVisitor);
frontMatterVisitor.getData().forEach((key, values) -> {
- String fieldName =
FieldNameSanitizer.sanitizeFieldName(key);
for (String value : flattenFlowSequences(values)) {
if (!value.isEmpty()) {
- doc.addField(fieldName, value);
+ doc.addField(key, value);
}
}
});
diff --git
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/SolrDocumentCreator.java
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/SolrDocumentCreator.java
index 5761fed3..10f07fbe 100644
---
a/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/SolrDocumentCreator.java
+++
b/src/main/java/org/apache/solr/mcp/server/indexing/documentcreator/SolrDocumentCreator.java
@@ -26,8 +26,7 @@ import org.apache.solr.common.SolrInputDocument;
* <p>
* This interface provides a unified abstraction for converting different
* document formats (JSON, CSV, XML, etc.) into Solr-compatible
- * SolrInputDocument objects. Implementations handle format-specific parsing
and
- * field sanitization to ensure proper Solr indexing.
+ * SolrInputDocument objects. Implementations handle format-specific parsing.
*
* <p>
* <strong>Design Principles:</strong>
@@ -38,8 +37,6 @@ import org.apache.solr.common.SolrInputDocument;
* without predefined schema
* <li><strong>Error Handling</strong>: Consistent exception handling across
* implementations
- * <li><strong>Field Sanitization</strong>: Automatic cleanup of field names
for
- * Solr compatibility
* </ul>
*
* <p>
@@ -47,7 +44,6 @@ import org.apache.solr.common.SolrInputDocument;
*
* <ul>
* <li>Handle null or empty input gracefully
- * <li>Sanitize field names using {@link FieldNameSanitizer}
* <li>Preserve original data types where possible
* <li>Throw {@link DocumentProcessingException} for processing errors
* </ul>
@@ -63,7 +59,6 @@ import org.apache.solr.common.SolrInputDocument;
*
* @see SolrInputDocument
* @see DocumentProcessingException
- * @see FieldNameSanitizer
*/
public interface SolrDocumentCreator {
@@ -79,8 +74,6 @@ public interface SolrDocumentCreator {
* <strong>Processing Behavior:</strong>
*
* <ul>
- * <li><strong>Field Sanitization</strong>: All field names are
sanitized for
- * Solr compatibility
* <li><strong>Type Preservation</strong>: Original data types are
maintained
* where possible
* <li><strong>Multiple Documents</strong>: Single content string may
produce
diff --git
a/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceIntegrationTest.java
b/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceIntegrationTest.java
index c9f10840..b1b07000 100644
---
a/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceIntegrationTest.java
+++
b/src/test/java/org/apache/solr/mcp/server/indexing/IndexingServiceIntegrationTest.java
@@ -145,26 +145,24 @@ class IndexingServiceIntegrationTest {
}
@Test
- void indexJsonDocuments_reportsSanitizedFieldNames() throws Exception {
+ void indexJsonDocuments_reportsFieldNamesAsIndexed() throws Exception {
String json = """
[
{
- "id": "sanitize001",
+ "id": "names001",
"User-Name": "Jane Doe",
- "product.price": 9.99
+ "product": {"price": 9.99}
}
]
""";
String result =
indexingService.indexJsonDocuments(COLLECTION_NAME, TestDocuments.json(json));
- // The response must list the names as indexed, not as
submitted, so MCP
- // clients query the fields that actually exist.
- assertTrue(result.contains("user_name"));
- assertTrue(result.contains("product_price"));
- assertFalse(result.contains("User-Name"));
+ // Names are kept as given; nested objects are flattened with
underscores,
+ // which is why the response lists the indexed names.
+ assertTrue(result.contains("User-Name"), result);
+ assertTrue(result.contains("product_price"), result);
}
-
@Test
void testIndexJsonDocuments() throws Exception {
@@ -346,69 +344,30 @@ class IndexingServiceIntegrationTest {
}
@Test
- void testSanitizeFieldName() throws Exception {
-
- // Test JSON string with field names that need sanitizing
+ void testFieldNamesAreUsedAsGiven() throws Exception {
String json = """
[
{
"id": "test005",
- "invalid-field": "Value with hyphen",
- "another.invalid": "Value with dot",
- "UPPERCASE": "Value with uppercase",
- "multiple__underscores": "Value with
multiple underscores"
+ "first-name": "Value with hyphen",
+ "product.price": "Value with dot",
+ "Title": "Value with uppercase"
}
]
""";
- // Index documents
indexingService.indexJsonDocuments(COLLECTION_NAME,
TestDocuments.json(json));
- // Verify documents were indexed with sanitized field names
SearchResponse result = searchService.search(COLLECTION_NAME,
"id:test005", null, null, null, null, null);
-
- assertNotNull(result);
- List<Map<String, Object>> documents = result.documents();
- assertEquals(1, documents.size());
-
- Map<String, Object> doc = documents.getFirst();
-
- // Check that field names were sanitized
- assertNotNull(doc.get("invalid_field"));
- Object invalidFieldValue = doc.get("invalid_field");
- if (invalidFieldValue instanceof List) {
- assertEquals("Value with hyphen", ((List<?>)
invalidFieldValue).getFirst());
- } else {
- assertEquals("Value with hyphen", invalidFieldValue);
- }
-
- assertNotNull(doc.get("another_invalid"));
- Object anotherInvalidValue = doc.get("another_invalid");
- if (anotherInvalidValue instanceof List) {
- assertEquals("Value with dot", ((List<?>)
anotherInvalidValue).getFirst());
- } else {
- assertEquals("Value with dot", anotherInvalidValue);
- }
-
- // Should be lowercase
- assertNotNull(doc.get("uppercase"));
- Object uppercaseValue = doc.get("uppercase");
- if (uppercaseValue instanceof List) {
- assertEquals("Value with uppercase", ((List<?>)
uppercaseValue).getFirst());
- } else {
- assertEquals("Value with uppercase", uppercaseValue);
- }
-
- // Multiple underscores should be collapsed
- assertNotNull(doc.get("multiple_underscores"));
- Object multipleUnderscoresValue =
doc.get("multiple_underscores");
- if (multipleUnderscoresValue instanceof List) {
- assertEquals("Value with multiple underscores",
((List<?>) multipleUnderscoresValue).getFirst());
- } else {
- assertEquals("Value with multiple underscores",
multipleUnderscoresValue);
- }
+ assertEquals(1, result.documents().size());
+ Map<String, Object> doc = result.documents().getFirst();
+
+ // Solr accepts these names as they are; field names are
case-sensitive
+ assertEquals("Value with hyphen", getFieldValue(doc,
"first-name"));
+ assertEquals("Value with dot", getFieldValue(doc,
"product.price"));
+ assertEquals("Value with uppercase", getFieldValue(doc,
"Title"));
+ assertNull(doc.get("title"));
}
-
@Test
void testDeeplyNestedJsonStructures() throws Exception {
@@ -546,8 +505,8 @@ class IndexingServiceIntegrationTest {
Map<String, Object> doc = documents.getFirst();
- // Check that field names with special characters were sanitized
- // All special characters should be replaced with underscores
+ // The server sends these names as given; Solr's schemaless
update chain
+ // (the _default configset) replaces the special characters
with underscores
assertNotNull(doc.get("field_with_at"));
assertEquals("Value with @ symbols", getFieldValue(doc,
"field_with_at"));
@@ -727,44 +686,26 @@ class IndexingServiceIntegrationTest {
}
@Test
- void testDirectSanitizeFieldName() throws Exception {
- // Test sanitizing field names directly
- // Create a document with field names that need sanitizing
+ void testDirectFieldNamesAreUsedAsGiven() throws Exception {
String json = """
[
{
"id": "field_names_001",
"field-with-hyphens": "Value 1",
- "field.with.dots": "Value 2",
- "field with spaces": "Value 3",
- "UPPERCASE_FIELD": "Value 4",
- "__leading_underscores__": "Value 5",
- "trailing_underscores___": "Value 6",
- "multiple___underscores": "Value 7"
+ "field with spaces": "Value 2",
+ "UPPERCASE_FIELD": "Value 3",
+ "nested": {"Inner-Key": "Value 4"}
}
]
""";
- // Create documents
- List<SolrInputDocument> documents =
indexingDocumentCreator.createSchemalessDocumentsFromJson(json);
+ SolrInputDocument doc =
indexingDocumentCreator.createSchemalessDocumentsFromJson(json).getFirst();
- // Verify documents were created correctly
- assertNotNull(documents);
- assertEquals(1, documents.size());
-
- SolrInputDocument doc = documents.getFirst();
-
- // Verify field names were sanitized correctly
- assertEquals("field_names_001", doc.getFieldValue("id"));
- assertEquals("Value 1",
doc.getFieldValue("field_with_hyphens"));
- assertEquals("Value 2", doc.getFieldValue("field_with_dots"));
- assertEquals("Value 3", doc.getFieldValue("field_with_spaces"));
- assertEquals("Value 4", doc.getFieldValue("uppercase_field"));
- assertEquals("Value 5",
doc.getFieldValue("leading_underscores"));
- assertEquals("Value 6",
doc.getFieldValue("trailing_underscores"));
- assertEquals("Value 7",
doc.getFieldValue("multiple_underscores"));
+ assertEquals("Value 1",
doc.getFieldValue("field-with-hyphens"));
+ assertEquals("Value 2", doc.getFieldValue("field with spaces"));
+ assertEquals("Value 3", doc.getFieldValue("UPPERCASE_FIELD"));
+ assertEquals("Value 4", doc.getFieldValue("nested_Inner-Key"));
}
-
@Test
void
indexCsvDocuments_goesThroughSolrsCsvHandlerWithColumnNamesAsGiven() throws
Exception {
String result =
indexingService.indexCsvDocuments(COLLECTION_NAME, """
diff --git
a/src/test/java/org/apache/solr/mcp/server/indexing/MarkdownIndexingTest.java
b/src/test/java/org/apache/solr/mcp/server/indexing/MarkdownIndexingTest.java
index 2ec3a7c5..ab802c9a 100644
---
a/src/test/java/org/apache/solr/mcp/server/indexing/MarkdownIndexingTest.java
+++
b/src/test/java/org/apache/solr/mcp/server/indexing/MarkdownIndexingTest.java
@@ -153,7 +153,7 @@ class MarkdownIndexingTest {
}
@Test
- void testFrontMatterFieldNamesAreSanitized() throws Exception {
+ void testFrontMatterFieldNamesAreUsedAsGiven() throws Exception {
// Given
String markdown = """
---
@@ -172,8 +172,8 @@ class MarkdownIndexingTest {
assertThat(documents).hasSize(1);
SolrInputDocument doc = documents.getFirst();
- assertThat(doc.getFieldValue("created_by")).isEqualTo("Jane
Doe");
-
assertThat(doc.getFieldValue("last_updated")).isEqualTo("2026-01-01");
+ assertThat(doc.getFieldValue("Created-By")).isEqualTo("Jane
Doe");
+
assertThat(doc.getFieldValue("last.updated")).isEqualTo("2026-01-01");
}
@Test
diff --git
a/src/test/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreatorTest.java
b/src/test/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreatorTest.java
index 3172146f..bb294e66 100644
---
a/src/test/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreatorTest.java
+++
b/src/test/java/org/apache/solr/mcp/server/indexing/documentcreator/JsonDocumentCreatorTest.java
@@ -65,7 +65,7 @@ class JsonDocumentCreatorTest {
SolrInputDocument result =
creator.create(List.of(doc)).getFirst();
assertThat(result.getFieldValue("id")).isEqualTo("1");
-
assertThat(result.getFieldValue("author_name_first")).isEqualTo("Ann");
+
assertThat(result.getFieldValue("Author.Name_first")).isEqualTo("Ann");
assertThat(result.getFieldValues("tags")).containsExactly("a",
"b");
assertThat(result.getFieldNames()).doesNotContain("missing");
}