View Javadoc
1   /*
2    * Copyright 2012-2025 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.indexer;
17  
18  import java.util.Map;
19  
20  import org.apache.logging.log4j.LogManager;
21  import org.apache.logging.log4j.Logger;
22  import org.codelibs.fess.Constants;
23  import org.codelibs.fess.opensearch.config.exentity.BoostDocumentRule;
24  import org.codelibs.fess.util.ComponentUtil;
25  
26  /**
27   * A matcher class for applying document boost values based on configurable expressions.
28   * This class evaluates match and boost expressions against document data to determine
29   * if a document should receive a boost and what boost value to apply. It supports
30   * script-based expressions for flexible document scoring.
31   *
32   */
33  public class DocBoostMatcher {
34      private static final Logger logger = LogManager.getLogger(DocBoostMatcher.class);
35  
36      /** The expression used to calculate the boost value (defaults to "0") */
37      private String boostExpression = "0";
38  
39      /** The expression used to match documents for boosting */
40      private String matchExpression;
41  
42      /** The script engine type used for expression evaluation */
43      private final String scriptType;
44  
45      /**
46       * Default constructor that creates a DocBoostMatcher with default script type.
47       * Uses the default script engine as defined in Constants.DEFAULT_SCRIPT.
48       */
49      public DocBoostMatcher() {
50          scriptType = Constants.DEFAULT_SCRIPT;
51      }
52  
53      /**
54       * Constructor that creates a DocBoostMatcher from a BoostDocumentRule.
55       *
56       * @param rule the boost document rule containing match and boost expressions
57       */
58      public DocBoostMatcher(final BoostDocumentRule rule) {
59          matchExpression = rule.getUrlExpr();
60          boostExpression = rule.getBoostExpr();
61          scriptType = ComponentUtil.getFessConfig().getCrawlerDefaultScript();
62      }
63  
64      /**
65       * Determines if the given document data matches the configured match expression.
66       *
67       * @param map the document data as a map of field names to values
68       * @return true if the document matches the expression, false otherwise
69       */
70      public boolean match(final Map<String, Object> map) {
71  
72          if (map == null || map.isEmpty() || matchExpression == null) {
73              return false;
74          }
75  
76          final Object value = ComponentUtil.getScriptEngineFactory().getScriptEngine(scriptType).evaluate(matchExpression, map);
77          if (value instanceof Boolean) {
78              return (Boolean) value;
79          }
80  
81          return false;
82      }
83  
84      /**
85       * Calculates the boost value for the given document data using the boost expression.
86       * The method evaluates the boost expression and converts the result to a float value.
87       * Supports Integer, Long, Float, Double, and String representations of numbers.
88       *
89       * @param map the document data as a map of field names to values
90       * @return the calculated boost value as a float, or 0.0f if evaluation fails
91       */
92      public float getValue(final Map<String, Object> map) {
93          if (map == null || map.isEmpty()) {
94              return 0.0f;
95          }
96  
97          final Object value = ComponentUtil.getScriptEngineFactory().getScriptEngine(scriptType).evaluate(boostExpression, map);
98          if (value instanceof Integer) {
99              return ((Integer) value).floatValue();
100         }
101         if (value instanceof Long) {
102             return ((Long) value).floatValue();
103         }
104         if (value instanceof Float) {
105             return (Float) value;
106         }
107         if (value instanceof Double) {
108             return ((Double) value).floatValue();
109         }
110         if (value != null) {
111             try {
112                 return Float.parseFloat(value.toString());
113             } catch (final NumberFormatException e) {
114                 logger.warn("Failed to parse boost value: expression={}, value={}", boostExpression, value, e);
115                 return 0.0f;
116             }
117         }
118 
119         return 0.0f;
120     }
121 
122     /**
123      * Gets the current boost expression.
124      *
125      * @return the boost expression string
126      */
127     public String getBoostExpression() {
128         return boostExpression;
129     }
130 
131     /**
132      * Sets the boost expression used to calculate boost values.
133      *
134      * @param expression the boost expression string
135      */
136     public void setBoostExpression(final String expression) {
137         boostExpression = expression;
138     }
139 
140     /**
141      * Gets the current match expression.
142      *
143      * @return the match expression string
144      */
145     public String getMatchExpression() {
146         return matchExpression;
147     }
148 
149     /**
150      * Sets the match expression used to determine if documents should be boosted.
151      *
152      * @param expression the match expression string
153      */
154     public void setMatchExpression(final String expression) {
155         matchExpression = expression;
156     }
157 
158 }