Implemented a PluginBooleanGroupFilter (#2025)

Affects issues:
- Close #1860
This commit is contained in:
Risto Lahtela 2021-07-24 10:31:03 +03:00 committed by GitHub
parent 750428a033
commit a2cface3e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 279 additions and 2 deletions

View File

@ -53,4 +53,8 @@ public interface FiltersModule {
@IntoSet @IntoSet
Filter filter7(GeolocationsFilter filter); Filter filter7(GeolocationsFilter filter);
@Binds
@IntoSet
Filter filter8(PluginBooleanGroupFilter filter);
} }

View File

@ -0,0 +1,248 @@
/*
* This file is part of Player Analytics (Plan).
*
* Plan is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License v3 as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Plan is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Plan. If not, see <https://www.gnu.org/licenses/>.
*/
package com.djrapitops.plan.storage.database.queries.filter.filters;
import com.djrapitops.plan.identification.Server;
import com.djrapitops.plan.identification.ServerUUID;
import com.djrapitops.plan.storage.database.DBSystem;
import com.djrapitops.plan.storage.database.Database;
import com.djrapitops.plan.storage.database.queries.Query;
import com.djrapitops.plan.storage.database.queries.QueryAllStatement;
import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.queries.filter.SpecifiedFilterInformation;
import com.djrapitops.plan.storage.database.queries.objects.ServerQueries;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionPlayerValueTable;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionPluginTable;
import com.djrapitops.plan.storage.database.sql.tables.ExtensionProviderTable;
import com.djrapitops.plan.storage.database.sql.tables.ServerTable;
import org.apache.commons.lang3.StringUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;
import static com.djrapitops.plan.storage.database.sql.building.Sql.*;
@Singleton
public class PluginBooleanGroupFilter extends MultiOptionFilter {
private final DBSystem dbSystem;
@Inject
public PluginBooleanGroupFilter(DBSystem dbSystem) {
this.dbSystem = dbSystem;
}
private static Query<List<PluginBooleanOption>> pluginBooleanOptionsQuery() {
String selectOptions = SELECT +
"server." + ServerTable.SERVER_ID + " as server_id," +
"server." + ServerTable.NAME + " as server_name," +
"plugin." + ExtensionPluginTable.PLUGIN_NAME + " as plugin_name," +
"provider." + ExtensionProviderTable.TEXT + " as provider_text" +
FROM + ServerTable.TABLE_NAME + " server" +
INNER_JOIN + ExtensionPluginTable.TABLE_NAME + " plugin on plugin." + ExtensionPluginTable.SERVER_UUID + "=server." + ServerTable.SERVER_UUID +
INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " provider on provider." + ExtensionProviderTable.PLUGIN_ID + "=plugin." + ExtensionPluginTable.ID +
INNER_JOIN + ExtensionPlayerValueTable.TABLE_NAME + " value on value." + ExtensionPlayerValueTable.PROVIDER_ID + "=provider." + ExtensionProviderTable.ID +
WHERE + "value." + ExtensionPlayerValueTable.BOOLEAN_VALUE + " IS NOT NULL" +
ORDER_BY + "server_name ASC, plugin_name ASC, provider_text ASC";
return new QueryAllStatement<List<PluginBooleanOption>>(selectOptions) {
@Override
public List<PluginBooleanOption> processResults(ResultSet set) throws SQLException {
List<PluginBooleanOption> options = new ArrayList<>();
while (set.next()) {
int serverId = set.getInt("server_id");
String serverName = set.getString("server_name");
String pluginName = set.getString("plugin_name");
String providerText = set.getString("provider_text");
options.add(new PluginBooleanOption(
Server.getIdentifiableName(serverName, serverId),
pluginName,
providerText
));
}
Collections.sort(options);
return options;
}
};
}
private static Query<Set<UUID>> playersInGroups(
Map<PluginBooleanOption, SelectedBoolean> selected,
Map<String, ServerUUID> namesToUUIDs
) {
return db -> {
Set<UUID> playerUUIDs = new HashSet<>();
for (Map.Entry<PluginBooleanOption, SelectedBoolean> option : selected.entrySet()) {
PluginBooleanOption pluginBooleanOption = option.getKey();
SelectedBoolean selectedBoolean = option.getValue();
playerUUIDs.addAll(
db.query(playersInGroup(
namesToUUIDs.get(pluginBooleanOption.getServerName()),
pluginBooleanOption.getPluginName(),
pluginBooleanOption.getProviderText(),
selectedBoolean
))
);
}
return playerUUIDs;
};
}
private static Query<Set<UUID>> playersInGroup(
ServerUUID serverUUID, String pluginName, String providerText, SelectedBoolean selectedBoolean
) {
String selectUUIDsWithBooleanValues = SELECT + DISTINCT + "value." + ExtensionPlayerValueTable.USER_UUID + " as uuid" +
FROM + ExtensionPluginTable.TABLE_NAME + " plugin" +
INNER_JOIN + ExtensionProviderTable.TABLE_NAME + " provider on provider." + ExtensionProviderTable.PLUGIN_ID + "=plugin." + ExtensionPluginTable.ID +
INNER_JOIN + ExtensionPlayerValueTable.TABLE_NAME + " value on value." + ExtensionPlayerValueTable.PROVIDER_ID + "=provider." + ExtensionProviderTable.ID +
WHERE + "plugin." + ExtensionPluginTable.SERVER_UUID + "=?" +
AND + "plugin." + ExtensionPluginTable.PLUGIN_NAME + "=?" +
AND + "provider." + ExtensionProviderTable.TEXT + "=?" +
AND + "value." + ExtensionPlayerValueTable.BOOLEAN_VALUE + (selectedBoolean == SelectedBoolean.BOTH ? "IS NOT NULL" : "=?");
return new QueryStatement<Set<UUID>>(selectUUIDsWithBooleanValues) {
@Override
public void prepare(PreparedStatement statement) throws SQLException {
statement.setString(1, serverUUID.toString());
statement.setString(2, pluginName);
statement.setString(3, providerText);
if (selectedBoolean != SelectedBoolean.BOTH) {
statement.setBoolean(4, selectedBoolean == SelectedBoolean.TRUE);
}
}
@Override
public Set<UUID> processResults(ResultSet set) throws SQLException {
Set<UUID> uuids = new HashSet<>();
while (set.next()) {
uuids.add(UUID.fromString(set.getString("uuid")));
}
return uuids;
}
};
}
@Override
public String getKind() {
return "pluginsBooleanGroups";
}
private List<String> getOptionList() {
Database database = dbSystem.getDatabase();
List<PluginBooleanOption> pluginBooleanOptions = database.query(pluginBooleanOptionsQuery());
List<String> options = new ArrayList<>();
for (PluginBooleanOption pluginBooleanOption : pluginBooleanOptions) {
String names = pluginBooleanOption.format();
options.add(names + ": true");
options.add(names + ": false");
}
return options;
}
@Override
public Map<String, Object> getOptions() {
return Collections.singletonMap("options", getOptionList());
}
@Override
public Set<UUID> getMatchingUUIDs(SpecifiedFilterInformation query) {
Map<PluginBooleanOption, SelectedBoolean> selectedBooleanOptions = new HashMap<>();
for (String selected : getSelected(query)) {
String[] optionAndBoolean = StringUtils.split(selected, ":", 2);
PluginBooleanOption pluginBooleanOption = PluginBooleanOption.parse(optionAndBoolean[0].trim());
String selectedBoolean = optionAndBoolean[1].trim().toUpperCase();
selectedBooleanOptions.computeIfPresent(pluginBooleanOption, (key, existing) -> SelectedBoolean.BOTH);
selectedBooleanOptions.computeIfAbsent(pluginBooleanOption, key -> SelectedBoolean.valueOf(selectedBoolean));
}
Database db = dbSystem.getDatabase();
Map<String, ServerUUID> namesToUUIDs = db.query(ServerQueries.fetchServerNamesToUUIDs());
return db.query(playersInGroups(selectedBooleanOptions, namesToUUIDs));
}
public enum SelectedBoolean {
TRUE,
FALSE,
BOTH
}
private static class PluginBooleanOption implements Comparable<PluginBooleanOption> {
private final String serverName;
private final String pluginName;
private final String providerText;
public PluginBooleanOption(String serverName, String pluginName, String providerText) {
this.serverName = serverName;
this.pluginName = pluginName;
this.providerText = providerText;
}
public static PluginBooleanOption parse(String fromFormatted) {
String[] split1 = StringUtils.split(fromFormatted, ",", 2);
String[] split2 = StringUtils.split(split1[1], "-", 2);
String serverName = split1[0].trim();
String pluginName = split2[0].trim();
String providerName = split2[1].trim();
return new PluginBooleanOption(serverName, pluginName, providerName);
}
public String getServerName() {
return serverName;
}
public String getPluginName() {
return pluginName;
}
public String getProviderText() {
return providerText;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PluginBooleanOption that = (PluginBooleanOption) o;
return Objects.equals(getServerName(), that.getServerName()) && Objects.equals(getPluginName(), that.getPluginName()) && Objects.equals(getProviderText(), that.getProviderText());
}
@Override
public int hashCode() {
return Objects.hash(getServerName(), getPluginName(), getProviderText());
}
@Override
public int compareTo(PluginBooleanOption o) {
int serverNameAlphabetical = String.CASE_INSENSITIVE_ORDER.compare(serverName, o.serverName);
if (serverNameAlphabetical != 0) return serverNameAlphabetical;
int pluginNameAlphabetical = String.CASE_INSENSITIVE_ORDER.compare(pluginName, o.pluginName);
if (pluginNameAlphabetical != 0) return pluginNameAlphabetical;
return String.CASE_INSENSITIVE_ORDER.compare(providerText, o.providerText);
}
public String format() {
return serverName + ", " + pluginName + " - " + providerText;
}
}
}

