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
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: >
/update/json/docs now rejects field mapping parameters (f) combined with mapUniqueKeyOnly=true with a 400 error instead of silently ignoring the mappings.
type: changed
authors:
- name: Nick Shanin
links:
- name: SOLR-8240
url: https://issues.apache.org/jira/browse/SOLR-8240
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,16 @@ void processUpdate(Reader reader) throws IOException {
private void handleSplitMode(String split, String[] fields, final Reader reader)
throws IOException {
if (split == null) split = "/";
final boolean mapUniqueKeyOnly = req.getParams().getBool("mapUniqueKeyOnly", false);
if (mapUniqueKeyOnly && fields != null && fields.length > 0) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
"Field mappings (f) are ignored when mapUniqueKeyOnly=true; set mapUniqueKeyOnly=false"
+ " to map fields");
}
if (fields == null || fields.length == 0) fields = new String[] {"$FQN:/**"};
final boolean echo = "true".equals(req.getParams().get("echo"));
final String srcField = req.getParams().get("srcField");
final boolean mapUniqueKeyOnly = req.getParams().getBool("mapUniqueKeyOnly", false);
if (srcField != null) {
if (!"/".equals(split))
throw new SolrException(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?xml version="1.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.
-->

<config>

<dataDir>${solr.data.dir:}</dataDir>

<directoryFactory name="DirectoryFactory"
class="${solr.directoryFactory:solr.NRTCachingDirectoryFactory}"/>
<schemaFactory class="ClassicIndexSchemaFactory"/>

<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>

<xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>

<updateHandler class="solr.DirectUpdateHandler2">
<commitWithin>
<softCommit>${solr.commitwithin.softcommit:true}</softCommit>
</commitWithin>
</updateHandler>

<initParams path="/update/json/docs">
<lst name="defaults">
<bool name="mapUniqueKeyOnly">true</bool>
<str name="df">_catch_all</str>
</lst>
</initParams>

</config>
40 changes: 40 additions & 0 deletions solr/core/src/test/org/apache/solr/core/TestInitParams.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.util.ContentStreamBase;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequestBase;
import org.apache.solr.request.SolrRequestHandler;
import org.apache.solr.response.SolrQueryResponse;
import org.junit.BeforeClass;
Expand Down Expand Up @@ -127,4 +132,39 @@ public void testMatchPath() {
assertTrue(initParams.matchPath("/update/json"));
assertTrue(initParams.matchPath("/update"));
}

@Test
public void testUpdateJsonDocsInitParamsRejectsFieldMappings() throws Exception {
deleteCore();
initCore("solrconfig-update-json-docs-initparams.xml", "schema.xml");
try {
SolrRequestHandler handler = h.getCore().getRequestHandler("/update/json/docs");
SolrQueryResponse rsp = new SolrQueryResponse();
handler.handleRequest(jsonDocsRequest("f", "title:/title"), rsp);
Exception ex = rsp.getException();
assertNotNull("expected the handler to record a rejection on the response", ex);
assertTrue(ex.toString(), ex instanceof SolrException);
assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ((SolrException) ex).code());
assertTrue(ex.getMessage(), ex.getMessage().contains("mapUniqueKeyOnly"));

rsp = new SolrQueryResponse();
handler.handleRequest(
jsonDocsRequest("mapUniqueKeyOnly", "false", "echo", "true", "f", "title:/title"), rsp);
assertEquals("[{title=t}]", String.valueOf(rsp.getValues().get("docs")));
} finally {
deleteCore();
initCore("solrconfig-paramset.xml", "schema.xml");
}
}

private SolrQueryRequestBase jsonDocsRequest(String... keysAndValues) {
ModifiableSolrParams solrParams = params("json.command", "false");
for (int i = 0; i < keysAndValues.length; i += 2) {
solrParams.set(keysAndValues[i], keysAndValues[i + 1]);
}
SolrQueryRequestBase req = new SolrQueryRequestBase(h.getCore(), solrParams) {};
String json = "{\"id\":\"1\",\"title\":\"t\"}";
req.setContentStreams(List.of(new ContentStreamBase.StringStream(json, "application/json")));
return req;
}
}
39 changes: 39 additions & 0 deletions solr/core/src/test/org/apache/solr/handler/JsonLoaderTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,45 @@ public void testInvalidJsonProducesBadRequestSolrException() {
assertTrue(ex.getMessage().contains("JSON"));
}

@Test
public void testFieldMappingWithMapUniqueKeyOnlyIsRejected() {
String json = "{'id':'1','title':'t'}".replace('\'', '"');
SolrQueryResponse rsp = new SolrQueryResponse();
BufferingRequestProcessor p = new BufferingRequestProcessor(null);
JsonLoader loader = new JsonLoader();

SolrException ex =
expectThrows(
SolrException.class,
() ->
loader.load(
req(
"json.command", "false",
"mapUniqueKeyOnly", "true",
"df", "_catch_all",
"f", "title:/title"),
rsp,
new ContentStreamBase.StringStream(json),
p));
assertEquals(SolrException.ErrorCode.BAD_REQUEST.code, ex.code());
assertTrue(ex.getMessage(), ex.getMessage().contains("mapUniqueKeyOnly"));
}

@Test
public void testFieldMappingWithMapUniqueKeyOnlyFalseIsAccepted() throws Exception {
String json = "{'id':'1','title':'t'}".replace('\'', '"');
SolrQueryResponse rsp = new SolrQueryResponse();
BufferingRequestProcessor p = new BufferingRequestProcessor(null);
JsonLoader loader = new JsonLoader();
loader.load(
req("json.command", "false", "mapUniqueKeyOnly", "false", "f", "title:/title"),
rsp,
new ContentStreamBase.StringStream(json),
p);
assertEquals(1, p.addCommands.size());
assertEquals("t", p.addCommands.get(0).solrDoc.getFieldValue("title"));
}

public void testSimpleFormatInAdd() throws Exception {
String str = "{'add':[{'id':'1'},{'id':'2'}]}".replace('\'', '"');
SolrQueryRequest req = req();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Wildcards can be used here, see <<Using Wildcards for Field Names>> below for mo
This parameter is particularly convenient when the fields in the input JSON are not available in the schema and xref:schemaless-mode.adoc[schemaless mode] is not enabled.
This will index all the fields into the default search field (using the `df` parameter) and only the `uniqueKey` field is mapped to the corresponding field in the schema.
If the input JSON does not have a value for the `uniqueKey` field then a UUID is generated for the same.
Field mapping parameters (`f`) cannot be combined with this parameter: a request that supplies both is rejected with a 400 error, since the mappings would otherwise be silently ignored.

`df`::
+
Expand Down
Loading