mirror of
https://github.com/plan-player-analytics/Plan.git
synced 2024-12-27 09:00:28 +08:00
Refactored GeoInfoTable#getNetworkGeolocations to a query:
- Changed WorldMap to use the Map instead of calculating from a list, saves memory. - Network container now uses the new query to save memory
This commit is contained in:
parent
6478477eec
commit
0799674de3
@ -23,6 +23,7 @@ import com.djrapitops.plan.data.store.keys.ServerKeys;
|
||||
import com.djrapitops.plan.data.store.mutators.PlayersMutator;
|
||||
import com.djrapitops.plan.data.store.mutators.TPSMutator;
|
||||
import com.djrapitops.plan.data.store.mutators.health.NetworkHealthInformation;
|
||||
import com.djrapitops.plan.db.Database;
|
||||
import com.djrapitops.plan.db.access.queries.ServerAggregateQueries;
|
||||
import com.djrapitops.plan.system.database.DBSystem;
|
||||
import com.djrapitops.plan.system.info.server.Server;
|
||||
@ -63,10 +64,10 @@ public class NetworkContainer extends DataContainer {
|
||||
private final PlanConfig config;
|
||||
private final Locale locale;
|
||||
private final Theme theme;
|
||||
private final DBSystem dbSystem;
|
||||
private final ServerProperties serverProperties;
|
||||
private final Formatters formatters;
|
||||
private final Graphs graphs;
|
||||
private final Database database;
|
||||
|
||||
public NetworkContainer(
|
||||
ServerContainer bungeeContainer,
|
||||
@ -84,7 +85,7 @@ public class NetworkContainer extends DataContainer {
|
||||
this.config = config;
|
||||
this.locale = locale;
|
||||
this.theme = theme;
|
||||
this.dbSystem = dbSystem;
|
||||
this.database = dbSystem.getDatabase();
|
||||
this.serverProperties = serverProperties;
|
||||
this.formatters = formatters;
|
||||
this.graphs = graphs;
|
||||
@ -98,13 +99,13 @@ public class NetworkContainer extends DataContainer {
|
||||
}
|
||||
|
||||
private void addServerBoxes() {
|
||||
putSupplier(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA, () -> dbSystem.getDatabase().fetch().getPlayersOnlineForServers(
|
||||
putSupplier(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA, () -> database.fetch().getPlayersOnlineForServers(
|
||||
getValue(NetworkKeys.BUKKIT_SERVERS).orElse(new ArrayList<>()))
|
||||
);
|
||||
putSupplier(NetworkKeys.SERVERS_TAB, () -> {
|
||||
StringBuilder serverBoxes = new StringBuilder();
|
||||
Map<Integer, List<TPS>> playersOnlineData = getValue(NetworkKeys.NETWORK_PLAYER_ONLINE_DATA).orElse(new HashMap<>());
|
||||
Map<UUID, Integer> userCounts = dbSystem.getDatabase().query(ServerAggregateQueries.serverUserCounts());
|
||||
Map<UUID, Integer> userCounts = database.query(ServerAggregateQueries.serverUserCounts());
|
||||
Collection<Server> servers = getValue(NetworkKeys.BUKKIT_SERVERS).orElse(new ArrayList<>());
|
||||
servers.stream()
|
||||
.sorted((one, two) -> String.CASE_INSENSITIVE_ORDER.compare(one.getName(), two.getName()))
|
||||
@ -168,7 +169,7 @@ public class NetworkContainer extends DataContainer {
|
||||
private void addPlayerInformation() {
|
||||
putSupplier(NetworkKeys.PLAYERS_TOTAL, () -> getUnsafe(NetworkKeys.PLAYERS_MUTATOR).count());
|
||||
putSupplier(NetworkKeys.WORLD_MAP_SERIES, () ->
|
||||
graphs.special().worldMap(PlayersMutator.forContainer(bungeeContainer)).toHighChartsSeries()
|
||||
graphs.special().worldMap(database.query(ServerAggregateQueries.networkGeolocationCounts())).toHighChartsSeries()
|
||||
);
|
||||
Key<BarGraph> geolocationBarChart = new Key<>(BarGraph.class, "GEOLOCATION_BAR_GRAPH");
|
||||
putSupplier(geolocationBarChart, () -> graphs.bar().geolocationBarGraph(getUnsafe(NetworkKeys.PLAYERS_MUTATOR)));
|
||||
|
@ -180,4 +180,27 @@ public class ServerAggregateQueries {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static Query<Map<String, Integer>> networkGeolocationCounts() {
|
||||
String subQuery = "SELECT " +
|
||||
GeoInfoTable.GEOLOCATION + ", " +
|
||||
GeoInfoTable.LAST_USED + ", " +
|
||||
"MAX(" + GeoInfoTable.LAST_USED + ") as m" +
|
||||
" FROM " + GeoInfoTable.TABLE_NAME +
|
||||
" GROUP BY " + GeoInfoTable.USER_UUID;
|
||||
String sql = "SELECT " + GeoInfoTable.GEOLOCATION + ", COUNT(1) as c FROM (" + subQuery + ") AS q1" +
|
||||
" WHERE " + GeoInfoTable.LAST_USED + " = m" +
|
||||
" GROUP BY " + GeoInfoTable.GEOLOCATION;
|
||||
|
||||
return new QueryAllStatement<Map<String, Integer>>(sql) {
|
||||
@Override
|
||||
public Map<String, Integer> processResults(ResultSet set) throws SQLException {
|
||||
Map<String, Integer> geolocationCounts = new HashMap<>();
|
||||
while (set.next()) {
|
||||
geolocationCounts.put(set.getString(GeoInfoTable.GEOLOCATION), set.getInt("c"));
|
||||
}
|
||||
return geolocationCounts;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
@ -16,19 +16,11 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db.sql.tables;
|
||||
|
||||
import com.djrapitops.plan.data.container.GeoInfo;
|
||||
import com.djrapitops.plan.db.DBType;
|
||||
import com.djrapitops.plan.db.SQLDB;
|
||||
import com.djrapitops.plan.db.access.queries.LargeFetchQueries;
|
||||
import com.djrapitops.plan.db.patches.*;
|
||||
import com.djrapitops.plan.db.sql.parsing.CreateTableParser;
|
||||
import com.djrapitops.plan.db.sql.parsing.Sql;
|
||||
import com.djrapitops.plan.utilities.comparators.GeoInfoComparator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Table that is in charge of storing common IP and Geolocation data for users.
|
||||
@ -84,18 +76,4 @@ public class GeoInfoTable extends Table {
|
||||
.toString();
|
||||
}
|
||||
|
||||
public List<String> getNetworkGeolocations() {
|
||||
List<String> geolocations = new ArrayList<>();
|
||||
|
||||
Map<UUID, List<GeoInfo>> geoInfo = db.query(LargeFetchQueries.fetchAllGeoInformation());
|
||||
for (List<GeoInfo> userGeoInfos : geoInfo.values()) {
|
||||
if (userGeoInfos.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
userGeoInfos.sort(new GeoInfoComparator());
|
||||
geolocations.add(userGeoInfos.get(0).getGeolocation());
|
||||
}
|
||||
|
||||
return geolocations;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import com.djrapitops.plan.utilities.html.graphs.HighChart;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Factory class for different objects representing special HTML graphs.
|
||||
@ -41,6 +42,10 @@ public class SpecialGraphFactory {
|
||||
return new PunchCard(sessions);
|
||||
}
|
||||
|
||||
public HighChart worldMap(Map<String, Integer> geolocationCounts) {
|
||||
return new WorldMap(geolocationCounts);
|
||||
}
|
||||
|
||||
public HighChart worldMap(PlayersMutator mutator) {
|
||||
return new WorldMap(mutator);
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ import org.apache.commons.text.TextStringBuilder;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* World Map that uses iso-a3 specification of Country codes.
|
||||
@ -31,13 +32,17 @@ import java.util.Map;
|
||||
*/
|
||||
public class WorldMap implements HighChart {
|
||||
|
||||
private final List<String> geoLocations;
|
||||
private final Map<String, Integer> geoCodeCounts;
|
||||
|
||||
WorldMap(PlayersMutator mutator) {
|
||||
this.geoLocations = mutator.getGeolocations();
|
||||
this.geoCodeCounts = toGeoCodeCounts(mutator.getGeolocations());
|
||||
}
|
||||
|
||||
private static Map<String, String> getGeoCodes(Map<String, Integer> geoCodeCounts) {
|
||||
WorldMap(Map<String, Integer> geolocationCounts) {
|
||||
this.geoCodeCounts = toGeoCodeCounts(geolocationCounts);
|
||||
}
|
||||
|
||||
private static Map<String, String> getGeoCodes() {
|
||||
Map<String, String> geoCodes = new HashMap<>();
|
||||
// Countries & Codes have been copied from a iso-a3 specification file.
|
||||
// Each index corresponds to each code.
|
||||
@ -48,23 +53,30 @@ public class WorldMap implements HighChart {
|
||||
String countryCode = codes[i];
|
||||
|
||||
geoCodes.put(country.toLowerCase(), countryCode);
|
||||
geoCodeCounts.put(countryCode, 0);
|
||||
}
|
||||
return geoCodes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toHighChartsSeries() {
|
||||
private Map<String, Integer> toGeoCodeCounts(Map<String, Integer> geolocationCounts) {
|
||||
Map<String, String> geoCodes = getGeoCodes();
|
||||
return geolocationCounts.entrySet().stream()
|
||||
.collect(Collectors.toMap(entry -> geoCodes.get(entry.getKey().toLowerCase()), Map.Entry::getValue));
|
||||
}
|
||||
|
||||
private Map<String, Integer> toGeoCodeCounts(List<String> geoLocations) {
|
||||
Map<String, Integer> geoCodeCounts = new HashMap<>();
|
||||
Map<String, String> geoCodes = getGeoCodes(geoCodeCounts);
|
||||
Map<String, String> geoCodes = getGeoCodes();
|
||||
|
||||
for (String geoLocation : geoLocations) {
|
||||
String countryCode = geoCodes.get(geoLocation.toLowerCase());
|
||||
if (countryCode != null) {
|
||||
geoCodeCounts.computeIfPresent(countryCode, (computedCountry, amount) -> amount + 1);
|
||||
}
|
||||
geoCodeCounts.put(countryCode, geoCodeCounts.getOrDefault(countryCode, 0) + 1);
|
||||
}
|
||||
|
||||
return geoCodeCounts;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toHighChartsSeries() {
|
||||
TextStringBuilder dataBuilder = new TextStringBuilder("[");
|
||||
|
||||
dataBuilder.appendWithSeparators(
|
||||
|
@ -606,7 +606,7 @@ public abstract class CommonDBTest {
|
||||
securityTable.addNewUser(new WebUser("Test", "RandomGarbageBlah", 0));
|
||||
}
|
||||
|
||||
private void saveGeoInfo(UUID uuid, GeoInfo geoInfo) {
|
||||
void saveGeoInfo(UUID uuid, GeoInfo geoInfo) {
|
||||
db.executeTransaction(new GeoInfoStoreTransaction(uuid, geoInfo));
|
||||
}
|
||||
|
||||
@ -800,32 +800,6 @@ public abstract class CommonDBTest {
|
||||
assertTrue(db.check().isPlayerRegisteredOnThisServer(playerUUID));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetNetworkGeolocations() {
|
||||
GeoInfoTable geoInfoTable = db.getGeoInfoTable();
|
||||
UUID firstUuid = UUID.randomUUID();
|
||||
UUID secondUuid = UUID.randomUUID();
|
||||
UUID thirdUuid = UUID.randomUUID();
|
||||
|
||||
UsersTable usersTable = db.getUsersTable();
|
||||
usersTable.registerUser(firstUuid, 0, "");
|
||||
usersTable.registerUser(secondUuid, 0, "");
|
||||
usersTable.registerUser(thirdUuid, 0, "");
|
||||
|
||||
saveGeoInfo(firstUuid, new GeoInfo("-", "Test1", 0, "3"));
|
||||
GeoInfo secondInfo = new GeoInfo("-", "Test2", 5, "3");
|
||||
saveGeoInfo(firstUuid, secondInfo);
|
||||
saveGeoInfo(secondUuid, new GeoInfo("-", "Test3", 0, "3"));
|
||||
saveGeoInfo(thirdUuid, new GeoInfo("-", "Test4", 0, "3"));
|
||||
|
||||
List<String> geolocations = geoInfoTable.getNetworkGeolocations();
|
||||
|
||||
assertNotNull(geolocations);
|
||||
assertFalse(geolocations.isEmpty());
|
||||
assertEquals(3, geolocations.size());
|
||||
assertTrue(geolocations.contains(secondInfo.getGeolocation()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNewContainerForPlayer() throws NoSuchAlgorithmException {
|
||||
saveAllData();
|
||||
|
@ -16,11 +16,20 @@
|
||||
*/
|
||||
package com.djrapitops.plan.db;
|
||||
|
||||
import com.djrapitops.plan.data.container.GeoInfo;
|
||||
import com.djrapitops.plan.db.access.queries.ServerAggregateQueries;
|
||||
import com.djrapitops.plan.db.sql.tables.UsersTable;
|
||||
import com.djrapitops.plan.system.settings.config.PlanConfig;
|
||||
import com.djrapitops.plan.system.settings.paths.DatabaseSettings;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import utilities.CIProperties;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
/**
|
||||
@ -54,4 +63,31 @@ public class MySQLTest extends CommonDBTest {
|
||||
db.execute("CREATE DATABASE Plan");
|
||||
db.execute("USE Plan");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void networkGeolocationsAreCountedAppropriately() {
|
||||
UUID firstUuid = UUID.randomUUID();
|
||||
UUID secondUuid = UUID.randomUUID();
|
||||
UUID thirdUuid = UUID.randomUUID();
|
||||
|
||||
UsersTable usersTable = db.getUsersTable();
|
||||
usersTable.registerUser(firstUuid, 0, "");
|
||||
usersTable.registerUser(secondUuid, 0, "");
|
||||
usersTable.registerUser(thirdUuid, 0, "");
|
||||
|
||||
saveGeoInfo(firstUuid, new GeoInfo("-", "Norway", 0, "3"));
|
||||
saveGeoInfo(firstUuid, new GeoInfo("-", "Finland", 5, "3"));
|
||||
saveGeoInfo(secondUuid, new GeoInfo("-", "Sweden", 0, "3"));
|
||||
saveGeoInfo(thirdUuid, new GeoInfo("-", "Denmark", 0, "3"));
|
||||
|
||||
Map<String, Integer> got = db.query(ServerAggregateQueries.networkGeolocationCounts());
|
||||
|
||||
Map<String, Integer> expected = new HashMap<>();
|
||||
// first user has a more recent connection from Finland so their country should be counted as Finland.
|
||||
expected.put("Finland", 1);
|
||||
expected.put("Sweden", 1);
|
||||
expected.put("Denmark", 1);
|
||||
|
||||
assertEquals(expected, got);
|
||||
}
|
||||
}
|
||||
|
@ -17,15 +17,19 @@
|
||||
package com.djrapitops.plan.db;
|
||||
|
||||
import com.djrapitops.plan.api.exceptions.database.DBInitException;
|
||||
import com.djrapitops.plan.data.container.GeoInfo;
|
||||
import com.djrapitops.plan.db.access.queries.LargeFetchQueries;
|
||||
import com.djrapitops.plan.db.access.queries.OptionalFetchQueries;
|
||||
import com.djrapitops.plan.db.access.queries.ServerAggregateQueries;
|
||||
import com.djrapitops.plan.db.sql.tables.ServerTable;
|
||||
import com.djrapitops.plan.db.sql.tables.UsersTable;
|
||||
import com.djrapitops.plan.system.info.server.Server;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
@ -86,4 +90,31 @@ public class SQLiteTest extends CommonDBTest {
|
||||
assertEquals(1, serverInformation.values().stream().filter(Server::isNotProxy).count());
|
||||
assertEquals(1, serverInformation.values().stream().filter(Server::isProxy).count());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void networkGeolocationsAreCountedAppropriately() {
|
||||
UUID firstUuid = UUID.randomUUID();
|
||||
UUID secondUuid = UUID.randomUUID();
|
||||
UUID thirdUuid = UUID.randomUUID();
|
||||
|
||||
UsersTable usersTable = db.getUsersTable();
|
||||
usersTable.registerUser(firstUuid, 0, "");
|
||||
usersTable.registerUser(secondUuid, 0, "");
|
||||
usersTable.registerUser(thirdUuid, 0, "");
|
||||
|
||||
saveGeoInfo(firstUuid, new GeoInfo("-", "Norway", 0, "3"));
|
||||
saveGeoInfo(firstUuid, new GeoInfo("-", "Finland", 5, "3"));
|
||||
saveGeoInfo(secondUuid, new GeoInfo("-", "Sweden", 0, "3"));
|
||||
saveGeoInfo(thirdUuid, new GeoInfo("-", "Denmark", 0, "3"));
|
||||
|
||||
Map<String, Integer> got = db.query(ServerAggregateQueries.networkGeolocationCounts());
|
||||
|
||||
Map<String, Integer> expected = new HashMap<>();
|
||||
// first user has a more recent connection from Finland so their country should be counted as Finland.
|
||||
expected.put("Finland", 1);
|
||||
expected.put("Sweden", 1);
|
||||
expected.put("Denmark", 1);
|
||||
|
||||
assertEquals(expected, got);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user