package org.infinispan.persistence.mongodb.cache;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
import com.mongodb.client.FindIterable;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.Binary;
import org.infinispan.persistence.mongodb.configuration.MongoDBStoreConfiguration;
import org.infinispan.persistence.mongodb.store.MongoDBEntry;
import org.infinispan.util.TimeService;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Sorts.descending;
/**
* An implementation of the MongoDBCache interface.
*
* @param <K> - key
* @param <V> - value
* @author Gabriel Francisco <gabfssilva@gmail.com>
* @author gustavonalle
*/
public class MongoDBCacheImpl<K, V> implements MongoDBCache<K, V> {
private static final int pagingSize = 1024;
private final TimeService timeService;
private MongoClient mongoClient;
private MongoCollection<Document> collection;
private MongoDBStoreConfiguration mongoCacheConfiguration;
public MongoDBCacheImpl(MongoDBStoreConfiguration mongoCacheConfiguration, TimeService timeService) throws Exception {
this.mongoCacheConfiguration = mongoCacheConfiguration;
this.timeService = timeService;
init();
}
private void init() throws Exception {
start();
}
public void start() throws Exception {
MongoClientURI mongoClientURI = new MongoClientURI(mongoCacheConfiguration.getConnectionURI());
this.mongoClient = new MongoClient(mongoClientURI);
MongoDatabase database = mongoClient.getDatabase(mongoClientURI.getDatabase());
this.collection = database.getCollection(mongoCacheConfiguration.collection());
}
@Override
public int size() {
return (int) collection.count();
}
@Override
public void clear() {
collection.drop();
}
@Override
public boolean remove(byte[] key) {
BasicDBObject query = new BasicDBObject();
query.put("_id", key);
return collection.findOneAndDelete(query) != null;
}
@Override
public MongoDBEntry<K, V> get(byte[] key) {
BasicDBObject query = new BasicDBObject();
query.put("_id", key);
MongoCursor<Document> iterator = collection.find(query).iterator();
if (!iterator.hasNext()) {
return null;
}
return createEntry(iterator.next());
}
private MongoDBEntry<K, V> createEntry(Document document) {
byte[] k = ((Binary) document.get("_id")).getData();
byte[] v = ((Binary) document.get("value")).getData();
byte[] m = ((Binary) document.get("metadata")).getData();
MongoDBEntry.Builder<K, V> mongoDBEntryBuilder = MongoDBEntry.builder();
mongoDBEntryBuilder
.keyBytes(k)
.valueBytes(v)
.metadataBytes(m);
return mongoDBEntryBuilder.create();
}
public boolean containsKey(byte[] key) {
return get(key) != null;
}
@Override
public List<MongoDBEntry<K, V>> getPagedEntries(byte[] lastKey) {
FindIterable<Document> iterable = lastKey != null ? collection.find(lt("_id", lastKey)) : collection.find();
iterable.sort(descending("_id")).limit(pagingSize);
List<MongoDBEntry<K, V>> entries = new ArrayList<>();
iterable.map(this::createEntry).into(entries);
return entries;
}
@Override
public List<MongoDBEntry<K, V>> removeExpiredData(byte[] lastKey) {
long time = timeService.wallClockTime();
Bson filter = and(lte("expiryTime", new Date(time)), gt("expiryTime", new Date(-1)));
if (lastKey != null) {
filter = and(filter, lt("_id", lastKey));
}
FindIterable<Document> iterable = collection.find(filter).sort(descending("_id")).limit(pagingSize);
List<MongoDBEntry<K, V>> listOfExpiredEntries = new ArrayList<>();
iterable.map(this::createEntry).into(listOfExpiredEntries);
collection.deleteMany(filter);
return listOfExpiredEntries;
}
@Override
public void put(MongoDBEntry<K, V> entry) {
Document document = new Document("_id", entry.getKeyBytes())
.append("value", entry.getValueBytes())
.append("metadata", entry.getMetadataBytes())
.append("expiryTime", entry.getExpiryTime());
if (containsKey(entry.getKeyBytes())) {
collection.replaceOne(eq("_id", entry.getKeyBytes()), document);
} else {
collection.insertOne(document);
}
}
@Override
public void stop() {
mongoClient.close();
}
}