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 }