2
* Licensed to the Apache Software Foundation (ASF) under one or more
3
* contributor license agreements. See the NOTICE file distributed with
4
* this work for additional information regarding copyright ownership.
5
* The ASF licenses this file to You under the Apache License, Version 2.0
6
* (the "License"); you may not use this file except in compliance with
7
* the License. You may obtain a copy of the License at
9
* http://www.apache.org/licenses/LICENSE-2.0
11
* Unless required by applicable law or agreed to in writing, software
12
* distributed under the License is distributed on an "AS IS" BASIS,
13
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
* See the License for the specific language governing permissions and
15
* limitations under the License.
18
package org.apache.solr.schema;
20
import java.io.IOException;
23
import org.apache.lucene.document.Fieldable;
24
import org.apache.lucene.index.IndexReader;
25
import org.apache.lucene.search.*;
26
import org.apache.solr.response.TextResponseWriter;
27
import org.apache.solr.response.XMLWriter;
28
import org.apache.solr.search.QParser;
29
import org.apache.solr.search.function.DocValues;
30
import org.apache.solr.search.function.ValueSource;
31
import org.apache.solr.search.SolrIndexReader;
34
* Utility Field used for random sorting. It should not be passed a value.
36
* This random sorting implementation uses the dynamic field name to set the
37
* random 'seed'. To get random sorting order, you need to use a random
38
* dynamic field name. For example, you will need to configure schema.xml:
42
* <fieldType name="random" class="solr.RandomSortField" />
47
* <dynamicField name="random*" type="random" indexed="true" stored="false"/>
52
* Examples of queries:
54
* <li>http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_1234%20desc</li>
55
* <li>http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_2345%20desc</li>
56
* <li>http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_ABDC%20desc</li>
57
* <li>http://localhost:8983/solr/select/?q=*:*&fl=name&sort=rand_21%20desc</li>
59
* Note that multiple calls to the same URL will return the same sorting order.
61
* @version $Id: RandomSortField.java 1136465 2011-06-16 14:50:22Z mikemccand $
64
public class RandomSortField extends FieldType {
65
// Thomas Wang's hash32shift function, from http://www.cris.com/~Ttwang/tech/inthash.htm
66
// slightly modified to return only positive integers.
67
private static int hash(int key) {
68
key = ~key + (key << 15); // key = (key << 15) - key - 1;
69
key = key ^ (key >>> 12);
70
key = key + (key << 2);
71
key = key ^ (key >>> 4);
72
key = key * 2057; // key = (key + (key << 3)) + (key << 11);
73
key = key ^ (key >>> 16);
78
* Given a field name and an IndexReader, get a random hash seed.
79
* Using dynamic fields, you can force the random order to change
81
private static int getSeed(String fieldName, IndexReader r) {
82
SolrIndexReader top = (SolrIndexReader)r;
84
while (top.getParent() != null) {
85
base += top.getBase();
86
top = top.getParent();
89
// calling getVersion() on a segment will currently give you a null pointer exception, so
90
// we use the top-level reader.
91
return fieldName.hashCode() + base + (int)top.getVersion();
95
public SortField getSortField(SchemaField field, boolean reverse) {
96
return new SortField(field.getName(), randomComparatorSource, reverse);
100
public ValueSource getValueSource(SchemaField field, QParser parser) {
101
return new RandomValueSource(field.getName());
105
public void write(XMLWriter xmlWriter, String name, Fieldable f) throws IOException { }
108
public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException { }
111
private static FieldComparatorSource randomComparatorSource = new FieldComparatorSource() {
113
public FieldComparator<Integer> newComparator(final String fieldname, final int numHits, int sortPos, boolean reversed) throws IOException {
114
return new FieldComparator<Integer>() {
116
private final int[] values = new int[numHits];
120
public int compare(int slot1, int slot2) {
121
return values[slot1] - values[slot2]; // values will be positive... no overflow possible.
125
public void setBottom(int slot) {
126
bottomVal = values[slot];
130
public int compareBottom(int doc) throws IOException {
131
return bottomVal - hash(doc+seed);
135
public void copy(int slot, int doc) throws IOException {
136
values[slot] = hash(doc+seed);
140
public void setNextReader(IndexReader reader, int docBase) throws IOException {
141
seed = getSeed(fieldname, reader);
145
public Integer value(int slot) {
154
public class RandomValueSource extends ValueSource {
155
private final String field;
157
public RandomValueSource(String field) {
162
public String description() {
167
public DocValues getValues(Map context, final IndexReader reader) throws IOException {
168
return new DocValues() {
169
private final int seed = getSeed(field, reader);
171
public float floatVal(int doc) {
172
return (float)hash(doc+seed);
176
public int intVal(int doc) {
177
return hash(doc+seed);
181
public long longVal(int doc) {
182
return (long)hash(doc+seed);
186
public double doubleVal(int doc) {
187
return (double)hash(doc+seed);
191
public String strVal(int doc) {
192
return Integer.toString(hash(doc+seed));
196
public String toString(int doc) {
197
return description() + '=' + intVal(doc);
203
public boolean equals(Object o) {
204
if (!(o instanceof RandomValueSource)) return false;
205
RandomValueSource other = (RandomValueSource)o;
206
return this.field.equals(other.field);
210
public int hashCode() {
211
return field.hashCode();