feat(search): full unique path first + immediate folder in results (#51)
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go 1.25) (pull_request) Successful in 21s
Tests / Test (Go stable) (pull_request) Successful in 26s
Release Please / Release Please (push) Skipped
Release / GoReleaser (push) Skipped
Tests / Secret scan (gitleaks) (push) Skipped
Tests / Test (Go 1.25) (push) Skipped
Tests / Test (Go stable) (push) Skipped
Tests / Secret scan (gitleaks) (pull_request) Successful in 4s
Tests / Test (Go 1.25) (pull_request) Successful in 21s
Tests / Test (Go stable) (pull_request) Successful in 26s
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package onlyoffice
|
||||
|
||||
// Human-readable folder paths for search results (F9). The OnlyOffice ES
|
||||
// index stores only ancestor folder ids; titles live in the Documents tree, so
|
||||
// resolving a path costs one GET /api/2.0/files/{id} per distinct folder,
|
||||
// cached on the client. Folders that cannot be listed (e.g. a section root)
|
||||
// fall back to their id, so a path is always produced.
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// FolderTitle returns the title of a Documents folder id, cached on the client.
|
||||
// An empty id yields an empty title. Unknown/unlistable ids (section roots)
|
||||
// return ("", nil) so callers can fall back to the id.
|
||||
func (c *Client) FolderTitle(ctx context.Context, folderID string) (string, error) {
|
||||
folderID = strings.TrimSpace(folderID)
|
||||
if folderID == "" {
|
||||
return "", nil
|
||||
}
|
||||
c.folderTitlesMu.Lock()
|
||||
if c.folderTitles != nil {
|
||||
if t, ok := c.folderTitles[folderID]; ok {
|
||||
c.folderTitlesMu.Unlock()
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
c.folderTitlesMu.Unlock()
|
||||
|
||||
title := ""
|
||||
out, err := c.ListFolder(ctx, folderID)
|
||||
if err == nil {
|
||||
if cur, ok := out["current"].(map[string]any); ok {
|
||||
if s, ok := cur["title"].(string); ok {
|
||||
title = strings.TrimSpace(s)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.folderTitlesMu.Lock()
|
||||
if c.folderTitles == nil {
|
||||
c.folderTitles = map[string]string{}
|
||||
}
|
||||
c.folderTitles[folderID] = title
|
||||
c.folderTitlesMu.Unlock()
|
||||
return title, nil
|
||||
}
|
||||
|
||||
// FolderPath resolves an ancestor folder id chain (root → leaf, as the ES
|
||||
// backend reports it) into folder titles, falling back to the id when a title
|
||||
// cannot be read. The result never fails on a single lookup: only the whole
|
||||
// call honours ctx cancellation.
|
||||
func (c *Client) FolderPath(ctx context.Context, ids []string) []string {
|
||||
out := make([]string, 0, len(ids))
|
||||
for _, id := range ids {
|
||||
if err := ctx.Err(); err != nil {
|
||||
break
|
||||
}
|
||||
title, err := c.FolderTitle(ctx, id)
|
||||
if err != nil || title == "" {
|
||||
title = id
|
||||
}
|
||||
out = append(out, title)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// UniquePath builds a stable, human-readable, unique path for a result: the
|
||||
// resolved folder chain plus the file title. "." separates nothing — the
|
||||
// segments are joined with "/", matching the Documents breadcrumb the web UI
|
||||
// shows.
|
||||
func (c *Client) UniquePath(ctx context.Context, folderPath []string, title string) string {
|
||||
parts := c.FolderPath(ctx, folderPath)
|
||||
if t := strings.TrimSpace(title); t != "" {
|
||||
parts = append(parts, t)
|
||||
}
|
||||
return strings.Join(parts, "/")
|
||||
}
|
||||
Reference in New Issue
Block a user