0
# Plugin Framework
1
2
Extensible plugin system for adding custom functionality including actions, search components, analysis, and storage engines. OpenSearch provides comprehensive extension points throughout the system for building custom plugins and integrations.
3
4
## Capabilities
5
6
### Core Plugin System
7
8
Base plugin architecture and lifecycle management for extending OpenSearch functionality.
9
10
```java { .api }
11
/**
12
* Base plugin class providing extension points for all OpenSearch functionality
13
*/
14
abstract class Plugin implements Closeable {
15
/**
16
* Get plugin description information
17
*/
18
String getDescription();
19
20
/**
21
* Create Guice modules for dependency injection
22
* @return Collection of Guice modules to register
23
*/
24
Collection<Module> createGuiceModules();
25
26
/**
27
* Provide additional settings that should be added to the cluster
28
* @return Settings to add to cluster configuration
29
*/
30
Settings additionalSettings();
31
32
/**
33
* Get list of setting definitions for this plugin
34
* @return List of setting definitions
35
*/
36
List<Setting<?>> getSettings();
37
38
/**
39
* Get list of settings that require keystore access
40
* @return List of secure setting definitions
41
*/
42
List<Setting<?>> getSecureSettings();
43
44
/**
45
* Called when plugin is closed during shutdown
46
*/
47
void close() throws IOException;
48
49
/**
50
* Get plugin classloader for loading plugin resources
51
*/
52
ClassLoader getPluginClassLoader();
53
}
54
55
/**
56
* Plugin information container with metadata and dependencies
57
*/
58
class PluginInfo implements Streamable {
59
/**
60
* Create plugin info
61
* @param name Plugin name
62
* @param description Plugin description
63
* @param version Plugin version
64
* @param classname Main plugin class name
65
* @param hasNativeController Whether plugin has native controller
66
*/
67
PluginInfo(String name, String description, String version, String classname,
68
boolean hasNativeController);
69
70
/**
71
* Get plugin name
72
*/
73
String getName();
74
75
/**
76
* Get plugin description
77
*/
78
String getDescription();
79
80
/**
81
* Get plugin version
82
*/
83
String getVersion();
84
85
/**
86
* Get main plugin class name
87
*/
88
String getClassname();
89
90
/**
91
* Check if plugin has native controller
92
*/
93
boolean hasNativeController();
94
95
/**
96
* Get OpenSearch version compatibility
97
*/
98
String getOpenSearchVersion();
99
100
/**
101
* Get Java version requirement
102
*/
103
String getJavaVersion();
104
}
105
106
/**
107
* Service for managing plugin lifecycle and registration
108
*/
109
class PluginsService {
110
/**
111
* Create plugins service with configuration
112
* @param settings OpenSearch settings
113
* @param modulesDirectory Modules directory path
114
* @param pluginsDirectory Plugins directory path
115
* @param classpathPlugins Classpath plugin classes
116
*/
117
PluginsService(Settings settings, Path modulesDirectory, Path pluginsDirectory,
118
Collection<Class<? extends Plugin>> classpathPlugins);
119
120
/**
121
* Get all loaded plugins
122
*/
123
List<Tuple<PluginInfo, Plugin>> plugins();
124
125
/**
126
* Get plugins filtered by type
127
* @param type Plugin interface class
128
*/
129
<T> List<T> filterPlugins(Class<T> type);
130
131
/**
132
* Get plugin info by name
133
* @param name Plugin name
134
*/
135
PluginInfo getPluginInfo(String name);
136
137
/**
138
* Check if plugin is loaded
139
* @param name Plugin name
140
*/
141
boolean isPluginLoaded(String name);
142
}
143
```
144
145
### Action Plugin Interface
146
147
Plugin interface for adding custom actions and REST endpoints to OpenSearch.
148
149
```java { .api }
150
/**
151
* Plugin interface for providing custom actions and REST handlers
152
*/
153
interface ActionPlugin {
154
/**
155
* Get list of action handlers provided by this plugin
156
* @return List of action handler registrations
157
*/
158
List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions();
159
160
/**
161
* Get list of REST handlers provided by this plugin
162
* @param settings Cluster settings
163
* @param restController REST controller for registration
164
* @param clusterSettings Cluster settings manager
165
* @param indexScopedSettings Index-scoped settings manager
166
* @param settingsFilter Settings filter for security
167
* @param indexNameExpressionResolver Index name pattern resolver
168
* @param nodesInCluster Supplier for cluster nodes information
169
* @return List of REST handler implementations
170
*/
171
List<RestHandler> getRestHandlers(Settings settings, RestController restController,
172
ClusterSettings clusterSettings, IndexScopedSettings indexScopedSettings,
173
SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver,
174
Supplier<DiscoveryNodes> nodesInCluster);
175
176
/**
177
* Get list of action filters for intercepting requests
178
* @return List of action filter implementations
179
*/
180
default List<ActionFilter> getActionFilters() {
181
return Collections.emptyList();
182
}
183
184
/**
185
* Get list of task headers that should be preserved across requests
186
* @return Collection of header names to preserve
187
*/
188
default Collection<String> getTaskHeaders() {
189
return Collections.emptyList();
190
}
191
}
192
193
/**
194
* Registration container for action handlers
195
*/
196
class ActionHandler<Request extends ActionRequest, Response extends ActionResponse> {
197
/**
198
* Create action handler registration
199
* @param action Action type definition
200
* @param transportAction Transport action implementation class
201
*/
202
ActionHandler(ActionType<Response> action,
203
Class<? extends TransportAction<Request, Response>> transportAction);
204
205
/**
206
* Create action handler with support actions
207
* @param action Action type definition
208
* @param transportAction Transport action implementation class
209
* @param supportTransportActions Additional support action classes
210
*/
211
ActionHandler(ActionType<Response> action,
212
Class<? extends TransportAction<Request, Response>> transportAction,
213
Class<?>... supportTransportActions);
214
215
/**
216
* Get action type
217
*/
218
ActionType<Response> getAction();
219
220
/**
221
* Get transport action class
222
*/
223
Class<? extends TransportAction<Request, Response>> getTransportAction();
224
225
/**
226
* Get support transport action classes
227
*/
228
Class<?>[] getSupportTransportActions();
229
}
230
231
/**
232
* Base class for REST request handlers
233
*/
234
abstract class BaseRestHandler implements RestHandler {
235
/**
236
* Get routes supported by this handler
237
* @return List of route definitions
238
*/
239
abstract List<Route> routes();
240
241
/**
242
* Handle REST request
243
* @param request REST request
244
* @param channel Response channel
245
* @param client OpenSearch client
246
*/
247
abstract RestChannelConsumer prepareRequest(RestRequest request, NodeClient client)
248
throws IOException;
249
250
/**
251
* Check if request can be handled without content
252
*/
253
protected boolean canTripCircuitBreaker() {
254
return true;
255
}
256
257
/**
258
* Check if handler supports working with content streams
259
*/
260
protected boolean supportsContentStream() {
261
return false;
262
}
263
}
264
```
265
266
### Search Plugin Interface
267
268
Plugin interface for extending search functionality with custom queries, aggregations, and processors.
269
270
```java { .api }
271
/**
272
* Plugin interface for extending search functionality
273
*/
274
interface SearchPlugin {
275
/**
276
* Get custom query builders provided by this plugin
277
* @return List of query builder registrations
278
*/
279
default List<QuerySpec<?>> getQueries() {
280
return Collections.emptyList();
281
}
282
283
/**
284
* Get custom aggregation builders provided by this plugin
285
* @return List of aggregation builder registrations
286
*/
287
default List<AggregationSpec> getAggregations() {
288
return Collections.emptyList();
289
}
290
291
/**
292
* Get custom pipeline aggregations provided by this plugin
293
* @return List of pipeline aggregation registrations
294
*/
295
default List<PipelineAggregationSpec> getPipelineAggregations() {
296
return Collections.emptyList();
297
}
298
299
/**
300
* Get custom function score functions provided by this plugin
301
* @return List of score function registrations
302
*/
303
default List<ScoreFunctionSpec<?>> getScoreFunctions() {
304
return Collections.emptyList();
305
}
306
307
/**
308
* Get custom significance heuristics provided by this plugin
309
* @return List of significance heuristic registrations
310
*/
311
default List<SignificanceHeuristicSpec<?>> getSignificanceHeuristics() {
312
return Collections.emptyList();
313
}
314
315
/**
316
* Get custom MovAvg models provided by this plugin
317
* @return List of moving average model registrations
318
*/
319
default List<MovAvgModelSpec> getMovingAverageModels() {
320
return Collections.emptyList();
321
}
322
323
/**
324
* Get custom fetch sub-phases provided by this plugin
325
* @return List of fetch sub-phase implementations
326
*/
327
default List<FetchSubPhase> getFetchSubPhases(FetchPhaseConstructionContext context) {
328
return Collections.emptyList();
329
}
330
331
/**
332
* Get custom search extensions provided by this plugin
333
* @return List of search extension implementations
334
*/
335
default List<SearchExtension> getSearchExtensions() {
336
return Collections.emptyList();
337
}
338
}
339
340
/**
341
* Query builder specification for plugin registration
342
*/
343
class QuerySpec<T extends QueryBuilder> implements NamedWriteable.Entry<QueryBuilder> {
344
/**
345
* Create query spec
346
* @param name Query name for parsing
347
* @param reader Reader function for deserialization
348
* @param parser Parser function for XContent parsing
349
*/
350
QuerySpec(String name, Writeable.Reader<T> reader, CheckedFunction<XContentParser, T, IOException> parser);
351
352
/**
353
* Get query name
354
*/
355
String getName();
356
357
/**
358
* Get query class
359
*/
360
Class<T> getQueryClass();
361
}
362
363
/**
364
* Aggregation specification for plugin registration
365
*/
366
class AggregationSpec implements NamedWriteable.Entry<AggregationBuilder> {
367
/**
368
* Create aggregation spec
369
* @param name Aggregation name
370
* @param reader Reader function for deserialization
371
* @param parser Parser function for XContent parsing
372
*/
373
AggregationSpec(String name, Writeable.Reader<? extends AggregationBuilder> reader,
374
AggregationParser parser);
375
376
/**
377
* Get aggregation name
378
*/
379
String getName();
380
381
/**
382
* Get aggregation builder class
383
*/
384
Class<? extends AggregationBuilder> getBuilderClass();
385
}
386
```
387
388
### Analysis Plugin Interface
389
390
Plugin interface for providing custom text analysis components including analyzers, tokenizers, and filters.
391
392
```java { .api }
393
/**
394
* Plugin interface for providing text analysis components
395
*/
396
interface AnalysisPlugin {
397
/**
398
* Get custom analyzers provided by this plugin
399
* @return Map of analyzer name to analyzer provider
400
*/
401
default Map<String, AnalysisProvider<AnalyzerProvider<? extends Analyzer>>> getAnalyzers() {
402
return Collections.emptyMap();
403
}
404
405
/**
406
* Get custom character filters provided by this plugin
407
* @return Map of char filter name to char filter provider
408
*/
409
default Map<String, AnalysisProvider<CharFilterFactory>> getCharFilters() {
410
return Collections.emptyMap();
411
}
412
413
/**
414
* Get custom tokenizers provided by this plugin
415
* @return Map of tokenizer name to tokenizer provider
416
*/
417
default Map<String, AnalysisProvider<TokenizerFactory>> getTokenizers() {
418
return Collections.emptyMap();
419
}
420
421
/**
422
* Get custom token filters provided by this plugin
423
* @return Map of token filter name to token filter provider
424
*/
425
default Map<String, AnalysisProvider<TokenFilterFactory>> getTokenFilters() {
426
return Collections.emptyMap();
427
}
428
429
/**
430
* Get custom hunspell dictionaries provided by this plugin
431
* @return Map of language to hunspell dictionary
432
*/
433
default Map<String, Dictionary> getHunspellDictionaries() {
434
return Collections.emptyMap();
435
}
436
437
/**
438
* Get custom normalizers provided by this plugin
439
* @return Map of normalizer name to normalizer provider
440
*/
441
default Map<String, AnalysisProvider<AnalyzerProvider<? extends Analyzer>>> getNormalizers() {
442
return Collections.emptyMap();
443
}
444
}
445
446
/**
447
* Provider interface for analysis components
448
*/
449
interface AnalysisProvider<T> {
450
/**
451
* Create analysis component instance
452
* @param indexSettings Index settings context
453
* @param environment Plugin environment
454
* @param name Component name
455
* @param settings Component settings
456
*/
457
T get(IndexSettings indexSettings, Environment environment, String name, Settings settings);
458
}
459
460
/**
461
* Factory interface for creating analyzers
462
*/
463
interface AnalyzerProvider<T extends Analyzer> extends Closeable {
464
/**
465
* Get analyzer instance
466
*/
467
T get();
468
469
/**
470
* Close analyzer and release resources
471
*/
472
void close();
473
}
474
475
/**
476
* Factory interface for creating character filters
477
*/
478
interface CharFilterFactory {
479
/**
480
* Get character filter name
481
*/
482
String name();
483
484
/**
485
* Create character filter reader
486
* @param reader Input character reader
487
*/
488
Reader create(Reader reader);
489
}
490
491
/**
492
* Factory interface for creating tokenizers
493
*/
494
interface TokenizerFactory {
495
/**
496
* Create tokenizer instance
497
*/
498
Tokenizer create();
499
}
500
501
/**
502
* Factory interface for creating token filters
503
*/
504
interface TokenFilterFactory {
505
/**
506
* Create token filter instance
507
* @param tokenStream Input token stream
508
*/
509
TokenStream create(TokenStream tokenStream);
510
}
511
```
512
513
### Mapper Plugin Interface
514
515
Plugin interface for providing custom field mappers and data type support.
516
517
```java { .api }
518
/**
519
* Plugin interface for providing custom field mappers
520
*/
521
interface MapperPlugin {
522
/**
523
* Get custom field mappers provided by this plugin
524
* @return Map of type name to mapper parser
525
*/
526
Map<String, Mapper.TypeParser> getMappers();
527
528
/**
529
* Get custom metadata mappers provided by this plugin
530
* @return Map of name to metadata mapper parser
531
*/
532
default Map<String, MetadataFieldMapper.TypeParser> getMetadataMappers() {
533
return Collections.emptyMap();
534
}
535
536
/**
537
* Get custom field filter provided by this plugin
538
* @return Field filter implementation
539
*/
540
default Function<String, Predicate<String>> getFieldFilter() {
541
return MapperPlugin.NOOP_FIELD_FILTER;
542
}
543
544
/**
545
* No-op field filter that allows all fields
546
*/
547
Function<String, Predicate<String>> NOOP_FIELD_FILTER = index -> field -> true;
548
}
549
550
/**
551
* Base mapper for field mapping implementations
552
*/
553
abstract class Mapper implements Streamable {
554
/**
555
* Get mapper name
556
*/
557
String name();
558
559
/**
560
* Get mapper type name
561
*/
562
abstract String typeName();
563
564
/**
565
* Merge with another mapper
566
* @param mergeWith Mapper to merge with
567
* @param mergeReason Reason for merge operation
568
*/
569
abstract Mapper merge(Mapper mergeWith, MergeReason mergeReason);
570
571
/**
572
* Validate field mapping configuration
573
* @param indexSettings Index settings context
574
*/
575
abstract void validate(MappingLookup mappers, IndexSettings indexSettings);
576
577
/**
578
* Type parser interface for creating mapper instances
579
*/
580
interface TypeParser {
581
/**
582
* Parse mapper configuration and create mapper builder
583
* @param name Field name
584
* @param node Mapping configuration
585
* @param parserContext Parser context
586
*/
587
Builder parse(String name, Map<String, Object> node, ParserContext parserContext)
588
throws MapperParsingException;
589
}
590
591
/**
592
* Builder interface for constructing mappers
593
*/
594
abstract static class Builder {
595
/**
596
* Build mapper instance
597
* @param context Build context
598
*/
599
abstract Mapper build(ContentPath contentPath, MapperBuilderContext context);
600
}
601
}
602
```
603
604
### Ingest Plugin Interface
605
606
Plugin interface for providing custom ingest processors for document processing pipelines.
607
608
```java { .api }
609
/**
610
* Plugin interface for providing custom ingest processors
611
*/
612
interface IngestPlugin {
613
/**
614
* Get custom processors provided by this plugin
615
* @param processorFactories Existing processor factories
616
* @return Map of processor name to processor factory
617
*/
618
Map<String, Processor.Factory> getProcessors(Processor.Parameters processorFactories);
619
620
/**
621
* Get additional ingest capabilities provided by this plugin
622
* @return List of ingest capability extensions
623
*/
624
default List<IngestCapability> getIngestCapabilities() {
625
return Collections.emptyList();
626
}
627
}
628
629
/**
630
* Base processor interface for document processing in ingest pipelines
631
*/
632
interface Processor {
633
/**
634
* Get processor type name
635
*/
636
String getType();
637
638
/**
639
* Get processor tag for identification
640
*/
641
String getTag();
642
643
/**
644
* Get processor description
645
*/
646
String getDescription();
647
648
/**
649
* Execute processor on ingest document
650
* @param ingestDocument Document to process
651
* @return Processed ingest document
652
*/
653
IngestDocument execute(IngestDocument ingestDocument) throws Exception;
654
655
/**
656
* Factory interface for creating processor instances
657
*/
658
interface Factory {
659
/**
660
* Create processor instance
661
* @param processorFactories Available processor factories
662
* @param tag Processor tag
663
* @param description Processor description
664
* @param config Processor configuration
665
*/
666
Processor create(Map<String, Factory> processorFactories, String tag,
667
String description, Map<String, Object> config) throws Exception;
668
}
669
670
/**
671
* Parameters container for processor factory access
672
*/
673
class Parameters {
674
/**
675
* Get environment configuration
676
*/
677
public Environment env;
678
679
/**
680
* Get script service for script processors
681
*/
682
public ScriptService scriptService;
683
684
/**
685
* Get analysis registry for text analysis
686
*/
687
public AnalysisRegistry analysisRegistry;
688
689
/**
690
* Get thread pool for async operations
691
*/
692
public ThreadPool threadPool;
693
694
/**
695
* Get ingest service reference
696
*/
697
public IngestService ingestService;
698
}
699
}
700
701
/**
702
* Abstract base class for simple processor implementations
703
*/
704
abstract class AbstractProcessor implements Processor {
705
/**
706
* Create abstract processor
707
* @param tag Processor tag
708
* @param description Processor description
709
*/
710
protected AbstractProcessor(String tag, String description);
711
712
/**
713
* Get processor tag
714
*/
715
String getTag();
716
717
/**
718
* Get processor description
719
*/
720
String getDescription();
721
}
722
```
723
724
### Repository Plugin Interface
725
726
Plugin interface for providing custom snapshot repository implementations.
727
728
```java { .api }
729
/**
730
* Plugin interface for providing custom snapshot repositories
731
*/
732
interface RepositoryPlugin {
733
/**
734
* Get custom repository types provided by this plugin
735
* @return Map of repository type to repository factory
736
*/
737
Map<String, Repository.Factory> getRepositories(Environment env, NamedXContentRegistry namedXContentRegistry,
738
ClusterService clusterService, BigArrays bigArrays, RecoverySettings recoverySettings);
739
740
/**
741
* Get custom repository cleanup extensions
742
* @return Map of repository type to cleanup extensions
743
*/
744
default Map<String, Repository.Factory> getInternalRepositories(Environment env,
745
NamedXContentRegistry namedXContentRegistry, ClusterService clusterService,
746
RecoverySettings recoverySettings) {
747
return Collections.emptyMap();
748
}
749
}
750
751
/**
752
* Base repository interface for snapshot storage implementations
753
*/
754
interface Repository extends Closeable {
755
/**
756
* Get repository metadata
757
*/
758
RepositoryMetadata getMetadata();
759
760
/**
761
* Initialize repository with cluster state
762
* @param clusterState Current cluster state
763
*/
764
void initializeSnapshot(SnapshotId snapshotId, List<IndexId> indices, Metadata metadata);
765
766
/**
767
* Finalize snapshot creation
768
* @param finalizeContext Snapshot finalization context
769
*/
770
SnapshotInfo finalizeSnapshot(FinalizeSnapshotContext finalizeContext);
771
772
/**
773
* Delete snapshot from repository
774
* @param snapshotId Snapshot to delete
775
* @param repositoryStateId Repository state version
776
* @param writeShardGens Whether to write shard generation files
777
* @param listener Completion callback
778
*/
779
void deleteSnapshots(Collection<SnapshotId> snapshotIds, long repositoryStateId,
780
boolean writeShardGens, ActionListener<RepositoryData> listener);
781
782
/**
783
* Factory interface for creating repository instances
784
*/
785
interface Factory {
786
/**
787
* Create repository instance
788
* @param metadata Repository metadata configuration
789
*/
790
Repository create(RepositoryMetadata metadata) throws Exception;
791
792
/**
793
* Create repository instance with recovery settings
794
* @param metadata Repository metadata configuration
795
* @param typeLookup Type lookup for deserialization
796
*/
797
Repository create(RepositoryMetadata metadata, Function<String, Repository.Factory> typeLookup)
798
throws Exception;
799
}
800
}
801
```
802
803
## Usage Examples
804
805
### Creating a Custom Action Plugin
806
807
```java
808
import org.opensearch.plugins.ActionPlugin;
809
import org.opensearch.plugins.Plugin;
810
import org.opensearch.rest.BaseRestHandler;
811
import org.opensearch.rest.RestRequest;
812
import org.opensearch.client.node.NodeClient;
813
814
/**
815
* Example plugin that provides a custom action and REST endpoint
816
*/
817
public class CustomActionPlugin extends Plugin implements ActionPlugin {
818
819
@Override
820
public List<ActionHandler<? extends ActionRequest, ? extends ActionResponse>> getActions() {
821
return List.of(
822
new ActionHandler<>(CustomAction.INSTANCE, TransportCustomAction.class)
823
);
824
}
825
826
@Override
827
public List<RestHandler> getRestHandlers(Settings settings, RestController restController,
828
ClusterSettings clusterSettings, IndexScopedSettings indexScopedSettings,
829
SettingsFilter settingsFilter, IndexNameExpressionResolver indexNameExpressionResolver,
830
Supplier<DiscoveryNodes> nodesInCluster) {
831
832
return List.of(new RestCustomHandler());
833
}
834
}
835
836
/**
837
* Custom action type definition
838
*/
839
public class CustomAction extends ActionType<CustomResponse> {
840
public static final CustomAction INSTANCE = new CustomAction();
841
public static final String NAME = "cluster:admin/custom_action";
842
843
private CustomAction() {
844
super(NAME, CustomResponse::new);
845
}
846
}
847
848
/**
849
* Custom action request
850
*/
851
public class CustomRequest extends ActionRequest {
852
private String parameter;
853
854
public CustomRequest(String parameter) {
855
this.parameter = parameter;
856
}
857
858
public String getParameter() {
859
return parameter;
860
}
861
862
@Override
863
public ActionRequestValidationException validate() {
864
if (parameter == null || parameter.isEmpty()) {
865
ActionRequestValidationException validationException = new ActionRequestValidationException();
866
validationException.addValidationError("parameter cannot be null or empty");
867
return validationException;
868
}
869
return null;
870
}
871
}
872
873
/**
874
* Custom action response
875
*/
876
public class CustomResponse extends ActionResponse {
877
private String result;
878
879
public CustomResponse(String result) {
880
this.result = result;
881
}
882
883
public String getResult() {
884
return result;
885
}
886
887
@Override
888
public void writeTo(StreamOutput out) throws IOException {
889
out.writeString(result);
890
}
891
}
892
893
/**
894
* REST handler for custom action
895
*/
896
public class RestCustomHandler extends BaseRestHandler {
897
898
@Override
899
public List<Route> routes() {
900
return List.of(
901
new Route(GET, "/_custom/{param}"),
902
new Route(POST, "/_custom/{param}")
903
);
904
}
905
906
@Override
907
public String getName() {
908
return "custom_action_handler";
909
}
910
911
@Override
912
protected RestChannelConsumer prepareRequest(RestRequest request, NodeClient client) throws IOException {
913
String param = request.param("param");
914
CustomRequest customRequest = new CustomRequest(param);
915
916
return channel -> client.execute(CustomAction.INSTANCE, customRequest,
917
new RestToXContentListener<>(channel));
918
}
919
}
920
```
921
922
### Creating a Custom Query Plugin
923
924
```java
925
import org.opensearch.plugins.SearchPlugin;
926
import org.opensearch.index.query.QueryBuilder;
927
import org.opensearch.index.query.AbstractQueryBuilder;
928
929
/**
930
* Plugin providing a custom query builder
931
*/
932
public class CustomQueryPlugin extends Plugin implements SearchPlugin {
933
934
@Override
935
public List<QuerySpec<?>> getQueries() {
936
return List.of(
937
new QuerySpec<>(
938
CustomQueryBuilder.NAME,
939
CustomQueryBuilder::new,
940
CustomQueryBuilder::fromXContent
941
)
942
);
943
}
944
}
945
946
/**
947
* Custom query builder implementation
948
*/
949
public class CustomQueryBuilder extends AbstractQueryBuilder<CustomQueryBuilder> {
950
public static final String NAME = "custom_query";
951
952
private final String field;
953
private final String value;
954
private final float boost;
955
956
public CustomQueryBuilder(String field, String value) {
957
this.field = field;
958
this.value = value;
959
this.boost = 1.0f;
960
}
961
962
public CustomQueryBuilder boost(float boost) {
963
return new CustomQueryBuilder(field, value).boost(boost);
964
}
965
966
@Override
967
protected void doWriteTo(StreamOutput out) throws IOException {
968
out.writeString(field);
969
out.writeString(value);
970
out.writeFloat(boost);
971
}
972
973
@Override
974
protected void doXContent(XContentBuilder builder, Params params) throws IOException {
975
builder.startObject(NAME);
976
builder.startObject(field);
977
builder.field("value", value);
978
if (boost != 1.0f) {
979
builder.field("boost", boost);
980
}
981
builder.endObject();
982
builder.endObject();
983
}
984
985
public static CustomQueryBuilder fromXContent(XContentParser parser) throws IOException {
986
// Implementation for parsing from XContent
987
// ... parsing logic here
988
return new CustomQueryBuilder("field", "value");
989
}
990
991
@Override
992
protected Query doToQuery(QueryShardContext context) throws IOException {
993
// Convert to Lucene query
994
return new TermQuery(new Term(field, value));
995
}
996
997
@Override
998
protected boolean doEquals(CustomQueryBuilder other) {
999
return Objects.equals(field, other.field) &&
1000
Objects.equals(value, other.value) &&
1001
Float.compare(boost, other.boost) == 0;
1002
}
1003
1004
@Override
1005
protected int doHashCode() {
1006
return Objects.hash(field, value, boost);
1007
}
1008
1009
@Override
1010
public String getWriteableName() {
1011
return NAME;
1012
}
1013
}
1014
```
1015
1016
### Creating a Custom Analysis Plugin
1017
1018
```java
1019
import org.opensearch.plugins.AnalysisPlugin;
1020
import org.opensearch.index.analysis.TokenFilterFactory;
1021
1022
/**
1023
* Plugin providing custom text analysis components
1024
*/
1025
public class CustomAnalysisPlugin extends Plugin implements AnalysisPlugin {
1026
1027
@Override
1028
public Map<String, AnalysisProvider<TokenFilterFactory>> getTokenFilters() {
1029
return Map.of("custom_filter", CustomTokenFilterFactory::new);
1030
}
1031
1032
@Override
1033
public Map<String, AnalysisProvider<AnalyzerProvider<? extends Analyzer>>> getAnalyzers() {
1034
return Map.of("custom_analyzer", CustomAnalyzerProvider::new);
1035
}
1036
}
1037
1038
/**
1039
* Custom token filter factory
1040
*/
1041
public class CustomTokenFilterFactory implements TokenFilterFactory {
1042
private final Settings settings;
1043
1044
public CustomTokenFilterFactory(IndexSettings indexSettings, Environment environment,
1045
String name, Settings settings) {
1046
this.settings = settings;
1047
}
1048
1049
@Override
1050
public String name() {
1051
return "custom_filter";
1052
}
1053
1054
@Override
1055
public TokenStream create(TokenStream tokenStream) {
1056
return new CustomTokenFilter(tokenStream, settings);
1057
}
1058
}
1059
1060
/**
1061
* Custom token filter implementation
1062
*/
1063
public class CustomTokenFilter extends TokenFilter {
1064
private final CharTermAttribute termAttribute;
1065
private final Settings settings;
1066
1067
public CustomTokenFilter(TokenStream input, Settings settings) {
1068
super(input);
1069
this.settings = settings;
1070
this.termAttribute = addAttribute(CharTermAttribute.class);
1071
}
1072
1073
@Override
1074
public boolean incrementToken() throws IOException {
1075
if (input.incrementToken()) {
1076
// Custom token processing logic
1077
String term = termAttribute.toString();
1078
String processed = processTerm(term);
1079
termAttribute.setEmpty().append(processed);
1080
return true;
1081
}
1082
return false;
1083
}
1084
1085
private String processTerm(String term) {
1086
// Custom processing logic
1087
return term.toLowerCase();
1088
}
1089
}
1090
1091
/**
1092
* Custom analyzer provider
1093
*/
1094
public class CustomAnalyzerProvider implements AnalyzerProvider<Analyzer> {
1095
private final Analyzer analyzer;
1096
1097
public CustomAnalyzerProvider(IndexSettings indexSettings, Environment environment,
1098
String name, Settings settings) {
1099
this.analyzer = new CustomAnalyzer(settings);
1100
}
1101
1102
@Override
1103
public Analyzer get() {
1104
return analyzer;
1105
}
1106
1107
@Override
1108
public void close() {
1109
// Cleanup if needed
1110
}
1111
}
1112
```
1113
1114
### Creating a Custom Ingest Processor Plugin
1115
1116
```java
1117
import org.opensearch.plugins.IngestPlugin;
1118
import org.opensearch.ingest.Processor;
1119
import org.opensearch.ingest.AbstractProcessor;
1120
import org.opensearch.ingest.IngestDocument;
1121
1122
/**
1123
* Plugin providing custom ingest processors
1124
*/
1125
public class CustomIngestPlugin extends Plugin implements IngestPlugin {
1126
1127
@Override
1128
public Map<String, Processor.Factory> getProcessors(Processor.Parameters parameters) {
1129
return Map.of("custom_processor", new CustomProcessorFactory());
1130
}
1131
}
1132
1133
/**
1134
* Factory for creating custom processor instances
1135
*/
1136
public class CustomProcessorFactory implements Processor.Factory {
1137
1138
@Override
1139
public CustomProcessor create(Map<String, Processor.Factory> processorFactories,
1140
String tag, String description, Map<String, Object> config) {
1141
String field = ConfigurationUtils.readStringProperty("custom_processor", tag, config, "field");
1142
String value = ConfigurationUtils.readStringProperty("custom_processor", tag, config, "value");
1143
boolean ignoreMissing = ConfigurationUtils.readBooleanProperty("custom_processor", tag, config,
1144
"ignore_missing", false);
1145
1146
return new CustomProcessor(tag, description, field, value, ignoreMissing);
1147
}
1148
}
1149
1150
/**
1151
* Custom ingest processor implementation
1152
*/
1153
public class CustomProcessor extends AbstractProcessor {
1154
public static final String TYPE = "custom_processor";
1155
1156
private final String field;
1157
private final String value;
1158
private final boolean ignoreMissing;
1159
1160
public CustomProcessor(String tag, String description, String field, String value, boolean ignoreMissing) {
1161
super(tag, description);
1162
this.field = field;
1163
this.value = value;
1164
this.ignoreMissing = ignoreMissing;
1165
}
1166
1167
@Override
1168
public String getType() {
1169
return TYPE;
1170
}
1171
1172
@Override
1173
public IngestDocument execute(IngestDocument ingestDocument) throws Exception {
1174
if (!ingestDocument.hasField(field)) {
1175
if (ignoreMissing) {
1176
return ingestDocument;
1177
} else {
1178
throw new IllegalArgumentException("Field [" + field + "] does not exist");
1179
}
1180
}
1181
1182
// Custom processing logic
1183
String currentValue = ingestDocument.getFieldValue(field, String.class);
1184
String processedValue = processValue(currentValue);
1185
ingestDocument.setFieldValue(field, processedValue);
1186
1187
return ingestDocument;
1188
}
1189
1190
private String processValue(String input) {
1191
// Custom value processing
1192
return input + "_" + value;
1193
}
1194
}
1195
```
1196
1197
## Types
1198
1199
```java { .api }
1200
/**
1201
* Plugin environment providing access to configuration and resources
1202
*/
1203
class Environment {
1204
/**
1205
* Get settings configuration
1206
*/
1207
Settings settings();
1208
1209
/**
1210
* Get config directory path
1211
*/
1212
Path configDir();
1213
1214
/**
1215
* Get plugins directory path
1216
*/
1217
Path pluginsDir();
1218
1219
/**
1220
* Get modules directory path
1221
*/
1222
Path modulesDir();
1223
1224
/**
1225
* Get logs directory path
1226
*/
1227
Path logsDir();
1228
1229
/**
1230
* Get temporary directory path
1231
*/
1232
Path tmpDir();
1233
}
1234
1235
/**
1236
* Route definition for REST handlers
1237
*/
1238
class Route {
1239
/**
1240
* Create route with method and path
1241
* @param method HTTP method
1242
* @param path URL path pattern
1243
*/
1244
Route(RestRequest.Method method, String path);
1245
1246
/**
1247
* Create route with method, path, and deprecation message
1248
* @param method HTTP method
1249
* @param path URL path pattern
1250
* @param deprecationMessage Deprecation warning message
1251
*/
1252
Route(RestRequest.Method method, String path, String deprecationMessage);
1253
1254
/**
1255
* Get HTTP method
1256
*/
1257
RestRequest.Method getMethod();
1258
1259
/**
1260
* Get path pattern
1261
*/
1262
String getPath();
1263
1264
/**
1265
* Get deprecation message
1266
*/
1267
String getDeprecationMessage();
1268
}
1269
1270
/**
1271
* Transport action base class for handling cluster operations
1272
*/
1273
abstract class TransportAction<Request extends ActionRequest, Response extends ActionResponse> {
1274
/**
1275
* Execute action with request and response listener
1276
* @param task Task context
1277
* @param request Action request
1278
* @param listener Response callback
1279
*/
1280
protected abstract void doExecute(Task task, Request request, ActionListener<Response> listener);
1281
1282
/**
1283
* Get action name
1284
*/
1285
String actionName();
1286
1287
/**
1288
* Get transport service
1289
*/
1290
protected TransportService transportService();
1291
1292
/**
1293
* Get cluster service
1294
*/
1295
protected ClusterService clusterService();
1296
}
1297
1298
/**
1299
* Setting definition for plugin configuration
1300
*/
1301
class Setting<T> {
1302
/**
1303
* Create setting with key and default value
1304
* @param key Setting key
1305
* @param defaultValue Default value
1306
* @param parser Value parser function
1307
* @param properties Setting properties
1308
*/
1309
static <T> Setting<T> simpleString(String key, String defaultValue, Property... properties);
1310
1311
/**
1312
* Create integer setting
1313
* @param key Setting key
1314
* @param defaultValue Default integer value
1315
* @param minValue Minimum allowed value
1316
* @param properties Setting properties
1317
*/
1318
static Setting<Integer> intSetting(String key, int defaultValue, int minValue, Property... properties);
1319
1320
/**
1321
* Create boolean setting
1322
* @param key Setting key
1323
* @param defaultValue Default boolean value
1324
* @param properties Setting properties
1325
*/
1326
static Setting<Boolean> boolSetting(String key, boolean defaultValue, Property... properties);
1327
1328
/**
1329
* Get setting key
1330
*/
1331
String getKey();
1332
1333
/**
1334
* Get default value
1335
*/
1336
T getDefault(Settings settings);
1337
1338
/**
1339
* Setting properties enumeration
1340
*/
1341
enum Property {
1342
DYNAMIC, INDEX_SCOPE, CLUSTER_SCOPE, DEPRECATED, FILTERED
1343
}
1344
}
1345
```