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.
17
package org.apache.solr.handler.component;
19
import org.apache.lucene.search.FieldComparatorSource;
20
import org.apache.lucene.search.FieldDoc;
21
import org.apache.lucene.search.SortField;
22
import org.apache.lucene.util.PriorityQueue;
23
import org.apache.solr.common.util.NamedList;
24
import org.apache.solr.search.MissingStringLastComparatorSource;
26
import java.text.Collator;
27
import java.util.ArrayList;
28
import java.util.Comparator;
29
import java.util.List;
30
import java.util.Locale;
32
public class ShardDoc extends FieldDoc {
34
public String shardAddress; // TODO
37
// the position of this doc within the shard... this can be used
38
// to short-circuit comparisons if the shard is equal, and can
39
// also be used to break ties within the same shard.
42
// this is currently the uniqueKeyField but
43
// may be replaced with internal docid in a future release.
47
NamedList sortFieldValues;
48
// sort field values for *all* docs in a particular shard.
49
// this doc's values are in position orderInShard
51
// TODO: store the SolrDocument here?
52
// Store the order in the merged list for lookup when getting stored fields?
53
// (other components need this ordering to store data in order, like highlighting)
54
// but we shouldn't expose uniqueKey (have a map by it) until the stored-field
57
public int positionInResponse;
58
// the ordinal position in the merged response arraylist
60
public ShardDoc(float score, Object[] fields, Object uniqueId, String shard) {
61
super(-1, score, fields);
71
public boolean equals(Object o) {
72
if (this == o) return true;
73
if (o == null || getClass() != o.getClass()) return false;
75
ShardDoc shardDoc = (ShardDoc) o;
77
if (id != null ? !id.equals(shardDoc.id) : shardDoc.id != null) return false;
83
public int hashCode() {
84
return id != null ? id.hashCode() : 0;
88
public String toString(){
92
+" ,orderInShard="+orderInShard
93
+" ,positionInResponse="+positionInResponse
94
+" ,sortFieldValues="+sortFieldValues;
100
// used by distributed search to merge results.
101
class ShardFieldSortedHitQueue extends PriorityQueue<ShardDoc> {
103
/** Stores a comparator corresponding to each field being sorted by */
104
protected Comparator[] comparators;
106
/** Stores the sort criteria being used. */
107
protected SortField[] fields;
109
/** The order of these fieldNames should correspond to the order of sort field values retrieved from the shard */
110
protected List<String> fieldNames = new ArrayList<String>();
112
public ShardFieldSortedHitQueue(SortField[] fields, int size) {
113
final int n = fields.length;
114
comparators = new Comparator[n];
115
this.fields = new SortField[n];
116
for (int i = 0; i < n; ++i) {
118
// keep track of the named fields
119
int type = fields[i].getType();
120
if (type!=SortField.SCORE && type!=SortField.DOC) {
121
fieldNames.add(fields[i].getField());
124
String fieldname = fields[i].getField();
125
comparators[i] = getCachedComparator(fieldname, fields[i]
126
.getType(), fields[i].getLocale(), fields[i].getComparatorSource());
128
if (fields[i].getType() == SortField.STRING) {
129
this.fields[i] = new SortField(fieldname, fields[i].getLocale(),
130
fields[i].getReverse());
132
this.fields[i] = new SortField(fieldname, fields[i].getType(),
133
fields[i].getReverse());
136
//System.out.println("%%%%%%%%%%%%%%%%%% got "+fields[i].getType() +" for "+ fieldname +" fields[i].getReverse(): "+fields[i].getReverse());
143
protected boolean lessThan(ShardDoc docA, ShardDoc docB) {
144
// If these docs are from the same shard, then the relative order
145
// is how they appeared in the response from that shard.
146
if (docA.shard == docB.shard) {
147
// if docA has a smaller position, it should be "larger" so it
148
// comes before docB.
149
// This will handle sorting by docid within the same shard
151
// comment this out to test comparators.
152
return !(docA.orderInShard < docB.orderInShard);
157
final int n = comparators.length;
159
for (int i = 0; i < n && c == 0; i++) {
160
c = (fields[i].getReverse()) ? comparators[i].compare(docB, docA)
161
: comparators[i].compare(docA, docB);
164
// solve tiebreaks by comparing shards (similar to using docid)
165
// smaller docid's beat larger ids, so reverse the natural ordering
167
c = -docA.shard.compareTo(docB.shard);
173
Comparator getCachedComparator(String fieldname, int type, Locale locale, FieldComparatorSource factory) {
174
Comparator comparator = null;
176
case SortField.SCORE:
177
comparator = comparatorScore(fieldname);
179
case SortField.STRING:
181
comparator = comparatorStringLocale(fieldname, locale);
183
comparator = comparatorNatural(fieldname);
185
case SortField.CUSTOM:
186
if (factory instanceof MissingStringLastComparatorSource){
187
comparator = comparatorMissingStringLast(fieldname);
189
// TODO: support other types such as random... is there a way to
190
// support generically? Perhaps just comparing Object
191
comparator = comparatorNatural(fieldname);
192
// throw new RuntimeException("Custom sort not supported factory is "+factory.getClass());
196
// TODO: we can support this!
197
throw new RuntimeException("Doc sort not supported");
199
comparator = comparatorNatural(fieldname);
205
class ShardComparator implements Comparator {
208
public ShardComparator(String fieldName) {
209
this.fieldName = fieldName;
211
for (int i=0; i<fieldNames.size(); i++) {
212
if (fieldNames.get(i).equals(fieldName)) {
219
Object sortVal(ShardDoc shardDoc) {
220
assert(shardDoc.sortFieldValues.getName(fieldNum).equals(fieldName));
221
List lst = (List)shardDoc.sortFieldValues.getVal(fieldNum);
222
return lst.get(shardDoc.orderInShard);
225
public int compare(Object o1, Object o2) {
230
static Comparator comparatorScore(final String fieldName) {
231
return new Comparator() {
232
public final int compare(final Object o1, final Object o2) {
233
ShardDoc e1 = (ShardDoc) o1;
234
ShardDoc e2 = (ShardDoc) o2;
236
final float f1 = e1.score;
237
final float f2 = e2.score;
247
// The lucene natural sort ordering corresponds to numeric
248
// and string natural sort orderings (ascending). Since
249
// the PriorityQueue keeps the biggest elements by default,
250
// we need to reverse the natural compare ordering so that the
251
// smallest elements are kept instead of the largest... hence
252
// the negative sign on the final compareTo().
253
Comparator comparatorNatural(String fieldName) {
254
return new ShardComparator(fieldName) {
256
public final int compare(final Object o1, final Object o2) {
257
ShardDoc sd1 = (ShardDoc) o1;
258
ShardDoc sd2 = (ShardDoc) o2;
259
Comparable v1 = (Comparable)sortVal(sd1);
260
Comparable v2 = (Comparable)sortVal(sd2);
267
return -v1.compareTo(v2);
273
Comparator comparatorStringLocale(final String fieldName,
275
final Collator collator = Collator.getInstance(locale);
276
return new ShardComparator(fieldName) {
278
public final int compare(final Object o1, final Object o2) {
279
ShardDoc sd1 = (ShardDoc) o1;
280
ShardDoc sd2 = (ShardDoc) o2;
281
Comparable v1 = (Comparable)sortVal(sd1);
282
Comparable v2 = (Comparable)sortVal(sd2);
289
return -collator.compare(v1,v2);
295
Comparator comparatorMissingStringLast(final String fieldName) {
296
return new ShardComparator(fieldName) {
298
public final int compare(final Object o1, final Object o2) {
299
ShardDoc sd1 = (ShardDoc) o1;
300
ShardDoc sd2 = (ShardDoc) o2;
301
Comparable v1 = (Comparable)sortVal(sd1);
302
Comparable v2 = (Comparable)sortVal(sd2);
309
return -v1.compareTo(v2);