/* * 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. */ /* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Modifications Copyright OpenSearch Contributors. See * GitHub history for details. */ package org.opensearch.script; import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.search.Scorable; import org.opensearch.OpenSearchException; import org.opensearch.common.logging.DeprecationLogger; import org.opensearch.index.fielddata.ScriptDocValues; import org.opensearch.search.lookup.LeafSearchLookup; import org.opensearch.search.lookup.SearchLookup; import org.opensearch.search.lookup.SourceLookup; import java.io.IOException; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.function.Function; /** * Contexts for scripted metric aggregations * * @opensearch.internal */ public class ScriptedMetricAggContexts { /** * Base initialization script * * @opensearch.internal */ public abstract static class InitScript { private final Map params; private final Map state; public InitScript(Map params, Map state) { this.params = params; this.state = state; } public Map getParams() { return params; } public Object getState() { return state; } public abstract void execute(); /** * Factory for a scripted metric agg context * * @opensearch.internal */ public interface Factory extends ScriptFactory { InitScript newInstance(Map params, Map state); } public static String[] PARAMETERS = {}; public static ScriptContext CONTEXT = new ScriptContext<>("aggs_init", Factory.class); } /** * Base map script * * @opensearch.internal */ public abstract static class MapScript { private static final DeprecationLogger deprecationLogger = DeprecationLogger.getLogger(DynamicMap.class); private static final Map> PARAMS_FUNCTIONS = Map.of("doc", value -> { deprecationLogger.deprecate( "map-script_doc", "Accessing variable [doc] via [params.doc] from within an scripted metric agg map script " + "is deprecated in favor of directly accessing [doc]." ); return value; }, "_doc", value -> { deprecationLogger.deprecate( "map-script__doc", "Accessing variable [doc] via [params._doc] from within an scripted metric agg map script " + "is deprecated in favor of directly accessing [doc]." ); return value; }, "_agg", value -> { deprecationLogger.deprecate( "map-script__agg", "Accessing variable [_agg] via [params._agg] from within a scripted metric agg map script " + "is deprecated in favor of using [state]." ); return value; }, "_source", value -> ((SourceLookup) value).loadSourceIfNeeded()); private final Map params; private final Map state; private final LeafSearchLookup leafLookup; private Scorable scorer; public MapScript(Map params, Map state, SearchLookup lookup, LeafReaderContext leafContext) { this.state = state; this.leafLookup = leafContext == null ? null : lookup.getLeafSearchLookup(leafContext); if (leafLookup != null) { params = new HashMap<>(params); // copy params so we aren't modifying input params.putAll(leafLookup.asMap()); // add lookup vars params = new DynamicMap(params, PARAMS_FUNCTIONS); // wrap with deprecations } this.params = params; } public Map getParams() { return params; } public Map getState() { return state; } // Return the doc as a map (instead of LeafDocLookup) in order to abide by type allowlisting rules for // Painless scripts. public Map> getDoc() { return leafLookup == null ? null : leafLookup.doc(); } public void setDocument(int docId) { if (leafLookup != null) { leafLookup.setDocument(docId); } } public void setScorer(Scorable scorer) { this.scorer = scorer; } // get_score() is named this way so that it's picked up by Painless as '_score' public double get_score() { if (scorer == null) { return 0.0; } try { return scorer.score(); } catch (IOException e) { throw new OpenSearchException("Couldn't look up score", e); } } public abstract void execute(); /** * Factory for a scripted metric agg context * * @opensearch.internal */ public interface LeafFactory { MapScript newInstance(LeafReaderContext ctx); } /** * Factory for a scripted metric agg factory * * @opensearch.internal */ public interface Factory extends ScriptFactory { LeafFactory newFactory(Map params, Map state, SearchLookup lookup); } public static String[] PARAMETERS = new String[] {}; public static ScriptContext CONTEXT = new ScriptContext<>("aggs_map", Factory.class); } /** * Base combination script * * @opensearch.internal */ public abstract static class CombineScript { private final Map params; private final Map state; public CombineScript(Map params, Map state) { this.params = params; this.state = state; } public Map getParams() { return params; } public Map getState() { return state; } public abstract Object execute(); /** * Factory for a scripted metric agg context * * @opensearch.internal */ public interface Factory extends ScriptFactory { CombineScript newInstance(Map params, Map state); } public static String[] PARAMETERS = {}; public static ScriptContext CONTEXT = new ScriptContext<>("aggs_combine", Factory.class); } /** * Base reduce script * * @opensearch.internal */ public abstract static class ReduceScript { private final Map params; private final List states; public ReduceScript(Map params, List states) { this.params = params; this.states = states; } public Map getParams() { return params; } public List getStates() { return states; } public abstract Object execute(); /** * Factory for a scripted metric agg context * * @opensearch.internal */ public interface Factory extends ScriptFactory { ReduceScript newInstance(Map params, List states); } public static String[] PARAMETERS = {}; public static ScriptContext CONTEXT = new ScriptContext<>("aggs_reduce", Factory.class); } }