Is there a simpler way to achieve this style of user messaging?

前端 未结 13 1144
轮回少年
轮回少年 2020-12-24 12:20

I have created a messaging system for users, it allows them to send a message to another user. If it is the first time they have spoken then a new conversation is initiated,

相关标签:
13条回答
  • 2020-12-24 13:03

    If you're asking for a way to be able to keep all of your current functionality and work flows, yet keep the data in a single table I think you're pretty close.

    Instead of having the conversationId be a key to a different table, I would instead have it point to the ID of the message that began the conversation. This would create a parent-child relationship between messages that began a conversation and all those that followed after it. To be able to see all conversations, you would just select all messages where the conversationId is null. Below is a representation of a 2 message conversation:

    +----+---------+------+------------------+----------------+--------+----------+
    | id | message | read | time             | conversationId | toUser | fromUser |
    +----+---------+------+------------------+----------------+--------+----------+
    | 1  |  test 1 |  0   | (some timestamp) |  null          |  3     |   4      |
    | 2  |  test 2 |  0   | (some timestamp) |   1            |  4     |   3      |
    +----+---------+------+------------------+----------------+--------+----------+
    

    The conversation was initiated by user 3. All messages in the conversation can be filter by conversationId. One limitation of this design is that only 2 users can be apart of the conversation.

    Update

    You could get the last message given a conversation id this way:

    SELECT id, message 
    FROM userMessages 
    WHERE conversationId = {conversationId} 
    ORDER BY time DESC 
    LIMIT 1
    
    0 讨论(0)
  • 2020-12-24 13:07

    it is being used on fiverr.com and www.infinitbin.com. I developed the infinitbin own. It has two databases like yours too. the inbox table:-

    +----+--------+----------+-------------+------------+--------------------------------+
    | id | useridto | useridfrom | conversation | last_content | lastviewed | datecreated|
    +----+--------+----------+-------------+------------+--------------------------------+
    

    This table is very important, used to list the conversations/inbox. The last_content field is 140 charaters from the last message between the conversations. lastviewed is an integer field, the user who lasts sends a message is the last viewed, if the other user in the conversation reads the message. it gets updated to NULL. Therefore to get notifications, you for lastviewed that is not null and not the logged in user's id.

    The conversation field is 'userid-userid', therefor strings. To check if users have started a conversation, you concatenate there user_ids with a hyphen and check it.

    This kind of messaging system is a very complicating one.

    The second table is quite simple.

    +----+--------+----------+-------------+-------+
    | id | inboxid | userid | content | datecreated|
    +----+--------+----------+-------------+-------+
    
    0 讨论(0)
  • 2020-12-24 13:08

    If you want to simplify your query you should add the last message id in your table userconversation :

    ALTER TABLE userconversation ADD lastusermessageid
    

    then each time you add a new message you should update your table userconversation :

    INSERT INTO userconversation(userId, friendId, lastusermessageid)
    VALUES (:userId, :friendId, :lastusermessageid)
    ON DUPLICATE KEY UPDATE lastusermessageid = VALUES(lastusermessageid)
    

    and finally add indexes on all foreign keys :

    SELECT  
        c.id,
        c.userId,
        c.friendId,
        m.message,
        m.read,
        UNIX_TIMESTAMP(m.time),      
        user1.username,
        user2.username  
    FROM 
        userconversation c
        INNER JOIN usermessages m ON c.lastusermessageid = m.id 
        INNER JOIN user user1 ON c.userId = user.id
        INNER JOIN user user2 ON c.friendId = user.id
    WHERE 
        c.userId = :userId OR c.friendId = :userId
    ORDER BY
        m.id DESC
    LIMIT 10
    
    0 讨论(0)
  • 2020-12-24 13:10

    How to create a fast Facebook-like messages system. tested and widely used by Arutz Sheva users - http://www.inn.co.il (Hebrew).

    1. create a "topic" (conversation) table:

        CREATE TABLE pb_topics (
        t_id int(11) NOT NULL AUTO_INCREMENT,
        t_last int(11) NOT NULL DEFAULT '0',
        t_user int(11) NOT NULL DEFAULT '0',
        PRIMARY KEY (t_id),
        KEY last (t_last)
      ) ENGINE=InnoDB AUTO_INCREMENT=137106342 DEFAULT CHARSET=utf8

    2. create link between user and conversation:

          CREATE TABLE pb_links (
        l_id int(11) NOT NULL AUTO_INCREMENT,
        l_user int(11) NOT NULL DEFAULT '0',
        l_new int(11) NOT NULL DEFAULT '0',
        l_topic int(11) NOT NULL DEFAULT '0',
        l_visible int(11) NOT NULL DEFAULT '1',
        l_bcc int(11) NOT NULL DEFAULT '0',
        PRIMARY KEY (l_id) USING BTREE,
        UNIQUE KEY topic-user (l_topic,l_user),
        KEY user-topicnew (l_user,l_new,l_topic) USING BTREE,
        KEY user-topic (l_user,l_visible,l_topic) USING BTREE
      ) ENGINE=InnoDB AUTO_INCREMENT=64750078 DEFAULT CHARSET=utf8

    3. create a message

          CREATE TABLE pb_messages (
        m_id int(11) NOT NULL AUTO_INCREMENT,
        m_from int(11) NOT NULL,
        m_date datetime NOT NULL DEFAULT '1987-11-13 00:00:00',
        m_title varchar(75) NOT NULL,
        m_content mediumtext NOT NULL,
        m_topic int(11) NOT NULL,
        PRIMARY KEY (m_id),
        KEY date_topic (m_date,m_topic),
        KEY topic_date_from (m_topic,m_date,m_from)
      ) ENGINE=InnoDB 

    A conversation can be with 2 or more friends (BCC was added like email, but you can skip it).

    Insert a new message: 1. Create new topic 2. Create Links for users (from/to) 3. Add Message (4. Update users cache table - user have messages)

    Add Message to topic: Add Message

    Select folder:

    select 
         z.*, group_concat(u_name) as users_name from
             (select max(m_id) as m_id, m_topic as t_id,  m_From, m_title,m_date, l_new 
                  from pb_links as l1, pb_messages 
                  where l1.l_user=<user>  and m_from < If(inbox, "<>", "=") > and m_topic=l_topic and l1.l_visible=1 
                   group by m_topic order by m_id desc limit " & iPage * 35 & ",35) z
               left join  pb_links l2  on (l2.l_topic=t_id)
               left join  users  on (l_user=u_id and l_bcc=0 and l_user<user>)  
                group by l_topic order by m_date desc;
    

    In details:

    The first is the inner select - this is the fastest way (I was check about 7 other options, checked also in Percona/MariaDB versions) to get all messages, and get also the last message to display in the list. Also look in the inner IF - in inbox, the last message is anyone but not me, and In outbox - the opposite. LIMIT used for paging.

    The outer one used to add user list (just name comma name string) and more information for only one message per topic, and after paging (I need to add user list just to the 35-per-page messages, and not for all my large history).

    Also, I wrote in hebrew here: http://blogs.microsoft.co.il/blogs/moshel/archive/2010/08/12/quot-x-quot.aspx to create a simple cache table, and forbid the workload of select count from busy message table.

    0 讨论(0)
  • 2020-12-24 13:11

    If you can only have one conversation between users, I don't see a reason for a dedicated conversations table. For this query to work fast, you would need a composite index on (user, message_id) which is impossible if these fields are in different tables. Move user_id and friend_id to the userconversations. This will make your table 8 bytes per record heavier (even assuming 8-byte identifiers) which is hardly a problem for a table containing text messages.

    If you have few conversations per user with many messages in each, use this:

    SELECT  um.*
    FROM    (
            (
            SELECT  MAX(id) AS messageId
            FROM    usermessages m1
            WHERE   user_id = :me
            GROUP BY
                    friend_id
            ORDER BY
                    messageId DESC
            LIMIT 10
            )
            UNION ALL
            (
            SELECT  MAX(id) AS messageId
            FROM    usermessages m1
            WHERE   frient_id = :me
            GROUP BY
                    user_id
            ORDER BY
                    messageId DESC
            LIMIT 10
            )
            ) q
    JOIN    usermessages um
    ON      um.id = q.messageId
    ORDER BY
            id DESC
    LIMIT 10
    

    Create separate indexes on user_id and friend_id

    If you have many conversations with few messages in each, use this query:

    (
    SELECT  *
    FROM    usermessages um
    WHERE   user_id = :me
            AND id = 
            (
            SELECT  MAX(id)
            FROM    usermessages umi
            WHERE   umi.user_id = um.user_id
                    AND umi.friend_id = um.friend_id
            )
    ORDER BY
            id DESC
    LIMIT 10
    )
    UNION ALL
    (
    SELECT  *
    FROM    usermessages um
    WHERE   frient_id = :me
            AND id = 
            (
            SELECT  MAX(id)
            FROM    usermessages umi
            WHERE   umi.user_id = um.user_id
                    AND umi.friend_id = um.friend_id
            )
    ORDER BY
            id DESC
    LIMIT 10
    )
    ORDER BY
            id DESC
    LIMIT 10
    

    The idea behind this query is that it just descends all messages for the given user, checking that each message is the last in its conversation. This may be much faster than sorting all last messages for all conversations (if you have many of them).

    For this to work fast, create indexes on

    friend_id
    user_id, friend_id
    
    0 讨论(0)
  • 2020-12-24 13:12

    I haven't tested this approach as I don't have access to mysqldb right now. But, I think you should be able to get this done by using a ranking function. Since mysql doesn't have an equivalent of row_number function of Oracle I think you can do it like this:

    Select * from (
    Select 
        uc.id, 
        uc.user_id, 
        uc.friend_id 
        um.message
        um.read, 
        um.time,
        @rownum := IF(@prev_val = um.conversation_id, @rownum + 1, 1) AS rank,
        @prev_val := um.conversation_id
    From
        userconversation uc,
        usermessages um,
        (select @row_num:=1) rows,
        (select @prev_val:='') partitions
    Where 
        uc.id=um.conversation_id        
        and c.userId = 222 OR c.friendId = 222 
    
    Order By 
        um.conversation_id,um.id desc
    )t where t.rank=1
    
    0 讨论(0)
提交回复
热议问题