View File

@ -23,6 +23,7 @@ import com.djrapitops.plan.storage.database.queries.QueryAllStatement;
import com.djrapitops.plan.storage.database.queries.QueryStatement; import com.djrapitops.plan.storage.database.queries.QueryStatement;
import com.djrapitops.plan.storage.database.sql.building.Select; import com.djrapitops.plan.storage.database.sql.building.Select;
import com.djrapitops.plan.storage.database.sql.tables.ServerTable; import com.djrapitops.plan.storage.database.sql.tables.ServerTable;
import com.djrapitops.plan.utilities.java.Maps;
import org.apache.commons.lang3.math.NumberUtils; import org.apache.commons.lang3.math.NumberUtils;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
@ -148,7 +149,7 @@ public class ServerQueries {
public static Query<Map<ServerUUID, String>> fetchServerNames() { public static Query<Map<ServerUUID, String>> fetchServerNames() {
String sql = Select.from(ServerTable.TABLE_NAME, String sql = Select.from(ServerTable.TABLE_NAME,
ServerTable.SERVER_UUID, ServerTable.NAME) ServerTable.SERVER_ID, ServerTable.SERVER_UUID, ServerTable.NAME)
.toString(); .toString();
return new QueryAllStatement<Map<ServerUUID, String>>(sql) { return new QueryAllStatement<Map<ServerUUID, String>>(sql) {
@ -157,7 +158,7 @@ public class ServerQueries {
Map<ServerUUID, String> names = new HashMap<>(); Map<ServerUUID, String> names = new HashMap<>();
while (set.next()) { while (set.next()) {
ServerUUID serverUUID = ServerUUID.fromString(set.getString(ServerTable.SERVER_UUID)); ServerUUID serverUUID = ServerUUID.fromString(set.getString(ServerTable.SERVER_UUID));
names.put(serverUUID, set.getString(ServerTable.NAME)); names.put(serverUUID, Server.getIdentifiableName(set.getString(ServerTable.NAME), set.getInt(ServerTable.SERVER_ID)));
} }
return names; return names;
} }
@ -233,4 +234,8 @@ public class ServerQueries {
} }
}; };
} }
public static Query<Map<String, ServerUUID>> fetchServerNamesToUUIDs() {
return db -> Maps.reverse(db.query(fetchServerNames()));
}
} }

