diff --git a/CHANGELOG.md b/CHANGELOG.md index 57bfb1d2a0a78..da3dc8a9e5648 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), - Add overload constructor for Translog to accept Channel Factory as a parameter ([#18918](https://github.com/opensearch-project/OpenSearch/pull/18918)) - Add subdirectory-aware store module with recovery support ([#19132](https://github.com/opensearch-project/OpenSearch/pull/19132)) - Add a dynamic cluster setting to control the enablement of the merged segment warmer ([#18929](https://github.com/opensearch-project/OpenSearch/pull/18929)) +- Publish transport-grpc-spi exposing QueryBuilderProtoConverter and QueryBuilderProtoConverterRegistry ([#18949](https://github.com/opensearch-project/OpenSearch/pull/18949)) - Support system generated search pipeline. ([#19128](https://github.com/opensearch-project/OpenSearch/pull/19128)) ### Changed diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ad4c4bc5be459..d2e182270befb 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -21,6 +21,7 @@ woodstox = "6.4.0" kotlin = "1.7.10" antlr4 = "4.13.1" guava = "33.2.1-jre" +opensearchprotobufs = "0.6.0" protobuf = "3.25.8" jakarta_annotation = "1.3.5" google_http_client = "1.44.1" diff --git a/modules/transport-grpc/build.gradle b/modules/transport-grpc/build.gradle index 1e51154877d3b..806779a93fb97 100644 --- a/modules/transport-grpc/build.gradle +++ b/modules/transport-grpc/build.gradle @@ -21,6 +21,7 @@ testClusters { } dependencies { + api project('spi') compileOnly "com.google.code.findbugs:jsr305:3.0.2" runtimeOnly "com.google.guava:guava:${versions.guava}" implementation "com.google.errorprone:error_prone_annotations:2.24.1" diff --git a/modules/transport-grpc/spi/README.md b/modules/transport-grpc/spi/README.md new file mode 100644 index 0000000000000..9a3609abbe528 --- /dev/null +++ b/modules/transport-grpc/spi/README.md @@ -0,0 +1,277 @@ +# transport-grpc-spi + +Service Provider Interface (SPI) for the OpenSearch gRPC transport module. This module provides interfaces and utilities that allow external plugins to extend the gRPC transport functionality. + +## Overview + +The `transport-grpc-spi` module enables plugin developers to: +- Implement custom query converters for gRPC transport +- Extend gRPC protocol buffer handling +- Register custom query types that can be processed via gRPC + +## Key Components + +### QueryBuilderProtoConverter + +Interface for converting protobuf query messages to OpenSearch QueryBuilder objects. + +```java +public interface QueryBuilderProtoConverter { + QueryContainer.QueryContainerCase getHandledQueryCase(); + QueryBuilder fromProto(QueryContainer queryContainer); +} +``` + +### QueryBuilderProtoConverterRegistry + +Interface for accessing the query converter registry. This provides a clean abstraction for plugins that need to convert nested queries without exposing internal implementation details. + +## Usage for Plugin Developers + +### 1. Add Dependency + +Add the SPI dependency to your plugin's `build.gradle`: + +```gradle +dependencies { + compileOnly 'org.opensearch.plugin:transport-grpc-spi:${opensearch.version}' + compileOnly 'org.opensearch:protobufs:${protobufs.version}' +} +``` + +### 2. Implement Custom Query Converter + +```java +public class MyCustomQueryConverter implements QueryBuilderProtoConverter { + + @Override + public QueryContainer.QueryContainerCase getHandledQueryCase() { + return QueryContainer.QueryContainerCase.MY_CUSTOM_QUERY; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + // Convert your custom protobuf query to QueryBuilder + MyCustomQuery customQuery = queryContainer.getMyCustomQuery(); + return new MyCustomQueryBuilder(customQuery.getField(), customQuery.getValue()); + } +} +``` + +### 3. Register Your Converter + +In your plugin's main class, return the converter from createComponents: + +```java +public class MyPlugin extends Plugin { + + @Override + public Collection createComponents(Client client, ClusterService clusterService, + ThreadPool threadPool, ResourceWatcherService resourceWatcherService, + ScriptService scriptService, NamedXContentRegistry xContentRegistry, + Environment environment, NodeEnvironment nodeEnvironment, + NamedWriteableRegistry namedWriteableRegistry, + IndexNameExpressionResolver indexNameExpressionResolver, + Supplier repositoriesServiceSupplier) { + + // Return your converter instance - the transport-grpc plugin will discover and register it + return Collections.singletonList(new MyCustomQueryConverter()); + } +} +``` + +**Step 3b: Create SPI Registration File** + +Create a file at `src/main/resources/META-INF/services/org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter`: + +``` +org.opensearch.mypackage.MyCustomQueryConverter +``` + +**Step 3c: Declare Extension in Plugin Descriptor** + +In your `plugin-descriptor.properties`, declare that your plugin extends transport-grpc: + +```properties +extended.plugins=transport-grpc +``` + +### 4. Accessing the Registry (For Complex Queries) + +If your converter needs to handle nested queries (like k-NN's filter clause), you'll need access to the registry to convert other query types. The transport-grpc plugin will inject the registry into your converter. + +```java +public class MyCustomQueryConverter implements QueryBuilderProtoConverter { + + private QueryBuilderProtoConverterRegistry registry; + + @Override + public void setRegistry(QueryBuilderProtoConverterRegistry registry) { + this.registry = registry; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + MyCustomQuery customQuery = queryContainer.getMyCustomQuery(); + + MyCustomQueryBuilder builder = new MyCustomQueryBuilder( + customQuery.getField(), + customQuery.getValue() + ); + + // Handle nested queries using the injected registry + if (customQuery.hasFilter()) { + QueryContainer filterContainer = customQuery.getFilter(); + QueryBuilder filterQuery = registry.fromProto(filterContainer); + builder.filter(filterQuery); + } + + return builder; + } +} +``` + +**Registry Injection Pattern** + +**How k-NN Now Accesses Built-in Converters**: + +The gRPC plugin **injects the populated registry** into converters that need it: + +```java +// 1. Converter interface has a default setRegistry method +public interface QueryBuilderProtoConverter { + QueryBuilder fromProto(QueryContainer queryContainer); + + default void setRegistry(QueryBuilderProtoConverterRegistry registry) { + // By default, converters don't need a registry + // Converters that handle nested queries should override this method + } +} + +// 2. GrpcPlugin injects registry into loaded extensions +for (QueryBuilderProtoConverter converter : queryConverters) { + // Inject the populated registry into the converter + converter.setRegistry(queryRegistry); + + // Register the converter + queryRegistry.registerConverter(converter); +} +``` + +**Registry Access Pattern for Converters with Nested Queries**: +```java +public class KNNQueryBuilderProtoConverter implements QueryBuilderProtoConverter { + + private QueryBuilderProtoConverterRegistry registry; + + @Override + public void setRegistry(QueryBuilderProtoConverterRegistry registry) { + this.registry = registry; + // Pass the registry to utility classes that need it + KNNQueryBuilderProtoUtils.setRegistry(registry); + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + // The utility class can now convert nested queries using the injected registry + return KNNQueryBuilderProtoUtils.fromProto(queryContainer.getKnn()); + } +} +``` + + +## Testing + +### Unit Tests + +```bash +./gradlew :modules:transport-grpc:spi:test +``` + +### Testing Your Custom Converter + +```java +@Test +public void testCustomQueryConverter() { + MyCustomQueryConverter converter = new MyCustomQueryConverter(); + + // Create test protobuf query + QueryContainer queryContainer = QueryContainer.newBuilder() + .setMyCustomQuery(MyCustomQuery.newBuilder() + .setField("test_field") + .setValue("test_value") + .build()) + .build(); + + // Convert and verify + QueryBuilder result = converter.fromProto(queryContainer); + assertThat(result, instanceOf(MyCustomQueryBuilder.class)); + + MyCustomQueryBuilder customQuery = (MyCustomQueryBuilder) result; + assertEquals("test_field", customQuery.fieldName()); + assertEquals("test_value", customQuery.value()); +} +``` + +## Real-World Example: k-NN Plugin +See the k-NN plugin https://github.com/opensearch-project/k-NN/pull/2833/files for an example on how to use this SPI, including handling nested queries. + +**1. Dependency in build.gradle:** +```gradle +compileOnly "org.opensearch.plugin:transport-grpc-spi:${opensearch.version}" +compileOnly "org.opensearch:protobufs:0.8.0" +``` + +**2. Converter Implementation with Registry Access:** +```java +public class KNNQueryBuilderProtoConverter implements QueryBuilderProtoConverter { + + private QueryBuilderProtoConverterRegistry registry; + + @Override + public void setRegistry(QueryBuilderProtoConverterRegistry registry) { + this.registry = registry; + } + + @Override + public QueryContainer.QueryContainerCase getHandledQueryCase() { + return QueryContainer.QueryContainerCase.KNN; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + KnnQuery knnQuery = queryContainer.getKnn(); + + KNNQueryBuilder builder = new KNNQueryBuilder( + knnQuery.getField(), + knnQuery.getVectorList().toArray(new Float[0]), + knnQuery.getK() + ); + + // Handle nested filter query using injected registry + if (knnQuery.hasFilter()) { + QueryContainer filterContainer = knnQuery.getFilter(); + QueryBuilder filterQuery = registry.fromProto(filterContainer); + builder.filter(filterQuery); + } + + return builder; + } +} +``` + +**3. Plugin Registration:** +```java +// In KNNPlugin.createComponents() +KNNQueryBuilderProtoConverter knnQueryConverter = new KNNQueryBuilderProtoConverter(); +return ImmutableList.of(knnStats, knnQueryConverter); +``` + +**4. SPI File:** +``` +# src/main/resources/META-INF/services/org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter +org.opensearch.knn.grpc.proto.request.search.query.KNNQueryBuilderProtoConverter +``` + +**Why k-NN needs the registry:** +The k-NN query's `filter` field is a `QueryContainer` protobuf type that can contain any query type (MatchAll, Term, Terms, etc.). The k-NN converter needs access to the registry to convert these nested queries to their corresponding QueryBuilder objects. diff --git a/modules/transport-grpc/spi/build.gradle b/modules/transport-grpc/spi/build.gradle new file mode 100644 index 0000000000000..82ccdd824a696 --- /dev/null +++ b/modules/transport-grpc/spi/build.gradle @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +apply plugin: 'opensearch.build' +apply plugin: 'opensearch.publish' + +base { + group = 'org.opensearch.plugin' + archivesName = 'transport-grpc-spi' +} + +dependencies { + api project(":server") + api "org.opensearch:protobufs:${versions.opensearchprotobufs}" + + testImplementation project(":test:framework") +} diff --git a/modules/transport-grpc/spi/licenses/protobufs-0.6.0.jar.sha1 b/modules/transport-grpc/spi/licenses/protobufs-0.6.0.jar.sha1 new file mode 100644 index 0000000000000..a02b4926d87f5 --- /dev/null +++ b/modules/transport-grpc/spi/licenses/protobufs-0.6.0.jar.sha1 @@ -0,0 +1 @@ +1675c5085e1376fd1a107b87f7e325944ab5b4dc \ No newline at end of file diff --git a/modules/transport-grpc/spi/licenses/protobufs-LICENSE.txt b/modules/transport-grpc/spi/licenses/protobufs-LICENSE.txt new file mode 100644 index 0000000000000..44cbce8f123a1 --- /dev/null +++ b/modules/transport-grpc/spi/licenses/protobufs-LICENSE.txt @@ -0,0 +1,475 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from unicode conversion examples available at +http://www.unicode.org/Public/PROGRAMS/CVTUTF. Here is the copyright +from those sources: + +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + + +Some code in core/src/java/org/apache/lucene/util/ArrayUtil.java was +derived from Python 2.4.2 sources available at +http://www.python.org. Full license is here: + + http://www.python.org/download/releases/2.4.2/license/ + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from Python 3.1.2 sources available at +http://www.python.org. Full license is here: + + http://www.python.org/download/releases/3.1.2/license/ + +Some code in core/src/java/org/apache/lucene/util/automaton was +derived from Brics automaton sources available at +www.brics.dk/automaton/. Here is the copyright from those sources: + +/* + * Copyright (c) 2001-2009 Anders Moeller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +The levenshtein automata tables in core/src/java/org/apache/lucene/util/automaton +were automatically generated with the moman/finenight FSA package. +Here is the copyright for those sources: + +# Copyright (c) 2010, Jean-Philippe Barrette-LaPierre, +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from ICU (http://www.icu-project.org) +The full license is available here: + http://source.icu-project.org/repos/icu/icu/trunk/license.html + +/* + * Copyright (C) 1999-2010, International Business Machines + * Corporation and others. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * provided that the above copyright notice(s) and this permission notice appear + * in all copies of the Software and that both the above copyright notice(s) and + * this permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE + * LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR + * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall not + * be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +The following license applies to the Snowball stemmers: + +Copyright (c) 2001, Dr Martin Porter +Copyright (c) 2002, Richard Boulton +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holders nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The following license applies to the KStemmer: + +Copyright © 2003, +Center for Intelligent Information Retrieval, +University of Massachusetts, Amherst. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. The names "Center for Intelligent Information Retrieval" and +"University of Massachusetts" must not be used to endorse or promote products +derived from this software without prior written permission. To obtain +permission, contact info@ciir.cs.umass.edu. + +THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF MASSACHUSETTS AND OTHER CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The following license applies to the Morfologik project: + +Copyright (c) 2006 Dawid Weiss +Copyright (c) 2007-2011 Dawid Weiss, Marcin Miłkowski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Morfologik nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- + +The dictionary comes from Morfologik project. Morfologik uses data from +Polish ispell/myspell dictionary hosted at http://www.sjp.pl/slownik/en/ and +is licenced on the terms of (inter alia) LGPL and Creative Commons +ShareAlike. The part-of-speech tags were added in Morfologik project and +are not found in the data from sjp.pl. The tagset is similar to IPI PAN +tagset. + +--- + +The following license applies to the Morfeusz project, +used by org.apache.lucene.analysis.morfologik. + +BSD-licensed dictionary of Polish (SGJP) +http://sgjp.pl/morfeusz/ + +Copyright © 2011 Zygmunt Saloni, Włodzimierz Gruszczyński, + Marcin Woliński, Robert Wołosz + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS “AS IS” AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/modules/transport-grpc/spi/licenses/protobufs-NOTICE.txt b/modules/transport-grpc/spi/licenses/protobufs-NOTICE.txt new file mode 100644 index 0000000000000..44cbce8f123a1 --- /dev/null +++ b/modules/transport-grpc/spi/licenses/protobufs-NOTICE.txt @@ -0,0 +1,475 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. + + + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from unicode conversion examples available at +http://www.unicode.org/Public/PROGRAMS/CVTUTF. Here is the copyright +from those sources: + +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + + +Some code in core/src/java/org/apache/lucene/util/ArrayUtil.java was +derived from Python 2.4.2 sources available at +http://www.python.org. Full license is here: + + http://www.python.org/download/releases/2.4.2/license/ + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from Python 3.1.2 sources available at +http://www.python.org. Full license is here: + + http://www.python.org/download/releases/3.1.2/license/ + +Some code in core/src/java/org/apache/lucene/util/automaton was +derived from Brics automaton sources available at +www.brics.dk/automaton/. Here is the copyright from those sources: + +/* + * Copyright (c) 2001-2009 Anders Moeller + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +The levenshtein automata tables in core/src/java/org/apache/lucene/util/automaton +were automatically generated with the moman/finenight FSA package. +Here is the copyright for those sources: + +# Copyright (c) 2010, Jean-Philippe Barrette-LaPierre, +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation +# files (the "Software"), to deal in the Software without +# restriction, including without limitation the rights to use, +# copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the +# Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +Some code in core/src/java/org/apache/lucene/util/UnicodeUtil.java was +derived from ICU (http://www.icu-project.org) +The full license is available here: + http://source.icu-project.org/repos/icu/icu/trunk/license.html + +/* + * Copyright (C) 1999-2010, International Business Machines + * Corporation and others. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * provided that the above copyright notice(s) and this permission notice appear + * in all copies of the Software and that both the above copyright notice(s) and + * this permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS NOTICE BE + * LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR + * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall not + * be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +The following license applies to the Snowball stemmers: + +Copyright (c) 2001, Dr Martin Porter +Copyright (c) 2002, Richard Boulton +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the copyright holders nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The following license applies to the KStemmer: + +Copyright © 2003, +Center for Intelligent Information Retrieval, +University of Massachusetts, Amherst. +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +3. The names "Center for Intelligent Information Retrieval" and +"University of Massachusetts" must not be used to endorse or promote products +derived from this software without prior written permission. To obtain +permission, contact info@ciir.cs.umass.edu. + +THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF MASSACHUSETTS AND OTHER CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The following license applies to the Morfologik project: + +Copyright (c) 2006 Dawid Weiss +Copyright (c) 2007-2011 Dawid Weiss, Marcin Miłkowski +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name of Morfologik nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- + +The dictionary comes from Morfologik project. Morfologik uses data from +Polish ispell/myspell dictionary hosted at http://www.sjp.pl/slownik/en/ and +is licenced on the terms of (inter alia) LGPL and Creative Commons +ShareAlike. The part-of-speech tags were added in Morfologik project and +are not found in the data from sjp.pl. The tagset is similar to IPI PAN +tagset. + +--- + +The following license applies to the Morfeusz project, +used by org.apache.lucene.analysis.morfologik. + +BSD-licensed dictionary of Polish (SGJP) +http://sgjp.pl/morfeusz/ + +Copyright © 2011 Zygmunt Saloni, Włodzimierz Gruszczyński, + Marcin Woliński, Robert Wołosz + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS “AS IS” AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDERS OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverter.java b/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverter.java similarity index 65% rename from modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverter.java rename to modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverter.java index 43ae8b2d6d97e..8e1d95d216ce9 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverter.java +++ b/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverter.java @@ -5,7 +5,7 @@ * this file be licensed under the Apache-2.0 license or a * compatible open source license. */ -package org.opensearch.transport.grpc.proto.request.search.query; +package org.opensearch.transport.grpc.spi; import org.opensearch.index.query.QueryBuilder; import org.opensearch.protobufs.QueryContainer; @@ -31,4 +31,16 @@ public interface QueryBuilderProtoConverter { * @throws IllegalArgumentException if the query cannot be converted */ QueryBuilder fromProto(QueryContainer queryContainer); + + /** + * Sets the registry for converting nested queries. + * This method is called by the gRPC plugin to inject the populated registry + * into converters that need to handle nested query types. + * + * @param registry The registry containing all available converters + */ + default void setRegistry(QueryBuilderProtoConverterRegistry registry) { + // By default, converters don't need a registry + // Converters that handle nested queries should override this method + } } diff --git a/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverterRegistry.java b/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverterRegistry.java new file mode 100644 index 0000000000000..60dd167ca488d --- /dev/null +++ b/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverterRegistry.java @@ -0,0 +1,29 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.transport.grpc.spi; + +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.protobufs.QueryContainer; + +/** + * Interface for a registry that can convert protobuf queries to OpenSearch QueryBuilder objects. + * This interface provides a clean abstraction for plugins that need to convert nested queries + * without exposing the internal implementation details of the registry. + */ +public interface QueryBuilderProtoConverterRegistry { + + /** + * Converts a protobuf query container to an OpenSearch QueryBuilder. + * This method handles the lookup and delegation to the appropriate converter. + * + * @param queryContainer The protobuf query container to convert + * @return The corresponding OpenSearch QueryBuilder + * @throws IllegalArgumentException if no converter can handle the query type + */ + QueryBuilder fromProto(QueryContainer queryContainer); +} diff --git a/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/package-info.java b/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/package-info.java new file mode 100644 index 0000000000000..79adf99760520 --- /dev/null +++ b/modules/transport-grpc/spi/src/main/java/org/opensearch/transport/grpc/spi/package-info.java @@ -0,0 +1,42 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +/** + * Service Provider Interface (SPI) for extending gRPC transport query conversion capabilities. + *

+ * This package provides a minimal, stable API for implementing custom query converters + * that can transform protobuf query messages into OpenSearch QueryBuilder objects. External plugins + * can implement the {@link org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter} interface + * to add support for custom query types in gRPC requests. + *

+ *

+ * The SPI contains only two interfaces: + *

+ *
    + *
  • {@link org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter} - + * Interface for implementing custom query converters
  • + *
  • {@link org.opensearch.transport.grpc.spi.QueryBuilderProtoConverterRegistry} - + * Interface for accessing the registry to convert nested queries
  • + *
+ *

+ * The SPI mechanism leverages OpenSearch's {@code ExtensiblePlugin} framework. Plugins must: + *

+ *
    + *
  • Implement {@link org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter}
  • + *
  • Return the converter instance from their plugin's {@code createComponents()} method
  • + *
  • Create a {@code META-INF/services} file listing their converter implementation
  • + *
  • Declare {@code transport-grpc} in their plugin descriptor's {@code extended.plugins} list
  • + *
+ *

+ * For converters that need to handle nested queries (e.g., filter clauses), the registry injection + * pattern allows access to built-in converters for standard query types like MatchAll, Term, and Terms. + *

+ * + * @since 3.2.0 + */ +package org.opensearch.transport.grpc.spi; diff --git a/modules/transport-grpc/spi/src/test/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverterTests.java b/modules/transport-grpc/spi/src/test/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverterTests.java new file mode 100644 index 0000000000000..8a46054f3aa2d --- /dev/null +++ b/modules/transport-grpc/spi/src/test/java/org/opensearch/transport/grpc/spi/QueryBuilderProtoConverterTests.java @@ -0,0 +1,171 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.transport.grpc.spi; + +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.index.query.TermQueryBuilder; +import org.opensearch.protobufs.QueryContainer; +import org.opensearch.test.OpenSearchTestCase; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + +public class QueryBuilderProtoConverterTests extends OpenSearchTestCase { + + public void testConverterInterface() { + TestQueryConverter converter = new TestQueryConverter(); + + // Test getHandledQueryCase + assertEquals(QueryContainer.QueryContainerCase.TERM, converter.getHandledQueryCase()); + + // Test fromProto + QueryContainer queryContainer = createMockTermQueryContainer(); + + QueryBuilder result = converter.fromProto(queryContainer); + assertThat(result, instanceOf(TermQueryBuilder.class)); + + TermQueryBuilder termQuery = (TermQueryBuilder) result; + assertThat(termQuery.fieldName(), equalTo("test_field")); + assertThat(termQuery.value(), equalTo("test_value")); + } + + public void testConverterWithInvalidQueryContainer() { + TestQueryConverter converter = new TestQueryConverter(); + + // Create a query container without a term query + QueryContainer queryContainer = createMockRangeQueryContainer(); + + IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> converter.fromProto(queryContainer)); + assertTrue(exception.getMessage().contains("QueryContainer does not contain a Term query")); + } + + public void testConverterWithComplexFieldValue() { + TestQueryConverter converter = new TestQueryConverter(); + + // Test with different field value types + QueryContainer queryContainer = createMockNumericTermQueryContainer(); + + QueryBuilder result = converter.fromProto(queryContainer); + assertThat(result, instanceOf(TermQueryBuilder.class)); + + TermQueryBuilder termQuery = (TermQueryBuilder) result; + assertThat(termQuery.fieldName(), equalTo("numeric_field")); + assertThat(termQuery.value(), equalTo(42.5f)); + } + + public void testMultipleConverterInstances() { + TestQueryConverter converter1 = new TestQueryConverter(); + TestQueryConverter converter2 = new TestQueryConverter(); + + // Both should handle the same query case + assertEquals(converter1.getHandledQueryCase(), converter2.getHandledQueryCase()); + + // Both should produce equivalent results + QueryContainer queryContainer = createMockTermQueryContainer(); + + QueryBuilder result1 = converter1.fromProto(queryContainer); + QueryBuilder result2 = converter2.fromProto(queryContainer); + + assertThat(result1, instanceOf(TermQueryBuilder.class)); + assertThat(result2, instanceOf(TermQueryBuilder.class)); + + TermQueryBuilder termQuery1 = (TermQueryBuilder) result1; + TermQueryBuilder termQuery2 = (TermQueryBuilder) result2; + + assertEquals(termQuery1.fieldName(), termQuery2.fieldName()); + assertEquals(termQuery1.value(), termQuery2.value()); + } + + /** + * Helper method to create a mock term query container + */ + private QueryContainer createMockTermQueryContainer() { + return QueryContainer.newBuilder() + .setTerm( + org.opensearch.protobufs.TermQuery.newBuilder() + .setField("test_field") + .setValue(org.opensearch.protobufs.FieldValue.newBuilder().setStringValue("test_value").build()) + .build() + ) + .build(); + } + + /** + * Helper method to create a mock range query container + */ + private QueryContainer createMockRangeQueryContainer() { + return QueryContainer.newBuilder() + .setRange(org.opensearch.protobufs.RangeQuery.newBuilder().setField("range_field").build()) + .build(); + } + + /** + * Helper method to create a mock numeric term query container + */ + private QueryContainer createMockNumericTermQueryContainer() { + return QueryContainer.newBuilder() + .setTerm( + org.opensearch.protobufs.TermQuery.newBuilder() + .setField("numeric_field") + .setValue( + org.opensearch.protobufs.FieldValue.newBuilder() + .setGeneralNumber(org.opensearch.protobufs.GeneralNumber.newBuilder().setFloatValue(42.5f).build()) + .build() + ) + .build() + ) + .build(); + } + + /** + * Test implementation of QueryBuilderProtoConverter for TERM queries + */ + private static class TestQueryConverter implements QueryBuilderProtoConverter { + @Override + public QueryContainer.QueryContainerCase getHandledQueryCase() { + return QueryContainer.QueryContainerCase.TERM; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + if (!queryContainer.hasTerm()) { + throw new IllegalArgumentException("QueryContainer does not contain a Term query"); + } + + org.opensearch.protobufs.TermQuery termQuery = queryContainer.getTerm(); + String field = termQuery.getField(); + + // Handle different field value types + org.opensearch.protobufs.FieldValue fieldValue = termQuery.getValue(); + Object value; + + if (fieldValue.hasStringValue()) { + value = fieldValue.getStringValue(); + } else if (fieldValue.hasGeneralNumber()) { + org.opensearch.protobufs.GeneralNumber number = fieldValue.getGeneralNumber(); + if (number.hasFloatValue()) { + value = number.getFloatValue(); + } else if (number.hasDoubleValue()) { + value = number.getDoubleValue(); + } else if (number.hasInt32Value()) { + value = number.getInt32Value(); + } else if (number.hasInt64Value()) { + value = number.getInt64Value(); + } else { + throw new IllegalArgumentException("Unsupported number type in TermQuery"); + } + } else if (fieldValue.hasBoolValue()) { + value = fieldValue.getBoolValue(); + } else { + throw new IllegalArgumentException("Unsupported field value type in TermQuery"); + } + + return new TermQueryBuilder(field, value); + } + } +} diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/GrpcPlugin.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/GrpcPlugin.java index aef65ee3e958a..eb1432715fef2 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/GrpcPlugin.java +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/GrpcPlugin.java @@ -7,6 +7,8 @@ */ package org.opensearch.transport.grpc; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.network.NetworkService; @@ -29,10 +31,10 @@ import org.opensearch.transport.AuxTransport; import org.opensearch.transport.client.Client; import org.opensearch.transport.grpc.proto.request.search.query.AbstractQueryBuilderProtoUtils; -import org.opensearch.transport.grpc.proto.request.search.query.QueryBuilderProtoConverter; -import org.opensearch.transport.grpc.proto.request.search.query.QueryBuilderProtoConverterRegistry; +import org.opensearch.transport.grpc.proto.request.search.query.QueryBuilderProtoConverterRegistryImpl; import org.opensearch.transport.grpc.services.DocumentServiceImpl; import org.opensearch.transport.grpc.services.SearchServiceImpl; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; import org.opensearch.transport.grpc.ssl.SecureNetty4GrpcServerTransport; import org.opensearch.watcher.ResourceWatcherService; @@ -65,9 +67,11 @@ */ public final class GrpcPlugin extends Plugin implements NetworkPlugin, ExtensiblePlugin { + private static final Logger logger = LogManager.getLogger(GrpcPlugin.class); + private Client client; private final List queryConverters = new ArrayList<>(); - private QueryBuilderProtoConverterRegistry queryRegistry; + private QueryBuilderProtoConverterRegistryImpl queryRegistry; private AbstractQueryBuilderProtoUtils queryUtils; /** @@ -85,8 +89,19 @@ public GrpcPlugin() {} public void loadExtensions(ExtensiblePlugin.ExtensionLoader loader) { // Load query converters from other plugins List extensions = loader.loadExtensions(QueryBuilderProtoConverter.class); - if (extensions != null) { - queryConverters.addAll(extensions); + if (extensions != null && !extensions.isEmpty()) { + logger.info("Loading {} QueryBuilderProtoConverter extensions from other plugins", extensions.size()); + for (QueryBuilderProtoConverter converter : extensions) { + logger.info( + "Discovered QueryBuilderProtoConverter extension: {} (handles: {})", + converter.getClass().getName(), + converter.getHandledQueryCase() + ); + queryConverters.add(converter); + } + logger.info("Successfully loaded {} QueryBuilderProtoConverter extensions", extensions.size()); + } else { + logger.info("No QueryBuilderProtoConverter extensions found from other plugins"); } } @@ -262,14 +277,31 @@ public Collection createComponents( this.client = client; // Create the registry - this.queryRegistry = new QueryBuilderProtoConverterRegistry(); + this.queryRegistry = new QueryBuilderProtoConverterRegistryImpl(); // Create the query utils instance this.queryUtils = new AbstractQueryBuilderProtoUtils(queryRegistry); - // Register external converters - for (QueryBuilderProtoConverter converter : queryConverters) { - queryRegistry.registerConverter(converter); + // Inject registry into external converters and register them + if (!queryConverters.isEmpty()) { + logger.info("Injecting registry and registering {} external QueryBuilderProtoConverter(s)", queryConverters.size()); + for (QueryBuilderProtoConverter converter : queryConverters) { + logger.info( + "Processing external converter: {} (handles: {})", + converter.getClass().getName(), + converter.getHandledQueryCase() + ); + + // Inject the populated registry into the converter + converter.setRegistry(queryRegistry); + logger.info("Injected registry into converter: {}", converter.getClass().getName()); + + // Register the converter + queryRegistry.registerConverter(converter); + } + logger.info("Successfully injected registry and registered all {} external converters", queryConverters.size()); + } else { + logger.info("No external QueryBuilderProtoConverter(s) to register"); } return super.createComponents( diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/AbstractQueryBuilderProtoUtils.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/AbstractQueryBuilderProtoUtils.java index e67cc8d86bc5a..64ebf7a38efd1 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/AbstractQueryBuilderProtoUtils.java +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/AbstractQueryBuilderProtoUtils.java @@ -19,7 +19,7 @@ */ public class AbstractQueryBuilderProtoUtils { - private final QueryBuilderProtoConverterRegistry registry; + private final QueryBuilderProtoConverterRegistryImpl registry; /** * Creates a new instance with the specified registry. @@ -27,7 +27,7 @@ public class AbstractQueryBuilderProtoUtils { * @param registry The registry to use for query conversion * @throws IllegalArgumentException if registry is null */ - public AbstractQueryBuilderProtoUtils(QueryBuilderProtoConverterRegistry registry) { + public AbstractQueryBuilderProtoUtils(QueryBuilderProtoConverterRegistryImpl registry) { if (registry == null) { throw new IllegalArgumentException("Registry cannot be null"); } diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchAllQueryBuilderProtoConverter.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchAllQueryBuilderProtoConverter.java index b00dc1385ad20..89aba4fe68ef3 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchAllQueryBuilderProtoConverter.java +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchAllQueryBuilderProtoConverter.java @@ -9,6 +9,7 @@ import org.opensearch.index.query.QueryBuilder; import org.opensearch.protobufs.QueryContainer; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; /** * Converter for MatchAll queries. diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchNoneQueryBuilderProtoConverter.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchNoneQueryBuilderProtoConverter.java index 58ddb123d4dd2..1259ad35b7e9f 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchNoneQueryBuilderProtoConverter.java +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/MatchNoneQueryBuilderProtoConverter.java @@ -9,6 +9,7 @@ import org.opensearch.index.query.QueryBuilder; import org.opensearch.protobufs.QueryContainer; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; /** * Converter for MatchNone queries. diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistryImpl.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistryImpl.java new file mode 100644 index 0000000000000..428594629596b --- /dev/null +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistryImpl.java @@ -0,0 +1,74 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.transport.grpc.proto.request.search.query; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.opensearch.common.inject.Inject; +import org.opensearch.common.inject.Singleton; +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.protobufs.QueryContainer; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverterRegistry; + +/** + * Registry for QueryBuilderProtoConverter implementations. + * This class wraps the SPI registry and adds built-in converters for the transport-grpc module. + */ +@Singleton +public class QueryBuilderProtoConverterRegistryImpl implements QueryBuilderProtoConverterRegistry { + + private static final Logger logger = LogManager.getLogger(QueryBuilderProtoConverterRegistryImpl.class); + private final QueryBuilderProtoConverterSpiRegistry delegate; + + /** + * Creates a new registry and loads all available converters. + */ + @Inject + public QueryBuilderProtoConverterRegistryImpl() { + // Create the SPI registry which loads external converters + this.delegate = new QueryBuilderProtoConverterSpiRegistry(); + + // Register built-in converters for this module + registerBuiltInConverters(); + } + + /** + * Registers the built-in converters. + * Protected for testing purposes. + */ + protected void registerBuiltInConverters() { + // Add built-in converters + delegate.registerConverter(new MatchAllQueryBuilderProtoConverter()); + delegate.registerConverter(new MatchNoneQueryBuilderProtoConverter()); + delegate.registerConverter(new TermQueryBuilderProtoConverter()); + delegate.registerConverter(new TermsQueryBuilderProtoConverter()); + + logger.info("Registered {} built-in query converters", delegate.size()); + } + + /** + * Converts a protobuf query container to an OpenSearch QueryBuilder. + * + * @param queryContainer The protobuf query container + * @return The corresponding OpenSearch QueryBuilder + * @throws IllegalArgumentException if the query cannot be converted + */ + public QueryBuilder fromProto(QueryContainer queryContainer) { + return delegate.fromProto(queryContainer); + } + + /** + * Registers a new converter. + * + * @param converter The converter to register + */ + public void registerConverter(QueryBuilderProtoConverter converter) { + delegate.registerConverter(converter); + } +} diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistry.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterSpiRegistry.java similarity index 61% rename from modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistry.java rename to modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterSpiRegistry.java index fa30794b6ba67..7b22c4bea04d6 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistry.java +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterSpiRegistry.java @@ -13,71 +13,29 @@ import org.opensearch.common.inject.Singleton; import org.opensearch.index.query.QueryBuilder; import org.opensearch.protobufs.QueryContainer; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; -import java.util.ServiceLoader; /** * Registry for QueryBuilderProtoConverter implementations. * This class discovers and manages all available converters. */ @Singleton -public class QueryBuilderProtoConverterRegistry { +public class QueryBuilderProtoConverterSpiRegistry { - private static final Logger logger = LogManager.getLogger(QueryBuilderProtoConverterRegistry.class); + private static final Logger logger = LogManager.getLogger(QueryBuilderProtoConverterSpiRegistry.class); private final Map converterMap = new HashMap<>(); /** - * Creates a new registry and loads all available converters. - */ + * Creates a new registry. External converters will be registered + * via the OpenSearch ExtensiblePlugin mechanism. + */ @Inject - public QueryBuilderProtoConverterRegistry() { - // Load built-in converters - registerBuiltInConverters(); - - // Discover converters from other plugins using Java's ServiceLoader - loadExternalConverters(); - } - - /** - * Registers the built-in converters. - * Protected for testing purposes. - */ - protected void registerBuiltInConverters() { - // Add built-in converters - registerConverter(new MatchAllQueryBuilderProtoConverter()); - registerConverter(new MatchNoneQueryBuilderProtoConverter()); - registerConverter(new TermQueryBuilderProtoConverter()); - registerConverter(new TermsQueryBuilderProtoConverter()); - - logger.info("Registered {} built-in query converters", converterMap.size()); - } - - /** - * Loads external converters using Java's ServiceLoader mechanism. - * Protected for testing purposes. - */ - protected void loadExternalConverters() { - ServiceLoader serviceLoader = ServiceLoader.load(QueryBuilderProtoConverter.class); - Iterator iterator = serviceLoader.iterator(); - - int count = 0; - int failedCount = 0; - while (iterator.hasNext()) { - try { - QueryBuilderProtoConverter converter = iterator.next(); - registerConverter(converter); - count++; - logger.info("Loaded external query converter for {}: {}", converter.getHandledQueryCase(), converter.getClass().getName()); - } catch (Exception e) { - failedCount++; - logger.error("Failed to load external query converter", e); - } - } - - logger.info("Loaded {} external query converters ({} failed)", count, failedCount); + public QueryBuilderProtoConverterSpiRegistry() { + // External converters are loaded via OpenSearch's ExtensiblePlugin mechanism + // and registered manually via registerConverter() calls } /** @@ -104,6 +62,15 @@ public QueryBuilder fromProto(QueryContainer queryContainer) { throw new IllegalArgumentException("Unsupported query type in container: " + queryContainer + " (case: " + queryCase + ")"); } + /** + * Gets the number of registered converters. + * + * @return The number of registered converters + */ + public int size() { + return converterMap.size(); + } + /** * Registers a new converter. * @@ -137,6 +104,6 @@ public void registerConverter(QueryBuilderProtoConverter converter) { ); } - logger.debug("Registered query converter for {}: {}", queryCase, converter.getClass().getName()); + logger.info("Registered query converter for {}: {}", queryCase, converter.getClass().getName()); } } diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermQueryBuilderProtoConverter.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermQueryBuilderProtoConverter.java index 19c70df31d80c..c57bbfe69736f 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermQueryBuilderProtoConverter.java +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermQueryBuilderProtoConverter.java @@ -9,6 +9,7 @@ import org.opensearch.index.query.QueryBuilder; import org.opensearch.protobufs.QueryContainer; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; /** * Converter for Term queries. diff --git a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermsQueryBuilderProtoConverter.java b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermsQueryBuilderProtoConverter.java index 36b135ffcec63..612970181faf4 100644 --- a/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermsQueryBuilderProtoConverter.java +++ b/modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/query/TermsQueryBuilderProtoConverter.java @@ -9,6 +9,7 @@ import org.opensearch.index.query.QueryBuilder; import org.opensearch.protobufs.QueryContainer; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; /** * Converter for Terms queries. diff --git a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/GrpcPluginTests.java b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/GrpcPluginTests.java index 7ce74470e0395..7f912935f24fc 100644 --- a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/GrpcPluginTests.java +++ b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/GrpcPluginTests.java @@ -20,7 +20,7 @@ import org.opensearch.threadpool.ThreadPool; import org.opensearch.transport.AuxTransport; import org.opensearch.transport.client.Client; -import org.opensearch.transport.grpc.proto.request.search.query.QueryBuilderProtoConverter; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; import org.opensearch.transport.grpc.ssl.SecureNetty4GrpcServerTransport; import org.junit.Before; @@ -325,6 +325,13 @@ public void testCreateComponentsWithExternalConverters() { assertNotNull("QueryUtils should be initialized after createComponents", newPlugin.getQueryUtils()); // Verify that the external converter was registered by checking it was called - Mockito.verify(mockConverter).getHandledQueryCase(); + // Note: getHandledQueryCase() is called 3 times: + // 1. In loadExtensions() for logging + // 2. In createComponents() for logging + // 3. In QueryBuilderProtoConverterSpiRegistry.registerConverter() for registration + Mockito.verify(mockConverter, Mockito.times(3)).getHandledQueryCase(); + + // Verify that setRegistry was called to inject the registry + Mockito.verify(mockConverter, Mockito.times(1)).setRegistry(Mockito.any()); } } diff --git a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/EmptyQueryBuilderProtoConverterRegistry.java b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/EmptyQueryBuilderProtoConverterRegistry.java deleted file mode 100644 index 1b5c20df855f3..0000000000000 --- a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/EmptyQueryBuilderProtoConverterRegistry.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - */ -package org.opensearch.transport.grpc.proto.request.search.query; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -/** - * A test-specific implementation of QueryBuilderProtoConverterRegistry that starts with no converters. - * This class is used for testing scenarios where we need a clean registry without any built-in converters. - */ -public class EmptyQueryBuilderProtoConverterRegistry extends QueryBuilderProtoConverterRegistry { - - private static final Logger logger = LogManager.getLogger(EmptyQueryBuilderProtoConverterRegistry.class); - - /** - * Creates a new empty registry with no converters. - * This constructor calls the parent constructor but doesn't register any converters. - */ - public EmptyQueryBuilderProtoConverterRegistry() { - // The parent constructor will call registerBuiltInConverters() and loadExternalConverters(), - // but we'll override those methods to do nothing - } - - /** - * Override the parent's registerBuiltInConverters method to do nothing. - * This ensures no built-in converters are registered. - */ - @Override - protected void registerBuiltInConverters() { - // Do nothing - we want an empty registry for testing - logger.debug("Skipping registration of built-in converters for testing"); - } - - /** - * Override the parent's loadExternalConverters method to do nothing. - * This ensures no external converters are loaded. - */ - @Override - protected void loadExternalConverters() { - // Do nothing - we want an empty registry for testing - logger.debug("Skipping loading of external converters for testing"); - } -} diff --git a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistryTests.java b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistryTests.java index e4f969b973b6d..7b7e6dd9381ed 100644 --- a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistryTests.java +++ b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterRegistryTests.java @@ -13,18 +13,19 @@ import org.opensearch.protobufs.QueryContainer; import org.opensearch.protobufs.TermQuery; import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; /** * Test class for QueryBuilderProtoConverterRegistry to verify the map-based optimization. */ public class QueryBuilderProtoConverterRegistryTests extends OpenSearchTestCase { - private QueryBuilderProtoConverterRegistry registry; + private QueryBuilderProtoConverterRegistryImpl registry; @Override public void setUp() throws Exception { super.setUp(); - registry = new QueryBuilderProtoConverterRegistry(); + registry = new QueryBuilderProtoConverterRegistryImpl(); } public void testMatchAllQueryConversion() { diff --git a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterSpiRegistryTests.java b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterSpiRegistryTests.java new file mode 100644 index 0000000000000..89277787744e4 --- /dev/null +++ b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoConverterSpiRegistryTests.java @@ -0,0 +1,211 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ +package org.opensearch.transport.grpc.proto.request.search.query; + +import org.opensearch.index.query.QueryBuilder; +import org.opensearch.index.query.TermQueryBuilder; +import org.opensearch.protobufs.QueryContainer; +import org.opensearch.test.OpenSearchTestCase; +import org.opensearch.transport.grpc.spi.QueryBuilderProtoConverter; + +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + +public class QueryBuilderProtoConverterSpiRegistryTests extends OpenSearchTestCase { + + private QueryBuilderProtoConverterSpiRegistry registry; + + @Override + public void setUp() throws Exception { + super.setUp(); + registry = new QueryBuilderProtoConverterSpiRegistry(); + } + + public void testEmptyRegistry() { + assertEquals(0, registry.size()); + } + + public void testRegisterConverter() { + TestQueryConverter converter = new TestQueryConverter(); + registry.registerConverter(converter); + + assertEquals(1, registry.size()); + } + + public void testRegisterNullConverter() { + IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> registry.registerConverter(null)); + assertThat(exception.getMessage(), containsString("Converter cannot be null")); + } + + public void testRegisterConverterWithNullQueryCase() { + QueryBuilderProtoConverter converter = new QueryBuilderProtoConverter() { + @Override + public QueryContainer.QueryContainerCase getHandledQueryCase() { + return null; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + return null; + } + }; + + IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> registry.registerConverter(converter)); + assertThat(exception.getMessage(), containsString("Handled query case cannot be null")); + } + + public void testRegisterConverterWithNotSetQueryCase() { + QueryBuilderProtoConverter converter = new QueryBuilderProtoConverter() { + @Override + public QueryContainer.QueryContainerCase getHandledQueryCase() { + return QueryContainer.QueryContainerCase.QUERYCONTAINER_NOT_SET; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + return null; + } + }; + + IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> registry.registerConverter(converter)); + assertThat(exception.getMessage(), containsString("Cannot register converter for QUERYCONTAINER_NOT_SET case")); + } + + public void testFromProtoWithRegisteredConverter() { + TestQueryConverter converter = new TestQueryConverter(); + registry.registerConverter(converter); + + // Create a mock query container that the test converter can handle + QueryContainer queryContainer = createMockTermQueryContainer(); + + QueryBuilder result = registry.fromProto(queryContainer); + assertThat(result, instanceOf(TermQueryBuilder.class)); + + TermQueryBuilder termQuery = (TermQueryBuilder) result; + assertThat(termQuery.fieldName(), equalTo("test_field")); + assertThat(termQuery.value(), equalTo("test_value")); + } + + public void testFromProtoWithNullQueryContainer() { + IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> registry.fromProto(null)); + assertThat(exception.getMessage(), containsString("Query container cannot be null")); + } + + public void testFromProtoWithUnregisteredQueryType() { + QueryContainer queryContainer = createMockTermQueryContainer(); + + IllegalArgumentException exception = expectThrows(IllegalArgumentException.class, () -> registry.fromProto(queryContainer)); + assertThat(exception.getMessage(), containsString("Unsupported query type in container")); + assertThat(exception.getMessage(), containsString("TERM")); + } + + public void testReplaceExistingConverter() { + TestQueryConverter converter1 = new TestQueryConverter(); + TestQueryConverter converter2 = new TestQueryConverter(); + + registry.registerConverter(converter1); + assertEquals(1, registry.size()); + + // Register second converter for same query case - should replace + registry.registerConverter(converter2); + assertEquals(1, registry.size()); + + // Verify the second converter is used + QueryContainer queryContainer = createMockTermQueryContainer(); + + QueryBuilder result = registry.fromProto(queryContainer); + assertThat(result, instanceOf(TermQueryBuilder.class)); + } + + public void testMultipleConvertersForDifferentQueryTypes() { + TestQueryConverter termConverter = new TestQueryConverter(); + TestRangeQueryConverter rangeConverter = new TestRangeQueryConverter(); + + registry.registerConverter(termConverter); + registry.registerConverter(rangeConverter); + + assertEquals(2, registry.size()); + + // Test term query + QueryContainer termContainer = createMockTermQueryContainer(); + QueryBuilder termResult = registry.fromProto(termContainer); + assertThat(termResult, instanceOf(TermQueryBuilder.class)); + + // Test range query + QueryContainer rangeContainer = createMockRangeQueryContainer(); + QueryBuilder rangeResult = registry.fromProto(rangeContainer); + assertNotNull(rangeResult); // TestRangeQueryConverter returns a mock QueryBuilder + } + + /** + * Helper method to create a mock term query container + */ + private QueryContainer createMockTermQueryContainer() { + return QueryContainer.newBuilder() + .setTerm( + org.opensearch.protobufs.TermQuery.newBuilder() + .setField("test_field") + .setValue(org.opensearch.protobufs.FieldValue.newBuilder().setStringValue("test_value").build()) + .build() + ) + .build(); + } + + /** + * Helper method to create a mock range query container + */ + private QueryContainer createMockRangeQueryContainer() { + return QueryContainer.newBuilder() + .setRange(org.opensearch.protobufs.RangeQuery.newBuilder().setField("range_field").build()) + .build(); + } + + /** + * Test converter implementation for TERM queries + */ + private static class TestQueryConverter implements QueryBuilderProtoConverter { + @Override + public QueryContainer.QueryContainerCase getHandledQueryCase() { + return QueryContainer.QueryContainerCase.TERM; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + if (!queryContainer.hasTerm()) { + throw new IllegalArgumentException("QueryContainer does not contain a Term query"); + } + + org.opensearch.protobufs.TermQuery termQuery = queryContainer.getTerm(); + String field = termQuery.getField(); + String value = termQuery.getValue().getStringValue(); + + return new TermQueryBuilder(field, value); + } + } + + /** + * Test converter implementation for RANGE queries + */ + private static class TestRangeQueryConverter implements QueryBuilderProtoConverter { + @Override + public QueryContainer.QueryContainerCase getHandledQueryCase() { + return QueryContainer.QueryContainerCase.RANGE; + } + + @Override + public QueryBuilder fromProto(QueryContainer queryContainer) { + if (!queryContainer.hasRange()) { + throw new IllegalArgumentException("QueryContainer does not contain a Range query"); + } + + // Return a simple mock QueryBuilder for testing + return new TermQueryBuilder("mock_field", "mock_value"); + } + } +} diff --git a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoTestUtils.java b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoTestUtils.java index 71a9e24b1d5f6..0d38566db0b97 100644 --- a/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoTestUtils.java +++ b/modules/transport-grpc/src/test/java/org/opensearch/transport/grpc/proto/request/search/query/QueryBuilderProtoTestUtils.java @@ -24,7 +24,7 @@ private QueryBuilderProtoTestUtils() { */ public static AbstractQueryBuilderProtoUtils createQueryUtils() { // Create a new registry - QueryBuilderProtoConverterRegistry registry = new QueryBuilderProtoConverterRegistry(); + QueryBuilderProtoConverterRegistryImpl registry = new QueryBuilderProtoConverterRegistryImpl(); // Register all built-in converters registry.registerConverter(new MatchAllQueryBuilderProtoConverter());