🎯 Goal
Implement API GET /analytics/network/chord để trả về dữ liệu liên kết hợp tác nghiên cứu xuyên quốc gia giữa các quốc gia / khu vực nghiên cứu lớn.
API này phục vụ biểu đồ Country Collaboration Chord dạng Chord Diagram, hiển thị các dây nối giữa các quốc gia dựa trên mức độ hợp tác học thuật.
Mục tiêu là cung cấp danh sách connection pair gồm:
source: quốc gia nguồn
target: quốc gia đích
coAuthorshipValue: cường độ hợp tác, thường tính bằng số bài báo đồng tác giả xuyên quốc gia
🖼️ UI Mapping
UI Block
Country Collaboration Chord
Chart Type
Chord Diagram / Circular Network Diagram
Description
Biểu đồ vòng tròn hiển thị quan hệ trao đổi học thuật xuyên biên giới giữa các cường quốc nghiên cứu.
Ví dụ:
UNITED STATES <-> CHINA
CHINA <-> JAPAN
EU <-> UNITED STATES
Mỗi dây nối thể hiện mức độ hợp tác đồng tác giả giữa 2 quốc gia.
📡 Endpoint
GET /analytics/network/chord
📥 Query Parameters
| Param |
Type |
Required |
Description |
project_id |
string / number |
Yes |
ID của project cần lấy country collaboration chord |
subject_area |
string |
No |
Lọc hẹp thêm theo subject area cụ thể |
keywords |
string[] / comma-separated |
No |
Lọc hẹp thêm theo danh sách keyword |
from_year |
number |
No |
Năm bắt đầu lọc dữ liệu |
to_year |
number |
No |
Năm kết thúc lọc dữ liệu |
limit_countries |
number |
No |
Số lượng quốc gia tối đa. Default: 10 |
min_value |
number |
No |
Chỉ lấy connection có coAuthorshipValue tối thiểu. Default: 1 |
✅ Example Requests
1. Fetch chord data by project only
GET /analytics/network/chord?project_id=PROJECT_ID
2. Fetch chord data with subject area filter
GET /analytics/network/chord?project_id=PROJECT_ID&subject_area=Computer Science
3. Fetch chord data with keywords filter
GET /analytics/network/chord?project_id=PROJECT_ID&keywords=AI,Machine Learning,RAG
4. Fetch chord data with year range
GET /analytics/network/chord?project_id=PROJECT_ID&from_year=2021&to_year=2026
5. Fetch chord data with limits
GET /analytics/network/chord?project_id=PROJECT_ID&limit_countries=8&min_value=5
🧾 Response Contract
{
"code": 200,
"message": "Fetch collaboration chord successfully",
"data": [
{
"source": "UNITED STATES",
"target": "CHINA",
"coAuthorshipValue": 450
},
{
"source": "CHINA",
"target": "JAPAN",
"coAuthorshipValue": 210
}
]
}
📌 Response Field Explanation
| Field |
Type |
Description |
source |
string |
Tên quốc gia / khu vực nguồn |
target |
string |
Tên quốc gia / khu vực đích |
coAuthorshipValue |
number |
Số lượng bài báo đồng tác giả hoặc cường độ hợp tác giữa 2 quốc gia |
🧠 Business Logic
1. Project-based filtering
API bắt buộc nhận project_id.
Từ project_id, hệ thống cần lấy scope nghiên cứu của project:
- Subject area của project
- Subject categories thuộc subject area đó
- Keywords project đang theo dõi
Sau đó dùng scope này để lọc tập bài báo trước khi build country collaboration topology.
Ví dụ project đang theo dõi:
{
"project_id": 1,
"subject_area": "Artificial Intelligence",
"subject_categories": ["Computer Science Applications", "Artificial Intelligence"],
"keywords": ["LLM", "RAG", "Machine Learning"]
}
API chỉ tính collaboration chord trên các bài báo thuộc scope này.
2. Filter priority
Nếu client chỉ truyền:
API tự động lấy toàn bộ subject area, subject categories và keywords của project để lọc.
Nếu client truyền thêm:
API sẽ lọc hẹp hơn trong phạm vi project.
Ví dụ:
GET /analytics/network/chord?project_id=1&subject_area=AI&keywords=LLM,RAG
Kết quả chỉ tính collaboration chord trên các bài báo:
- Thuộc project scope
- Thuộc subject area
AI
- Có keyword liên quan đến
LLM hoặc RAG
3. Country collaboration definition
Một connection giữa 2 quốc gia được tạo khi cùng một bài báo có tác giả / tổ chức đến từ ít nhất 2 quốc gia khác nhau.
Ví dụ:
Article A:
- Author 1 affiliation: UNITED STATES
- Author 2 affiliation: CHINA
- Author 3 affiliation: JAPAN
Tạo các pair:
UNITED STATES - CHINA
UNITED STATES - JAPAN
CHINA - JAPAN
Mỗi pair tăng:
4. Country source
Country nên lấy từ dữ liệu affiliation / institution của tác giả.
Suggested sources:
Institution.country
Author_Affiliation.country
Article_Affiliation.country
Important: Country collaboration không nên lấy từ Publisher hoặc Journal, vì publisher/journal không đại diện cho quốc gia hợp tác nghiên cứu của tác giả.
Nếu chưa có institution / affiliation country data:
- Phase 1 có thể trả mock data
- Hoặc trả
data: []
- Tạo follow-up task bổ sung Institution/Affiliation/Country schema
5. Country normalization
API cần normalize tên quốc gia để tránh duplicate.
Ví dụ mapping:
| Raw Value |
Normalized Value |
USA |
UNITED STATES |
US |
UNITED STATES |
United States of America |
UNITED STATES |
PR China |
CHINA |
People's Republic of China |
CHINA |
Japan |
JAPAN |
European Union |
EU |
Suggested phase 1:
Return uppercase country names
6. EU handling
Nếu data có quốc gia cụ thể thuộc EU như Germany, France, Netherlands, Italy, Spain, có 2 option:
Option A: Keep country-level data
GERMANY
FRANCE
NETHERLANDS
Option B: Group EU countries into EU
GERMANY -> EU
FRANCE -> EU
NETHERLANDS -> EU
Suggested phase 1:
Keep country-level data unless UI explicitly requires EU grouping.
Nếu UI mock có EU, có thể thêm query param sau này:
7. Pair canonicalization
Để tránh duplicate edge 2 chiều:
UNITED STATES -> CHINA
CHINA -> UNITED STATES
phải được merge thành một pair duy nhất.
Suggested rule:
source = alphabetically smaller country name
target = alphabetically larger country name
Hoặc giữ theo predefined order nếu UI cần:
UNITED STATES, CHINA, JAPAN, EU
Yêu cầu:
No duplicate reverse pairs.
8. coAuthorshipValue calculation
Suggested phase 1:
coAuthorshipValue = count(distinct article_id) for each country pair
Nếu một article có nhiều authors từ cùng 2 quốc gia, vẫn chỉ tính 1 cho pair đó trong article.
Ví dụ:
Article A:
- 3 authors from UNITED STATES
- 2 authors from CHINA
Pair:
UNITED STATES - CHINA += 1
Không tính thành 6.
9. Country limiting rule
Để FE render chord diagram không quá rối:
- Default
limit_countries = 10
- Suggested max
limit_countries = 30
Suggested logic:
1. Tính tổng collaboration value của từng country
2. Chọn top N countries
3. Chỉ giữ pair có cả source và target trong top countries
4. Apply min_value
5. Sort by coAuthorshipValue DESC
10. Data validation rule
Mỗi item trả về phải đảm bảo:
source không null
target không null
source !== target
coAuthorshipValue là number
coAuthorshipValue > 0
- Không có duplicate pair
- Không có reverse duplicate pair
- Không có
NaN
📦 Data Source
API cần dữ liệu bài báo, tác giả và quốc gia affiliation.
Có thể dùng các bảng hiện tại nếu có:
Project
Project_Keyword
Subject_Area
Subject_Category
Topic
Sub_Topic
Keyword
Keyword_Article
Article
Author
Author_Article
Institution
Author_Institution
Article_Affiliation
Relevant schema flow:
Project.subject_area -> Subject_Area.subject_area_id
Subject_Category.subject_area_id -> Subject_Area.subject_area_id
Article.primary_topic -> Topic.topic_id
Topic.subject_category_id -> Subject_Category.subject_category_id
Sub_Topic.article_id -> Article.article_id
Sub_Topic.topic_id -> Topic.topic_id
Keyword_Article.article_id -> Article.article_id
Keyword_Article.keyword_id -> Keyword.keyword_id
Project_Keyword.keyword_id -> Keyword.keyword_id
Author_Article.article_id -> Article.article_id
Author_Article.author_id -> Author.author_id
Author -> Author_Institution -> Institution
Institution.country -> Country name
🧮 Suggested Query Logic
1. Resolve project scope
project_id
-> subject_area
-> subject_category_ids
-> keyword_ids
2. Filter related articles
Article match nếu thỏa mãn ít nhất một trong các điều kiện:
Article.primary_topic thuộc subject_category_ids
OR Article có Sub_Topic thuộc subject_category_ids
OR Article có Keyword_Article thuộc project keyword_ids
3. Get countries per article
For each filtered article:
article_id -> authors -> institutions -> countries
Deduplicate countries per article:
countries = unique countries for that article
4. Build country pairs
Nếu một article có countries:
UNITED STATES, CHINA, JAPAN
Tạo pairs:
UNITED STATES - CHINA
UNITED STATES - JAPAN
CHINA - JAPAN
5. Aggregate co-authorship value
coAuthorshipValue = count distinct article_id per country pair
6. Apply country limit and min value
limit_countries
min_value
7. Build response
{
"source": "UNITED STATES",
"target": "CHINA",
"coAuthorshipValue": 450
}
⚠️ Edge Cases
1. Project not found
Nếu project_id không tồn tại:
{
"code": 404,
"message": "Project not found",
"data": null
}
2. Project has no scope
Nếu project chưa có subject area hoặc keywords:
{
"code": 200,
"message": "Fetch collaboration chord successfully",
"data": []
}
3. Empty filtered dataset
Nếu filter xong không có bài báo phù hợp:
{
"code": 200,
"message": "Fetch collaboration chord successfully",
"data": []
}
4. Article has authors from one country only
Nếu bài báo chỉ có tác giả từ một quốc gia:
Không tạo chord pair cho bài đó
5. Article has missing country
Nếu author/institution thiếu country:
Nếu article sau khi ignore chỉ còn 1 country:
6. No country / affiliation data available
Nếu hệ thống chưa có country hoặc affiliation data:
{
"code": 200,
"message": "Fetch collaboration chord successfully",
"data": []
}
Tạo follow-up task bổ sung schema:
Institution / Affiliation / Country
7. Invalid year range
Nếu from_year > to_year:
{
"code": 400,
"message": "Invalid year range",
"data": null
}
8. Invalid limit_countries
Nếu limit_countries <= 0 hoặc không phải number:
{
"code": 400,
"message": "Invalid limit_countries",
"data": null
}
Suggested max:
9. Invalid min_value
Nếu min_value <= 0 hoặc không phải number:
{
"code": 400,
"message": "Invalid min_value",
"data": null
}
🧪 Acceptance Criteria
🧪 Test Cases
TC-01: Fetch country collaboration chord by project_id only
Request
GET /analytics/network/chord?project_id=1
Expected
- API lấy project scope
- Filter bài báo theo project scope
- Lấy country từ author affiliation / institution
- Build country pairs
- Response đúng contract
TC-02: Fetch chord with subject_area filter
Request
GET /analytics/network/chord?project_id=1&subject_area=Artificial Intelligence
Expected
- Chỉ lấy bài báo thuộc subject area được truyền vào
- Không trả dữ liệu ngoài project scope
- Pairs vẫn đúng contract
TC-03: Fetch chord with keywords filter
Request
GET /analytics/network/chord?project_id=1&keywords=LLM,RAG
Expected
- Chỉ lấy bài báo có keyword match với
LLM hoặc RAG
- Không có item null
- Không có coAuthorshipValue null hoặc NaN
TC-04: Fetch chord with year range
Request
GET /analytics/network/chord?project_id=1&from_year=2021&to_year=2026
Expected
- Chỉ lấy bài báo trong khoảng năm 2021 đến 2026
- Response đúng contract
TC-05: Article with three countries
Input
Article có countries:
UNITED STATES
CHINA
JAPAN
Expected
Tạo 3 pairs:
[
{
"source": "CHINA",
"target": "JAPAN",
"coAuthorshipValue": 1
},
{
"source": "CHINA",
"target": "UNITED STATES",
"coAuthorshipValue": 1
},
{
"source": "JAPAN",
"target": "UNITED STATES",
"coAuthorshipValue": 1
}
]
TC-06: Duplicate country pair across multiple articles
Input
UNITED STATES và CHINA cùng xuất hiện trong 3 bài báo.
Expected
Chỉ trả 1 pair:
{
"source": "CHINA",
"target": "UNITED STATES",
"coAuthorshipValue": 3
}
TC-07: Article has one country only
Input
Article chỉ có authors từ:
Expected
- Không tạo pair
- API không crash
TC-08: Missing country data
Input
Article có một số author thiếu country.
Expected
- Ignore missing country
- Nếu còn ít nhất 2 country hợp lệ thì tạo pair
- Nếu còn dưới 2 country thì không tạo pair
TC-09: Project not found
Request
GET /analytics/network/chord?project_id=999
Expected
{
"code": 404,
"message": "Project not found",
"data": null
}
TC-10: Empty filtered dataset
Request
GET /analytics/network/chord?project_id=1&keywords=unknown-keyword
Expected
{
"code": 200,
"message": "Fetch collaboration chord successfully",
"data": []
}
TC-11: Invalid year range
Request
GET /analytics/network/chord?project_id=1&from_year=2026&to_year=2021
Expected
{
"code": 400,
"message": "Invalid year range",
"data": null
}
TC-12: Invalid limit_countries
Request
GET /analytics/network/chord?project_id=1&limit_countries=-1
Expected
{
"code": 400,
"message": "Invalid limit_countries",
"data": null
}
TC-13: Invalid min_value
Request
GET /analytics/network/chord?project_id=1&min_value=-1
Expected
{
"code": 400,
"message": "Invalid min_value",
"data": null
}
📌 Implementation Notes
Nên tách logic thành các phần riêng:
Controller
-> validate query params
Service
-> get project tracking scope
-> filter related articles
-> collect countries per article
-> build country pairs
-> aggregate co-authorship values
-> normalize country names
-> apply country limit and min value
-> build response contract
Gợi ý function:
getProjectTrackingScope(projectId)
buildArticleScopeFilter(scope, queryParams)
getFilteredArticles(scope, filters)
getCountriesByArticle(articleIds)
normalizeCountryName(rawCountry)
buildCountryPairs(countriesByArticle)
aggregateCountryPairs(pairs)
applyChordLimits(items, limitCountries, minValue)
buildCountryChordResponse(items)
📦 Suggested Mock Data
const mockCountryCollaborationChord = [
{
source: "UNITED STATES",
target: "CHINA",
coAuthorshipValue: 450
},
{
source: "CHINA",
target: "JAPAN",
coAuthorshipValue: 210
},
{
source: "UNITED STATES",
target: "EU",
coAuthorshipValue: 180
},
{
source: "JAPAN",
target: "EU",
coAuthorshipValue: 120
}
];
🚀 Future Improvements
- Add
sourceCode and targetCode using ISO Alpha-2 country codes
- Add
sourceRegion and targetRegion
- Add
group_eu=true
- Add
normalizedValue for chord thickness
- Add percentage share per connection
- Add
period object
- Add country-level total collaboration count
- Add Redis caching:
analytics:network:chord:{project_id}:{subject_area}:{keywords}:{from_year}:{to_year}:{limit_countries}:{min_value}
- Optimize query with indexes on:
project_id
publication_year
article_id
author_id
institution_id
country
keyword_id
subject_category_id
🎯 Goal
Implement API
GET /analytics/network/chordđể trả về dữ liệu liên kết hợp tác nghiên cứu xuyên quốc gia giữa các quốc gia / khu vực nghiên cứu lớn.API này phục vụ biểu đồ Country Collaboration Chord dạng Chord Diagram, hiển thị các dây nối giữa các quốc gia dựa trên mức độ hợp tác học thuật.
Mục tiêu là cung cấp danh sách connection pair gồm:
source: quốc gia nguồntarget: quốc gia đíchcoAuthorshipValue: cường độ hợp tác, thường tính bằng số bài báo đồng tác giả xuyên quốc gia🖼️ UI Mapping
UI Block
Chart Type
Description
Biểu đồ vòng tròn hiển thị quan hệ trao đổi học thuật xuyên biên giới giữa các cường quốc nghiên cứu.
Ví dụ:
Mỗi dây nối thể hiện mức độ hợp tác đồng tác giả giữa 2 quốc gia.
📡 Endpoint
📥 Query Parameters
project_idsubject_areakeywordsfrom_yearto_yearlimit_countries10min_value1✅ Example Requests
1. Fetch chord data by project only
2. Fetch chord data with subject area filter
3. Fetch chord data with keywords filter
4. Fetch chord data with year range
5. Fetch chord data with limits
🧾 Response Contract
{ "code": 200, "message": "Fetch collaboration chord successfully", "data": [ { "source": "UNITED STATES", "target": "CHINA", "coAuthorshipValue": 450 }, { "source": "CHINA", "target": "JAPAN", "coAuthorshipValue": 210 } ] }📌 Response Field Explanation
sourcetargetcoAuthorshipValue🧠 Business Logic
1. Project-based filtering
API bắt buộc nhận
project_id.Từ
project_id, hệ thống cần lấy scope nghiên cứu của project:Sau đó dùng scope này để lọc tập bài báo trước khi build country collaboration topology.
Ví dụ project đang theo dõi:
{ "project_id": 1, "subject_area": "Artificial Intelligence", "subject_categories": ["Computer Science Applications", "Artificial Intelligence"], "keywords": ["LLM", "RAG", "Machine Learning"] }API chỉ tính collaboration chord trên các bài báo thuộc scope này.
2. Filter priority
Nếu client chỉ truyền:
project_idAPI tự động lấy toàn bộ subject area, subject categories và keywords của project để lọc.
Nếu client truyền thêm:
API sẽ lọc hẹp hơn trong phạm vi project.
Ví dụ:
Kết quả chỉ tính collaboration chord trên các bài báo:
AILLMhoặcRAG3. Country collaboration definition
Một connection giữa 2 quốc gia được tạo khi cùng một bài báo có tác giả / tổ chức đến từ ít nhất 2 quốc gia khác nhau.
Ví dụ:
Tạo các pair:
Mỗi pair tăng:
4. Country source
Country nên lấy từ dữ liệu affiliation / institution của tác giả.
Suggested sources:
Nếu chưa có institution / affiliation country data:
data: []5. Country normalization
API cần normalize tên quốc gia để tránh duplicate.
Ví dụ mapping:
USAUNITED STATESUSUNITED STATESUnited States of AmericaUNITED STATESPR ChinaCHINAPeople's Republic of ChinaCHINAJapanJAPANEuropean UnionEUSuggested phase 1:
6. EU handling
Nếu data có quốc gia cụ thể thuộc EU như Germany, France, Netherlands, Italy, Spain, có 2 option:
Option A: Keep country-level data
Option B: Group EU countries into EU
Suggested phase 1:
Nếu UI mock có
EU, có thể thêm query param sau này:7. Pair canonicalization
Để tránh duplicate edge 2 chiều:
phải được merge thành một pair duy nhất.
Suggested rule:
Hoặc giữ theo predefined order nếu UI cần:
Yêu cầu:
8. coAuthorshipValue calculation
Suggested phase 1:
Nếu một article có nhiều authors từ cùng 2 quốc gia, vẫn chỉ tính 1 cho pair đó trong article.
Ví dụ:
Pair:
Không tính thành 6.
9. Country limiting rule
Để FE render chord diagram không quá rối:
limit_countries = 10limit_countries = 30Suggested logic:
10. Data validation rule
Mỗi item trả về phải đảm bảo:
sourcekhông nulltargetkhông nullsource !== targetcoAuthorshipValuelà numbercoAuthorshipValue > 0NaN📦 Data Source
API cần dữ liệu bài báo, tác giả và quốc gia affiliation.
Có thể dùng các bảng hiện tại nếu có:
Relevant schema flow:
🧮 Suggested Query Logic
1. Resolve project scope
2. Filter related articles
Article match nếu thỏa mãn ít nhất một trong các điều kiện:
3. Get countries per article
For each filtered article:
Deduplicate countries per article:
4. Build country pairs
Nếu một article có countries:
Tạo pairs:
5. Aggregate co-authorship value
6. Apply country limit and min value
7. Build response
{ "source": "UNITED STATES", "target": "CHINA", "coAuthorshipValue": 450 }1. Project not found
Nếu
project_idkhông tồn tại:{ "code": 404, "message": "Project not found", "data": null }2. Project has no scope
Nếu project chưa có subject area hoặc keywords:
{ "code": 200, "message": "Fetch collaboration chord successfully", "data": [] }3. Empty filtered dataset
Nếu filter xong không có bài báo phù hợp:
{ "code": 200, "message": "Fetch collaboration chord successfully", "data": [] }4. Article has authors from one country only
Nếu bài báo chỉ có tác giả từ một quốc gia:
5. Article has missing country
Nếu author/institution thiếu country:
Nếu article sau khi ignore chỉ còn 1 country:
6. No country / affiliation data available
Nếu hệ thống chưa có country hoặc affiliation data:
{ "code": 200, "message": "Fetch collaboration chord successfully", "data": [] }Tạo follow-up task bổ sung schema:
7. Invalid year range
Nếu
from_year > to_year:{ "code": 400, "message": "Invalid year range", "data": null }8. Invalid limit_countries
Nếu
limit_countries <= 0hoặc không phải number:{ "code": 400, "message": "Invalid limit_countries", "data": null }Suggested max:
9. Invalid min_value
Nếu
min_value <= 0hoặc không phải number:{ "code": 400, "message": "Invalid min_value", "data": null }🧪 Acceptance Criteria
project_idsubject_areakeywordsfrom_yearvàto_yearlimit_countriesmin_valuesource,target,coAuthorshipValuesourcekhông nulltargetkhông nullsource !== targetcoAuthorshipValueluôn là numbercoAuthorshipValue > 0NaNtrong response100ms🧪 Test Cases
TC-01: Fetch country collaboration chord by project_id only
Request
Expected
TC-02: Fetch chord with subject_area filter
Request
Expected
TC-03: Fetch chord with keywords filter
Request
Expected
LLMhoặcRAGTC-04: Fetch chord with year range
Request
Expected
TC-05: Article with three countries
Input
Article có countries:
Expected
Tạo 3 pairs:
[ { "source": "CHINA", "target": "JAPAN", "coAuthorshipValue": 1 }, { "source": "CHINA", "target": "UNITED STATES", "coAuthorshipValue": 1 }, { "source": "JAPAN", "target": "UNITED STATES", "coAuthorshipValue": 1 } ]TC-06: Duplicate country pair across multiple articles
Input
UNITED STATESvàCHINAcùng xuất hiện trong 3 bài báo.Expected
Chỉ trả 1 pair:
{ "source": "CHINA", "target": "UNITED STATES", "coAuthorshipValue": 3 }TC-07: Article has one country only
Input
Article chỉ có authors từ:
Expected
TC-08: Missing country data
Input
Article có một số author thiếu country.
Expected
TC-09: Project not found
Request
Expected
{ "code": 404, "message": "Project not found", "data": null }TC-10: Empty filtered dataset
Request
Expected
{ "code": 200, "message": "Fetch collaboration chord successfully", "data": [] }TC-11: Invalid year range
Request
Expected
{ "code": 400, "message": "Invalid year range", "data": null }TC-12: Invalid limit_countries
Request
Expected
{ "code": 400, "message": "Invalid limit_countries", "data": null }TC-13: Invalid min_value
Request
Expected
{ "code": 400, "message": "Invalid min_value", "data": null }📌 Implementation Notes
Nên tách logic thành các phần riêng:
Gợi ý function:
📦 Suggested Mock Data
🚀 Future Improvements
sourceCodeandtargetCodeusing ISO Alpha-2 country codessourceRegionandtargetRegiongroup_eu=truenormalizedValuefor chord thicknessperiodobjectanalytics:network:chord:{project_id}:{subject_area}:{keywords}:{from_year}:{to_year}:{limit_countries}:{min_value}project_idpublication_yeararticle_idauthor_idinstitution_idcountrykeyword_idsubject_category_id