aboutsummaryrefslogtreecommitdiff
path: root/juick-server-jdbc/src/main/java/com/juick/service/TagServiceImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'juick-server-jdbc/src/main/java/com/juick/service/TagServiceImpl.java')
-rw-r--r--juick-server-jdbc/src/main/java/com/juick/service/TagServiceImpl.java260
1 files changed, 260 insertions, 0 deletions
diff --git a/juick-server-jdbc/src/main/java/com/juick/service/TagServiceImpl.java b/juick-server-jdbc/src/main/java/com/juick/service/TagServiceImpl.java
new file mode 100644
index 00000000..b5bd0ae8
--- /dev/null
+++ b/juick-server-jdbc/src/main/java/com/juick/service/TagServiceImpl.java
@@ -0,0 +1,260 @@
+package com.juick.service;
+
+import com.juick.Tag;
+import com.juick.server.helpers.TagStats;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.core.RowMapper;
+import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
+import org.springframework.jdbc.support.GeneratedKeyHolder;
+import org.springframework.jdbc.support.KeyHolder;
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.Assert;
+
+import javax.inject.Inject;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * Created by aalexeev on 11/13/16.
+ */
+@Repository
+public class TagServiceImpl extends BaseJdbcService implements TagService {
+ private static final Pattern TAGS_PATTERN1 = Pattern.compile("^(?:(?:\\*[^ \\r\\n\\t]+)|\\s)+$");
+ private static final Pattern TAGS_PATTERN2 = Pattern.compile("^\\*([^ \\r\\n\\t]+)\\s+([\\s\\S]+)");
+ private static final Pattern TAG_PATTERN = Pattern.compile("\\*([^ \\r\\n\\t]+)");
+
+ private final MessagesService messagesService;
+
+ @Inject
+ public TagServiceImpl(JdbcTemplate jdbcTemplate, MessagesService messagesService) {
+ super(jdbcTemplate, null);
+ Assert.notNull(messagesService, "MessagesService must be initialized");
+ this.messagesService = messagesService;
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public com.juick.Tag getTag(final int tid) {
+ List<Tag> list = getJdbcTemplate().query(
+ "SELECT synonym_id,name FROM tags WHERE tag_id=?",
+ (rs, num) -> {
+ Tag ret = new Tag(rs.getString(2));
+ ret.TID = tid;
+ ret.SynonymID = rs.getInt(1);
+ return ret;
+ },
+ tid);
+
+ return list.isEmpty() ?
+ null : list.get(0);
+ }
+
+ @Transactional
+ @Override
+ public com.juick.Tag getTag(final String tag, final boolean autoCreate) {
+ if (StringUtils.isBlank(tag))
+ return null;
+
+ List<Tag> list = getJdbcTemplate().query(
+ "SELECT tag_id, synonym_id, name FROM tags WHERE name = ?",
+ (rs, rowNum) -> {
+ Tag ret1 = new Tag(rs.getString(3));
+ ret1.TID = rs.getInt(1);
+ ret1.SynonymID = rs.getInt(2);
+ return ret1;
+ },
+ tag);
+
+ Tag ret = list.isEmpty() ?
+ null : list.get(0);
+
+ if (ret == null && autoCreate) {
+ ret = new com.juick.Tag(tag);
+ ret.TID = createTag(tag);
+ }
+
+ return ret;
+ }
+
+ @Override
+ public List<Tag> getTags(final String[] tags, final boolean autoCreate) {
+ if (ArrayUtils.isEmpty(tags))
+ return Collections.emptyList();
+
+ List<Tag> ret = new ArrayList<>();
+
+ for (String tag : tags) {
+ if (!tag.isEmpty()) {
+ Tag t = getTag(tag, autoCreate);
+
+ if (t != null)
+ ret.add(t);
+ }
+ }
+
+ return ret.stream().distinct().collect(Collectors.toList());
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public boolean getTagNoIndex(final int tagId) {
+ List<Integer> list = getJdbcTemplate().queryForList(
+ "SELECT noindex FROM tags WHERE tag_id=?", Integer.class, tagId);
+
+ return !list.isEmpty() && list.get(0) == 1;
+ }
+
+ @Transactional
+ @Override
+ public int createTag(final String name) {
+ KeyHolder holder = new GeneratedKeyHolder();
+ getJdbcTemplate().update(
+ con -> {
+ PreparedStatement stmt = con.prepareStatement(
+ "INSERT INTO tags(name) VALUES (?)",
+ Statement.RETURN_GENERATED_KEYS);
+ stmt.setString(1, name);
+ return stmt;
+ },
+ holder);
+
+ return holder.getKey().intValue();
+ }
+
+ private class TagStatsMapper implements RowMapper<TagStats> {
+
+ @Override
+ public TagStats mapRow(ResultSet rs, int rowNum) throws SQLException {
+ Tag t = new Tag(rs.getString(1));
+ TagStats s = new TagStats();
+ s.setTag(t);
+ s.setUsageCount(rs.getInt(2));
+ return s;
+ }
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public List<TagStats> getUserTagStats(final int uid) {
+ return getJdbcTemplate().query(
+ "SELECT tags.name,COUNT(messages.message_id) " +
+ "FROM (messages INNER JOIN messages_tags ON (messages.user_id=? " +
+ "AND messages.message_id=messages_tags.message_id)) " +
+ "INNER JOIN tags ON messages_tags.tag_id=tags.tag_id GROUP BY tags.tag_id ORDER BY tags.name ASC",
+ new TagStatsMapper(),
+ uid);
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public List<String> getUserBLTags(final int uid) {
+ return getJdbcTemplate().queryForList(
+ "SELECT tags.name FROM tags INNER JOIN bl_tags " +
+ "ON (bl_tags.user_id = ? AND bl_tags.tag_id = tags.tag_id) ORDER BY tags.name",
+ String.class, uid);
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public List<String> getPopularTags() {
+ return getJdbcTemplate().queryForList(
+ "SELECT name FROM tags WHERE top=1 ORDER BY name ASC", String.class)
+ .stream()
+ .collect(Collectors.toList());
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public List<TagStats> getTagStats() {
+ return getJdbcTemplate().query(
+ "SELECT tags.name,COUNT(DISTINCT messages.user_id) AS cnt " +
+ "FROM (messages INNER JOIN messages_tags ON (messages.ts>TIMESTAMPADD(DAY,-3,NOW()) " +
+ "AND messages.message_id=messages_tags.message_id)) " +
+ "INNER JOIN tags ON messages_tags.tag_id=tags.tag_id " +
+ "WHERE tags.tag_id NOT IN (SELECT tag_id FROM tags_ignore) " +
+ "GROUP BY tags.tag_id ORDER BY cnt DESC LIMIT 20", new TagStatsMapper());
+ }
+
+ @Transactional
+ @Override
+ public List<Tag> updateTags(final int mid, final Collection<Tag> newTags) {
+ List<Tag> currentTags = getMessageTags(mid).stream()
+ .map(TagStats::getTag).collect(Collectors.toList());
+
+ if (CollectionUtils.isEmpty(newTags))
+ return currentTags;
+
+ List<Integer> idsForDelete = newTags.stream()
+ .filter(currentTags::contains)
+ .map(tag -> tag.TID)
+ .collect(Collectors.toList());
+
+ if (!idsForDelete.isEmpty())
+ getNamedParameterJdbcTemplate().update(
+ "DELETE FROM messages_tags WHERE message_id = :mid AND tag_id in (:ids)",
+ new MapSqlParameterSource().addValue("ids", idsForDelete).addValue("mid", mid));
+
+ newTags.stream().filter(t -> !currentTags.contains(t))
+ .forEach(t -> getJdbcTemplate().update("INSERT INTO messages_tags(message_id,tag_id) VALUES (?,?)", mid, t.TID));
+
+ return getMessageTags(mid).stream()
+ .map(TagStats::getTag).collect(Collectors.toList());
+ }
+
+ @Override
+ public List<Tag> fromString(final String txt, final boolean tagsOnly) {
+ Pattern tagsPattern = tagsOnly ? TAGS_PATTERN1 : TAGS_PATTERN2;
+
+ if (tagsPattern.matcher(txt).matches()) {
+ Matcher tagMatcher = TAG_PATTERN.matcher(txt);
+
+ List<Tag> tags = new ArrayList<>();
+
+ // TODO: process readonly, private, friends, public
+ while (tagMatcher.find()) {
+ for (int i = 1; i <= tagMatcher.groupCount(); i++)
+ tags.add(getTag(tagMatcher.group(i), true));
+ }
+ return tags;
+ }
+ return Collections.emptyList();
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public List<TagStats> getMessageTags(final int mid) {
+ return getJdbcTemplate().query(
+ "SELECT tags.tag_id,synonym_id,name,stat_messages FROM tags " +
+ "INNER JOIN messages_tags ON (messages_tags.message_id = ? AND messages_tags.tag_id = tags.tag_id)",
+ (rs, num) -> {
+ com.juick.Tag t = new com.juick.Tag(rs.getString(3));
+ t.TID = rs.getInt(1);
+ t.SynonymID = rs.getInt(2);
+ TagStats s = new TagStats();
+ s.setTag(t);
+ s.setUsageCount(rs.getInt(4));
+ return s;
+ }, mid);
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public List<Integer> getMessageTagsIDs(final int mid) {
+ return getJdbcTemplate().queryForList(
+ "SELECT tag_id FROM messages_tags WHERE message_id = ?",
+ Integer.class, mid);
+ }
+}