From f49dfacc335e00bacf94a41a295ac1b30b6b6768 Mon Sep 17 00:00:00 2001 From: Partow Roshani Date: Tue, 5 Aug 2025 13:59:01 +0330 Subject: [PATCH] fix search in messages --- .../Client/ActionHandler.java | 33 ++- .../Database/MessageDatabase.java | 252 ++++++++++++++---- .../telegramfinalproject/Models/Message.java | 162 ++++------- .../Server/ClientHandler.java | 27 +- 4 files changed, 310 insertions(+), 164 deletions(-) diff --git a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java index e08c013..36ab8f5 100644 --- a/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Client/ActionHandler.java @@ -607,12 +607,24 @@ public class ActionHandler { String type = item.getString("type"); if (type.equals("message")) { - System.out.println((i + 1) + ". [message] \"" + item.getString("content") + "\"" - + " (from: " + item.optString("sender", "N/A") + ", at: " + item.getString("time") + ")"); + String senderName = item.optString("sender_name", item.optString("sender", "Unknown")); + String time = item.optString("time", "Unknown time"); + String content = item.optString("content", "[No content]"); + String context = ""; + + if (item.has("group_name")) { + context = " in group: " + item.getString("group_name"); + } else if (item.has("channel_name")) { + context = " in channel: " + item.getString("channel_name"); + } + + System.out.println((i + 1) + ". [message] \"" + content + "\"" + + " (from: " + senderName + context + ", at: " + time + ")"); } else { System.out.println((i + 1) + ". [" + type + "] " + item.getString("name") + " (ID: " + item.getString("id") + ")"); } + } System.out.print("Select a result number to interact (or 0 to exit): "); @@ -714,6 +726,22 @@ public class ActionHandler { case "message" -> { String receiverId = selected.getString("receiver_id"); String receiverType = selected.getString("receiver_type"); + String senderName = selected.optString("sender_name", "Unknown"); + String content = selected.optString("content", "[No content]"); + String time = selected.optString("time", "Unknown time"); + + String context = ""; + if (receiverType.equals("group")) { + context = "in group: " + selected.optString("group_name", "Unknown group"); + } else if (receiverType.equals("channel")) { + context = "in channel: " + selected.optString("channel_name", "Unknown channel"); + } + + System.out.println("📩 Message preview:"); + System.out.println("From: " + senderName); + if (!context.isEmpty()) System.out.println(context); + System.out.println("Time: " + time); + System.out.println("Content: \"" + content + "\""); System.out.println("🔍 Looking for chat with displayId='" + receiverId + "' type='" + receiverType + "'"); @@ -725,6 +753,7 @@ public class ActionHandler { openChat(chat); } + default -> System.out.println("No interaction available for type: " + type); } } diff --git a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java index e3b25df..345bef5 100644 --- a/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java +++ b/src/main/java/org/to/telegramfinalproject/Database/MessageDatabase.java @@ -6,6 +6,7 @@ import org.to.telegramfinalproject.Models.Message; import java.sql.*; import java.time.LocalDateTime; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.UUID; import java.util.stream.Collectors; @@ -128,38 +129,43 @@ public class MessageDatabase { - public static List getUnreadMessages(UUID userId) { List messages = new ArrayList<>(); String sql = """ - SELECT m.* FROM messages m + SELECT m.* + FROM messages m LEFT JOIN message_receipts r ON m.message_id = r.message_id AND r.user_id = ? + LEFT JOIN deleted_messages d ON m.message_id = d.message_id AND d.user_id = ? WHERE r.user_id IS NULL - AND ( - (m.receiver_type = 'private' AND m.receiver_id = ?) - OR - (m.receiver_type = 'group' AND EXISTS ( - SELECT 1 FROM group_members gm WHERE gm.group_id = m.receiver_id AND gm.user_id = ? - )) - OR - (m.receiver_type = 'channel' AND EXISTS ( - SELECT 1 FROM channel_subscribers cs WHERE cs.channel_id = m.receiver_id AND cs.user_id = ? - )) - ) + AND d.message_id IS NULL + AND m.is_deleted_globally = FALSE + AND ( + (m.receiver_type = 'private' AND m.receiver_id = ?) + OR + (m.receiver_type = 'group' AND EXISTS ( + SELECT 1 FROM group_members gm WHERE gm.group_id = m.receiver_id AND gm.user_id = ? + )) + OR + (m.receiver_type = 'channel' AND EXISTS ( + SELECT 1 FROM channel_subscribers cs WHERE cs.channel_id = m.receiver_id AND cs.user_id = ? + )) + ) + ORDER BY m.send_at DESC """; try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { - stmt.setObject(1, userId); - stmt.setObject(2, userId); - stmt.setObject(3, userId); - stmt.setObject(4, userId); + stmt.setObject(1, userId); // for message_receipts + stmt.setObject(2, userId); // for deleted_messages + stmt.setObject(3, userId); // for private messages + stmt.setObject(4, userId); // for group members + stmt.setObject(5, userId); // for channel subscribers ResultSet rs = stmt.executeQuery(); while (rs.next()) { - messages.add(new Message( + Message message = new Message( UUID.fromString(rs.getString("message_id")), rs.getObject("sender_id") != null ? UUID.fromString(rs.getString("sender_id")) : null, rs.getString("receiver_type"), @@ -170,10 +176,13 @@ public class MessageDatabase { rs.getString("status"), rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null, rs.getBoolean("is_edited"), + rs.getBoolean("is_deleted_globally"), rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null, rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null, rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null - )); + ); + + messages.add(message); } } catch (SQLException e) { @@ -183,6 +192,8 @@ public class MessageDatabase { return messages; } + + public static List getAttachments(UUID messageId) { List attachments = new ArrayList<>(); String sql = "SELECT file_url, file_type FROM message_attachments WHERE message_id = ?"; @@ -271,6 +282,7 @@ public class MessageDatabase { rs.getString("status"), (UUID) rs.getObject("reply_to_id"), rs.getBoolean("is_edited"), + rs.getBoolean("is_deleted_globally"), (UUID) rs.getObject("original_message_id"), (UUID) rs.getObject("forwarded_by"), (UUID) rs.getObject("forwarded_from") @@ -279,33 +291,90 @@ public class MessageDatabase { + +// public static List searchMessagesForUser(UUID userId, String keyword) { +// List result = new ArrayList<>(); +// String sql = """ +// SELECT * FROM messages +// WHERE receiver_type = 'private' +// AND (sender_id = ? OR receiver_id = ?) +// AND content ILIKE ? +// ORDER BY send_at DESC +// """; +// +// try (Connection conn = ConnectionDb.connect(); +// PreparedStatement stmt = conn.prepareStatement(sql)) { +// stmt.setObject(1, userId); +// stmt.setObject(2, userId); +// stmt.setString(3, "%" + keyword + "%"); +// +// ResultSet rs = stmt.executeQuery(); +// while (rs.next()) { +// result.add(extractMessage(rs)); +// } +// } catch (SQLException e) { +// e.printStackTrace(); +// } +// +// return result; +// } + public static List searchMessagesForUser(UUID userId, String keyword) { - List result = new ArrayList<>(); String sql = """ - SELECT * FROM messages - WHERE receiver_type = 'private' - AND (sender_id = ? OR receiver_id = ?) - AND content ILIKE ? - ORDER BY send_at DESC - """; + SELECT * FROM messages m + WHERE m.receiver_type = 'private' + AND m.content ILIKE ? + AND m.is_deleted_globally = FALSE + AND (m.sender_id = ? OR m.receiver_id = ?) + AND NOT EXISTS ( + SELECT 1 FROM deleted_messages d + WHERE d.message_id = m.message_id AND d.user_id = ? + ) + ORDER BY m.send_at DESC + """; try (Connection conn = ConnectionDb.connect(); - PreparedStatement stmt = conn.prepareStatement(sql)) { - stmt.setObject(1, userId); - stmt.setObject(2, userId); - stmt.setString(3, "%" + keyword + "%"); + PreparedStatement ps = conn.prepareStatement(sql)) { - ResultSet rs = stmt.executeQuery(); + ps.setString(1, "%" + keyword + "%"); + ps.setObject(2, userId); // m.sender_id + ps.setObject(3, userId); // m.receiver_id + ps.setObject(4, userId); // deleted_messages + + ResultSet rs = ps.executeQuery(); + List messages = new ArrayList<>(); while (rs.next()) { - result.add(extractMessage(rs)); + messages.add(mapResultSetToMessage(rs)); } + return messages; + } catch (SQLException e) { e.printStackTrace(); + return Collections.emptyList(); } - - return result; } + private static Message mapResultSetToMessage(ResultSet rs) throws SQLException { + return new Message( + UUID.fromString(rs.getString("message_id")), + rs.getObject("sender_id") != null ? UUID.fromString(rs.getString("sender_id")) : null, + rs.getString("receiver_type"), + UUID.fromString(rs.getString("receiver_id")), + rs.getString("content"), + rs.getString("message_type"), + rs.getTimestamp("send_at").toLocalDateTime(), + rs.getString("status"), + rs.getObject("reply_to_id") != null ? UUID.fromString(rs.getString("reply_to_id")) : null, + rs.getBoolean("is_edited"), + rs.getBoolean("is_deleted_globally"), + rs.getObject("original_message_id") != null ? UUID.fromString(rs.getString("original_message_id")) : null, + rs.getObject("forwarded_by") != null ? UUID.fromString(rs.getString("forwarded_by")) : null, + rs.getObject("forwarded_from") != null ? UUID.fromString(rs.getString("forwarded_from")) : null + ); + } + + + // public static List privateChatHistory(UUID chatId) { // List result = new ArrayList<>(); // String sql = """ @@ -464,32 +533,74 @@ public class MessageDatabase { - public static List searchMessagesInGroups(List groupIds, String keyword) { +// public static List searchMessagesInGroups(List groupIds, String keyword) { +// List result = new ArrayList<>(); +// if (groupIds.isEmpty()) return result; +// +// String placeholders = groupIds.stream().map(id -> "?").collect(Collectors.joining(", ")); +// String sql = """ +// SELECT * FROM messages +// WHERE receiver_type = 'group' +// AND receiver_id IN (%s) +// AND content ILIKE ? +// ORDER BY send_at DESC +// """.formatted(placeholders); +// +// try (Connection conn = ConnectionDb.connect(); +// PreparedStatement stmt = conn.prepareStatement(sql)) { +// +// int i = 1; +// for (UUID id : groupIds) { +// stmt.setObject(i++, id); +// } +// stmt.setString(i, "%" + keyword + "%"); +// +// ResultSet rs = stmt.executeQuery(); +// while (rs.next()) { +// result.add(extractMessage(rs)); +// } +// } catch (SQLException e) { +// e.printStackTrace(); +// } +// +// return result; +// } + + + + public static List searchMessagesInGroups(List groupIds, String keyword, UUID userId) { List result = new ArrayList<>(); if (groupIds.isEmpty()) return result; String placeholders = groupIds.stream().map(id -> "?").collect(Collectors.joining(", ")); String sql = """ - SELECT * FROM messages - WHERE receiver_type = 'group' - AND receiver_id IN (%s) - AND content ILIKE ? - ORDER BY send_at DESC + SELECT m.* + FROM messages m + LEFT JOIN deleted_messages d ON m.message_id = d.message_id AND d.user_id = ? + WHERE m.receiver_type = 'group' + AND m.receiver_id IN (%s) + AND d.message_id IS NULL + AND m.is_deleted_globally = FALSE + AND m.content ILIKE ? + ORDER BY m.send_at DESC """.formatted(placeholders); try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { int i = 1; - for (UUID id : groupIds) { - stmt.setObject(i++, id); + stmt.setObject(i++, userId); + for (UUID groupId : groupIds) { + stmt.setObject(i++, groupId); } + stmt.setString(i, "%" + keyword + "%"); ResultSet rs = stmt.executeQuery(); while (rs.next()) { - result.add(extractMessage(rs)); + result.add(mapResultSetToMessage(rs)); } + } catch (SQLException e) { e.printStackTrace(); } @@ -498,32 +609,71 @@ public class MessageDatabase { } +// public static List searchMessagesInChannels(List channelIds, String keyword) { +// List result = new ArrayList<>(); +// if (channelIds.isEmpty()) return result; +// +// String placeholders = channelIds.stream().map(id -> "?").collect(Collectors.joining(", ")); +// String sql = """ +// SELECT * FROM messages +// WHERE receiver_type = 'channel' +// AND receiver_id IN (%s) +// AND content ILIKE ? +// ORDER BY send_at DESC +// """.formatted(placeholders); +// +// try (Connection conn = ConnectionDb.connect(); +// PreparedStatement stmt = conn.prepareStatement(sql)) { +// +// int i = 1; +// for (UUID id : channelIds) { +// stmt.setObject(i++, id); +// } +// stmt.setString(i, "%" + keyword + "%"); +// +// ResultSet rs = stmt.executeQuery(); +// while (rs.next()) { +// result.add(extractMessage(rs)); +// } +// } catch (SQLException e) { +// e.printStackTrace(); +// } +// +// return result; +// } + + + public static List searchMessagesInChannels(List channelIds, String keyword) { List result = new ArrayList<>(); if (channelIds.isEmpty()) return result; String placeholders = channelIds.stream().map(id -> "?").collect(Collectors.joining(", ")); String sql = """ - SELECT * FROM messages - WHERE receiver_type = 'channel' - AND receiver_id IN (%s) - AND content ILIKE ? - ORDER BY send_at DESC + SELECT m.* + FROM messages m + WHERE m.receiver_type = 'channel' + AND m.receiver_id IN (%s) + AND m.is_deleted_globally = FALSE + AND m.content ILIKE ? + ORDER BY m.send_at DESC """.formatted(placeholders); try (Connection conn = ConnectionDb.connect(); PreparedStatement stmt = conn.prepareStatement(sql)) { int i = 1; - for (UUID id : channelIds) { - stmt.setObject(i++, id); + for (UUID channelId : channelIds) { + stmt.setObject(i++, channelId); } + stmt.setString(i, "%" + keyword + "%"); ResultSet rs = stmt.executeQuery(); while (rs.next()) { - result.add(extractMessage(rs)); + result.add(mapResultSetToMessage(rs)); } + } catch (SQLException e) { e.printStackTrace(); } diff --git a/src/main/java/org/to/telegramfinalproject/Models/Message.java b/src/main/java/org/to/telegramfinalproject/Models/Message.java index a3de400..cccf880 100644 --- a/src/main/java/org/to/telegramfinalproject/Models/Message.java +++ b/src/main/java/org/to/telegramfinalproject/Models/Message.java @@ -7,23 +7,25 @@ import java.util.UUID; public class Message { private UUID message_id; private UUID sender_id; - private String receiver_type; // private, group, channel + private String receiver_type; private UUID receiver_id; private String content; - private String message_type; // TEXT, IMAGE, FILE, ... + private String message_type; private LocalDateTime send_at; - private String status; // SENT, DELIVERED, READ + private String status; private UUID reply_to_id; private boolean is_edited; + private boolean is_deleted_globally; private UUID original_message_id; private UUID forwarded_by; private UUID forwarded_from; private List attachments; + // ✅ Full Constructor public Message(UUID message_id, UUID sender_id, String receiver_type, UUID receiver_id, String content, String message_type, LocalDateTime send_at, String status, - UUID reply_to_id, boolean is_edited, UUID original_message_id, - UUID forwarded_by, UUID forwarded_from) { + UUID reply_to_id, boolean is_edited, boolean is_deleted_globally, + UUID original_message_id, UUID forwarded_by, UUID forwarded_from) { this.message_id = message_id; this.sender_id = sender_id; this.receiver_type = receiver_type; @@ -34,127 +36,69 @@ public class Message { this.status = status; this.reply_to_id = reply_to_id; this.is_edited = is_edited; + this.is_deleted_globally = is_deleted_globally; this.original_message_id = original_message_id; this.forwarded_by = forwarded_by; this.forwarded_from = forwarded_from; } - public Message(UUID messageId, UUID senderId, UUID receiverId, String receiverType, String content, String messageType, LocalDateTime now) { + // ✅ Short Constructor + public Message(UUID messageId, UUID senderId, UUID receiverId, String receiverType, + String content, String messageType, LocalDateTime sendAt) { this.message_id = messageId; this.sender_id = senderId; this.receiver_id = receiverId; this.receiver_type = receiverType; this.content = content; this.message_type = messageType; - this.send_at = now; + this.send_at = sendAt; } + // ✅ Getters & Setters + public UUID getMessage_id() { return message_id; } + public void setMessage_id(UUID messageId) { this.message_id = messageId; } - public UUID getMessage_id() { - return message_id; + public UUID getSender_id() { return sender_id; } + public void setSender_id(UUID sender_id) { this.sender_id = sender_id; } + + public String getReceiver_type() { return receiver_type; } + public void setReceiver_type(String receiver_type) { this.receiver_type = receiver_type; } + + public UUID getReceiver_id() { return receiver_id; } + public void setReceiver_id(UUID receiver_id) { this.receiver_id = receiver_id; } + + public String getContent() { return content; } + public void setContent(String content) { this.content = content; } + + public String getMessage_type() { return message_type; } + public void setMessage_type(String message_type) { this.message_type = message_type; } + + public LocalDateTime getSend_at() { return send_at; } + public void setSend_at(LocalDateTime send_at) { this.send_at = send_at; } + + public String getStatus() { return status; } + public void setStatus(String status) { this.status = status; } + + public UUID getReply_to_id() { return reply_to_id; } + public void setReply_to_id(UUID reply_to_id) { this.reply_to_id = reply_to_id; } + + public boolean isIs_edited() { return is_edited; } + public void setIs_edited(boolean is_edited) { this.is_edited = is_edited; } + + public boolean isIs_deleted_globally() { return is_deleted_globally; } + public void setIs_deleted_globally(boolean is_deleted_globally) { + this.is_deleted_globally = is_deleted_globally; } + public UUID getOriginal_message_id() { return original_message_id; } + public void setOriginal_message_id(UUID original_message_id) { this.original_message_id = original_message_id; } - public UUID getSender_id() { - return sender_id; - } + public UUID getForwarded_by() { return forwarded_by; } + public void setForwarded_by(UUID forwarded_by) { this.forwarded_by = forwarded_by; } - public void setSender_id(UUID sender_id) { - this.sender_id = sender_id; - } + public UUID getForwarded_from() { return forwarded_from; } + public void setForwarded_from(UUID forwarded_from) { this.forwarded_from = forwarded_from; } - public String getReceiver_type() { - return receiver_type; - } - - public void setReceiver_type(String receiver_type) { - this.receiver_type = receiver_type; - } - - public UUID getReceiver_id() { - return receiver_id; - } - - public void setReceiver_id(UUID receiver_id) { - this.receiver_id = receiver_id; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getMessage_type() { - return message_type; - } - - public void setMessage_type(String message_type) { - this.message_type = message_type; - } - - public LocalDateTime getSend_at() { - return send_at; - } - - public void setSend_at(LocalDateTime send_at) { - this.send_at = send_at; - } - - public String getStatus() { - return status; - } - - public void setStatus(String status) { - this.status = status; - } - - public UUID getReply_to_id() { - return reply_to_id; - } - - public void setReply_to_id(UUID reply_to_id) { - this.reply_to_id = reply_to_id; - } - - public boolean isIs_edited() { - return is_edited; - } - - public void setIs_edited(boolean is_edited) { - this.is_edited = is_edited; - } - - public UUID getOriginal_message_id() { - return original_message_id; - } - - public void setOriginal_message_id(UUID original_message_id) { - this.original_message_id = original_message_id; - } - - public UUID getForwarded_by() { - return forwarded_by; - } - - public void setForwarded_by(UUID forwarded_by) { - this.forwarded_by = forwarded_by; - } - - public UUID getForwarded_from() { - return forwarded_from; - } - - public void setForwarded_from(UUID forwarded_from) { - this.forwarded_from = forwarded_from; - } - - public void setAttachments(List attachments) { - this.attachments = attachments; - } - public List getAttachments() { - return attachments; - } + public List getAttachments() { return attachments; } + public void setAttachments(List attachments) { this.attachments = attachments; } } diff --git a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java index 07bfa1c..918f441 100644 --- a/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java +++ b/src/main/java/org/to/telegramfinalproject/Server/ClientHandler.java @@ -419,10 +419,14 @@ public class ClientHandler implements Runnable { JSONObject obj = new JSONObject(); obj.put("type", "message"); obj.put("content", m.getContent()); - obj.put("sender", m.getSender_id().toString()); obj.put("time", m.getSend_at().toString()); + obj.put("sender", m.getSender_id() != null ? m.getSender_id().toString() : "N/A"); obj.put("receiver_id", m.getReceiver_id().toString()); obj.put("receiver_type", m.getReceiver_type()); + User senderUser = userDatabase.findByInternalUUID(m.getSender_id()); + String senderName = senderUser != null ? senderUser.getProfile_name() : "Unknown"; + obj.put("sender_name", senderName); + results.add(obj); } @@ -439,13 +443,23 @@ public class ClientHandler implements Runnable { } - List groupMessages = MessageDatabase.searchMessagesInGroups(groupIds, keyword); + List groupMessages = MessageDatabase.searchMessagesInGroups(groupIds, keyword, currentUser.getInternal_uuid()); for (Message m : groupMessages) { JSONObject obj = new JSONObject(); obj.put("type", "message"); obj.put("from", "group"); obj.put("content", m.getContent()); obj.put("time", m.getSend_at().toString()); + obj.put("sender", m.getSender_id() != null ? m.getSender_id().toString() : "N/A"); + obj.put("receiver_id", m.getReceiver_id().toString()); + obj.put("receiver_type", "group"); + Group senderGroup = GroupDatabase.findByInternalUUID(m.getReceiver_id()); + String groupName = senderGroup != null ? senderGroup.getGroup_name() : "Unknown"; + obj.put("group_name", groupName); + User senderUser = userDatabase.findByInternalUUID(m.getSender_id()); + String senderName = senderUser != null ? senderUser.getProfile_name() : "Unknown"; + obj.put("sender_name", senderName); + results.add(obj); } @@ -456,6 +470,15 @@ public class ClientHandler implements Runnable { obj.put("from", "channel"); obj.put("content", m.getContent()); obj.put("time", m.getSend_at().toString()); + obj.put("sender", m.getSender_id() != null ? m.getSender_id().toString() : "N/A"); + obj.put("receiver_id", m.getReceiver_id().toString()); + obj.put("receiver_type", "channel"); + Channel SenderChannel = ChannelDatabase.findByInternalUUID(m.getReceiver_id()); + String channelName = SenderChannel != null ? SenderChannel.getChannel_name() : "Unknown"; + obj.put("channel_name", channelName); + User senderUser = userDatabase.findByInternalUUID(m.getSender_id()); + String senderName = senderUser != null ? senderUser.getProfile_name() : "Unknown"; + obj.put("sender_name", senderName); results.add(obj); }