Skip to content

Commit 13d7da3

Browse files
committed
Use case-insensitive collation as default for Mongo pagination
Adds a default case-insensitive collation to DefaultMongoPaginationHelper so callers get case-insensitive, numeric-aware sorting without per-query workarounds. PaginatedStreamService drops its manual $toLower field transformation in favor of the shared collation.
1 parent b9e72de commit 13d7da3

2 files changed

Lines changed: 12 additions & 15 deletions

File tree

graylog2-server/src/main/java/org/graylog2/database/pagination/DefaultMongoPaginationHelper.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.mongodb.client.MongoIterable;
2323
import com.mongodb.client.model.Aggregates;
2424
import com.mongodb.client.model.Collation;
25+
import com.mongodb.client.model.CollationStrength;
2526
import org.bson.conversions.Bson;
2627
import org.graylog2.database.MongoCollection;
2728
import org.graylog2.database.MongoEntity;
@@ -32,8 +33,10 @@
3233
import org.slf4j.LoggerFactory;
3334

3435
import java.util.List;
36+
import java.util.Locale;
3537
import java.util.function.Predicate;
3638

39+
import static com.google.common.base.MoreObjects.firstNonNull;
3740
import static com.google.common.base.Preconditions.checkArgument;
3841
import static org.graylog2.database.utils.MongoUtils.stream;
3942

@@ -46,6 +49,11 @@
4649
*/
4750
public class DefaultMongoPaginationHelper<T extends MongoEntity> implements MongoPaginationHelper<T> {
4851
private static final Logger LOG = LoggerFactory.getLogger(DefaultMongoPaginationHelper.class);
52+
public static final Collation DEFAULT_COLLATION_WITH_CASE_INSENSITIVE_SORTING = Collation.builder()
53+
.locale(Locale.ENGLISH.toLanguageTag())
54+
.collationStrength(CollationStrength.SECONDARY)
55+
.numericOrdering(true)
56+
.build();
4957

5058
private final MongoCollection<T> collection;
5159
private final Bson filter;
@@ -81,7 +89,7 @@ private DefaultMongoPaginationHelper(MongoCollection<T> collection,
8189
this.perPage = perPage;
8290
this.includeGrandTotal = includeGrandTotal;
8391
this.grandTotalFilter = grandTotalFilter;
84-
this.collation = collation;
92+
this.collation = firstNonNull(collation, DEFAULT_COLLATION_WITH_CASE_INSENSITIVE_SORTING);
8593
this.pipeline = pipeline;
8694
this.postSortPipeline = postSortPipeline;
8795
this.includeSourceMetadata = includeSourceMetadata;

graylog2-server/src/main/java/org/graylog2/streams/PaginatedStreamService.java

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.graylog2.database.MongoCollections;
2828
import org.graylog2.database.PaginatedList;
2929
import org.graylog2.database.entities.EntityScopeService;
30+
import org.graylog2.database.pagination.DefaultMongoPaginationHelper;
3031
import org.graylog2.database.utils.MongoUtils;
3132
import org.graylog2.indexer.indexset.MongoIndexSetService;
3233
import org.graylog2.rest.models.SortOrder;
@@ -35,9 +36,7 @@
3536
import java.util.function.Predicate;
3637

3738
public class PaginatedStreamService {
38-
3939
private static final String COLLECTION_NAME = "streams";
40-
private static final List<String> STRING_FIELDS = List.of("title", "description", "index_set_title");
4140
private final MongoCollection<StreamDTO> collection;
4241
private final EntityScopeService scopeService;
4342

@@ -73,20 +72,14 @@ public PaginatedList<StreamImpl> findPaginated(Bson dbQuery, //query executed on
7372
.add(Aggregates.unset("index_set"));
7473
}
7574

76-
if (isStringField(sortField)) {
77-
pipelineBuilder.add(Aggregates.set(new Field<>("lower" + sortField, doc("$toLower", "$" + sortField))))
78-
.add(Aggregates.sort(order.toBsonSort("lower" + sortField)))
79-
.add(Aggregates.unset("lower" + sortField));
80-
} else {
81-
pipelineBuilder.add(Aggregates.sort(order.toBsonSort(sortField)));
82-
}
75+
pipelineBuilder.add(Aggregates.sort(order.toBsonSort(sortField)));
8376

8477
if (sortField.equals("index_set_title")) {
8578
pipelineBuilder.add(Aggregates.unset("index_set_title"));
8679
}
8780

8881
final List<StreamImpl> streamsList;
89-
try (final var results = MongoUtils.stream(collection.aggregate(pipelineBuilder.build()))) {
82+
try (final var results = MongoUtils.stream(collection.aggregate(pipelineBuilder.build()).collation(DefaultMongoPaginationHelper.DEFAULT_COLLATION_WITH_CASE_INSENSITIVE_SORTING))) {
9083
streamsList = results
9184
// Since we are bypassing the StreamService which properly sets the isEditable field when loading
9285
// streams we need to set the field here.
@@ -111,10 +104,6 @@ public PaginatedList<StreamImpl> findPaginated(Bson dbQuery, //query executed on
111104
return new PaginatedList<>(paginatedStreams, streamsList.size(), page, perPage, grandTotal);
112105
}
113106

114-
private boolean isStringField(String sortField) {
115-
return STRING_FIELDS.contains(sortField);
116-
}
117-
118107
private Document doc(String key, Object value) {
119108
return new Document(key, value);
120109
}

0 commit comments

Comments
 (0)