blob: 8740cec6ca82e03a1dde26eb4440cc90cdbb75d0 [file] [log] [blame]
/*
* 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.rest.schema.analysis;
import java.io.File;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.io.FileUtils;
import org.apache.solr.util.RestTestBase;
import org.eclipse.jetty.servlet.ServletHolder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.apache.solr.common.util.Utils.toJSONString;
public class TestManagedSynonymFilterFactory extends RestTestBase {
private static File tmpSolrHome;
/**
* Setup to make the schema mutable
*/
@Before
public void before() throws Exception {
tmpSolrHome = createTempDir().toFile();
FileUtils.copyDirectory(new File(TEST_HOME()), tmpSolrHome.getAbsoluteFile());
final SortedMap<ServletHolder,String> extraServlets = new TreeMap<>();
System.setProperty("managed.schema.mutable", "true");
System.setProperty("enable.update.log", "false");
createJettyAndHarness(tmpSolrHome.getAbsolutePath(), "solrconfig-managed-schema.xml", "schema-rest.xml",
"/solr", true, extraServlets);
}
@After
private void after() throws Exception {
if (null != jetty) {
jetty.stop();
jetty = null;
}
if (null != tmpSolrHome) {
FileUtils.deleteDirectory(tmpSolrHome);
tmpSolrHome = null;
}
System.clearProperty("managed.schema.mutable");
System.clearProperty("enable.update.log");
if (restTestHarness != null) {
restTestHarness.close();
}
restTestHarness = null;
}
@Test
public void testManagedSynonyms() throws Exception {
// this endpoint depends on at least one field type containing the following
// declaration in the schema-rest.xml:
//
// <filter class="solr.ManagedSynonymFilterFactory" managed="english" />
//
String endpoint = "/schema/analysis/synonyms/english";
assertJQ(endpoint,
"/synonymMappings/initArgs/ignoreCase==false",
"/synonymMappings/managedMap=={}");
// put a new mapping into the synonyms
Map<String,List<String>> syns = new HashMap<>();
syns.put("happy", Arrays.asList("glad","cheerful","joyful"));
assertJPut(endpoint,
toJSONString(syns),
"/responseHeader/status==0");
assertJQ(endpoint,
"/synonymMappings/managedMap/happy==['cheerful','glad','joyful']");
// request to a specific mapping
assertJQ(endpoint+"/happy",
"/happy==['cheerful','glad','joyful']");
// does not exist
assertJQ(endpoint+"/sad",
"/error/code==404");
// verify the user can update the ignoreCase initArg
assertJPut(endpoint,
json("{ 'initArgs':{ 'ignoreCase':true } }"),
"responseHeader/status==0");
assertJQ(endpoint,
"/synonymMappings/initArgs/ignoreCase==true");
syns = new HashMap<>();
syns.put("sad", Arrays.asList("unhappy"));
syns.put("SAD", Arrays.asList("bummed"));
assertJPut(endpoint,
toJSONString(syns),
"/responseHeader/status==0");
assertJQ(endpoint,
"/synonymMappings/managedMap/sad==['unhappy']");
assertJQ(endpoint,
"/synonymMappings/managedMap/SAD==['bummed']");
// expect a union of values when requesting the "sad" child
assertJQ(endpoint+"/sad",
"/sad==['bummed','unhappy']");
// verify delete works
assertJDelete(endpoint+"/sad",
"/responseHeader/status==0");
assertJQ(endpoint,
"/synonymMappings/managedMap=={'happy':['cheerful','glad','joyful']}");
// should fail with 404 as foo doesn't exist
assertJDelete(endpoint+"/foo",
"/error/code==404");
// verify that a newly added synonym gets expanded on the query side after core reload
String newFieldName = "managed_en_field";
// make sure the new field doesn't already exist
assertQ("/schema/fields/" + newFieldName + "?indent=on&wt=xml",
"count(/response/lst[@name='field']) = 0",
"/response/lst[@name='responseHeader']/int[@name='status'] = '404'",
"/response/lst[@name='error']/int[@name='code'] = '404'");
// add the new field
assertJPost("/schema", "{ add-field : { name: managed_en_field, type : managed_en}}",
"/responseHeader/status==0");
// make sure the new field exists now
assertQ("/schema/fields/" + newFieldName + "?indent=on&wt=xml",
"count(/response/lst[@name='field']) = 1",
"/response/lst[@name='responseHeader']/int[@name='status'] = '0'");
// multi-term synonym logic - SOLR-10264
final String multiTermOrigin;
final String multiTermSynonym;
if (random().nextBoolean()) {
multiTermOrigin = "hansestadt hamburg";
multiTermSynonym = "hh";
} else {
multiTermOrigin = "hh";
multiTermSynonym = "hansestadt hamburg";
}
// multi-term logic similar to the angry/mad logic (angry ~ origin, mad ~ synonym)
assertU(adoc(newFieldName, "I am a happy test today but yesterday I was angry", "id", "5150"));
assertU(adoc(newFieldName, multiTermOrigin+" is in North Germany.", "id", "040"));
assertU(commit());
assertQ("/select?q=" + newFieldName + ":angry",
"/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
"/response/result[@name='response'][@numFound='1']",
"/response/result[@name='response']/doc/str[@name='id'][.='5150']");
assertQ("/select?q=" + newFieldName + ":"+URLEncoder.encode(multiTermOrigin, "UTF-8"),
"/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
"/response/result[@name='response'][@numFound='1']",
"/response/result[@name='response']/doc/str[@name='id'][.='040']");
// add a mapping that will expand a query for "mad" to match docs with "angry"
syns = new HashMap<>();
syns.put("mad", Arrays.asList("angry"));
assertJPut(endpoint,
toJSONString(syns),
"/responseHeader/status==0");
assertJQ(endpoint,
"/synonymMappings/managedMap/mad==['angry']");
// add a mapping that will expand a query for "multi-term synonym" to match docs with "acronym"
syns = new HashMap<>();
syns.put(multiTermSynonym, Arrays.asList(multiTermOrigin));
assertJPut(endpoint,
toJSONString(syns),
"/responseHeader/status==0");
assertJQ(endpoint+"/"+URLEncoder.encode(multiTermSynonym, "UTF-8"),
"/"+multiTermSynonym+"==['"+multiTermOrigin+"']");
// should not match as the synonym mapping between mad and angry does not
// get applied until core reload
assertQ("/select?q=" + newFieldName + ":mad",
"/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
"/response/result[@name='response'][@numFound='0']");
// should not match as the synonym mapping between "origin" and "synonym"
// was not added before the document was indexed
assertQ("/select?q=" + newFieldName + ":("+URLEncoder.encode(multiTermSynonym, "UTF-8") + ")&sow=false",
"/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
"/response/result[@name='response'][@numFound='0']");
restTestHarness.reload();
// now query for mad and we should see our test doc
assertQ("/select?q=" + newFieldName + ":mad",
"/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
"/response/result[@name='response'][@numFound='1']",
"/response/result[@name='response']/doc/str[@name='id'][.='5150']");
// now query for "synonym" and we should see our test doc with "origin"
assertQ("/select?q=" + newFieldName + ":("+URLEncoder.encode(multiTermSynonym, "UTF-8") + ")&sow=false",
"/response/lst[@name='responseHeader']/int[@name='status'] = '0'",
"/response/result[@name='response'][@numFound='1']",
"/response/result[@name='response']/doc/str[@name='id'][.='040']");
// test for SOLR-6015
syns = new HashMap<>();
syns.put("mb", Arrays.asList("megabyte"));
assertJPut(endpoint,
toJSONString(syns),
"/responseHeader/status==0");
syns.put("MB", Arrays.asList("MiB", "Megabyte"));
assertJPut(endpoint,
toJSONString(syns),
"/responseHeader/status==0");
assertJQ(endpoint + "/MB",
"/MB==['Megabyte','MiB','megabyte']");
// test for SOLR-6878 - by default, expand is true, but only applies when sending in a list
List<String> m2mSyns = new ArrayList<>();
m2mSyns.addAll(Arrays.asList("funny", "entertaining", "whimiscal", "jocular"));
assertJPut(endpoint, toJSONString(m2mSyns), "/responseHeader/status==0");
assertJQ(endpoint + "/funny",
"/funny==['entertaining','funny','jocular','whimiscal']");
assertJQ(endpoint + "/entertaining",
"/entertaining==['entertaining','funny','jocular','whimiscal']");
assertJQ(endpoint + "/jocular",
"/jocular==['entertaining','funny','jocular','whimiscal']");
assertJQ(endpoint + "/whimiscal",
"/whimiscal==['entertaining','funny','jocular','whimiscal']");
}
/**
* Can we add and remove stopwords with umlauts
*/
@Test
public void testCanHandleDecodingAndEncodingForSynonyms() throws Exception {
String endpoint = "/schema/analysis/synonyms/german";
assertJQ(endpoint,
"/synonymMappings/initArgs/ignoreCase==false",
"/synonymMappings/managedMap=={}");
// does not exist
assertJQ(endpoint+"/fröhlich",
"/error/code==404");
Map<String,List<String>> syns = new HashMap<>();
// now put a synonym
syns.put("fröhlich", Arrays.asList("glücklick"));
assertJPut(endpoint,
toJSONString(syns),
"/responseHeader/status==0");
// and check if it exists
assertJQ(endpoint,
"/synonymMappings/managedMap/fröhlich==['glücklick']");
// verify delete works
assertJDelete(endpoint+"/fröhlich",
"/responseHeader/status==0");
// was it really deleted?
assertJDelete(endpoint+"/fröhlich",
"/error/code==404");
}
}