Use case: IM application with the Lindorm wide table and search engine
Learn how to build an instant messaging (IM) application by using the Lindorm wide table engine as the core storage. This guide walks you through instance activation, table design, and data operations, and shows how to use secondary indexes and search indexes to accelerate exact-match queries and full-text search.
Solution overview
This solution uses the Lindorm wide table engine as the core storage and leverages two indexing capabilities to meet diverse query requirements in IM scenarios:
-
Native secondary index: Accelerates high-frequency, deterministic queries on non-primary key columns, such as "querying a user's recent messages" or "querying the latest messages in a group."
-
Search index: Supports complex, multi-dimensional queries and full-text search, such as "searching for messages that contain specific keywords" or "filtering messages based on a combination of multiple conditions."
Prerequisites
-
You have created a Lindorm wide table engine instance.
-
The instance has the Lindorm search engine and the LTS engine enabled. For more information, see Activation Guide.
-
You have added the client IP address to the Lindorm allowlist. For more information, see Set an allowlist.
Create the message table
Table schema design
This guide uses a table named im_messages to store message data.
|
Parameter |
Type |
Description |
|
|
|
The primary key. A globally unique message ID to prevent write hotspots. |
|
|
|
The sender ID. |
|
|
|
The receiver ID, used for one-to-one chats. |
|
|
|
The group ID, used for group chats. |
|
|
|
The message content. Use the VARCHAR type to facilitate search index creation. |
|
|
|
The message type, such as text, image, or file. |
|
|
|
The timestamp when the message was sent, in milliseconds. |
|
|
|
The message status, such as sent, delivered, or read. |
|
|
|
A soft delete flag, used for features such as recalling messages. |
Create table statement
Run the following SQL statement to create the im_messages table:
CREATE TABLE IF NOT EXISTS im_messages (
msg_id VARCHAR,
from_user VARCHAR,
to_user VARCHAR,
group_id VARCHAR,
content VARCHAR,
msg_type VARCHAR,
send_time BIGINT,
status VARCHAR,
is_deleted BOOLEAN, -- We recommend adding a soft delete flag for recalling messages.
PRIMARY KEY(msg_id)
);
Write and query data
Write test data
Use the UPSERT statement to insert or update data. The following example shows how to insert five sample messages:
-- Insert five test messages
UPSERT INTO im_messages (
msg_id,
from_user,
to_user,
group_id,
content,
msg_type,
send_time,
status
) VALUES
('m001', 'u1001', 'u1002', NULL, 'Hello!', 'text', 1704000000000, 'read'),
('m002', 'u1002', 'u1001', NULL, 'Hi there', 'text', 1704000060000, 'read'),
('m003', 'u1001', NULL, 'g2001', 'Hello everyone', 'text', 1704000120000, 'sent'),
('m004', 'u1003', NULL, 'g2001', 'Got it', 'text', 1704000180000, 'delivered'),
('m005', 'u1001', 'u1004', NULL, 'image.jpg', 'image', 1704000240000, 'sent');
Query data
-
Primary key query (most efficient)
Query the content of a specific message based on its ID. This is the most efficient point query method in Lindorm.
SELECT content FROM im_messages WHERE msg_id = 'm001'; -
Simple aggregate analysis
Note: Without an index, the following aggregate queries trigger a full table scan. You can use them to verify logic on small datasets, but in a production environment, you must use a search index or columnar storage for efficient execution.
-
Count the total number of messages in the table
SELECT COUNT(*) FROM im_messages; -
Count the number of messages by message type
SELECT msg_type, COUNT(*) FROM im_messages GROUP BY msg_type; -
Check if a specific user has unread messages
SELECT COUNT(*) FROM im_messages WHERE to_user = 'u1002' AND status = 'sent';
-
Use a secondary index to accelerate queries
To support common queries on non-primary key columns, such as querying a user's messages or group messages, create secondary indexes on frequently queried columns.
Index design
The following indexes are designed for typical IM query scenarios:
|
Index name |
Indexed columns |
Query scenario |
|
|
|
Query the N most recent messages sent by a user. |
|
|
|
Query all messages in a specific group. |
|
|
|
Query by time range (usually combined with other conditions). |
|
|
|
Query the most recent direct messages received by a user. |
|
|
|
Query the N most recent messages in a group. |
Create index statements
Run the following SQL statements to create the secondary indexes. The creation process can take from seconds to several minutes, depending on the amount of existing data.
-- 1. [Key] Sender + time (descending) - for "querying a user's most recent messages"
CREATE INDEX IF NOT EXISTS idx_from_user_time ON im_messages (from_user, send_time DESC);
-- 2. [Key] Receiver + time (descending) - for "querying a user's most recent direct messages"
CREATE INDEX IF NOT EXISTS idx_to_user_time ON im_messages (to_user, send_time DESC);
-- 3. [Key] Group + time (descending) - for "querying a group's latest messages"
CREATE INDEX IF NOT EXISTS idx_group_time ON im_messages (group_id, send_time DESC);
-- 4. Time index - less common alone, typically used in combination with other conditions
CREATE INDEX IF NOT EXISTS idx_send_time ON im_messages (send_time);
Query examples using a secondary index
-
Query the 10 most recent messages sent by user u1001 (hits the
idx_from_user_timeindex)SELECT msg_id, content, send_time FROM im_messages WHERE from_user = 'u1001' ORDER BY send_time DESC LIMIT 10; -
Query the 5 most recent direct messages received by user u1002 (hits the
idx_to_user_timeindex)SELECT msg_id, from_user, content FROM im_messages WHERE to_user = 'u1002' ORDER BY send_time DESC LIMIT 5; -
Query the 20 most recent messages in group g2001 (hits the
idx_group_timeindex)SELECT msg_id, from_user, content, send_time FROM im_messages WHERE group_id = 'g2001' ORDER BY send_time DESC LIMIT 20; -
Query messages sent by u1001 within a specific time range (hits the
idx_from_user_timeindex)SELECT msg_id, content FROM im_messages WHERE from_user = 'u1001' AND send_time BETWEEN 1704000000000 AND 1704000300000 ORDER BY send_time DESC;
Search index for full-text search and complex queries
For full-text search and complex multi-condition queries, a search index is the ideal solution. It supports tokenization, fuzzy matching, and aggregate analysis, as well as efficient sorting and pagination.
Create a search index
Run the following SQL statement to create a search index named im_search_idx. Lindorm automatically synchronizes data from the wide table to the search engine by using the LTS engine.
-- Create a search index named im_search_idx
CREATE INDEX im_search_idx
USING SEARCH
ON im_messages (
msg_id, -- Exact match (primary key)
from_user, -- Sender (exact match)
to_user, -- Receiver (exact match)
group_id, -- Group ID (exact match)
content(type=text,analyzer=ik), -- Full-text search field (tokenization enabled)
msg_type, -- Message type (exact match)
send_time, -- Timestamp (supports range queries and sorting)
status -- Status (exact match)
);
You can run SHOW INDEX FROM im_messages; to check the index creation status.
Query examples using a search index
-
Search for messages that contain "image" (full-text search)
SELECT msg_id, from_user, content FROM im_messages WHERE content LIKE '%image%' -
Query for messages sent by user u1001 that contain "Hello" (multi-condition query)
SELECT msg_id, content, send_time FROM im_messages WHERE from_user = 'u1001' AND content LIKE '%Hello%' -
Query the 10 most recent messages in group g2001
SELECT msg_id, content FROM im_messages WHERE group_id = 'g2001' AND send_time IS NOT NULL ORDER BY send_time DESC LIMIT 10;