Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,12 @@ server/src/main/resources/transport/defined/manifest.txt

# JEnv
.java-version

# Claude Flow and Swarm directories
.claude-flow/
.swarm/

# Temporary test files
test-*.java
bug-*.json
*-bug-analysis.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,10 @@ public final void validate(MappingLookup mappers) {
throw new IllegalArgumentException("[copy_to] may not be used to copy from a multi-field: [" + this.fullPath() + "]");
}

// Check if dynamic mappings are disabled
ObjectMapper.Dynamic rootDynamic = ObjectMapper.Dynamic.getRootDynamic(mappers);
boolean isDynamicDisabled = rootDynamic == ObjectMapper.Dynamic.FALSE;

final String sourceScope = mappers.nestedLookup().getNestedParent(this.fullPath());
for (String copyTo : this.copyTo().copyToFields()) {
if (mappers.isMultiField(copyTo)) {
Expand All @@ -341,6 +345,13 @@ public final void validate(MappingLookup mappers) {
throw new IllegalArgumentException("Cannot copy to field [" + copyTo + "] since it is mapped as an object");
}

// When dynamic is false, check if the target field exists
if (isDynamicDisabled && mappers.getMapper(copyTo) == null) {
throw new IllegalArgumentException(
"Cannot copy to field [" + copyTo + "] because it does not exist and dynamic mappings are disabled"
);
}

final String targetScope = mappers.nestedLookup().getNestedParent(copyTo);
checkNestedScopeCompatibility(sourceScope, targetScope);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,62 @@

public class CopyToMapperTests extends MapperServiceTestCase {

public void testCopyToNonExistentFieldWithDynamicFalse() {
Exception e = expectThrows(IllegalArgumentException.class, () -> createDocumentMapper(topMapping(b -> {
b.field("dynamic", false);
b.startObject("properties");
{
b.startObject("test_field");
{
b.field("type", "text");
b.field("copy_to", "missing_field");
}
b.endObject();
}
b.endObject();
})));
assertThat(
e.getMessage(),
equalTo("Cannot copy to field [missing_field] because it does not exist and dynamic mappings are disabled")
);
}

public void testCopyToExistingFieldWithDynamicFalse() throws Exception {
// This should succeed as the target field exists
DocumentMapper mapper = createDocumentMapper(topMapping(b -> {
b.field("dynamic", false);
b.startObject("properties");
{
b.startObject("test_field");
{
b.field("type", "text");
b.field("copy_to", "target_field");
}
b.endObject();
b.startObject("target_field");
{
b.field("type", "text");
}
b.endObject();
}
b.endObject();
}));
assertNotNull(mapper);
}

public void testCopyToNonExistentFieldWithDynamicTrue() throws Exception {
// This should succeed as dynamic is true (default)
MapperService mapperService = createMapperService(mapping(b -> {
b.startObject("test_field");
{
b.field("type", "text");
b.field("copy_to", "missing_field");
}
b.endObject();
}));
assertNotNull(mapperService.documentMapper());
}

@SuppressWarnings("unchecked")
public void testCopyToFieldsParsing() throws Exception {

Expand Down