SOLR-14950: fix regenerating of copyfield with explicit src/dest matching dyn rule

CopyFields are regenerated in case of replace-field or replace-field-type.
While regenerating, source and destionation are checked against fields but source/dest
could match dynamic rule too.
For example,
<copyField source="something_s" dest="spellcheck"/>
<dynamicField name="*_s" type="string"/>
here, something_s is not present in schema but matches the dynamic rule.

To handle the above case, need to check dynamicFieldCache too while regenerating the
copyFields
This commit is contained in:
Munendra S N 2020-11-25 08:52:38 +05:30
parent 4ab5d31832
commit 0846da5c22
3 changed files with 110 additions and 3 deletions

View File

@ -301,7 +301,11 @@ Bug Fixes
* SOLR-15047: Fix collapse parser behavior when collapsing on numeric fields to differentiate '0' group from null group (hossman)
* SOLR-10860: Return proper error code for bad input in case of inplace updates (Tomas Eduardo Fernandez Lobbe, Munendra S N)
* SOLR-10860: Return proper error code for bad input in case of inplace updates (Tomas Eduardo Fernandez Lobbe, Munendra S N)
* SOLR-14950: Fix error in copyField regeneration when explicit source/destination is not present in schema but
matches the dynamic rule. The copyFields are rebuilt with replace-field and replace-field-type, if required
(Andrew Shumway, Munendra S N)
Other Changes
---------------------

View File

@ -936,8 +936,9 @@ public final class ManagedIndexSchema extends IndexSchema {
private void rebuildCopyFields(List<CopyField> oldCopyFields) {
if (oldCopyFields.size() > 0) {
for (CopyField copyField : oldCopyFields) {
SchemaField source = fields.get(copyField.getSource().getName());
SchemaField destination = fields.get(copyField.getDestination().getName());
// source or destination either could be explicit field which matches dynamic rule
SchemaField source = getFieldOrNull(copyField.getSource().getName());
SchemaField destination = getFieldOrNull(copyField.getDestination().getName());
registerExplicitSrcAndDestFields
(copyField.getSource().getName(), copyField.getMaxChars(), destination, source);
}

View File

@ -773,6 +773,108 @@ public class TestBulkSchemaAPI extends RestTestBase {
assertTrue("'bleh_s' copyField rule exists in the schema", l.isEmpty());
}
@SuppressWarnings({"rawtypes"})
public void testCopyFieldWithReplace() throws Exception {
RestTestHarness harness = restTestHarness;
String newFieldName = "test_solr_14950";
// add-field-type
String addFieldTypeAnalyzer = "{\n" +
"'add-field-type' : {" +
" 'name' : 'myNewTextField',\n" +
" 'class':'solr.TextField',\n" +
" 'analyzer' : {\n" +
" 'charFilters' : [{\n" +
" 'name':'patternReplace',\n" +
" 'replacement':'$1$1',\n" +
" 'pattern':'([a-zA-Z])\\\\\\\\1+'\n" +
" }],\n" +
" 'tokenizer' : { 'name':'whitespace' },\n" +
" 'filters' : [{ 'name':'asciiFolding' }]\n" +
" }\n"+
"}}";
String response = restTestHarness.post("/schema", json(addFieldTypeAnalyzer));
Map map = (Map) fromJSONString(response);
assertNull(response, map.get("error"));
map = getObj(harness, "myNewTextField", "fieldTypes");
assertNotNull("'myNewTextField' field type does not exist in the schema", map);
// add-field
String payload = "{\n" +
" 'add-field' : {\n" +
" 'name':'" + newFieldName + "',\n" +
" 'type':'myNewTextField',\n" +
" 'stored':true,\n" +
" 'indexed':true\n" +
" }\n" +
" }";
response = harness.post("/schema", json(payload));
map = (Map) fromJSONString(response);
assertNull(response, map.get("error"));
Map m = getObj(harness, newFieldName, "fields");
assertNotNull("'"+ newFieldName + "' field does not exist in the schema", m);
// add copy-field with explicit source and destination
List l = getSourceCopyFields(harness, "bleh_s");
assertTrue("'bleh_s' copyField rule exists in the schema", l.isEmpty());
payload = "{\n" +
" 'add-copy-field' : {\n" +
" 'source' :'bleh_s',\n" +
" 'dest':'"+ newFieldName + "'\n" +
" }\n" +
" }\n";
response = harness.post("/schema", json(payload));
map = (Map) fromJSONString(response);
assertNull(response, map.get("error"));
l = getSourceCopyFields(harness, "bleh_s");
assertFalse("'bleh_s' copyField rule doesn't exist", l.isEmpty());
assertEquals("bleh_s", ((Map)l.get(0)).get("source"));
assertEquals(newFieldName, ((Map)l.get(0)).get("dest"));
// replace-field-type
String replaceFieldTypeAnalyzer = "{\n" +
"'replace-field-type' : {" +
" 'name' : 'myNewTextField',\n" +
" 'class':'solr.TextField',\n" +
" 'analyzer' : {\n" +
" 'tokenizer' : { 'name':'whitespace' },\n" +
" 'filters' : [{ 'name':'asciiFolding' }]\n" +
" }\n"+
"}}";
response = restTestHarness.post("/schema", json(replaceFieldTypeAnalyzer));
map = (Map) fromJSONString(response);
assertNull(response, map.get("error"));
map = getObj(restTestHarness, "myNewTextField", "fieldTypes");
assertNotNull(map);
Map analyzer = (Map)map.get("analyzer");
assertNull("'myNewTextField' shouldn't contain charFilters", analyzer.get("charFilters"));
l = getSourceCopyFields(harness, "bleh_s");
assertFalse("'bleh_s' copyField rule doesn't exist", l.isEmpty());
assertEquals("bleh_s", ((Map)l.get(0)).get("source"));
assertEquals(newFieldName, ((Map)l.get(0)).get("dest"));
// with replace-field
String replaceField = "{'replace-field' : {'name':'" + newFieldName + "', 'type':'string'}}";
response = harness.post("/schema", json(replaceField));
map = (Map) fromJSONString(response);
assertNull(map.get("error"));
l = getSourceCopyFields(harness, "bleh_s");
assertFalse("'bleh_s' copyField rule doesn't exist", l.isEmpty());
assertEquals("bleh_s", ((Map)l.get(0)).get("source"));
assertEquals(newFieldName, ((Map)l.get(0)).get("dest"));
}
@SuppressWarnings({"unchecked", "rawtypes"})
public void testDeleteAndReplace() throws Exception {
RestTestHarness harness = restTestHarness;