@@ -33,6 +33,7 @@ type Meilisearch struct {
3333 IndexUid string
3434 FilterableAttributes []string
3535 SearchableAttributes []string
36+ taskQueue * TaskQueueManager
3637}
3738
3839func (m * Meilisearch ) Config () searcher.Config {
@@ -82,14 +83,17 @@ func (m *Meilisearch) Index(ctx context.Context, node model.SearchNode) error {
8283}
8384
8485func (m * Meilisearch ) BatchIndex (ctx context.Context , nodes []model.SearchNode ) error {
85- documents , _ := utils .SliceConvert (nodes , func (src model.SearchNode ) (* searchDocument , error ) {
86+ documents , err := utils .SliceConvert (nodes , func (src model.SearchNode ) (* searchDocument , error ) {
8687 parentHash := hashPath (src .Parent )
8788 nodePath := path .Join (src .Parent , src .Name )
8889 nodePathHash := hashPath (nodePath )
8990 parentPaths := utils .GetPathHierarchy (src .Parent )
90- parentPathHashes , _ := utils .SliceConvert (parentPaths , func (parentPath string ) (string , error ) {
91+ parentPathHashes , err := utils .SliceConvert (parentPaths , func (parentPath string ) (string , error ) {
9192 return hashPath (parentPath ), nil
9293 })
94+ if err != nil {
95+ return nil , err
96+ }
9397
9498 return & searchDocument {
9599 ID : nodePathHash ,
@@ -98,9 +102,12 @@ func (m *Meilisearch) BatchIndex(ctx context.Context, nodes []model.SearchNode)
98102 SearchNode : src ,
99103 }, nil
100104 })
105+ if err != nil {
106+ return err
107+ }
101108
102109 // max up to 10,000 documents per batch to reduce error rate while uploading over the Internet
103- _ , err : = m .Client .Index (m .IndexUid ).AddDocumentsInBatchesWithContext (ctx , documents , 10000 )
110+ _ , err = m .Client .Index (m .IndexUid ).AddDocumentsInBatchesWithContext (ctx , documents , 10000 )
104111 if err != nil {
105112 return err
106113 }
@@ -203,6 +210,9 @@ func (m *Meilisearch) Del(ctx context.Context, prefix string) error {
203210}
204211
205212func (m * Meilisearch ) Release (ctx context.Context ) error {
213+ if m .taskQueue != nil {
214+ m .taskQueue .Stop ()
215+ }
206216 return nil
207217}
208218
@@ -219,3 +229,115 @@ func (m *Meilisearch) getTaskStatus(ctx context.Context, taskUID int64) (meilise
219229 }
220230 return forTask .Status , nil
221231}
232+
233+ // EnqueueUpdate enqueues an update task to the task queue
234+ func (m * Meilisearch ) EnqueueUpdate (parent string , objs []model.Obj ) {
235+ if m .taskQueue == nil {
236+ return
237+ }
238+
239+ m .taskQueue .Enqueue (parent , objs )
240+ }
241+
242+ // batchIndexWithTaskUID indexes documents and returns all taskUIDs
243+ func (m * Meilisearch ) batchIndexWithTaskUID (ctx context.Context , nodes []model.SearchNode ) ([]int64 , error ) {
244+ if len (nodes ) == 0 {
245+ return nil , nil
246+ }
247+
248+ documents , err := utils .SliceConvert (nodes , func (src model.SearchNode ) (* searchDocument , error ) {
249+ parentHash := hashPath (src .Parent )
250+ nodePath := path .Join (src .Parent , src .Name )
251+ nodePathHash := hashPath (nodePath )
252+ parentPaths := utils .GetPathHierarchy (src .Parent )
253+ parentPathHashes , err := utils .SliceConvert (parentPaths , func (parentPath string ) (string , error ) {
254+ return hashPath (parentPath ), nil
255+ })
256+ if err != nil {
257+ return nil , err
258+ }
259+
260+ return & searchDocument {
261+ ID : nodePathHash ,
262+ ParentHash : parentHash ,
263+ ParentPathHashes : parentPathHashes ,
264+ SearchNode : src ,
265+ }, nil
266+ })
267+ if err != nil {
268+ return nil , err
269+ }
270+
271+ // max up to 10,000 documents per batch to reduce error rate while uploading over the Internet
272+ tasks , err := m .Client .Index (m .IndexUid ).AddDocumentsInBatchesWithContext (ctx , documents , 10000 )
273+ if err != nil {
274+ return nil , err
275+ }
276+
277+ // Return all task UIDs
278+ taskUIDs := make ([]int64 , 0 , len (tasks ))
279+ for _ , task := range tasks {
280+ taskUIDs = append (taskUIDs , task .TaskUID )
281+ }
282+ return taskUIDs , nil
283+ }
284+
285+ // batchDeleteWithTaskUID deletes documents and returns all taskUIDs
286+ func (m * Meilisearch ) batchDeleteWithTaskUID (ctx context.Context , paths []string ) ([]int64 , error ) {
287+ if len (paths ) == 0 {
288+ return nil , nil
289+ }
290+
291+ // Deduplicate paths first
292+ pathSet := make (map [string ]struct {})
293+ uniquePaths := make ([]string , 0 , len (paths ))
294+ for _ , p := range paths {
295+ p = utils .FixAndCleanPath (p )
296+ if _ , exists := pathSet [p ]; ! exists {
297+ pathSet [p ] = struct {}{}
298+ uniquePaths = append (uniquePaths , p )
299+ }
300+ }
301+
302+ const batchSize = 100 // max paths per batch to avoid filter length limits
303+ var taskUIDs []int64
304+
305+ // Process in batches to avoid filter length limits
306+ for i := 0 ; i < len (uniquePaths ); i += batchSize {
307+ end := i + batchSize
308+ if end > len (uniquePaths ) {
309+ end = len (uniquePaths )
310+ }
311+ batch := uniquePaths [i :end ]
312+
313+ // Build combined filter to delete all children in one request
314+ // Format: parent_path_hashes = 'hash1' OR parent_path_hashes = 'hash2' OR ...
315+ var filters []string
316+ for _ , p := range batch {
317+ pathHash := hashPath (p )
318+ filters = append (filters , fmt .Sprintf ("parent_path_hashes = '%s'" , pathHash ))
319+ }
320+ if len (filters ) > 0 {
321+ combinedFilter := strings .Join (filters , " OR " )
322+ // Delete all children for all paths in one request
323+ task , err := m .Client .Index (m .IndexUid ).DeleteDocumentsByFilterWithContext (ctx , combinedFilter )
324+ if err != nil {
325+ return nil , err
326+ }
327+ taskUIDs = append (taskUIDs , task .TaskUID )
328+ }
329+
330+ // Convert paths to document IDs and batch delete
331+ documentIDs := make ([]string , 0 , len (batch ))
332+ for _ , p := range batch {
333+ documentIDs = append (documentIDs , hashPath (p ))
334+ }
335+ // Use batch delete API
336+ task , err := m .Client .Index (m .IndexUid ).DeleteDocumentsWithContext (ctx , documentIDs )
337+ if err != nil {
338+ return nil , err
339+ }
340+ taskUIDs = append (taskUIDs , task .TaskUID )
341+ }
342+ return taskUIDs , nil
343+ }
0 commit comments