Skip to content

Commit bb54f0f

Browse files
committed
feat: add sortableAttributes for createdAt/updatedAt in MeiliSearch indexes
1 parent f277b32 commit bb54f0f

File tree

7 files changed

+1463
-71
lines changed

7 files changed

+1463
-71
lines changed

api/db/indexSync.js

Lines changed: 66 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,44 @@ async function deleteDocumentsWithoutUserField(index, indexName) {
8181
return deletedCount;
8282
}
8383

84+
/**
85+
* Checks and configures a MeiliSearch index with required filterable and sortable attributes
86+
* @param {import('meilisearch').Index} index - MeiliSearch index instance
87+
* @param {string} indexName - Name of the index for logging
88+
* @returns {Promise<boolean>} - Whether settings were updated
89+
*/
90+
async function checkAndConfigureIndex(index, indexName) {
91+
try {
92+
const settings = await index.getSettings();
93+
const requiredFilterable = ['user'];
94+
const requiredSortable = ['createdAt', 'updatedAt'];
95+
96+
const hasAllFilterable = requiredFilterable.every((attr) =>
97+
settings.filterableAttributes?.includes(attr),
98+
);
99+
const hasAllSortable = requiredSortable.every((attr) =>
100+
settings.sortableAttributes?.includes(attr),
101+
);
102+
103+
if (!hasAllFilterable || !hasAllSortable) {
104+
logger.info(`[indexSync] Configuring ${indexName} index for filtering and sorting...`);
105+
await index.updateSettings({
106+
filterableAttributes: requiredFilterable,
107+
sortableAttributes: requiredSortable,
108+
});
109+
logger.info(
110+
`[indexSync] ${indexName} index configured for user filtering and timestamps sortable`,
111+
);
112+
return true;
113+
}
114+
} catch (error) {
115+
if (error.code !== 'index_not_found') {
116+
logger.warn(`[indexSync] Could not check/update ${indexName} index settings:`, error.message);
117+
}
118+
}
119+
return false;
120+
}
121+
84122
/**
85123
* Ensures indexes have proper filterable attributes configured and checks if documents have user field
86124
* @param {MeiliSearch} client - MeiliSearch client instance
@@ -92,67 +130,41 @@ async function ensureFilterableAttributes(client) {
92130

93131
try {
94132
// Check and update messages index
95-
try {
96-
const messagesIndex = client.index('messages');
97-
const settings = await messagesIndex.getSettings();
98-
99-
if (!settings.filterableAttributes || !settings.filterableAttributes.includes('user')) {
100-
logger.info('[indexSync] Configuring messages index to filter by user...');
101-
await messagesIndex.updateSettings({
102-
filterableAttributes: ['user'],
103-
});
104-
logger.info('[indexSync] Messages index configured for user filtering');
105-
settingsUpdated = true;
106-
}
133+
const messagesIndex = client.index('messages');
134+
if (await checkAndConfigureIndex(messagesIndex, 'messages')) {
135+
settingsUpdated = true;
136+
}
107137

108-
// Check if existing documents have user field indexed
109-
try {
110-
const searchResult = await messagesIndex.search('', { limit: 1 });
111-
if (searchResult.hits.length > 0 && !searchResult.hits[0].user) {
112-
logger.info(
113-
'[indexSync] Existing messages missing user field, will clean up orphaned documents...',
114-
);
115-
hasOrphanedDocs = true;
116-
}
117-
} catch (searchError) {
118-
logger.debug('[indexSync] Could not check message documents:', searchError.message);
119-
}
120-
} catch (error) {
121-
if (error.code !== 'index_not_found') {
122-
logger.warn('[indexSync] Could not check/update messages index settings:', error.message);
138+
// Check if existing documents have user field indexed
139+
try {
140+
const searchResult = await messagesIndex.search('', { limit: 1 });
141+
if (searchResult.hits.length > 0 && !searchResult.hits[0].user) {
142+
logger.info(
143+
'[indexSync] Existing messages missing user field, will clean up orphaned documents...',
144+
);
145+
hasOrphanedDocs = true;
123146
}
147+
} catch (searchError) {
148+
logger.debug('[indexSync] Could not check message documents:', searchError.message);
124149
}
125150

126151
// Check and update conversations index
127-
try {
128-
const convosIndex = client.index('convos');
129-
const settings = await convosIndex.getSettings();
130-
131-
if (!settings.filterableAttributes || !settings.filterableAttributes.includes('user')) {
132-
logger.info('[indexSync] Configuring convos index to filter by user...');
133-
await convosIndex.updateSettings({
134-
filterableAttributes: ['user'],
135-
});
136-
logger.info('[indexSync] Convos index configured for user filtering');
137-
settingsUpdated = true;
138-
}
152+
const convosIndex = client.index('convos');
153+
if (await checkAndConfigureIndex(convosIndex, 'convos')) {
154+
settingsUpdated = true;
155+
}
139156

140-
// Check if existing documents have user field indexed
141-
try {
142-
const searchResult = await convosIndex.search('', { limit: 1 });
143-
if (searchResult.hits.length > 0 && !searchResult.hits[0].user) {
144-
logger.info(
145-
'[indexSync] Existing conversations missing user field, will clean up orphaned documents...',
146-
);
147-
hasOrphanedDocs = true;
148-
}
149-
} catch (searchError) {
150-
logger.debug('[indexSync] Could not check conversation documents:', searchError.message);
151-
}
152-
} catch (error) {
153-
if (error.code !== 'index_not_found') {
154-
logger.warn('[indexSync] Could not check/update convos index settings:', error.message);
157+
// Check if existing documents have user field indexed
158+
try {
159+
const searchResult = await convosIndex.search('', { limit: 1 });
160+
if (searchResult.hits.length > 0 && !searchResult.hits[0].user) {
161+
logger.info(
162+
'[indexSync] Existing conversations missing user field, will clean up orphaned documents...',
163+
);
164+
hasOrphanedDocs = true;
155165
}
166+
} catch (searchError) {
167+
logger.debug('[indexSync] Could not check conversation documents:', searchError.message);
156168
}
157169

158170
// If either index has orphaned documents, clean them up (but don't force resync)

0 commit comments

Comments
 (0)