This is an automated email from the ASF dual-hosted git repository.
janhoy pushed a commit to branch branch_10_0
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/branch_10_0 by this push:
new bd254bd696f PR#3864 Remove ManagedSynonymFilterFactory (#3864)
bd254bd696f is described below
commit bd254bd696f4312cb722873049c472133049c89b
Author: Jan Høydahl <[email protected]>
AuthorDate: Sun Nov 16 16:41:07 2025 +0100
PR#3864 Remove ManagedSynonymFilterFactory (#3864)
(cherry picked from commit 12ba8377ecb66d0233fc5e138ee5ee8cf4e42c62)
---
.../PR#3864-Remove-ManagedSynonymFilterFactory.yml | 9 +
.../analysis/ManagedSynonymFilterFactory.java | 461 ---------------------
.../org.apache.lucene.analysis.TokenFilterFactory | 3 +-
.../solr/collection1/conf/schema-rest.xml | 6 +-
.../analysis/TestManagedSynonymFilterFactory.java | 277 -------------
.../modules/indexing-guide/pages/filters.adoc | 14 -
.../pages/major-changes-in-solr-10.adoc | 2 +
7 files changed, 16 insertions(+), 756 deletions(-)
diff --git
a/changelog/unreleased/PR#3864-Remove-ManagedSynonymFilterFactory.yml
b/changelog/unreleased/PR#3864-Remove-ManagedSynonymFilterFactory.yml
new file mode 100644
index 00000000000..24fa009c36f
--- /dev/null
+++ b/changelog/unreleased/PR#3864-Remove-ManagedSynonymFilterFactory.yml
@@ -0,0 +1,9 @@
+title: Remove deprecated ManagedSynonymFilterFactory, use
ManagedSynonymGraphFilterFactory instead
+type: removed
+authors:
+- name: Jan Høydahl
+links:
+- name: PR#3864
+ url: https://github.com/apache/solr/pull/3864
+- name: SOLR-10379
+ url: https://issues.apache.org/jira/browse/SOLR-10379
diff --git
a/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedSynonymFilterFactory.java
b/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedSynonymFilterFactory.java
deleted file mode 100644
index 94fd01114fe..00000000000
---
a/solr/core/src/java/org/apache/solr/rest/schema/analysis/ManagedSynonymFilterFactory.java
+++ /dev/null
@@ -1,461 +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.rest.schema.analysis;
-
-import java.io.IOException;
-import java.io.Reader;
-import java.lang.invoke.MethodHandles;
-import java.text.ParseException;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
-import org.apache.lucene.analysis.Analyzer;
-import org.apache.lucene.analysis.TokenStream;
-import org.apache.lucene.analysis.core.FlattenGraphFilterFactory;
-import org.apache.lucene.analysis.synonym.SynonymFilterFactory;
-import org.apache.lucene.analysis.synonym.SynonymMap;
-import org.apache.lucene.util.CharsRef;
-import org.apache.lucene.util.CharsRefBuilder;
-import org.apache.lucene.util.ResourceLoader;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.SolrException.ErrorCode;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.core.SolrResourceLoader;
-import org.apache.solr.response.SolrQueryResponse;
-import org.apache.solr.rest.BaseSolrResource;
-import org.apache.solr.rest.ManagedResource;
-import org.apache.solr.rest.ManagedResourceStorage.StorageIO;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * TokenFilterFactory and ManagedResource implementation for doing CRUD on
synonyms using the REST
- * API.
- *
- * @deprecated Use {@link ManagedSynonymGraphFilterFactory} instead, but be
sure to also use {@link
- * FlattenGraphFilterFactory} at index time (not at search time) as well.
- * @since 4.8.0
- * @lucene.spi {@value #NAME}
- */
-@Deprecated
-public class ManagedSynonymFilterFactory extends BaseManagedTokenFilterFactory
{
-
- /** SPI name */
- public static final String NAME = "managedSynonym";
-
- private static final Logger log =
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
-
- public static final String SYNONYM_MAPPINGS = "synonymMappings";
- public static final String IGNORE_CASE_INIT_ARG = "ignoreCase";
-
- /**
- * Used internally to preserve the case of synonym mappings regardless of
the ignoreCase setting.
- */
- private static class CasePreservedSynonymMappings {
- Map<String, Set<String>> mappings = new TreeMap<>();
-
- /**
- * Provides a view of the mappings for a given term; specifically, if
ignoreCase is true, then
- * the returned "view" contains the mappings for all known cases of the
term, if it is false,
- * then only the mappings for the specific case is returned.
- */
- Set<String> getMappings(boolean ignoreCase, String key) {
- Set<String> synMappings = null;
- if (ignoreCase) {
- // TODO: should we return the mapped values in all lower-case here?
- if (mappings.size() == 1) {
- // if only one in the map (which is common) just return it directly
- return mappings.values().iterator().next();
- }
-
- synMappings = new TreeSet<>();
- for (Set<String> next : mappings.values()) synMappings.addAll(next);
- } else {
- synMappings = mappings.get(key);
- }
- return synMappings;
- }
-
- @Override
- public String toString() {
- return mappings.toString();
- }
- }
-
- /**
- * ManagedResource implementation for synonyms, which are so specialized
that it makes sense to
- * implement this class as an inner class as it has little application
outside the
- * SynonymFilterFactory use cases.
- */
- public static class SynonymManager extends ManagedResource
- implements ManagedResource.ChildResourceSupport {
- protected Map<String, CasePreservedSynonymMappings> synonymMappings;
-
- public SynonymManager(String resourceId, SolrResourceLoader loader,
StorageIO storageIO)
- throws SolrException {
- super(resourceId, loader, storageIO);
- }
-
- @SuppressWarnings("unchecked")
- @Override
- protected void onManagedDataLoadedFromStorage(NamedList<?>
managedInitArgs, Object managedData)
- throws SolrException {
- NamedList<Object> initArgs = (NamedList<Object>) managedInitArgs;
-
- String format = (String) initArgs.get("format");
- if (format != null && !"solr".equals(format)) {
- throw new SolrException(
- ErrorCode.BAD_REQUEST, "Invalid format " + format + "! Only 'solr'
is supported.");
- }
-
- // the default behavior is to not ignore case,
- // so if not supplied, then install the default
- if (initArgs.get(IGNORE_CASE_INIT_ARG) == null) {
- initArgs.add(IGNORE_CASE_INIT_ARG, Boolean.FALSE);
- }
-
- boolean ignoreCase = getIgnoreCase(managedInitArgs);
- synonymMappings = new TreeMap<>();
- if (managedData != null) {
- Map<String, Object> storedSyns = (Map<String, Object>) managedData;
- for (Map.Entry<String, Object> entry : storedSyns.entrySet()) {
- String key = entry.getKey();
-
- String caseKey = applyCaseSetting(ignoreCase, key);
- CasePreservedSynonymMappings cpsm = synonymMappings.get(caseKey);
- if (cpsm == null) {
- cpsm = new CasePreservedSynonymMappings();
- synonymMappings.put(caseKey, cpsm);
- }
-
- // give the nature of our JSON parsing solution, we really have
- // no guarantees on what is in the file
- Object mapping = entry.getValue();
- if (!(mapping instanceof List)) {
- throw new SolrException(
- ErrorCode.SERVER_ERROR,
- "Invalid synonym file format! Expected a list of synonyms for "
- + key
- + " but got "
- + mapping.getClass().getName());
- }
-
- Set<String> sortedVals = new TreeSet<>((List<String>)
entry.getValue());
- cpsm.mappings.put(key, sortedVals);
- }
- }
- if (log.isInfoEnabled()) {
- log.info("Loaded {} synonym mappings for {}", synonymMappings.size(),
getResourceId());
- }
- }
-
- @SuppressWarnings("unchecked")
- @Override
- protected Object applyUpdatesToManagedData(Object updates) {
- boolean ignoreCase = getIgnoreCase();
- boolean madeChanges = false;
- if (updates instanceof List) {
- madeChanges = applyListUpdates((List<String>) updates, ignoreCase);
- } else if (updates instanceof Map) {
- madeChanges = applyMapUpdates((Map<String, Object>) updates,
ignoreCase);
- } else {
- throw new SolrException(
- ErrorCode.BAD_REQUEST,
- "Unsupported data format ("
- + updates.getClass().getName()
- + "); expected a JSON object (Map or List)!");
- }
- return madeChanges ? getStoredView() : null;
- }
-
- protected boolean applyListUpdates(List<String> jsonList, boolean
ignoreCase) {
- boolean madeChanges = false;
- for (String term : jsonList) {
- // find the mappings using the case aware key
- String origTerm = term;
- term = applyCaseSetting(ignoreCase, term);
- CasePreservedSynonymMappings cpsm = synonymMappings.get(term);
- if (cpsm == null) cpsm = new CasePreservedSynonymMappings();
-
- Set<String> treeTerms = new TreeSet<>(jsonList);
- cpsm.mappings.put(origTerm, treeTerms);
- madeChanges = true;
- // only add the cpsm to the synonymMappings if it has valid data
- if (!synonymMappings.containsKey(term) && cpsm.mappings.get(origTerm)
!= null) {
- synonymMappings.put(term, cpsm);
- }
- }
- return madeChanges;
- }
-
- protected boolean applyMapUpdates(Map<String, Object> jsonMap, boolean
ignoreCase) {
- boolean madeChanges = false;
-
- for (String term : jsonMap.keySet()) {
-
- String origTerm = term;
- term = applyCaseSetting(ignoreCase, term);
-
- // find the mappings using the case aware key
- CasePreservedSynonymMappings cpsm = synonymMappings.get(term);
- if (cpsm == null) cpsm = new CasePreservedSynonymMappings();
-
- Set<String> output = cpsm.mappings.get(origTerm);
-
- Object val = jsonMap.get(origTerm); // IMPORTANT: use the original
- if (val instanceof String strVal) {
-
- if (output == null) {
- output = new TreeSet<>();
- cpsm.mappings.put(origTerm, output);
- }
-
- if (output.add(strVal)) {
- madeChanges = true;
- }
- } else if (val instanceof List) {
- @SuppressWarnings({"unchecked"})
- List<String> vals = (List<String>) val;
-
- if (output == null) {
- output = new TreeSet<>();
- cpsm.mappings.put(origTerm, output);
- }
-
- for (String nextVal : vals) {
- if (output.add(nextVal)) {
- madeChanges = true;
- }
- }
-
- } else {
- throw new SolrException(
- ErrorCode.BAD_REQUEST,
- "Unsupported value "
- + val
- + " for "
- + term
- + "; expected single value or a JSON array!");
- }
-
- // only add the cpsm to the synonymMappings if it has valid data
- if (!synonymMappings.containsKey(term) && cpsm.mappings.get(origTerm)
!= null) {
- synonymMappings.put(term, cpsm);
- }
- }
-
- return madeChanges;
- }
-
- /**
- * Returns a Map of how we store and load data managed by this resource,
which is different than
- * how it is managed at runtime in order to support the ignoreCase setting.
- */
- protected Map<String, Set<String>> getStoredView() {
- Map<String, Set<String>> storedView = new TreeMap<>();
- for (CasePreservedSynonymMappings cpsm : synonymMappings.values()) {
- for (Map.Entry<String, Set<String>> entry : cpsm.mappings.entrySet()) {
- storedView.put(entry.getKey(), entry.getValue());
- }
- }
- return storedView;
- }
-
- protected String applyCaseSetting(boolean ignoreCase, String str) {
- return (ignoreCase && str != null) ? str.toLowerCase(Locale.ROOT) : str;
- }
-
- public boolean getIgnoreCase() {
- return getIgnoreCase(managedInitArgs);
- }
-
- public boolean getIgnoreCase(NamedList<?> initArgs) {
- Boolean ignoreCase = initArgs.getBooleanArg(IGNORE_CASE_INIT_ARG);
- // ignoreCase = false by default
- return null == ignoreCase ? false : ignoreCase;
- }
-
- @Override
- public void doGet(BaseSolrResource endpoint, String childId) {
- SolrQueryResponse response = endpoint.getSolrResponse();
- if (childId != null) {
- boolean ignoreCase = getIgnoreCase();
- String key = applyCaseSetting(ignoreCase, childId);
-
- // if ignoreCase==true, then we get the mappings using the lower-cased
key
- // and then return a union of all case-sensitive keys, if false, then
- // we only return the mappings for the exact case requested
- CasePreservedSynonymMappings cpsm = synonymMappings.get(key);
- Set<String> mappings = (cpsm != null) ? cpsm.getMappings(ignoreCase,
childId) : null;
- if (mappings == null)
- throw new SolrException(
- ErrorCode.NOT_FOUND,
- String.format(Locale.ROOT, "%s not found in %s", childId,
getResourceId()));
-
- response.add(childId, mappings);
- } else {
- response.add(SYNONYM_MAPPINGS, buildMapToStore(getStoredView()));
- }
- }
-
- @Override
- public synchronized void doDeleteChild(BaseSolrResource endpoint, String
childId) {
- boolean ignoreCase = getIgnoreCase();
- String key = applyCaseSetting(ignoreCase, childId);
-
- CasePreservedSynonymMappings cpsm = synonymMappings.get(key);
- if (cpsm == null)
- throw new SolrException(
- ErrorCode.NOT_FOUND,
- String.format(Locale.ROOT, "%s not found in %s", childId,
getResourceId()));
-
- if (ignoreCase) {
- // delete all mappings regardless of case
- synonymMappings.remove(key);
- } else {
- // just delete the mappings for the specific case-sensitive key
- if (cpsm.mappings.containsKey(childId)) {
- cpsm.mappings.remove(childId);
-
- if (cpsm.mappings.isEmpty()) synonymMappings.remove(key);
- } else {
- throw new SolrException(
- ErrorCode.NOT_FOUND,
- String.format(Locale.ROOT, "%s not found in %s", childId,
getResourceId()));
- }
- }
-
- // store the updated data (using the stored view)
- storeManagedData(getStoredView());
-
- log.info("Removed synonym mappings for: {}", childId);
- }
- }
-
- /**
- * Custom SynonymMap.Parser implementation that provides synonym mappings
from the managed JSON in
- * this class during SynonymMap building.
- */
- private static class ManagedSynonymParser extends SynonymMap.Parser {
-
- SynonymManager synonymManager;
-
- public ManagedSynonymParser(SynonymManager synonymManager, boolean dedup,
Analyzer analyzer) {
- super(dedup, analyzer);
- this.synonymManager = synonymManager;
- }
-
- /** Add the managed synonyms and their mappings into the SynonymMap
builder. */
- @Override
- public void parse(Reader in) throws IOException, ParseException {
- boolean ignoreCase = synonymManager.getIgnoreCase();
- for (CasePreservedSynonymMappings cpsm :
synonymManager.synonymMappings.values()) {
- for (Map.Entry<String, Set<String>> entry : cpsm.mappings.entrySet()) {
- for (String mapping : entry.getValue()) {
- // apply the case setting to match the behavior of the SynonymMap
builder
- CharsRef casedTerm =
- analyze(
- synonymManager.applyCaseSetting(ignoreCase,
entry.getKey()),
- new CharsRefBuilder());
- CharsRef casedMapping =
- analyze(
- synonymManager.applyCaseSetting(ignoreCase, mapping), new
CharsRefBuilder());
- add(casedTerm, casedMapping, false);
- }
- }
- }
- }
- }
-
- protected SynonymFilterFactory delegate;
-
- public ManagedSynonymFilterFactory(Map<String, String> args) {
- super(args);
- }
-
- /** Default ctor for compatibility with SPI */
- public ManagedSynonymFilterFactory() {
- throw defaultCtorException();
- }
-
- @Override
- public String getResourceId() {
- return "/schema/analysis/synonyms/" + handle;
- }
-
- @Override
- protected Class<? extends ManagedResource> getManagedResourceImplClass() {
- return SynonymManager.class;
- }
-
- /**
- * Called once, during core initialization, to initialize any analysis
components that depend on
- * the data managed by this resource. It is important that the analysis
component is only
- * initialized once during core initialization so that text analysis is
consistent, especially in
- * a distributed environment, as we don't want one server applying a
different set of stop words
- * than other servers.
- */
- @SuppressWarnings("unchecked")
- @Override
- public void onManagedResourceInitialized(NamedList<?> initArgs, final
ManagedResource res)
- throws SolrException {
- NamedList<Object> args = (NamedList<Object>) initArgs;
- args.add("synonyms", getResourceId());
- args.add("expand", "false");
- args.add("format", "solr");
-
- Map<String, String> filtArgs = new HashMap<>();
- for (Map.Entry<String, ?> entry : args) {
- filtArgs.put(entry.getKey(), entry.getValue().toString());
- }
- // create the actual filter factory that pulls the synonym mappings
- // from synonymMappings using a custom parser implementation
- delegate =
- new SynonymFilterFactory(filtArgs) {
- @Override
- protected SynonymMap loadSynonyms(
- ResourceLoader loader, String cname, boolean dedup, Analyzer
analyzer)
- throws IOException, ParseException {
-
- ManagedSynonymParser parser =
- new ManagedSynonymParser((SynonymManager) res, dedup,
analyzer);
- // null is safe here because there's no actual parsing done
against a input Reader
- parser.parse(null);
- return parser.build();
- }
- };
- try {
- delegate.inform(res.getResourceLoader());
- } catch (IOException e) {
- throw new SolrException(ErrorCode.SERVER_ERROR, e);
- }
- }
-
- @Override
- public TokenStream create(TokenStream input) {
- if (delegate == null)
- throw new IllegalStateException(
- this.getClass().getName()
- + " not initialized correctly! The SynonymFilterFactory delegate
was not initialized.");
-
- return delegate.create(input);
- }
-}
diff --git
a/solr/core/src/resources/META-INF/services/org.apache.lucene.analysis.TokenFilterFactory
b/solr/core/src/resources/META-INF/services/org.apache.lucene.analysis.TokenFilterFactory
index d95a5e707b1..12e4add3702 100644
---
a/solr/core/src/resources/META-INF/services/org.apache.lucene.analysis.TokenFilterFactory
+++
b/solr/core/src/resources/META-INF/services/org.apache.lucene.analysis.TokenFilterFactory
@@ -15,5 +15,4 @@
org.apache.solr.analysis.ReversedWildcardFilterFactory
org.apache.solr.rest.schema.analysis.ManagedStopFilterFactory
-org.apache.solr.rest.schema.analysis.ManagedSynonymFilterFactory
-org.apache.solr.rest.schema.analysis.ManagedSynonymGraphFilterFactory
\ No newline at end of file
+org.apache.solr.rest.schema.analysis.ManagedSynonymGraphFilterFactory
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
b/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
index 517182771bd..2abfa8fc92b 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema-rest.xml
@@ -484,7 +484,8 @@
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.ManagedStopFilterFactory" managed="english"/>
- <filter class="solr.ManagedSynonymFilterFactory" managed="english"/>
+ <filter class="solr.ManagedSynonymGraphFilterFactory" managed="english"/>
+ <filter class="solr.FlattenGraphFilterFactory"/>
</analyzer>
</fieldType>
@@ -493,7 +494,8 @@
<analyzer>
<tokenizer class="solr.StandardTokenizerFactory"/>
<filter class="solr.ManagedStopFilterFactory" managed="german"/>
- <filter class="solr.ManagedSynonymFilterFactory" managed="german"/>
+ <filter class="solr.ManagedSynonymGraphFilterFactory" managed="german"/>
+ <filter class="solr.FlattenGraphFilterFactory"/>
</analyzer>
</fieldType>
diff --git
a/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedSynonymFilterFactory.java
b/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedSynonymFilterFactory.java
deleted file mode 100644
index 8b767a6e416..00000000000
---
a/solr/core/src/test/org/apache/solr/rest/schema/analysis/TestManagedSynonymFilterFactory.java
+++ /dev/null
@@ -1,277 +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.rest.schema.analysis;
-
-import static org.apache.solr.common.util.Utils.toJSONString;
-
-import java.net.URLEncoder;
-import java.nio.file.Path;
-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.file.PathUtils;
-import org.apache.solr.util.RestTestBase;
-import org.eclipse.jetty.ee10.servlet.ServletHolder;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-public class TestManagedSynonymFilterFactory extends RestTestBase {
-
- private static Path tmpSolrHome;
-
- /** Setup to make the schema mutable */
- @Before
- public void before() throws Exception {
- tmpSolrHome = createTempDir();
- PathUtils.copyDirectory(TEST_PATH(), tmpSolrHome);
-
- final SortedMap<ServletHolder, String> extraServlets = new TreeMap<>();
-
- System.setProperty("managed.schema.mutable", "true");
- System.setProperty("solr.index.updatelog.enabled", "false");
- createJettyAndHarness(
- tmpSolrHome,
- "solrconfig-managed-schema.xml",
- "schema-rest.xml",
- "/solr",
- true,
- extraServlets);
- }
-
- @After
- public void after() throws Exception {
- solrClientTestRule.reset();
- if (null != tmpSolrHome) {
- PathUtils.deleteDirectory(tmpSolrHome);
- tmpSolrHome = null;
- }
- System.clearProperty("managed.schema.mutable");
- System.clearProperty("solr.index.updatelog.enabled");
-
- 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", "whimsical",
"jocular"));
- assertJPut(endpoint, toJSONString(m2mSyns), "/responseHeader/status==0");
-
- assertJQ(endpoint + "/funny",
"/funny==['entertaining','funny','jocular','whimsical']");
- assertJQ(
- endpoint + "/entertaining",
- "/entertaining==['entertaining','funny','jocular','whimsical']");
- assertJQ(endpoint + "/jocular",
"/jocular==['entertaining','funny','jocular','whimsical']");
- assertJQ(endpoint + "/whimsical",
"/whimsical==['entertaining','funny','jocular','whimsical']");
- }
-
- /** 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");
- }
-}
diff --git a/solr/solr-ref-guide/modules/indexing-guide/pages/filters.adoc
b/solr/solr-ref-guide/modules/indexing-guide/pages/filters.adoc
index ba90cb3c725..dfc68ce56b9 100644
--- a/solr/solr-ref-guide/modules/indexing-guide/pages/filters.adoc
+++ b/solr/solr-ref-guide/modules/indexing-guide/pages/filters.adoc
@@ -1808,20 +1808,6 @@ With class name (legacy)::
See <<Stop Filter>> for example input/output.
-== Managed Synonym Filter
-
-This is specialized version of the <<Synonym Filter>> that uses a mapping on
synonyms that is xref:configuration-guide:managed-resources.adoc[managed from a
REST API].
-
-.Managed Synonym Filter has been Deprecated
-[WARNING]
-====
-Managed Synonym Filter has been deprecated in favor of Managed Synonym Graph
Filter, which is required for multi-term synonym support.
-====
-
-*Factory class:* `solr.ManagedSynonymFilterFactory`
-
-For arguments and examples, see the <<Synonym Graph Filter>> below.
-
== Managed Synonym Graph Filter
This is specialized version of the <<Synonym Graph Filter>> that uses a
mapping on synonyms that is
xref:configuration-guide:managed-resources.adoc[managed from a REST API].
diff --git
a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
index 86f9f4214a2..e8707afb94b 100644
---
a/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
+++
b/solr/solr-ref-guide/modules/upgrade-notes/pages/major-changes-in-solr-10.adoc
@@ -195,6 +195,8 @@ Nowadays, the HTTP request is available via internal APIs:
`SolrQueryRequest.get
* SolrInfoMBeanHandler and PluginInfoHandler have been removed
+* The deprecated `ManagedSynonymFilterFactory` has been removed. Use
`ManagedSynonymGraphFilterFactory` instead with `FlattenGraphFilterFactory` at
index time.
+
* The deprecated `LowerCaseTokenizer` and `LowerCaseTokenizerFactory` have
been removed. These classes were deprecated in Solr 8 and can be replaced by
combining `LetterTokenizerFactory` with `LowerCaseFilterFactory`.
=== Security