View File

@ -44,6 +44,14 @@ public class Maps {
return new Builder<>(); return new Builder<>();
} }
public static <K, V> Map<V, K> reverse(Map<K, V> map) {
Map<V, K> reversed = new HashMap<>();
for (Map.Entry<K, V> entry : map.entrySet()) {
reversed.put(entry.getValue(), entry.getKey());
}
return reversed;
}
public static class Builder<K, V> { public static class Builder<K, V> {
private final Map<K, V> map; private final Map<K, V> map;

View File

@ -102,6 +102,14 @@ class geolocationsFilter extends MultipleChoiceFilter {
} }
} }
class PluginBooleanGroupsFilter extends MultipleChoiceFilter {
constructor(
id, options
) {
super(id, "pluginsBooleanGroups", `have Plugin boolean value`, options);
}
}
class PluginGroupsFilter extends MultipleChoiceFilter { class PluginGroupsFilter extends MultipleChoiceFilter {
constructor( constructor(
id, kind, options id, kind, options
@ -220,6 +228,8 @@ function createFilter(filter, id) {
return new PlayedBetweenFilter(id, filter.options); return new PlayedBetweenFilter(id, filter.options);
case "registeredBetween": case "registeredBetween":
return new RegisteredBetweenFilter(id, filter.options); return new RegisteredBetweenFilter(id, filter.options);
case "pluginsBooleanGroups":
return new PluginBooleanGroupsFilter(id, filter.options);
default: default:
throw new Error("Unsupported filter kind: '" + filter.kind + "'"); throw new Error("Unsupported filter kind: '" + filter.kind + "'");
} }
@ -246,6 +256,8 @@ function getReadableFilterName(filter) {
return "Played between"; return "Played between";
case "registeredBetween": case "registeredBetween":
return "Registered between"; return "Registered between";
case "pluginsBooleanGroups":
return "Has plugin boolean value";
default: default:
return filter.kind; return filter.kind;
} }