diff --git a/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java b/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java index f3c80e45abff7..332f9d67a1167 100644 --- a/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java +++ b/src/main/java/org/elasticsearch/common/lucene/search/function/RandomScoreFunction.java @@ -64,9 +64,10 @@ public Explanation explainFactor(int docId) { return exp; } + /** - * Algorithm based on {@link java.util.Random} except this one is not - * thread safe + * Algorithm largely based on {@link java.util.Random} except this one is not + * thread safe and it incorporates the doc id on next(); */ static class PRNG { @@ -84,15 +85,20 @@ static class PRNG { public float random(int doc) { if (doc == 0) { - doc = -17; + doc = 0xCAFEBAB; } - return nextFloat() * (doc ^ 0xCAFEBAB); + + long rand = doc; + rand |= rand << 32; + rand ^= rand; + return nextFloat(rand); } - public float nextFloat() { + public float nextFloat(long rand) { seed = (seed * multiplier + addend) & mask; - int r = (int)(seed >>> 24); - return r / ((float)(1 << 24)); + rand ^= seed; + double result = rand / (double)(1L << 54); + return (float) result; } } diff --git a/src/main/java/org/elasticsearch/index/query/functionscore/random/RandomScoreFunctionParser.java b/src/main/java/org/elasticsearch/index/query/functionscore/random/RandomScoreFunctionParser.java index 77bf97c4f4132..4c6ac7df4ccf6 100644 --- a/src/main/java/org/elasticsearch/index/query/functionscore/random/RandomScoreFunctionParser.java +++ b/src/main/java/org/elasticsearch/index/query/functionscore/random/RandomScoreFunctionParser.java @@ -24,15 +24,14 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.lucene.search.function.RandomScoreFunction; import org.elasticsearch.common.lucene.search.function.ScoreFunction; -import org.elasticsearch.common.lucene.search.function.ScriptScoreFunction; import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryParsingException; import org.elasticsearch.index.query.functionscore.ScoreFunctionParser; -import org.elasticsearch.script.SearchScript; +import org.elasticsearch.index.shard.ShardId; +import org.elasticsearch.search.internal.SearchContext; import java.io.IOException; -import java.util.Map; /** * @@ -73,6 +72,17 @@ public ScoreFunction parse(QueryParseContext parseContext, XContentParser parser seed = parseContext.nowInMillis(); } + ShardId shardId = SearchContext.current().indexShard().shardId(); + seed = salt(seed, shardId.index().name(), shardId.id()); + return new RandomScoreFunction(seed); } + + public static long salt(long seed, String index, int shardId) { + long salt = index.hashCode(); + salt = salt << 32; + salt |= shardId; + return salt^seed; + } + } \ No newline at end of file diff --git a/src/test/java/org/elasticsearch/test/integration/search/functionscore/RandomScoreFunctionTests.java b/src/test/java/org/elasticsearch/test/integration/search/functionscore/RandomScoreFunctionTests.java index a61ae448b6597..5e628215c3ade 100644 --- a/src/test/java/org/elasticsearch/test/integration/search/functionscore/RandomScoreFunctionTests.java +++ b/src/test/java/org/elasticsearch/test/integration/search/functionscore/RandomScoreFunctionTests.java @@ -107,15 +107,19 @@ public void distribution() throws Exception { int filled = 0; int maxRepeat = 0; + int sumRepeat = 0; for (int i = 0; i < matrix.length; i++) { int value = matrix[i]; + sumRepeat += value; maxRepeat = Math.max(maxRepeat, value); if (value > 0) { filled++; } } + System.out.println(); System.out.println("max repeat: " + maxRepeat); + System.out.println("avg repeat: " + sumRepeat / (double)filled); System.out.println("distribution: " + filled/(double)count); int percentile50 = filled / 2;