1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved. 22 * Portions Copyright (c) 2019, Chris Fraire <cfraire@me.com>. 23 */ 24 package org.opengrok.indexer.configuration; 25 26 import com.cronutils.model.CronType; 27 import com.cronutils.model.definition.CronDefinitionBuilder; 28 import com.cronutils.parser.CronParser; 29 import org.opengrok.indexer.search.QueryBuilder; 30 31 import java.util.Arrays; 32 import java.util.HashSet; 33 import java.util.Objects; 34 import java.util.Set; 35 36 /** 37 * The suggester specific configuration. 38 */ 39 public class SuggesterConfig { 40 41 public static final boolean ENABLED_DEFAULT = true; 42 public static final int MAX_RESULTS_DEFAULT = 10; 43 public static final int MIN_CHARS_DEFAULT = 0; 44 public static final int MAX_PROJECTS_DEFAULT = Short.MAX_VALUE; 45 public static final boolean ALLOW_COMPLEX_QUERIES_DEFAULT = true; 46 public static final boolean ALLOW_MOST_POPULAR_DEFAULT = true; 47 public static final boolean SHOW_SCORES_DEFAULT = false; 48 public static final boolean SHOW_PROJECTS_DEFAULT = true; 49 public static final boolean SHOW_TIME_DEFAULT = false; 50 public static final String REBUILD_CRON_CONFIG_DEFAULT = "0 0 * * *"; // every day at midnight 51 public static final int BUILD_TERMINATION_TIME_DEFAULT = 1800; // half an hour should be enough 52 public static final int TIME_THRESHOLD_DEFAULT = 2000; // 2 sec 53 public static final int REBUILD_THREAD_POOL_PERCENT_NCPUS_DEFAULT = 80; 54 55 private static final Set<String> allowedProjectsDefault = null; 56 private static final Set<String> allowedFieldsDefault = Set.of( 57 QueryBuilder.FULL, 58 QueryBuilder.DEFS, 59 QueryBuilder.REFS, 60 QueryBuilder.PATH, 61 QueryBuilder.HIST, 62 QueryBuilder.TYPE 63 ); 64 65 /** 66 * Specifies if the suggester is enabled. 67 */ 68 private boolean enabled; 69 70 /** 71 * Specifies maximum number of results suggester should return. 72 */ 73 private int maxResults; 74 75 /** 76 * Specifies minimum number of characters that are needed for suggester to start looking for suggestions. 77 */ 78 private int minChars; 79 80 /** 81 * Specifies set of projects for which the suggester should be enabled. If {@code null} then all projects are 82 * enabled. 83 */ 84 private Set<String> allowedProjects; 85 86 /** 87 * Specifies how many maximum projects can be selected at the same time and the suggestions will work. 88 */ 89 private int maxProjects; 90 91 /** 92 * Specifies the fields for which the suggester should be enabled. If {@code null} then all fields are enabled. 93 */ 94 private Set<String> allowedFields; 95 96 /** 97 * Specifies if the suggester should support complex queries. 98 */ 99 private boolean allowComplexQueries; 100 101 /** 102 * Specifies if the most popular completion should be enabled. 103 */ 104 private boolean allowMostPopular; 105 106 /** 107 * Specifies if the scores should be displayed next to the suggestions. 108 */ 109 private boolean showScores; 110 111 /** 112 * Specifies if the suggestions should show in which project the term was found. 113 */ 114 private boolean showProjects; 115 116 /** 117 * Specifies if the time it took the suggester to find the suggestions should be displayed. 118 */ 119 private boolean showTime; 120 121 /** 122 * Specifies how often should the suggester rebuild the WFST data structures. (Data structures for simple prefix 123 * queries.) 124 */ 125 private String rebuildCronConfig; 126 127 /** 128 * Specifies after how much time (in seconds) the suggester should kill the threads that build the suggester data 129 * structures. 130 */ 131 private int buildTerminationTime; 132 133 /** 134 * Time threshold for suggestions in milliseconds. If the computation exceeds this time, 135 * it will be stopped and partial results will be returned. 136 */ 137 private int timeThreshold; 138 139 /** 140 * Number of threads used for rebuild pool expressed in percent of available CPUs in the system. 141 */ 142 private int rebuildThreadPoolSizeInNcpuPercent; 143 SuggesterConfig()144 public SuggesterConfig() { 145 setEnabled(ENABLED_DEFAULT); 146 setMaxResults(MAX_RESULTS_DEFAULT); 147 setMinChars(MIN_CHARS_DEFAULT); 148 setAllowedProjects(allowedProjectsDefault); 149 setMaxProjects(MAX_PROJECTS_DEFAULT); 150 setAllowedFields(allowedFieldsDefault); 151 setAllowComplexQueries(ALLOW_COMPLEX_QUERIES_DEFAULT); 152 setAllowMostPopular(ALLOW_MOST_POPULAR_DEFAULT); 153 setShowScores(SHOW_SCORES_DEFAULT); 154 setShowProjects(SHOW_PROJECTS_DEFAULT); 155 setShowTime(SHOW_TIME_DEFAULT); 156 setTimeThreshold(TIME_THRESHOLD_DEFAULT); 157 setRebuildCronConfig(REBUILD_CRON_CONFIG_DEFAULT); 158 setBuildTerminationTime(BUILD_TERMINATION_TIME_DEFAULT); 159 setRebuildThreadPoolSizeInNcpuPercent(REBUILD_THREAD_POOL_PERCENT_NCPUS_DEFAULT); 160 } 161 isEnabled()162 public boolean isEnabled() { 163 return enabled; 164 } 165 setEnabled(final boolean enabled)166 public void setEnabled(final boolean enabled) { 167 this.enabled = enabled; 168 } 169 getMaxResults()170 public int getMaxResults() { 171 return maxResults; 172 } 173 setMaxResults(final int maxResults)174 public void setMaxResults(final int maxResults) { 175 if (maxResults <= 0) { 176 throw new IllegalArgumentException("Max results cannot be negative or zero"); 177 } 178 this.maxResults = maxResults; 179 } 180 getMinChars()181 public int getMinChars() { 182 return minChars; 183 } 184 setMinChars(final int minChars)185 public void setMinChars(final int minChars) { 186 if (minChars < 0) { 187 throw new IllegalArgumentException( 188 "Minimum number of characters needed for suggester to provide suggestions cannot be negative"); 189 } 190 this.minChars = minChars; 191 } 192 getAllowedProjects()193 public Set<String> getAllowedProjects() { 194 return allowedProjects; 195 } 196 setAllowedProjects(final Set<String> allowedProjects)197 public void setAllowedProjects(final Set<String> allowedProjects) { 198 this.allowedProjects = allowedProjects; 199 } 200 getMaxProjects()201 public int getMaxProjects() { 202 return maxProjects; 203 } 204 setMaxProjects(final int maxProjects)205 public void setMaxProjects(final int maxProjects) { 206 if (maxProjects < 1) { 207 throw new IllegalArgumentException("Maximum projects for suggestions cannot be less than 1"); 208 } 209 this.maxProjects = maxProjects; 210 } 211 getAllowedFields()212 public Set<String> getAllowedFields() { 213 return allowedFields; 214 } 215 setAllowedFields(final Set<String> allowedFields)216 public void setAllowedFields(final Set<String> allowedFields) { 217 this.allowedFields = new HashSet<>(allowedFields); 218 } 219 isAllowComplexQueries()220 public boolean isAllowComplexQueries() { 221 return allowComplexQueries; 222 } 223 setAllowComplexQueries(final boolean allowComplexQueries)224 public void setAllowComplexQueries(final boolean allowComplexQueries) { 225 this.allowComplexQueries = allowComplexQueries; 226 } 227 isAllowMostPopular()228 public boolean isAllowMostPopular() { 229 return allowMostPopular; 230 } 231 setAllowMostPopular(final boolean allowMostPopular)232 public void setAllowMostPopular(final boolean allowMostPopular) { 233 this.allowMostPopular = allowMostPopular; 234 } 235 isShowScores()236 public boolean isShowScores() { 237 return showScores; 238 } 239 setShowScores(final boolean showScores)240 public void setShowScores(final boolean showScores) { 241 this.showScores = showScores; 242 } 243 isShowProjects()244 public boolean isShowProjects() { 245 return showProjects; 246 } 247 setShowProjects(final boolean showProjects)248 public void setShowProjects(final boolean showProjects) { 249 this.showProjects = showProjects; 250 } 251 isShowTime()252 public boolean isShowTime() { 253 return showTime; 254 } 255 setShowTime(final boolean showTime)256 public void setShowTime(final boolean showTime) { 257 this.showTime = showTime; 258 } 259 getRebuildCronConfig()260 public String getRebuildCronConfig() { 261 return rebuildCronConfig; 262 } 263 setRebuildCronConfig(final String rebuildCronConfig)264 public void setRebuildCronConfig(final String rebuildCronConfig) { 265 if (rebuildCronConfig != null) { // check cron format 266 CronParser parser = new CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX)); 267 parser.parse(rebuildCronConfig); // throws IllegalArgumentException if invalid 268 } 269 this.rebuildCronConfig = rebuildCronConfig; 270 } 271 getBuildTerminationTime()272 public int getBuildTerminationTime() { 273 return buildTerminationTime; 274 } 275 setBuildTerminationTime(final int buildTerminationTime)276 public void setBuildTerminationTime(final int buildTerminationTime) { 277 if (buildTerminationTime < 0) { 278 throw new IllegalArgumentException("Suggester build termination time cannot be negative"); 279 } 280 this.buildTerminationTime = buildTerminationTime; 281 } 282 getTimeThreshold()283 public int getTimeThreshold() { 284 return timeThreshold; 285 } 286 setTimeThreshold(final int timeThreshold)287 public void setTimeThreshold(final int timeThreshold) { 288 if (timeThreshold < 0) { 289 throw new IllegalArgumentException("Time threshold for suggestions cannot be negative"); 290 } 291 this.timeThreshold = timeThreshold; 292 } 293 setRebuildThreadPoolSizeInNcpuPercent(final int percent)294 public void setRebuildThreadPoolSizeInNcpuPercent(final int percent) { 295 if (percent < 0 || percent > 100) { 296 throw new IllegalArgumentException("Need percentage value"); 297 } 298 this.rebuildThreadPoolSizeInNcpuPercent = percent; 299 } 300 getRebuildThreadPoolSizeInNcpuPercent()301 public int getRebuildThreadPoolSizeInNcpuPercent() { 302 return rebuildThreadPoolSizeInNcpuPercent; 303 } 304 305 @Override equals(Object o)306 public boolean equals(Object o) { 307 if (this == o) { 308 return true; 309 } 310 if (o == null || getClass() != o.getClass()) { 311 return false; 312 } 313 SuggesterConfig that = (SuggesterConfig) o; 314 return enabled == that.enabled && 315 maxResults == that.maxResults && 316 minChars == that.minChars && 317 maxProjects == that.maxProjects && 318 allowComplexQueries == that.allowComplexQueries && 319 allowMostPopular == that.allowMostPopular && 320 showScores == that.showScores && 321 showProjects == that.showProjects && 322 showTime == that.showTime && 323 buildTerminationTime == that.buildTerminationTime && 324 Objects.equals(allowedProjects, that.allowedProjects) && 325 Objects.equals(allowedFields, that.allowedFields) && 326 Objects.equals(rebuildCronConfig, that.rebuildCronConfig) && 327 rebuildThreadPoolSizeInNcpuPercent == that.rebuildThreadPoolSizeInNcpuPercent; 328 } 329 330 @Override hashCode()331 public int hashCode() { 332 return Objects.hash(enabled, maxResults, minChars, allowedProjects, maxProjects, allowedFields, 333 allowComplexQueries, allowMostPopular, showScores, showProjects, showTime, rebuildCronConfig, 334 buildTerminationTime, rebuildThreadPoolSizeInNcpuPercent); 335 } 336 337 /** 338 * Gets an instance version suitable for helper documentation by shifting 339 * most default properties slightly. 340 */ getForHelp()341 static SuggesterConfig getForHelp() { 342 SuggesterConfig res = new SuggesterConfig(); 343 res.setEnabled(!res.isEnabled()); 344 res.setMaxResults(1 + res.getMaxResults()); 345 res.setMinChars(1 + res.getMinChars()); 346 res.setAllowedProjects(new HashSet<>(Arrays.asList("project-1", "project-2"))); 347 res.setMaxProjects(1 + res.getMaxProjects()); 348 res.setAllowedFields(getAllowedFieldsForHelp(res.getAllowedFields())); 349 res.setAllowComplexQueries(!res.isAllowComplexQueries()); 350 res.setAllowMostPopular(!res.isAllowMostPopular()); 351 res.setShowScores(!res.isShowScores()); 352 res.setShowProjects(!res.isShowProjects()); 353 res.setShowTime(!res.isShowTime()); 354 res.setTimeThreshold(1 + res.getTimeThreshold()); 355 res.setRebuildCronConfig("1 0 * * *"); 356 res.setBuildTerminationTime(1 + res.getBuildTerminationTime()); 357 res.setRebuildThreadPoolSizeInNcpuPercent(1 + res.getRebuildThreadPoolSizeInNcpuPercent()); 358 return res; 359 } 360 getAllowedFieldsForHelp(Set<String> allowedFields)361 private static HashSet<String> getAllowedFieldsForHelp(Set<String> allowedFields) { 362 HashSet<String> res = new HashSet<>(allowedFields); 363 res.remove(QueryBuilder.FULL); 364 return res; 365 } 366 } 367