Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion backend/internal/infrastructure/meilisearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ func EnsureMeiliSearchIndex(client *meilisearch.Client, indexName string) error
"lyricText",
"platformIds_ncm", "platformIds_qq", "platformIds_spotify", "platformIds_apple",
"ttmlAuthorGithub",
"ttmlAuthorGithubLogin",
}
filterable := []string{
"platformIds_ncm", "platformIds_qq", "platformIds_spotify", "platformIds_apple",
"artists", "albums", "ttmlAuthorGithub",
"artists", "albums", "ttmlAuthorGithub", "ttmlAuthorGithubLogin",
}
sortable := []string{
"commitTimestamp",
Expand Down
33 changes: 17 additions & 16 deletions backend/internal/model/meilisearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,23 @@ package model

// MeiliSearch 索引文档结构(与 Rust Worker 写入保持一致)
type MeiliSongDocument struct {
ID string `json:"id"`
MusicNames []string `json:"musicNames"`
MusicNamesPinyin []string `json:"musicNamesPinyin"`
Artists []string `json:"artists"`
ArtistsPinyin []string `json:"artistsPinyin"`
Albums []string `json:"albums"`
AlbumsPinyin []string `json:"albumsPinyin"`
LyricText string `json:"lyricText"`
PlatformIdsNcm []string `json:"platformIds_ncm,omitempty"`
PlatformIdsQq []string `json:"platformIds_qq,omitempty"`
PlatformIdsSpotify []string `json:"platformIds_spotify,omitempty"`
PlatformIdsApple []string `json:"platformIds_apple,omitempty"`
RawLyricFile string `json:"rawLyricFile"`
TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"`
WordCount int `json:"wordCount"`
LineCount int `json:"lineCount"`
ID string `json:"id"`
MusicNames []string `json:"musicNames"`
MusicNamesPinyin []string `json:"musicNamesPinyin"`
Artists []string `json:"artists"`
ArtistsPinyin []string `json:"artistsPinyin"`
Albums []string `json:"albums"`
AlbumsPinyin []string `json:"albumsPinyin"`
LyricText string `json:"lyricText"`
PlatformIdsNcm []string `json:"platformIds_ncm,omitempty"`
PlatformIdsQq []string `json:"platformIds_qq,omitempty"`
PlatformIdsSpotify []string `json:"platformIds_spotify,omitempty"`
PlatformIdsApple []string `json:"platformIds_apple,omitempty"`
RawLyricFile string `json:"rawLyricFile"`
TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"`
TtmlAuthorGithubLogin string `json:"ttmlAuthorGithubLogin,omitempty"`
WordCount int `json:"wordCount"`
LineCount int `json:"lineCount"`
}

// SearchHit MeiliSearch 搜索响应中单条命中(仅 Go 端关心的字段)
Expand Down
35 changes: 20 additions & 15 deletions backend/internal/service/search_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,18 @@ type SearchRequest struct {

// SearchHitResult 单条命中
type SearchHitResult struct {
ID string `json:"id"`
MusicNames []string `json:"musicNames"`
Artists []string `json:"artists"`
Albums []string `json:"albums"`
PlatformIds map[string][]string `json:"platformIds"`
RawLyricFile string `json:"rawLyricFile"`
WordCount int `json:"wordCount"`
LineCount int `json:"lineCount"`
CommitTimestamp *int64 `json:"commitTimestamp,omitempty"`
LyricSnippet string `json:"lyricSnippet,omitempty"` // 歌词匹配片段(高亮)
TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"`
ID string `json:"id"`
MusicNames []string `json:"musicNames"`
Artists []string `json:"artists"`
Albums []string `json:"albums"`
PlatformIds map[string][]string `json:"platformIds"`
RawLyricFile string `json:"rawLyricFile"`
WordCount int `json:"wordCount"`
LineCount int `json:"lineCount"`
CommitTimestamp *int64 `json:"commitTimestamp,omitempty"`
LyricSnippet string `json:"lyricSnippet,omitempty"` // 歌词匹配片段(高亮)
TtmlAuthorGithub string `json:"ttmlAuthorGithub,omitempty"`
TtmlAuthorGithubLogin string `json:"ttmlAuthorGithubLogin,omitempty"`
}

// SearchResult 搜索结果
Expand Down Expand Up @@ -116,12 +117,13 @@ func (s *SearchService) Search(ctx context.Context, req SearchRequest) (*SearchR
}, nil
}

// searchByExactID 使用 filter 精确匹配所有平台 ID 字段
// searchByExactID 使用 filter 精确匹配所有平台 ID 字段和投稿者 ID
func (s *SearchService) searchByExactID(ctx context.Context, index *meilisearch.Index, req SearchRequest) (*SearchResult, error) {
escaped := meiliEscape(req.Query)
// 支持:平台 ID(ncm/qq/spotify/apple) + 投稿者 GitHub ID + 投稿者 GitHub 用户名
filter := fmt.Sprintf(
`platformIds_ncm = "%s" OR platformIds_qq = "%s" OR platformIds_spotify = "%s" OR platformIds_apple = "%s"`,
escaped, escaped, escaped, escaped,
`platformIds_ncm = "%s" OR platformIds_qq = "%s" OR platformIds_spotify = "%s" OR platformIds_apple = "%s" OR ttmlAuthorGithub = "%s" OR ttmlAuthorGithubLogin = "%s"`,
escaped, escaped, escaped, escaped, escaped, escaped,
)
req2 := meilisearch.SearchRequest{
Query: "",
Expand Down Expand Up @@ -163,7 +165,7 @@ func searchOnFields(field string) []string {
case "lyric":
return []string{"lyricText"}
case "author":
return []string{"ttmlAuthorGithub"}
return []string{"ttmlAuthorGithub", "ttmlAuthorGithubLogin"}
case "", "all":
return []string{
"musicNames", "musicNamesPinyin",
Expand Down Expand Up @@ -197,6 +199,9 @@ func convertHit(raw interface{}) SearchHitResult {
if v, ok := m["ttmlAuthorGithub"].(string); ok {
hit.TtmlAuthorGithub = v
}
if v, ok := m["ttmlAuthorGithubLogin"].(string); ok {
hit.TtmlAuthorGithubLogin = v
}
if v := toStringSlice(m["platformIds_ncm"]); len(v) > 0 {
hit.PlatformIds["ncm"] = v
}
Expand Down
12 changes: 6 additions & 6 deletions worker/src/db/repository.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ pub struct SongUpsert {
pub platform_mappings: Vec<(String, String)>, // (platform, platform_id)
/// 从 raw_lyric_file 解析出的提交毫秒时间戳
pub commit_timestamp: Option<i64>,
/// 从时间戳转换而来的人类可读时间
pub commit_time: Option<chrono::DateTime<chrono::FixedOffset>>,
}

Expand All @@ -42,18 +41,19 @@ impl Repository {
Self { db }
}

/// 查询本地已有的 raw_lyric_file 集合
pub async fn list_raw_lyric_files(&self) -> anyhow::Result<HashMap<String, i64>> {
/// 查询本地已有的 raw_lyric_file
/// 返回 HashMap
pub async fn list_raw_lyric_files(&self) -> anyhow::Result<HashMap<String, Option<i64>>> {
tracing::debug!("repository.list_raw_lyric_files - 开始查询本地文件列表");
let rows = song::Entity::find()
.select_only()
.column(song::Column::Id)
.column(song::Column::RawLyricFile)
.into_tuple::<(i64, String)>()
.column(song::Column::CommitTimestamp)
.into_tuple::<(String, Option<i64>)>()
.all(&self.db)
.await?;
tracing::debug!("repository.list_raw_lyric_files - 查询完成,共 {} 个文件", rows.len());
Ok(rows.into_iter().map(|(id, f)| (f, id)).collect())
Ok(rows.into_iter().collect())
}

/// 通过 raw_lyric_file 查找 song id
Expand Down
2 changes: 2 additions & 0 deletions worker/src/search/meilisearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct MeiliDocument {
pub platform_ids_apple: Vec<String>,
pub raw_lyric_file: String,
pub ttml_author_github: Option<String>,
pub ttml_author_github_login: Option<String>,
pub word_count: i64,
pub line_count: i64,
/// 提交时间戳(毫秒),用于搜索结果按新旧排序
Expand All @@ -43,6 +44,7 @@ impl MeiliDocument {
"platformIds_apple": self.platform_ids_apple,
"rawLyricFile": self.raw_lyric_file,
"ttmlAuthorGithub": self.ttml_author_github,
"ttmlAuthorGithubLogin": self.ttml_author_github_login,
"wordCount": self.word_count,
"lineCount": self.line_count,
"commitTimestamp": self.commit_timestamp,
Expand Down
26 changes: 20 additions & 6 deletions worker/src/sync/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ pub struct Diff {
/// 计算远程索引与本地已有 raw_lyric_file 的差异
///
/// - 远程有,本地无 -> to_add
/// - 远程有,本地有 -> to_update(简化处理:全部重新下载与更新;CC0 仓库以文件名唯一标识,文件名相同即视为同一首歌)
/// - 远程有,本地有,但 commit_timestamp 不同 -> to_update(只有时间戳不同才需要更新)
/// - 远程有,本地有,且 commit_timestamp 相同 -> 跳过(文件内容和 metadata 未变化)
/// - 远程无,本地有 -> to_delete(当前 CC0 仓库不主动删除,留空实现预留)
pub fn compute_diff(remote: Vec<IndexEntry>, local: HashMap<String, i64>) -> Diff {
pub fn compute_diff(remote: Vec<IndexEntry>, local: HashMap<String, Option<i64>>) -> Diff {
let mut to_add = Vec::new();
let mut to_update = Vec::new();
let mut remote_seen: std::collections::HashSet<String> = std::collections::HashSet::new();
Expand All @@ -24,10 +25,23 @@ pub fn compute_diff(remote: Vec<IndexEntry>, local: HashMap<String, i64>) -> Dif
if let Some(raw) = entry.raw_file() {
let raw = raw.to_string();
remote_seen.insert(raw.clone());
if local.contains_key(&raw) {
to_update.push(entry);
} else {
to_add.push(entry);

// 从文件名中提取 commit_timestamp,转换为 i64 以便比较
let remote_ts = entry.parse_file_meta().map(|(ts, _)| ts as i64);

match local.get(&raw) {
Some(local_ts) => {
// 本地有此文件,比较 commit_timestamp
if *local_ts != remote_ts {
// 时间戳不同,需要更新
to_update.push(entry);
}
// 时间戳相同,跳过(不需要更新)
}
None => {
// 本地没有此文件,需要新增
to_add.push(entry);
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions worker/src/worker/sync_task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@ async fn process_one(
.collect(),
raw_lyric_file: d.raw_lyric_file.clone(),
ttml_author_github: entry.ttml_author_github(),
ttml_author_github_login: entry.ttml_author_github_login(),
word_count: parsed.word_count as i64,
line_count: parsed.line_count as i64,
commit_timestamp,
Expand Down
Loading