├── .gitignore ├── LICENSE ├── pom.xml ├── readme.md └── src ├── main ├── assemblies │ └── plugin.xml ├── java │ └── org │ │ └── bkatwal │ │ └── elasticsearch │ │ └── plugin │ │ ├── helper │ │ ├── MinMaxNormalizer.java │ │ ├── Normalizer.java │ │ ├── NormalizerServiceLocator.java │ │ └── ZScoreNormalizer.java │ │ └── rescorer │ │ ├── MinMaxSameScoreStrategy.java │ │ ├── NormalizerFactorMathOp.java │ │ ├── NormalizerRescorerBuilder.java │ │ ├── NormalizerType.java │ │ ├── ScoreNormalizerRescorer.java │ │ └── ScoreNormalizerRescorerPlugin.java └── resources │ └── plugin-descriptor.properties └── test └── java └── org └── bkatwal └── elasticsearch └── plugin └── helper ├── MinMaxNormalizerTest.java └── ZScoreNormalizerTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # Project specific excludes 3 | # 4 | 5 | tomcat 6 | 7 | # 8 | # Default excludes 9 | # 10 | 11 | # Binaries 12 | *.7z 13 | *.dmg 14 | *.gz 15 | *.iso 16 | *.jar 17 | *.rar 18 | *.tar 19 | *.zip 20 | *.war 21 | *.ear 22 | *.sar 23 | *.class 24 | 25 | # Maven 26 | target/ 27 | 28 | # IntelliJ project files 29 | *.iml 30 | *.iws 31 | *.ipr 32 | .idea/ 33 | 34 | # eclipse project file 35 | .settings/ 36 | .classpath 37 | .project 38 | 39 | # NetBeans specific 40 | nbproject/private/ 41 | build/ 42 | nbbuild/ 43 | dist/ 44 | nbdist/ 45 | nbactions.xml 46 | nb-configuration.xml 47 | 48 | 49 | # OS 50 | .DS_Store 51 | 52 | # Misc 53 | *.swp 54 | release.properties 55 | pom.xml.releaseBackup 56 | pom.xml.tag 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | org.bkatwal.elasticsearch.plugin 8 | elasticsearch-score-normalizer-rescorer 9 | 1.0-SNAPSHOT 10 | 11 | elasticsearch-score-normalizer-rescorer 12 | 13 | 14 | UTF-8 15 | 1.8 16 | 1.8 17 | 7.4.0 18 | 3.5.1 19 | ${basedir}/src/main/assemblies/plugin.xml 20 | 21 | 22 | 23 | 24 | org.elasticsearch 25 | elasticsearch 26 | ${elasticsearch.version} 27 | provided 28 | 29 | 30 | 31 | junit 32 | junit 33 | 4.12 34 | test 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.plugins 42 | maven-compiler-plugin 43 | ${maven.compiler.plugin.version} 44 | 45 | ${maven.compiler.target} 46 | ${maven.compiler.target} 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-assembly-plugin 52 | 53 | false 54 | ${project.build.directory}/releases/ 55 | 56 | ${elasticsearch.assembly.descriptor} 57 | 58 | 59 | 60 | 61 | package 62 | 63 | attached 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Elasticsearch Score Normalizer 2 | Plugin to normalize score using Min Max or Z Score normalizer and updates normalized score by a 3 | given `factor` and `factor_mode`. 4 | This plugin is built on top of elasticsearch's rescorer feature and normalizes the top docs. 5 | 6 | If page `size` is 10 and `from` offset is 20. Then all 30 docs will be normalized. 7 | 8 | ### Common Attributes 9 | #### normalizer_type (Optional) 10 | Type of the normalizer. Accepts `z_score` or `min_max`, if nothing passed defaults to z_score. 11 | 12 | #### factor (Optional) 13 | A float value. If passed updates the normalized score using this factor and given operation 14 | (`factor_mode`) 15 | #### factor_mode (Optional) 16 | Tells how to combine normalized score and the `factor`. Accepted values are `sum`, `multiply` 17 | and `increase_by_percent`. `increase_by_percent` increases the score by given factor(values 18 | from 0 to 1) 19 | 20 | 21 | ### Min Max Normalizer 22 | 23 | #### Attributes 24 | `min_score` - minimum score value of the returned top docs. Default is 1. 25 | 26 | `max_score` - maximum score value of the returned top docs. Default is 5. 27 | 28 | `on_score_same` - Normalize strategy when all docs score are same. Accepted values are `avg` 29 | (average of max and min), `max`(maximum value), `min`(minimum value) 30 | 31 | Normalize scores between given `max_score` and `min_score`. If no `min_score` and `max_score` is 32 | passed, defaults to 1 and 5 respectively. 33 | 34 | Example: 35 | ```json 36 | 37 | { 38 | "query": { 39 | ... some query 40 | }, 41 | "from" : 0, 42 | "size" : 50, 43 | "rescore" : { 44 | "score_normalizer" : { 45 | "normalizer_type" : "min_max", 46 | "min_score" : 1, 47 | "max_score" : 10 48 | } 49 | } 50 | } 51 | ``` 52 | 53 | ### Z Score Normalizer 54 | Normalize scores using Z Score. 55 | 56 | Below example first normalizes the scores using z-score and then increase the score by 60 percent. 57 | Example: 58 | ```json 59 | { 60 | "query": { 61 | ... some query 62 | }, 63 | "from" : 0, 64 | "size" : 50, 65 | "rescore" : { 66 | "score_normalizer" : { 67 | "normalizer_type" : "z_score", 68 | "min_score" : 1, 69 | "factor" : 0.6, 70 | "factor_mode" : "increase_by_percent" 71 | } 72 | } 73 | } 74 | ``` 75 | ### Installation 76 | 0. Change the elasticsearch version in pom.xml with your Elasticsearch server version. You can safely change the version between 7.0 to 7.12 without any code changes. 77 | 1. Build using: `mvn clean install` 78 | 2. Install the zip file generated in folder: 79 | `/project/target/releases/elasticsearch-score-normalizer-rescorer-1.0-SNAPSHOT.zip` 80 | 81 | To install, go to ES bin folder and type command: 82 | ```shell 83 | ./elasticsearch-plugin install file: 84 | ``` 85 | 86 | ### License 87 | The MIT License (MIT) 88 | 89 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 90 | 91 | Permission is hereby granted, free of charge, to any person obtaining a copy 92 | of this software and associated documentation files (the "Software"), to deal 93 | in the Software without restriction, including without limitation the rights 94 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 95 | copies of the Software, and to permit persons to whom the Software is 96 | furnished to do so, subject to the following conditions: 97 | 98 | The above copyright notice and this permission notice shall be included in all 99 | copies or substantial portions of the Software. 100 | 101 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 102 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 103 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 104 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 105 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 106 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 107 | SOFTWARE. 108 | 109 | 110 | -------------------------------------------------------------------------------- /src/main/assemblies/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | plugin 4 | 5 | zip 6 | 7 | false 8 | 9 | 10 | target 11 | / 12 | 13 | *.jar 14 | 15 | 16 | 17 | 18 | 19 | ${project.basedir}/src/main/resources/plugin-descriptor.properties 20 | / 21 | true 22 | 23 | 24 | 25 | 26 | / 27 | false 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/helper/MinMaxNormalizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.helper; 25 | 26 | import org.apache.lucene.search.TopDocs; 27 | import org.bkatwal.elasticsearch.plugin.rescorer.MinMaxSameScoreStrategy; 28 | import org.bkatwal.elasticsearch.plugin.rescorer.NormalizerFactorMathOp; 29 | import org.bkatwal.elasticsearch.plugin.rescorer.ScoreNormalizerRescorer; 30 | 31 | public class MinMaxNormalizer implements Normalizer { 32 | 33 | @Override 34 | public TopDocs normalize( 35 | TopDocs topDocs, ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context) { 36 | 37 | if (context.getMinScore() >= context.getMaxScore()) { 38 | throw new IllegalArgumentException( 39 | "max_score can not be lesser than or equal to " + "min_score"); 40 | } 41 | if (topDocs.scoreDocs.length == 0) { 42 | return topDocs; 43 | } 44 | if (topDocs.scoreDocs.length == 1) { 45 | topDocs.scoreDocs[0].score = 46 | getFinalScore(context.getFactorMode(), context.getFactor(), context.getMaxScore()); 47 | return topDocs; 48 | } 49 | 50 | float oldMax = topDocs.scoreDocs[0].score; 51 | float oldMin = topDocs.scoreDocs[topDocs.scoreDocs.length - 1].score; 52 | 53 | if (Float.compare(oldMax, oldMin) == 0) { 54 | for (int i = 0; i < topDocs.scoreDocs.length; i++) { 55 | if (context.getOnScoresSame().equals(MinMaxSameScoreStrategy.avg.name())) { 56 | topDocs.scoreDocs[i].score = (context.getMaxScore() + context.getMinScore()) / 2; 57 | } else if (context.getOnScoresSame().equals(MinMaxSameScoreStrategy.min.name())) { 58 | topDocs.scoreDocs[i].score = context.getMinScore(); 59 | } else if (context.getOnScoresSame().equals(MinMaxSameScoreStrategy.max.name())) { 60 | topDocs.scoreDocs[i].score = context.getMaxScore(); 61 | } else { 62 | topDocs.scoreDocs[i].score = (context.getMaxScore() + context.getMinScore()) / 2; 63 | } 64 | } 65 | return topDocs; 66 | } 67 | 68 | for (int i = 0; i < topDocs.scoreDocs.length; i++) { 69 | float normalizedScore = 70 | calculate( 71 | topDocs.scoreDocs[i].score, 72 | oldMin, 73 | oldMax, 74 | context.getMaxScore(), 75 | context.getMinScore()); 76 | 77 | topDocs.scoreDocs[i].score = 78 | getFinalScore(context.getFactorMode(), context.getFactor(), normalizedScore); 79 | } 80 | if (topDocs.scoreDocs.length > 2) { 81 | topDocs.scoreDocs[0].score = 82 | topDocs.scoreDocs[0].score + (topDocs.scoreDocs[0].score - topDocs.scoreDocs[1].score); 83 | } 84 | return topDocs; 85 | } 86 | 87 | private static float calculate(float v, float oldMin, float oldMax, float newMax, float newMin) { 88 | return ((v - oldMin) / (oldMax - oldMin)) * (newMax - newMin) + newMin; 89 | } 90 | 91 | private static float getFinalScore(String factorMode, float factor, float normalizedValue) { 92 | 93 | if (factorMode.equals(NormalizerFactorMathOp.sum.name())) { 94 | normalizedValue = normalizedValue + factor; 95 | } else if (factorMode.equals(NormalizerFactorMathOp.multiply.name())) { 96 | normalizedValue = normalizedValue * factor; 97 | } else { 98 | if (normalizedValue == 0.0f) { 99 | normalizedValue = factor; 100 | } else { 101 | if (factor < 0 || factor > 1) { 102 | throw new IllegalArgumentException( 103 | "Invalid `factor` for `factor_mode` " 104 | + "increase_by_percent, " 105 | + "allowed " 106 | + "factor " 107 | + "range " 108 | + "0-1 " 109 | + "including 0 and 1."); 110 | } 111 | normalizedValue = normalizedValue + normalizedValue * factor; 112 | } 113 | } 114 | return normalizedValue; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/helper/Normalizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.helper; 25 | 26 | import org.apache.lucene.search.TopDocs; 27 | import org.bkatwal.elasticsearch.plugin.rescorer.ScoreNormalizerRescorer; 28 | 29 | public interface Normalizer { 30 | 31 | TopDocs normalize( 32 | TopDocs topDocs, ScoreNormalizerRescorer.ScoreNormalizerRescorerContext rescoreContext); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/helper/NormalizerServiceLocator.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.helper; 25 | 26 | import org.bkatwal.elasticsearch.plugin.rescorer.NormalizerType; 27 | 28 | public final class NormalizerServiceLocator { 29 | 30 | private NormalizerServiceLocator() {} 31 | 32 | private static final Normalizer minMaxNormalizer = new MinMaxNormalizer(); 33 | private static final Normalizer zScoreNormalizer = new ZScoreNormalizer(); 34 | 35 | public static Normalizer getInstance(NormalizerType normalizerType) { 36 | if (normalizerType == NormalizerType.min_max) { 37 | return minMaxNormalizer; 38 | } 39 | if (normalizerType == NormalizerType.z_score) { 40 | return zScoreNormalizer; 41 | } 42 | 43 | return zScoreNormalizer; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/helper/ZScoreNormalizer.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.helper; 25 | 26 | import org.apache.lucene.search.ScoreDoc; 27 | import org.apache.lucene.search.TopDocs; 28 | import org.bkatwal.elasticsearch.plugin.rescorer.NormalizerFactorMathOp; 29 | import org.bkatwal.elasticsearch.plugin.rescorer.ScoreNormalizerRescorer; 30 | 31 | public class ZScoreNormalizer implements Normalizer { 32 | @Override 33 | public TopDocs normalize( 34 | TopDocs topDocs, ScoreNormalizerRescorer.ScoreNormalizerRescorerContext rescoreContext) { 35 | 36 | if (topDocs.scoreDocs.length == 0) { 37 | return topDocs; 38 | } 39 | 40 | ScoreDoc[] scoreDocs = topDocs.scoreDocs; 41 | float mean = meanScore(scoreDocs); 42 | float sd = standardDeviation(scoreDocs, mean); 43 | 44 | if (sd == 0.0f) { 45 | sd = 1.0f; 46 | } 47 | for (ScoreDoc scoreDoc : scoreDocs) { 48 | float normalizedScore = zScore(scoreDoc.score, mean, sd); 49 | 50 | scoreDoc.score = 51 | getFinalScore( 52 | rescoreContext.getFactorMode(), rescoreContext.getFactor(), normalizedScore); 53 | } 54 | return topDocs; 55 | } 56 | 57 | private float meanScore(ScoreDoc[] scoreDocs) { 58 | 59 | float total = 0.0f; 60 | 61 | for (ScoreDoc scoreDoc : scoreDocs) { 62 | total = total + scoreDoc.score; 63 | } 64 | 65 | return total / scoreDocs.length; 66 | } 67 | 68 | private float standardDeviation(ScoreDoc[] scoreDocs, float mean) { 69 | 70 | float totalVariance = 0.0f; 71 | for (ScoreDoc scoreDoc : scoreDocs) { 72 | float delta = scoreDoc.score - mean; 73 | float deltaSq = delta * delta; 74 | totalVariance = totalVariance + deltaSq; 75 | } 76 | return (float) Math.sqrt(totalVariance / scoreDocs.length); 77 | } 78 | 79 | private float zScore(float val, float mean, float sd) { 80 | 81 | return (val - mean) / sd; 82 | } 83 | 84 | private static float getFinalScore(String factorMode, float factor, float normalizedValue) { 85 | 86 | if (factorMode.equals(NormalizerFactorMathOp.sum.name())) { 87 | normalizedValue = normalizedValue + factor; 88 | } else { 89 | float v = normalizedValue + Math.abs(normalizedValue) * factor; 90 | if (factorMode.equals(NormalizerFactorMathOp.multiply.name())) { 91 | normalizedValue = normalizedValue >= 0 ? normalizedValue * factor : v; 92 | } else { 93 | if (normalizedValue == 0.0f) { 94 | normalizedValue = factor; 95 | } else { 96 | if (factor < 0 || factor > 1) { 97 | throw new IllegalArgumentException( 98 | "Invalid `factor` for `factor_mode` " 99 | + "increase_by_percent, " 100 | + "allowed " 101 | + "factor " 102 | + "range " 103 | + "0-1 " 104 | + "including 0 and 1."); 105 | } 106 | normalizedValue = v; 107 | } 108 | } 109 | } 110 | return normalizedValue; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/rescorer/MinMaxSameScoreStrategy.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.rescorer; 25 | 26 | public enum MinMaxSameScoreStrategy { 27 | avg, 28 | max, 29 | min 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/rescorer/NormalizerFactorMathOp.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.rescorer; 25 | 26 | public enum NormalizerFactorMathOp { 27 | sum, 28 | multiply, 29 | increase_by_percent 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/rescorer/NormalizerRescorerBuilder.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.rescorer; 25 | 26 | import org.elasticsearch.common.ParseField; 27 | import org.elasticsearch.common.io.stream.StreamInput; 28 | import org.elasticsearch.common.io.stream.StreamOutput; 29 | import org.elasticsearch.common.xcontent.ObjectParser; 30 | import org.elasticsearch.common.xcontent.XContentBuilder; 31 | import org.elasticsearch.common.xcontent.XContentParser; 32 | import org.elasticsearch.index.query.QueryRewriteContext; 33 | import org.elasticsearch.index.query.QueryShardContext; 34 | import org.elasticsearch.search.rescore.RescoreContext; 35 | import org.elasticsearch.search.rescore.RescorerBuilder; 36 | 37 | import java.io.IOException; 38 | 39 | import static org.bkatwal.elasticsearch.plugin.rescorer.NormalizerType.isValid; 40 | 41 | public class NormalizerRescorerBuilder extends RescorerBuilder { 42 | public static final String NAME = "score_normalizer"; 43 | 44 | private static final ParseField NORMALIZER_TYPE = new ParseField("normalizer_type"); 45 | private static final ParseField MIN_SCORE = new ParseField("min_score"); 46 | private static final ParseField MAX_SCORE = new ParseField("max_score"); 47 | private static final ParseField FACTOR = new ParseField("factor"); 48 | private static final ParseField FACTOR_MODE = new ParseField("factor_mode"); 49 | private static final ParseField ON_SCORES_SAME = new ParseField("on_score_same"); 50 | private static final float DEFAULT_MIN_SCORE_V = 1.0f; 51 | private static final float DEFAULT_MAX_SCORE_V = 5.0f; 52 | private static final float DEFAULT_FACTOR = 0.0f; 53 | private static final String DEFAULT_ON_SCORES_SAME = MinMaxSameScoreStrategy.avg.name(); 54 | 55 | private static final NormalizerType DEFAULT_NORMALIZER_TYPE = NormalizerType.z_score; 56 | private static final String DEFAULT_FACTOR_MODE = 57 | NormalizerFactorMathOp.increase_by_percent.name(); 58 | 59 | private float minScore; 60 | private float maxScore; 61 | private String normalizerType; 62 | private float factor; 63 | private String factorMode; 64 | private String onScoresSame; 65 | 66 | private static final ObjectParser NORMALIZER_RESCORER_PARSER = 67 | new ObjectParser<>(NAME, null); 68 | 69 | static { 70 | NORMALIZER_RESCORER_PARSER.declareString( 71 | NormalizerRescorerBuilder.NRCoreBuilder::setNormalizerType, NORMALIZER_TYPE); 72 | NORMALIZER_RESCORER_PARSER.declareFloat( 73 | NormalizerRescorerBuilder.NRCoreBuilder::setMinScore, MIN_SCORE); 74 | NORMALIZER_RESCORER_PARSER.declareFloat( 75 | NormalizerRescorerBuilder.NRCoreBuilder::setMaxScore, MAX_SCORE); 76 | NORMALIZER_RESCORER_PARSER.declareFloat( 77 | NormalizerRescorerBuilder.NRCoreBuilder::setFactor, FACTOR); 78 | NORMALIZER_RESCORER_PARSER.declareString( 79 | NormalizerRescorerBuilder.NRCoreBuilder::setFactorMode, FACTOR_MODE); 80 | NORMALIZER_RESCORER_PARSER.declareString( 81 | NormalizerRescorerBuilder.NRCoreBuilder::setOnScoresSame, ON_SCORES_SAME); 82 | } 83 | 84 | public NormalizerRescorerBuilder() {} 85 | 86 | public NormalizerRescorerBuilder(StreamInput in) throws IOException { 87 | super(in); 88 | normalizerType = in.readOptionalString(); 89 | minScore = in.readOptionalFloat(); 90 | maxScore = in.readOptionalFloat(); 91 | factor = in.readOptionalFloat(); 92 | factorMode = in.readOptionalString(); 93 | onScoresSame = in.readOptionalString(); 94 | } 95 | 96 | @Override 97 | protected void doWriteTo(StreamOutput out) throws IOException { 98 | out.writeString(normalizerType); 99 | out.writeFloat(minScore); 100 | out.writeFloat(maxScore); 101 | out.writeFloat(factor); 102 | out.writeString(factorMode); 103 | out.writeString(onScoresSame); 104 | } 105 | 106 | @Override 107 | protected void doXContent(XContentBuilder builder, Params params) throws IOException { 108 | builder.startObject(NAME); 109 | builder.field(NORMALIZER_TYPE.getPreferredName(), normalizerType); 110 | builder.field(MIN_SCORE.getPreferredName(), minScore); 111 | builder.field(MAX_SCORE.getPreferredName(), maxScore); 112 | builder.field(FACTOR.getPreferredName(), factor); 113 | builder.field(FACTOR_MODE.getPreferredName(), factorMode); 114 | builder.field(ON_SCORES_SAME.getPreferredName(), onScoresSame); 115 | builder.endObject(); 116 | } 117 | 118 | @Override 119 | protected RescoreContext innerBuildContext(int windowSize, QueryShardContext context) 120 | throws IOException { 121 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext scoreNormalizerRescorerContext = 122 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 123 | windowSize, normalizerType, minScore, maxScore, factor, factorMode, onScoresSame); 124 | // query is rewritten at this point already 125 | scoreNormalizerRescorerContext.setNormalizerType(normalizerType); 126 | scoreNormalizerRescorerContext.setMinScore(minScore); 127 | scoreNormalizerRescorerContext.setMaxScore(maxScore); 128 | scoreNormalizerRescorerContext.setFactor(factor); 129 | scoreNormalizerRescorerContext.setFactorMode(factorMode); 130 | scoreNormalizerRescorerContext.setOnScoresSame(onScoresSame); 131 | return scoreNormalizerRescorerContext; 132 | } 133 | 134 | public static NormalizerRescorerBuilder fromXContent(XContentParser parser) throws IOException { 135 | NormalizerRescorerBuilder.NRCoreBuilder nrCoreBuilder = 136 | NORMALIZER_RESCORER_PARSER.parse( 137 | parser, new NormalizerRescorerBuilder.NRCoreBuilder(), null); 138 | return nrCoreBuilder.build(); 139 | } 140 | 141 | @Override 142 | public String getWriteableName() { 143 | return NAME; 144 | } 145 | 146 | @Override 147 | public RescorerBuilder rewrite(QueryRewriteContext ctx) 148 | throws IOException { 149 | return this; 150 | } 151 | 152 | public NormalizerRescorerBuilder setMinScore(float minScore) { 153 | this.minScore = minScore; 154 | return this; 155 | } 156 | 157 | public NormalizerRescorerBuilder setFactor(float factor) { 158 | this.factor = factor; 159 | return this; 160 | } 161 | 162 | public NormalizerRescorerBuilder setFactorMode(String factorMode) { 163 | this.factorMode = factorMode; 164 | return this; 165 | } 166 | 167 | public NormalizerRescorerBuilder setMaxScore(float maxScore) { 168 | this.maxScore = maxScore; 169 | return this; 170 | } 171 | 172 | public NormalizerRescorerBuilder setNormalizerType(String normalizerType) { 173 | this.normalizerType = normalizerType; 174 | return this; 175 | } 176 | 177 | public NormalizerRescorerBuilder setOnScoresSame(String onScoresSame) { 178 | this.onScoresSame = onScoresSame; 179 | return this; 180 | } 181 | 182 | private static class NRCoreBuilder { 183 | 184 | private float minScore = DEFAULT_MIN_SCORE_V; 185 | private float maxScore = DEFAULT_MAX_SCORE_V; 186 | private String normalizerType = DEFAULT_NORMALIZER_TYPE.name(); 187 | private float factor = DEFAULT_FACTOR; 188 | private String factorMode = DEFAULT_FACTOR_MODE; 189 | private String onScoresSame = DEFAULT_ON_SCORES_SAME; 190 | 191 | NormalizerRescorerBuilder build() { 192 | NormalizerRescorerBuilder normalizerRescorerBuilder = new NormalizerRescorerBuilder(); 193 | normalizerRescorerBuilder.setNormalizerType(normalizerType); 194 | normalizerRescorerBuilder.setMinScore(minScore); 195 | normalizerRescorerBuilder.setMaxScore(maxScore); 196 | normalizerRescorerBuilder.setFactor(factor); 197 | normalizerRescorerBuilder.setFactorMode(factorMode); 198 | normalizerRescorerBuilder.setOnScoresSame(onScoresSame); 199 | return normalizerRescorerBuilder; 200 | } 201 | 202 | public void setMinScore(float minScore) { 203 | this.minScore = minScore; 204 | } 205 | 206 | public void setMaxScore(float maxScore) { 207 | this.maxScore = maxScore; 208 | } 209 | 210 | public void setFactor(float factor) { 211 | this.factor = factor; 212 | } 213 | 214 | public void setFactorMode(String factorMode) { 215 | this.factorMode = factorMode; 216 | } 217 | 218 | public void setOnScoresSame(String onScoresSame) { 219 | this.onScoresSame = onScoresSame; 220 | } 221 | 222 | public void setNormalizerType(String normalizerType) { 223 | if (normalizerType == null) { 224 | return; 225 | } 226 | if (isValid(normalizerType)) { 227 | this.normalizerType = normalizerType; 228 | } 229 | } 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/rescorer/NormalizerType.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.rescorer; 25 | 26 | public enum NormalizerType { 27 | min_max, 28 | z_score; 29 | 30 | public static boolean isValid(String normalizerType) { 31 | try { 32 | NormalizerType.valueOf(normalizerType); 33 | } catch (IllegalArgumentException e) { 34 | return false; 35 | } 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/rescorer/ScoreNormalizerRescorer.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.rescorer; 25 | 26 | import org.apache.lucene.search.Explanation; 27 | import org.apache.lucene.search.IndexSearcher; 28 | import org.apache.lucene.search.TopDocs; 29 | import org.bkatwal.elasticsearch.plugin.helper.NormalizerServiceLocator; 30 | import org.elasticsearch.common.Nullable; 31 | import org.elasticsearch.search.rescore.RescoreContext; 32 | import org.elasticsearch.search.rescore.Rescorer; 33 | 34 | import static java.util.Collections.singletonList; 35 | 36 | public class ScoreNormalizerRescorer implements Rescorer { 37 | 38 | public static final Rescorer INSTANCE = new ScoreNormalizerRescorer(); 39 | 40 | /** 41 | * this function returns the top k normalized docs from each shard. 42 | * 43 | * @param topDocs top docs matched for given query 44 | * @param searcher Index Searcher 45 | * @param rescoreContext Context/params needed for rescore function. 46 | * @return return top k normalized docs from each shard 47 | */ 48 | @Override 49 | public TopDocs rescore(TopDocs topDocs, IndexSearcher searcher, RescoreContext rescoreContext) { 50 | 51 | assert rescoreContext != null; 52 | if (topDocs == null || topDocs.scoreDocs.length == 0) { 53 | return topDocs; 54 | } 55 | 56 | ScoreNormalizerRescorerContext context = (ScoreNormalizerRescorerContext) rescoreContext; 57 | String normalizerType = context.normalizerType; 58 | 59 | topDocs = 60 | NormalizerServiceLocator.getInstance(NormalizerType.valueOf(normalizerType)) 61 | .normalize(topDocs, context); 62 | return topDocs; 63 | } 64 | 65 | @Override 66 | public Explanation explain( 67 | int topLevelDocId, 68 | IndexSearcher searcher, 69 | RescoreContext rescoreContext, 70 | Explanation sourceExplanation) { 71 | 72 | ScoreNormalizerRescorerContext context = (ScoreNormalizerRescorerContext) rescoreContext; 73 | String factorMode = context.getFactorMode(); 74 | float factor = context.factor; 75 | String operation = factorMode + " using " + factor + " on:"; 76 | 77 | return Explanation.match( 78 | 0.0f, 79 | "Final score -> normalize using, " + context.getNormalizerType() + " and then " + operation, 80 | singletonList(sourceExplanation)); 81 | } 82 | 83 | public static class ScoreNormalizerRescorerContext extends RescoreContext { 84 | private String normalizerType; 85 | private float minScore; 86 | private float maxScore; 87 | private float factor; 88 | private String factorMode; 89 | private String onScoresSame; 90 | 91 | public ScoreNormalizerRescorerContext( 92 | int windowSize, 93 | @Nullable String normalizerType, 94 | @Nullable float minScore, 95 | @Nullable float maxScore, 96 | @Nullable float factor, 97 | @Nullable String factorMode, 98 | @Nullable String onScoresSame) { 99 | super(windowSize, INSTANCE); 100 | this.minScore = minScore; 101 | this.maxScore = maxScore; 102 | this.normalizerType = normalizerType; 103 | this.factorMode = factorMode; 104 | this.factor = factor; 105 | this.onScoresSame = onScoresSame; 106 | } 107 | 108 | public void setFactor(float factor) { 109 | this.factor = factor; 110 | } 111 | 112 | public void setFactorMode(String factorMode) { 113 | this.factorMode = factorMode; 114 | } 115 | 116 | public String getNormalizerType() { 117 | return normalizerType; 118 | } 119 | 120 | public void setNormalizerType(String normalizerType) { 121 | this.normalizerType = normalizerType; 122 | } 123 | 124 | public float getMinScore() { 125 | return minScore; 126 | } 127 | 128 | public float getFactor() { 129 | return factor; 130 | } 131 | 132 | public String getOnScoresSame() { 133 | return onScoresSame; 134 | } 135 | 136 | public void setOnScoresSame(String onScoresSame) { 137 | this.onScoresSame = onScoresSame; 138 | } 139 | 140 | public String getFactorMode() { 141 | return factorMode; 142 | } 143 | 144 | public void setMinScore(float minScore) { 145 | this.minScore = minScore; 146 | } 147 | 148 | public float getMaxScore() { 149 | return maxScore; 150 | } 151 | 152 | public void setMaxScore(float maxScore) { 153 | this.maxScore = maxScore; 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/org/bkatwal/elasticsearch/plugin/rescorer/ScoreNormalizerRescorerPlugin.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.rescorer; 25 | 26 | import org.elasticsearch.plugins.Plugin; 27 | import org.elasticsearch.plugins.SearchPlugin; 28 | 29 | import java.util.List; 30 | 31 | import static java.util.Collections.singletonList; 32 | 33 | public class ScoreNormalizerRescorerPlugin extends Plugin implements SearchPlugin { 34 | @Override 35 | public List> getRescorers() { 36 | return singletonList( 37 | new SearchPlugin.RescorerSpec<>( 38 | NormalizerRescorerBuilder.NAME, 39 | NormalizerRescorerBuilder::new, 40 | NormalizerRescorerBuilder::fromXContent)); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/resources/plugin-descriptor.properties: -------------------------------------------------------------------------------- 1 | description=${project.description} 2 | version=${project.version} 3 | name=${project.artifactId} 4 | classname=org.bkatwal.elasticsearch.plugin.rescorer.ScoreNormalizerRescorerPlugin 5 | java.version=1.8 6 | elasticsearch.version=${elasticsearch.version} 7 | -------------------------------------------------------------------------------- /src/test/java/org/bkatwal/elasticsearch/plugin/helper/MinMaxNormalizerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.helper; 25 | 26 | import org.apache.lucene.search.ScoreDoc; 27 | import org.apache.lucene.search.TopDocs; 28 | import org.apache.lucene.search.TotalHits; 29 | import org.bkatwal.elasticsearch.plugin.rescorer.MinMaxSameScoreStrategy; 30 | import org.bkatwal.elasticsearch.plugin.rescorer.ScoreNormalizerRescorer; 31 | import org.junit.Assert; 32 | import org.junit.Before; 33 | import org.junit.Test; 34 | 35 | public class MinMaxNormalizerTest { 36 | 37 | private Normalizer minMaxNormalizer; 38 | 39 | @Before 40 | public void init() { 41 | minMaxNormalizer = new MinMaxNormalizer(); 42 | } 43 | 44 | @Test 45 | public void assertMinMaxNormalizer() { 46 | TotalHits totalHits = new TotalHits(5, TotalHits.Relation.EQUAL_TO); 47 | ScoreDoc[] scoreDocs = new ScoreDoc[5]; 48 | scoreDocs[0] = new ScoreDoc(1, 10.5f); 49 | scoreDocs[1] = new ScoreDoc(2, 9); 50 | scoreDocs[2] = new ScoreDoc(3, 8); 51 | scoreDocs[3] = new ScoreDoc(4, 6.5f); 52 | scoreDocs[4] = new ScoreDoc(5, 2f); 53 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 54 | 55 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 56 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 57 | 3, "min_max", 1, 4, .6f, "increase_by_percent", null); 58 | 59 | topDocs = minMaxNormalizer.normalize(topDocs, context); 60 | Assert.assertEquals(7.2f, topDocs.scoreDocs[0].score, 0.1f); 61 | Assert.assertEquals(5.5, topDocs.scoreDocs[1].score, 0.1f); 62 | Assert.assertEquals(4.9, topDocs.scoreDocs[2].score, 0.1f); 63 | Assert.assertEquals(4.1f, topDocs.scoreDocs[3].score, 0.1f); 64 | Assert.assertEquals(1.6f, topDocs.scoreDocs[4].score, 0.1f); 65 | } 66 | 67 | @Test 68 | public void assertMinMaxNormalizerZeroDocs() { 69 | TotalHits totalHits = new TotalHits(0, TotalHits.Relation.EQUAL_TO); 70 | ScoreDoc[] scoreDocs = new ScoreDoc[0]; 71 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 72 | 73 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 74 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 75 | 3, "min_max", 1, 4, .6f, "increase_by_percent", null); 76 | 77 | topDocs = minMaxNormalizer.normalize(topDocs, context); 78 | Assert.assertNotNull(topDocs); 79 | } 80 | 81 | @Test 82 | public void assertMinMaxNormalizerOneDocs() { 83 | TotalHits totalHits = new TotalHits(1, TotalHits.Relation.EQUAL_TO); 84 | ScoreDoc[] scoreDocs = new ScoreDoc[1]; 85 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 86 | scoreDocs[0] = new ScoreDoc(1, 10.5f); 87 | 88 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 89 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 90 | 3, "min_max", 1, 4, .6f, "increase_by_percent", null); 91 | 92 | topDocs = minMaxNormalizer.normalize(topDocs, context); 93 | Assert.assertEquals(6.4f, topDocs.scoreDocs[0].score, 0.0f); 94 | } 95 | 96 | @Test 97 | public void assertSameScoreDocs() { 98 | TotalHits totalHits = new TotalHits(5, TotalHits.Relation.EQUAL_TO); 99 | ScoreDoc[] scoreDocs = new ScoreDoc[5]; 100 | scoreDocs[0] = new ScoreDoc(1, 4f); 101 | scoreDocs[1] = new ScoreDoc(2, 4f); 102 | scoreDocs[2] = new ScoreDoc(3, 4f); 103 | scoreDocs[3] = new ScoreDoc(4, 4f); 104 | scoreDocs[4] = new ScoreDoc(5, 4f); 105 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 106 | 107 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 108 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 109 | 3, "min_max", 1, 5, .6f, "increase_by_percent", MinMaxSameScoreStrategy.avg.name()); 110 | 111 | topDocs = minMaxNormalizer.normalize(topDocs, context); 112 | Assert.assertEquals(3f, topDocs.scoreDocs[0].score, 0.0f); 113 | Assert.assertEquals(3f, topDocs.scoreDocs[1].score, 0.0f); 114 | Assert.assertEquals(3f, topDocs.scoreDocs[2].score, 0.0f); 115 | Assert.assertEquals(3f, topDocs.scoreDocs[3].score, 0.0f); 116 | Assert.assertEquals(3f, topDocs.scoreDocs[4].score, 0.0f); 117 | } 118 | 119 | @Test 120 | public void assertValueSameAsNewMin() { 121 | TotalHits totalHits = new TotalHits(2, TotalHits.Relation.EQUAL_TO); 122 | ScoreDoc[] scoreDocs = new ScoreDoc[2]; 123 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 124 | scoreDocs[0] = new ScoreDoc(1, 10); 125 | scoreDocs[1] = new ScoreDoc(2, 5); 126 | 127 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 128 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 129 | 2, "min_max", 5, 20, 0.0f, "increase_by_percent", null); 130 | 131 | topDocs = minMaxNormalizer.normalize(topDocs, context); 132 | Assert.assertEquals(20f, topDocs.scoreDocs[0].score, 0.0f); 133 | Assert.assertEquals(5f, topDocs.scoreDocs[1].score, 0.0f); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/test/java/org/bkatwal/elasticsearch/plugin/helper/ZScoreNormalizerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | The MIT License (MIT) 3 | 4 | Copyright (c) Bikas Katwal - bikas.katwal10@gmail.com 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | package org.bkatwal.elasticsearch.plugin.helper; 25 | 26 | import org.apache.lucene.search.ScoreDoc; 27 | import org.apache.lucene.search.TopDocs; 28 | import org.apache.lucene.search.TotalHits; 29 | import org.bkatwal.elasticsearch.plugin.rescorer.ScoreNormalizerRescorer; 30 | import org.junit.Assert; 31 | import org.junit.Before; 32 | import org.junit.Test; 33 | 34 | public class ZScoreNormalizerTest { 35 | private Normalizer zScoreNormalizer; 36 | 37 | @Before 38 | public void init() { 39 | zScoreNormalizer = new ZScoreNormalizer(); 40 | } 41 | 42 | @Test 43 | public void assertNormalization() { 44 | TotalHits totalHits = new TotalHits(5, TotalHits.Relation.EQUAL_TO); 45 | ScoreDoc[] scoreDocs = new ScoreDoc[5]; 46 | scoreDocs[0] = new ScoreDoc(1, 10f); 47 | scoreDocs[1] = new ScoreDoc(2, 6f); 48 | scoreDocs[2] = new ScoreDoc(3, 4f); 49 | scoreDocs[3] = new ScoreDoc(4, 3.5f); 50 | scoreDocs[4] = new ScoreDoc(5, 3f); 51 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 52 | 53 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 54 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 55 | 5, "z_score", 1, 4, 0.0f, "increase_by_percent", null); 56 | 57 | topDocs = zScoreNormalizer.normalize(topDocs, context); 58 | Assert.assertEquals(1.8f, topDocs.scoreDocs[0].score, 0.1f); 59 | Assert.assertEquals(0.27f, topDocs.scoreDocs[1].score, 0.1f); 60 | Assert.assertEquals(-0.5f, topDocs.scoreDocs[2].score, 0.1f); 61 | Assert.assertEquals(-0.7f, topDocs.scoreDocs[3].score, 0.1f); 62 | Assert.assertEquals(-0.89, topDocs.scoreDocs[4].score, 0.1f); 63 | } 64 | 65 | @Test 66 | public void assertNormalizeSameScore() { 67 | TotalHits totalHits = new TotalHits(5, TotalHits.Relation.EQUAL_TO); 68 | ScoreDoc[] scoreDocs = new ScoreDoc[3]; 69 | scoreDocs[0] = new ScoreDoc(1, 10f); 70 | scoreDocs[1] = new ScoreDoc(2, 10f); 71 | scoreDocs[2] = new ScoreDoc(3, 10f); 72 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 73 | 74 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 75 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 76 | 3, "z_score", 1, 4, 0.0f, "increase_by_percent", null); 77 | 78 | topDocs = zScoreNormalizer.normalize(topDocs, context); 79 | Assert.assertEquals(0.0f, topDocs.scoreDocs[0].score, 0.1f); 80 | Assert.assertEquals(0.0f, topDocs.scoreDocs[1].score, 0.1f); 81 | Assert.assertEquals(0.0f, topDocs.scoreDocs[2].score, 0.1f); 82 | } 83 | 84 | @Test 85 | public void assertFactorWithNormalization() { 86 | TotalHits totalHits = new TotalHits(3, TotalHits.Relation.EQUAL_TO); 87 | ScoreDoc[] scoreDocs = new ScoreDoc[5]; 88 | scoreDocs[0] = new ScoreDoc(1, 10f); 89 | scoreDocs[1] = new ScoreDoc(2, 6f); 90 | scoreDocs[2] = new ScoreDoc(3, 4f); 91 | scoreDocs[3] = new ScoreDoc(4, 3.5f); 92 | scoreDocs[4] = new ScoreDoc(5, 3f); 93 | TopDocs topDocs = new TopDocs(totalHits, scoreDocs); 94 | 95 | ScoreNormalizerRescorer.ScoreNormalizerRescorerContext context = 96 | new ScoreNormalizerRescorer.ScoreNormalizerRescorerContext( 97 | 5, "z_score", 1, 4, 0.5f, "increase_by_percent", null); 98 | 99 | topDocs = zScoreNormalizer.normalize(topDocs, context); 100 | Assert.assertEquals(2.7f, topDocs.scoreDocs[0].score, 0.1f); 101 | Assert.assertEquals(0.405f, topDocs.scoreDocs[1].score, 0.1f); 102 | Assert.assertEquals(-0.25f, topDocs.scoreDocs[2].score, 0.1f); 103 | } 104 | } 105 | --------------------------------------------------------------------------------