/*
* gw2live - GuildWars 2 Dynamic Map
*
* Website: http://gw2map.com
*
* Copyright 2013 zyclonite networx
* http://zyclonite.net
* Developer: Lukas Prettenthaler
*/
package net.zyclonite.gw2live.service;
import com.mongodb.AggregationOutput;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.MongoClient;
import com.mongodb.MongoClientOptions;
import com.mongodb.MongoClientURI;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.zyclonite.gw2live.model.ChatMessage;
import net.zyclonite.gw2live.model.Coordinate;
import net.zyclonite.gw2live.model.GuildDetails;
import net.zyclonite.gw2live.model.GwMap;
import net.zyclonite.gw2live.model.KeyValueLanguage;
import net.zyclonite.gw2live.model.PveEvent;
import net.zyclonite.gw2live.model.PveEventDetails;
import net.zyclonite.gw2live.model.StatsItem;
import net.zyclonite.gw2live.model.WvwEvent;
import net.zyclonite.gw2live.model.WvwGuildStatistic;
import net.zyclonite.gw2live.model.WvwMatch;
import net.zyclonite.gw2live.model.WvwObjectiveDetails;
import net.zyclonite.gw2live.model.WvwScore;
import net.zyclonite.gw2live.util.AppConfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.mongojack.DBCursor;
import org.mongojack.DBQuery;
import org.mongojack.DBSort;
import org.mongojack.JacksonDBCollection;
/**
*
* @author zyclonite
*/
public final class MongoDB {
private static final Log LOG = LogFactory.getLog(MongoDB.class);
private static final MongoDB instance;
private DB database;
private JacksonDBCollection<PveEvent, String> pveevents;
private JacksonDBCollection<WvwEvent, String> wvwevents;
private JacksonDBCollection<KeyValueLanguage, String> pveeventnames;
private JacksonDBCollection<KeyValueLanguage, String> pvemapnames;
private JacksonDBCollection<KeyValueLanguage, String> pveworldnames;
private JacksonDBCollection<WvwMatch, String> wvwmatches;
private JacksonDBCollection<WvwScore, String> wvwscores;
private JacksonDBCollection<KeyValueLanguage, String> wvwobjectivenames;
private JacksonDBCollection<KeyValueLanguage, String> wvwobjectivelongnames;
private JacksonDBCollection<KeyValueLanguage, String> wvwmapnames;
private JacksonDBCollection<WvwObjectiveDetails, String> wvwobjectivedetails;
private JacksonDBCollection<GuildDetails, String> guilddetails;
private JacksonDBCollection<Coordinate, String> wvwcoordinates;
private JacksonDBCollection<Coordinate, String> pvecoordinates;
private JacksonDBCollection<ChatMessage, String> chatmessages;
private JacksonDBCollection<PveEventDetails, String> pveeventdetails;
private JacksonDBCollection<GwMap, String> maps;
private JacksonDBCollection<WvwGuildStatistic, String> wvwguildstatistics;
private final Map<String, JacksonDBCollection<StatsItem, String>> statscolls;
private final long statssize;
private final long chatsize;
static {
instance = new MongoDB();
}
private MongoDB() {
final AppConfig config = AppConfig.getInstance();
final String uri = config.getString("mongodb.uri", "mongodb://127.0.0.1:27017/gw2live");
statssize = config.getLong("mongodb.statcollsize", 10485760L);//in bytes 1048576L=1MB,
chatsize = config.getLong("mongodb.chatcollsize", 104857600L);//in bytes 1048576L=1MB,
statscolls = new HashMap<>();
try {
final MongoClientOptions.Builder options = new MongoClientOptions.Builder();
options.autoConnectRetry(true);
options.maxAutoConnectRetryTime(15); //s
options.connectTimeout(10000); //ms
options.socketTimeout(30000); //ms
options.socketKeepAlive(false);
options.connectionsPerHost(10);
options.threadsAllowedToBlockForConnectionMultiplier(5);
options.maxWaitTime(120000); //ms
final MongoClientURI mongouri = new MongoClientURI(uri, options);
final MongoClient mongo = new MongoClient(mongouri);
database = mongo.getDB(mongouri.getDatabase());
if ((mongouri.getUsername() != null) && (!mongouri.getUsername().trim().isEmpty()) && (!database.authenticate(mongouri.getUsername(), mongouri.getPassword()))) {
throw new Exception("Unable to authenticate with MongoDB server.");
}
initCollections();
ensureIndexes();
} catch (Exception e) {
database = null;
LOG.error("Connection to MongoDB could not be established: " + e.getMessage(), e);
}
LOG.debug("MongoDB initialized");
}
private void initCollections() {
pveevents = JacksonDBCollection.wrap(database.getCollection("pveevents"), PveEvent.class, String.class);
wvwevents = JacksonDBCollection.wrap(database.getCollection("wvwevents"), WvwEvent.class, String.class);
pveeventnames = JacksonDBCollection.wrap(database.getCollection("pveeventnames"), KeyValueLanguage.class, String.class);
pvemapnames = JacksonDBCollection.wrap(database.getCollection("pvemapnames"), KeyValueLanguage.class, String.class);
pveworldnames = JacksonDBCollection.wrap(database.getCollection("pveworldnames"), KeyValueLanguage.class, String.class);
wvwmatches = JacksonDBCollection.wrap(database.getCollection("wvwmatches"), WvwMatch.class, String.class);
wvwscores = JacksonDBCollection.wrap(database.getCollection("wvwscores"), WvwScore.class, String.class);
wvwobjectivenames = JacksonDBCollection.wrap(database.getCollection("wvwobjectivenames"), KeyValueLanguage.class, String.class);
wvwobjectivelongnames = JacksonDBCollection.wrap(database.getCollection("wvwobjectivelongnames"), KeyValueLanguage.class, String.class);
wvwmapnames = JacksonDBCollection.wrap(database.getCollection("wvwmapnames"), KeyValueLanguage.class, String.class);
wvwobjectivedetails = JacksonDBCollection.wrap(database.getCollection("wvwobjectivedetails"), WvwObjectiveDetails.class, String.class);
guilddetails = JacksonDBCollection.wrap(database.getCollection("guilddetails"), GuildDetails.class, String.class);
wvwcoordinates = JacksonDBCollection.wrap(database.getCollection("wvwcoordinates"), Coordinate.class, String.class);
pvecoordinates = JacksonDBCollection.wrap(database.getCollection("pvecoordinates"), Coordinate.class, String.class);
pveeventdetails = JacksonDBCollection.wrap(database.getCollection("pveeventdetails"), PveEventDetails.class, String.class);
maps = JacksonDBCollection.wrap(database.getCollection("maps"), GwMap.class, String.class);
wvwguildstatistics = JacksonDBCollection.wrap(database.getCollection("wvwguildstatistics"), WvwGuildStatistic.class, String.class);
final DBCollection chatmessagescoll;
if (database.collectionExists("chatmessages")) {
chatmessagescoll = database.getCollection("chatmessages");
} else {
final BasicDBObject capped = new BasicDBObject();
capped.put("capped", true);
capped.put("size", chatsize);
chatmessagescoll = database.createCollection("chatmessages", capped);
}
chatmessages = JacksonDBCollection.wrap(chatmessagescoll, ChatMessage.class, String.class);
}
private void ensureIndexes() {
pveevents.ensureIndex(new BasicDBObject("world_id", 1));
pveevents.ensureIndex(new BasicDBObject("world_id", 1).append("map_id", 1));
pveevents.ensureIndex(new BasicDBObject("world_id", 1).append("map_id", 1).append("event_id", 1));
wvwevents.ensureIndex(new BasicDBObject("match_id", 1));
wvwevents.ensureIndex(new BasicDBObject("match_id", 1).append("map_type", 1));
wvwevents.ensureIndex(new BasicDBObject("match_id", 1).append("map_type", 1).append("objective_id", 1));
pveeventnames.ensureIndex(new BasicDBObject("lang", 1));
pveeventnames.ensureIndex(new BasicDBObject("id", 1).append("lang", 1), new BasicDBObject("unique", true));
pvemapnames.ensureIndex(new BasicDBObject("lang", 1));
pvemapnames.ensureIndex(new BasicDBObject("id", 1).append("lang", 1), new BasicDBObject("unique", true));
pveworldnames.ensureIndex(new BasicDBObject("lang", 1));
pveworldnames.ensureIndex(new BasicDBObject("id", 1).append("lang", 1), new BasicDBObject("unique", true));
wvwmatches.ensureIndex(new BasicDBObject("wvw_match_id", 1), new BasicDBObject("unique", true));
wvwscores.ensureIndex(new BasicDBObject("match_id", 1));
wvwscores.ensureIndex(new BasicDBObject("match_id", 1).append("map_type", 1), new BasicDBObject("unique", true));
wvwobjectivenames.ensureIndex(new BasicDBObject("lang", 1));
wvwobjectivenames.ensureIndex(new BasicDBObject("id", 1).append("lang", 1), new BasicDBObject("unique", true));
wvwobjectivelongnames.ensureIndex(new BasicDBObject("lang", 1));
wvwobjectivelongnames.ensureIndex(new BasicDBObject("id", 1).append("lang", 1), new BasicDBObject("unique", true));
wvwmapnames.ensureIndex(new BasicDBObject("lang", 1));
wvwmapnames.ensureIndex(new BasicDBObject("id", 1).append("lang", 1), new BasicDBObject("unique", true));
wvwobjectivedetails.ensureIndex(new BasicDBObject("id", 1), new BasicDBObject("unique", true));
guilddetails.ensureIndex(new BasicDBObject("guild_name", 1));
chatmessages.ensureIndex(new BasicDBObject("timestamp", -1));
chatmessages.ensureIndex(new BasicDBObject("channel", 1));
pveeventdetails.ensureIndex(new BasicDBObject("map_id", 1));
pveeventdetails.ensureIndex(new BasicDBObject("event_id", 1), new BasicDBObject("unique", true));
maps.ensureIndex(new BasicDBObject("map_id", 1), new BasicDBObject("unique", true));
wvwguildstatistics.ensureIndex(new BasicDBObject("guild_id", 1));
wvwguildstatistics.ensureIndex(new BasicDBObject("guild_id", 1).append("timestamp", 1));
}
public void initStatsCollections(final String coll) {
final String collname = "stats_" + coll;
final DBCollection collection;
if (database.collectionExists(collname)) {
collection = database.getCollection(collname);
} else {
final BasicDBObject capped = new BasicDBObject();
capped.put("capped", true);
capped.put("size", statssize);
collection = database.createCollection(collname, capped);
}
final JacksonDBCollection<StatsItem, String> statscoll = JacksonDBCollection.wrap(collection, StatsItem.class, String.class);
statscoll.ensureIndex(new BasicDBObject("timestamp", -1));
statscolls.put(coll, statscoll);
}
public List<StatsItem> findStats(final String coll) {
return findStats(coll, new Date(), 1000);
}
public List<StatsItem> findStats(final String coll, final Date from) {
return findStats(coll, from, 1000);
}
public List<StatsItem> findStats(final String coll, final Date from, int limit) {
if (limit > 1000) {
limit = 1000;
}
List<StatsItem> result = new ArrayList<>();
if (statscolls.containsKey(coll)) {
result = statscolls.get(coll).find().lessThanEquals("timestamp", from).sort(DBSort.desc("$natural").asc("timestamp")).limit(limit).toArray();
}
return result;
}
public DBCursor<PveEvent> findPveEvents() {
return pveevents.find();
}
public List<PveEvent> findPveEvents(final Long world) {
final List<PveEvent> result = pveevents.find().is("world_id", world).toArray();
return result;
}
public List<PveEvent> findPveEvents(final Long world, final Long map) {
final List<PveEvent> result = pveevents.find().is("world_id", world).and(DBQuery.is("map_id", map)).toArray();
return result;
}
public DBCursor<WvwEvent> findWvwEvents() {
return wvwevents.find();
}
public List<WvwEvent> findWvwEvents(final String match) {
final List<WvwEvent> result = wvwevents.find().is("match_id", match).toArray();
return result;
}
public List<WvwEvent> findWvwEvents(final String match, final String maptype) {
final List<WvwEvent> result = wvwevents.find().is("match_id", match).and(DBQuery.is("map_type", maptype)).toArray();
return result;
}
public List<KeyValueLanguage> findPveEventNames() {
return findPveEventNames("en");
}
public List<KeyValueLanguage> findPveEventNames(final String lang) {
final List<KeyValueLanguage> result = pveeventnames.find().is("lang", lang).toArray();
return result;
}
public List<KeyValueLanguage> findPveMapNames() {
return findPveMapNames("en");
}
public List<KeyValueLanguage> findPveMapNames(final String lang) {
final List<KeyValueLanguage> result = pvemapnames.find().is("lang", lang).toArray();
return result;
}
public List<KeyValueLanguage> findPveWorldNames() {
return findPveWorldNames("en");
}
public List<KeyValueLanguage> findPveWorldNames(final String lang) {
final List<KeyValueLanguage> result = pveworldnames.find().is("lang", lang).toArray();
return result;
}
public List<WvwMatch> findWvwMatches() {
//final List<WvwMatch> result = wvwmatches.find(DBQuery.is("red_world_id", world_id).or(DBQuery.is("blue_world_id", world_id).or(DBQuery.is("green_world_id", world_id)))).toArray();
final List<WvwMatch> result = wvwmatches.find().toArray();
return result;
}
public WvwMatch findWvwMatch(final String matchid) {
//final List<WvwMatch> result = wvwmatches.find(DBQuery.is("red_world_id", world_id).or(DBQuery.is("blue_world_id", world_id).or(DBQuery.is("green_world_id", world_id)))).toArray();
final WvwMatch result = wvwmatches.findOne(DBQuery.is("wvw_match_id", matchid));
return result;
}
public List<WvwScore> findWvwScores() {
final List<WvwScore> result = wvwscores.find().toArray();
return result;
}
public List<WvwScore> findWvwScores(final String match_id) {
final List<WvwScore> result = wvwscores.find().is("match_id", match_id).toArray();
return result;
}
public List<KeyValueLanguage> findWvwObjectiveNames() {
return findWvwObjectiveNames("en");
}
public List<KeyValueLanguage> findWvwObjectiveNames(final String lang) {
final List<KeyValueLanguage> result = wvwobjectivenames.find().is("lang", lang).toArray();
return result;
}
public List<KeyValueLanguage> findWvwObjectiveLongNames() {
return findWvwObjectiveLongNames("en");
}
public List<KeyValueLanguage> findWvwObjectiveLongNames(final String lang) {
final List<KeyValueLanguage> result = wvwobjectivelongnames.find().is("lang", lang).toArray();
return result;
}
public List<KeyValueLanguage> findWvwMapNames() {
return findWvwMapNames("en");
}
public List<KeyValueLanguage> findWvwMapNames(final String lang) {
final List<KeyValueLanguage> result = wvwmapnames.find().is("lang", lang).toArray();
return result;
}
public List<WvwObjectiveDetails> findWvwObjectiveDetails() {
final List<WvwObjectiveDetails> result = wvwobjectivedetails.find().toArray();
return result;
}
public GuildDetails findGuildDetailsById(final String guild_id) {
final GuildDetails result = guilddetails.findOne(DBQuery.is("guild_id", guild_id));
return result;
}
public List<GuildDetails> findGuildDetailsByName(final String guild_name) {
final List<GuildDetails> result = guilddetails.find().is("guild_name", guild_name).toArray();
return result;
}
public List<Coordinate> findWvwCoordinates() {
final List<Coordinate> result = wvwcoordinates.find().toArray();
return result;
}
public List<Coordinate> findPveCoordinates() {
final List<Coordinate> result = pvecoordinates.find().toArray();
return result;
}
public List<ChatMessage> findChatMessages() {
final List<ChatMessage> result = chatmessages.find().sort(DBSort.desc("$natural")).limit(100).toArray();
return result;
}
public List<ChatMessage> findChatMessages(final String channel) {
final List<ChatMessage> result = chatmessages.find().is("channel", channel).sort(DBSort.desc("$natural")).limit(100).toArray();
return result;
}
public List<PveEventDetails> findPveEventDetails() {
final List<PveEventDetails> result = pveeventdetails.find().toArray();
return result;
}
public List<PveEventDetails> findPveEventDetails(final Long map) {
final List<PveEventDetails> result = pveeventdetails.find().is("map_id", map).toArray();
return result;
}
public List<PveEventDetails> findPveEventDetails(final String event) {
final List<PveEventDetails> result = pveeventdetails.find().is("event_id", event).toArray();
return result;
}
public List<GwMap> findMap(final String map) {
final List<GwMap> result = maps.find().is("map_id", map).toArray();
return result;
}
public String getTopGuilds(final String matchid) {
final WvwMatch wvwmatch = findWvwMatch(matchid);
if(wvwmatch == null){
return "[]";
}
final DBObject timerange = new BasicDBObject("$gt", wvwmatch.getStart_time());
timerange.put("$lt", wvwmatch.getEnd_time());
final DBObject matchFields = new BasicDBObject("timestamp", timerange);
matchFields.put("match_id", matchid);
final DBObject match = new BasicDBObject("$match", matchFields);
final DBObject projectFields = new BasicDBObject("guild_id", 1);
projectFields.put("holdtime", 1);
projectFields.put("_id", 0);
final DBObject project = new BasicDBObject("$project", projectFields);
final DBObject groupFields = new BasicDBObject("_id", "$guild_id");
groupFields.put("holdtime", new BasicDBObject("$sum", "$holdtime"));
groupFields.put("count", new BasicDBObject("$sum", 1));
final DBObject group = new BasicDBObject("$group", groupFields);
final DBObject sortFields = new BasicDBObject("count", -1);
sortFields.put("holdtime", -1);
final DBObject sort = new BasicDBObject("$sort", sortFields);
final DBObject limit = new BasicDBObject("$limit", 10);
final AggregationOutput output = wvwguildstatistics.getDbCollection().aggregate(match, project, group, sort, limit);
return output.results().toString();
}
public void saveStats(final String coll, final List<StatsItem> items) {
if (statscolls.containsKey(coll)) {
for (final StatsItem item : items) {
statscolls.get(coll).save(item);
LOG.trace("saved stats-item in " + coll + " to mongodb");
}
}
}
public void savePveEvents(final List<PveEvent> events) {
for (final PveEvent event : events) {
pveevents.update(DBQuery.is("world_id", event.getWorld_id()).and(DBQuery.is("map_id", event.getMap_id()).and(DBQuery.is("event_id", event.getEvent_id()))), event, true, false);
LOG.trace("saved pve-event " + event.getEvent_id() + " to mongodb");
}
}
public void saveWvwEvents(final List<WvwEvent> events) {
for (final WvwEvent event : events) {
wvwevents.update(DBQuery.is("match_id", event.getMatch_id()).and(DBQuery.is("map_type", event.getMap_type()).and(DBQuery.is("objective_id", event.getObjective_id()))), event, true, false);
LOG.trace("saved wvw-event " + event.getMatch_id() + " " + event.getObjective_id() + " to mongodb");
}
}
public void savePveEventNames(final List<KeyValueLanguage> names, final String lang) {
for (final KeyValueLanguage name : names) {
name.setLang(lang);
pveeventnames.update(DBQuery.is("lang", lang).and(DBQuery.is("id", name.getId())), name, true, false);
LOG.trace("saved " + lang + " pve-event-name " + name.getName() + " to mongodb");
}
}
public void savePveMapNames(final List<KeyValueLanguage> names, final String lang) {
for (final KeyValueLanguage name : names) {
name.setLang(lang);
pvemapnames.update(DBQuery.is("lang", lang).and(DBQuery.is("id", name.getId())), name, true, false);
LOG.trace("saved " + lang + " pve-map-name " + name.getName() + " to mongodb");
}
}
public void savePveWorldNames(final List<KeyValueLanguage> names, final String lang) {
for (final KeyValueLanguage name : names) {
name.setLang(lang);
pveworldnames.update(DBQuery.is("lang", lang).and(DBQuery.is("id", name.getId())), name, true, false);
LOG.trace("saved " + lang + " pve-map-name " + name.getName() + " to mongodb");
}
}
public void saveWvwMatches(final List<WvwMatch> matches) {
for (final WvwMatch match : matches) {
wvwmatches.update(DBQuery.is("wvw_match_id", match.getWvw_match_id()), match, true, false);
LOG.trace("saved wvw-match " + match.getWvw_match_id() + " to mongodb");
}
}
public void saveWvwScores(final List<WvwScore> scores) {
for (final WvwScore score : scores) {
wvwscores.update(DBQuery.is("match_id", score.getMatch_id()).and(DBQuery.is("map_type", score.getMap_type())), score, true, false);
LOG.trace("saved wvw-score " + score.getMatch_id() + " " + score.getMap_type() + " to mongodb");
}
}
public void saveWvwObjectiveNames(final List<KeyValueLanguage> names, final String lang) {
for (final KeyValueLanguage name : names) {
name.setLang(lang);
wvwobjectivenames.update(DBQuery.is("lang", lang).and(DBQuery.is("id", name.getId())), name, true, false);
LOG.trace("saved " + lang + " wvw-objective-name " + name.getName() + " to mongodb");
}
}
public void saveWvwObjectiveLongNames(final List<KeyValueLanguage> names) {
for (final KeyValueLanguage name : names) {
wvwobjectivelongnames.update(DBQuery.is("lang", name.getLang()).and(DBQuery.is("id", name.getId())), name, true, false);
LOG.trace("saved " + name.getLang() + " wvw-objective-long-name " + name.getName() + " to mongodb");
}
}
public void saveWvwMapNames(final List<KeyValueLanguage> names) {
for (final KeyValueLanguage name : names) {
wvwmapnames.update(DBQuery.is("lang", name.getLang()).and(DBQuery.is("id", name.getId())), name, true, false);
LOG.trace("saved " + name.getLang() + " wvw-map-name " + name.getName() + " to mongodb");
}
}
public void saveWvwObjectiveDetails(final List<WvwObjectiveDetails> details) {
for (final WvwObjectiveDetails detail : details) {
wvwobjectivedetails.update(DBQuery.is("id", detail.getId()), detail, true, false);
LOG.trace("saved wvw-objective-detail " + detail.getId() + " to mongodb");
}
}
public void saveGuildDetails(final List<GuildDetails> guilds) {
for (final GuildDetails guild : guilds) {
guild.setTimestamp(new Date());
guilddetails.update(DBQuery.is("guild_id", guild.getGuild_id()), guild, true, false);
LOG.trace("saved guild-details " + guild.getGuild_id() + " to mongodb");
}
}
public void saveWvwCoordinates(final List<Coordinate> coordinates) {
for (final Coordinate coordinate : coordinates) {
wvwcoordinates.update(DBQuery.is("id", coordinate.getId()), coordinate, true, false);
LOG.trace("saved wvwcoordinates " + coordinate.getId() + " to mongodb");
}
}
public void savePveCoordinates(final List<Coordinate> coordinates) {
for (final Coordinate coordinate : coordinates) {
pvecoordinates.update(DBQuery.is("id", coordinate.getId()), coordinate, true, false);
LOG.trace("saved pvecoordinates " + coordinate.getId() + " to mongodb");
}
}
public void saveChatMessage(final ChatMessage message) {
chatmessages.insert(message);
LOG.trace("saved chatmessage to mongodb");
}
public void savePveEventDetails(final List<PveEventDetails> eventdetails) {
for (final PveEventDetails eventdetail : eventdetails) {
pveeventdetails.update(DBQuery.is("event_id", eventdetail.getEvent_id()), eventdetail, true, false);
LOG.trace("saved pve-event-details " + eventdetail.getEvent_id() + " to mongodb");
}
}
public void saveMaps(final List<GwMap> gwmaps) {
for (final GwMap map : gwmaps) {
maps.update(DBQuery.is("map_id", map.getMap_id()), map, true, false);
LOG.trace("saved maps " + map.getMap_id() + " to mongodb");
}
}
public void saveWvwGuildStatistics(final WvwGuildStatistic statsentry) {
wvwguildstatistics.insert(statsentry);
LOG.trace("saved wvwguildstatistic " + statsentry.getGuild_id() + " to mongodb");
}
public static MongoDB getInstance() {
return instance;
}
}