MongoDB Java驱动全局添加country字段过滤方案咨询
Hey there! Let's figure out how to automatically add a country: "GB" filter to all queries on your MongoDB collection using the plain Java driver—no Spring Data interceptors needed. I've got two solid approaches for you:
方法1:封装自定义MongoCollection实现
This approach wraps the native MongoCollection in a custom class, injecting the country filter automatically into all query-related methods. Your business code just uses this wrapped collection, and the filter gets added without any extra work on your part.
Here's a sample implementation:
import com.mongodb.client.MongoCollection; import com.mongodb.client.model.Filters; import org.bson.Document; import java.util.Objects; public class CountryFilteredCollection implements MongoCollection<Document> { private final MongoCollection<Document> delegate; private final String countryFilterValue; public CountryFilteredCollection(MongoCollection<Document> delegate, String countryFilterValue) { this.delegate = Objects.requireNonNull(delegate); this.countryFilterValue = Objects.requireNonNull(countryFilterValue); } // Override find() to add the country filter by default @Override public FindIterable<Document> find() { return delegate.find(Filters.eq("country", countryFilterValue)); } // Combine the original filter with the country filter @Override public FindIterable<Document> find(Document filter) { Document combinedFilter = new Document(); if (filter != null && !filter.isEmpty()) { combinedFilter.putAll(filter); } combinedFilter.put("country", countryFilterValue); return delegate.find(combinedFilter); } // Handle count queries similarly @Override public long countDocuments() { return delegate.countDocuments(Filters.eq("country", countryFilterValue)); } @Override public long countDocuments(Document filter) { Document combinedFilter = new Document(); if (filter != null && !filter.isEmpty()) { combinedFilter.putAll(filter); } combinedFilter.put("country", countryFilterValue); return delegate.countDocuments(combinedFilter); } // Pass through non-query methods directly to the original collection @Override public void insertOne(Document document) { delegate.insertOne(document); } // You'll want to override other query methods (like findOne, aggregate) the same way // For non-query methods (update, delete), just delegate to the original collection }
How to use it:
// Get your original collection from the database MongoCollection<Document> originalCollection = mongoDatabase.getCollection("your-collection-name"); // Create the filtered wrapper MongoCollection<Document> filteredCollection = new CountryFilteredCollection(originalCollection, "GB"); // Now all queries automatically include the country filter! filteredCollection.find().forEach(doc -> System.out.println(doc)); // Even when you add your own filters, they're combined with the country condition filteredCollection.find(Filters.eq("status", "active")).forEach(doc -> System.out.println(doc));
方法2:Use MongoDB Java Driver's CommandListener
The MongoDB Java Driver has a CommandListener interface that lets you intercept all commands sent to the server. You can modify query commands before they're sent to add the country filter globally—no changes needed to your existing business code.
Here's the listener implementation:
import com.mongodb.event.CommandListener; import com.mongodb.event.CommandStartedEvent; import org.bson.Document; public class CountryFilterCommandListener implements CommandListener { private final String countryValue; public CountryFilterCommandListener(String countryValue) { this.countryValue = countryValue; } @Override public void commandStarted(CommandStartedEvent event) { Document command = event.getCommand(); // Handle find commands if (command.containsKey("find")) { Document filter = command.get("filter", new Document()); filter.put("country", countryValue); command.put("filter", filter); } // Handle count commands else if (command.containsKey("count")) { Document query = command.get("query", new Document()); query.put("country", countryValue); command.put("query", query); } // Handle aggregate commands by adding a $match stage at the start else if (command.containsKey("aggregate")) { Document[] pipeline = command.get("pipeline", new Document[0]); Document[] newPipeline = new Document[pipeline.length + 1]; newPipeline[0] = new Document("$match", new Document("country", countryValue)); System.arraycopy(pipeline, 0, newPipeline, 1, pipeline.length); command.put("pipeline", newPipeline); } // Add more command types here if you need to filter them too } @Override public void commandSucceeded(com.mongodb.event.CommandSucceededEvent event) { // No action needed here } @Override public void commandFailed(com.mongodb.event.CommandFailedEvent event) { // No action needed here } }
Register the listener with your MongoClient:
import com.mongodb.MongoClientSettings; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import com.mongodb.ConnectionString; public class MongoClientFactory { public static MongoClient createFilteredMongoClient() { MongoClientSettings settings = MongoClientSettings.builder() .addCommandListener(new CountryFilterCommandListener("GB")) .applyConnectionString(new ConnectionString("mongodb://localhost:27017")) .build(); return MongoClients.create(settings); } }
Now every query sent through this MongoClient will automatically include the country: "GB" filter—your business code stays exactly the same.
Quick Comparison of the Two Methods
- Custom Collection Wrapper:
- ✅ Pros: Granular control (choose which collections get filtered), easy to debug, straightforward logic.
- ❌ Cons: Requires switching your code to use the wrapped collection, may need updates to existing code.
- CommandListener:
- ✅ Pros: Global effect, no changes to business code, handles all queries across all collections.
- ❌ Cons: Need to correctly identify and handle different command types (like aggregates), extra logic needed if you have queries that shouldn't be filtered.
内容的提问来源于stack exchange,提问作者Nandhakumar Balasubramaniam

