Skip to content

BACKEND ISSUE 12 — [FEATURE] Implement Global Collaboration Network Graph API With Project-Based Filters #28

Description

@LeDuyCoder

🎯 Goal

Implement API GET /analytics/network/collaboration để trả về dữ liệu mạng lưới hợp tác nghiên cứu toàn cầu.

API này phục vụ biểu đồ Global Collaboration Network, hiển thị dưới dạng graph topology gồm:

  • Nodes: đại diện cho Author hoặc Institution
  • Edges: đại diện cho quan hệ hợp tác nghiên cứu giữa các node

API cần nhận project_id, sau đó dựa trên phạm vi mà project đang theo dõi như:

  • subject_area
  • subject_category
  • keywords

để lọc dữ liệu bài báo trước khi xây dựng mạng lưới collaboration.


🖼️ UI Mapping

Khối UI:

Global Collaboration Network

Vị trí:

Bottom-left section of Screen 3

Chart type:

Network graph / Force-directed graph

Visual rules:

Node Type Color Meaning
AUTHOR #FF6B00 Tác giả
INSTITUTION #1A202C Tổ chức / viện nghiên cứu / trường đại học

📡 Endpoint

GET /analytics/network/collaboration

📥 Query Parameters

Param Type Required Description
project_id string / number Yes ID của project cần lấy collaboration network
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_nodes number No Số lượng node tối đa. Default: 50
min_weight number No Chỉ lấy edge có weight tối thiểu. Default: 1

✅ Example Requests

1. Fetch collaboration network by project only

GET /analytics/network/collaboration?project_id=PROJECT_ID

2. Fetch collaboration network with subject area filter

GET /analytics/network/collaboration?project_id=PROJECT_ID&subject_area=Computer Science

3. Fetch collaboration network with keywords filter

GET /analytics/network/collaboration?project_id=PROJECT_ID&keywords=AI,Machine Learning,RAG

4. Fetch collaboration network with year range

GET /analytics/network/collaboration?project_id=PROJECT_ID&from_year=2021&to_year=2026

5. Fetch limited network

GET /analytics/network/collaboration?project_id=PROJECT_ID&limit_nodes=40&min_weight=2

🧾 Response Contract

{
  "code": 200,
  "message": "Fetch global collaboration network successfully",
  "data": {
    "nodes": [
      {
        "id": "auth_991",
        "label": "Dr. Helena Vance",
        "type": "AUTHOR",
        "size": 28,
        "color": "#FF6B00"
      },
      {
        "id": "inst_001",
        "label": "Stanford Bio-Dynamics Lab",
        "type": "INSTITUTION",
        "size": 24,
        "color": "#1A202C"
      }
    ],
    "edges": [
      {
        "from": "auth_991",
        "to": "inst_001",
        "weight": 5,
        "label": "5 joint ventures"
      }
    ]
  }
}

🧠 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 network.

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ỉ build network dựa trên các bài báo thuộc scope này.


2. Filter priority

Nếu client chỉ truyền:

project_id

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:

subject_area
keywords

API sẽ lọc hẹp hơn trong phạm vi project.

Ví dụ:

GET /analytics/network/collaboration?project_id=1&subject_area=AI&keywords=LLM,RAG

Kết quả chỉ tính collaboration network 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. Node definition

API cần trả về 2 loại node chính:

AUTHOR node

Đại diện cho tác giả.

{
  "id": "auth_991",
  "label": "Dr. Helena Vance",
  "type": "AUTHOR",
  "size": 28,
  "color": "#FF6B00"
}

INSTITUTION node

Đại diện cho tổ chức / viện nghiên cứu / trường đại học.

{
  "id": "inst_001",
  "label": "Stanford Bio-Dynamics Lab",
  "type": "INSTITUTION",
  "size": 24,
  "color": "#1A202C"
}

Important: INSTITUTION không phải Publisher hoặc Journal. Institution phải đến từ affiliation / organization của author hoặc article.


4. Edge definition

Edge đại diện cho quan hệ hợp tác nghiên cứu giữa 2 node.

Các loại edge phase 1 có thể hỗ trợ:

Edge Type Meaning
Author -> Author Hai tác giả cùng xuất hiện trong một hoặc nhiều bài báo
Author -> Institution Tác giả thuộc / hợp tác với institution
Institution -> Institution Hai institution cùng tham gia trong một hoặc nhiều bài báo

Response hiện tại có thể trả generic edge:

{
  "from": "auth_991",
  "to": "inst_001",
  "weight": 5,
  "label": "5 joint ventures"
}

Trong đó:

Field Meaning
from ID node nguồn
to ID node đích
weight Số lượng hợp tác / số bài báo chung / số joint works
label Text hiển thị tooltip hoặc edge label

5. Node size calculation

size thể hiện độ quan trọng của node trong network.

Suggested phase 1 rules:

Author size

size = normalized(author_article_count + author_citation_count)

Hoặc đơn giản hơn:

size = 12 + min(article_count * 2, 20)

Institution size

size = normalized(institution_article_count + institution_citation_count)

Hoặc đơn giản hơn:

size = 12 + min(collaboration_count * 2, 20)

Yêu cầu:

min size = 12
max size = 36

6. Edge weight calculation

weight đại diện cho cường độ hợp tác.

Suggested rules:

Author -> Author

weight = số bài báo mà 2 author cùng tham gia

Author -> Institution

weight = số bài báo của author liên quan đến institution

Institution -> Institution

weight = số bài báo có author thuộc cả 2 institution

Label format:

"{weight} joint ventures"

Ví dụ:

{
  "from": "auth_991",
  "to": "inst_001",
  "weight": 5,
  "label": "5 joint ventures"
}

7. Cross-country collaboration

Vì UI mô tả quan hệ hợp tác nghiên cứu xuyên quốc gia, nếu có dữ liệu country/zone cho institution thì API nên ưu tiên edge giữa các node khác quốc gia.

Suggested logic:

crossCountry = sourceCountryCode !== targetCountryCode

Phase 1 nếu chưa có country institution data:

  • Không bắt buộc trả countryCode
  • Không bắt buộc filter cross-country thật
  • Có thể build general collaboration network trước

Future improvement có thể thêm:

{
  "from": "inst_001",
  "to": "inst_002",
  "weight": 8,
  "label": "8 cross-country collaborations",
  "crossCountry": true
}

8. Graph limiting rule

Để tránh FE render quá nặng:

  • Default limit_nodes = 50
  • Max suggested limit_nodes = 150
  • Chỉ trả edges có cả fromto tồn tại trong nodes
  • Nếu edge duplicate, merge edge bằng cách cộng weight
  • Sort nodes theo importance score giảm dần
  • Sort edges theo weight giảm dần

📦 Data Source

Available current schema

Có thể dùng các bảng hiện tại để build Author network:

Author
Author_Article
Article
Keyword_Article
Topic
Sub_Topic
Subject_Category
Subject_Area
Project
Project_Keyword

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
Author_Article.author_id -> Author.author_id
Author_Article.article_id -> Article.article_id
Keyword_Article.article_id -> Article.article_id
Keyword_Article.keyword_id -> Keyword.keyword_id
Project_Keyword.keyword_id -> Keyword.keyword_id

Institution data requirement

Để build đúng INSTITUTION node, hệ thống cần có dữ liệu affiliation / institution.

Suggested schema:

Institution
Author_Institution

Hoặc:

Institution
Article_Institution

Nếu hiện tại chưa có institution data:

  • Phase 1 có thể trả institution mock data
  • Hoặc chỉ trả author-author graph
  • Hoặc trả nodes chỉ gồm authors và edges chỉ gồm author-author collaborations
  • Tạo follow-up task để bổ sung Institution/Affiliation schema

Không dùng Publisher hoặc Journal để thay thế Institution vì chúng có ý nghĩa khác hoàn toàn.


🧮 Suggested Graph Construction 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 keyword_ids

3. Build author nodes

Group theo author:

Author -> Author_Article -> filtered Articles

Node fields:

id = "auth_" + author_id
label = Author.display_name
type = "AUTHOR"
color = "#FF6B00"
size = calculated size

4. Build author-author edges

Với mỗi article có nhiều author:

Tạo edge giữa từng cặp author trong cùng article

Ví dụ bài báo có 3 tác giả A, B, C:

A-B
A-C
B-C

Nếu nhiều bài lặp cùng cặp author:

weight += 1

5. Build institution nodes and edges

Nếu có Institution/Affiliation data:

Institution -> Author_Institution -> Author -> Author_Article -> Article

Tạo:

  • Institution nodes
  • Author -> Institution edges
  • Institution -> Institution edges nếu có hợp tác giữa institution khác nhau

⚠️ 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 global collaboration network successfully",
  "data": {
    "nodes": [],
    "edges": []
  }
}

3. Empty filtered dataset

Nếu filter xong không có bài báo phù hợp:

{
  "code": 200,
  "message": "Fetch global collaboration network successfully",
  "data": {
    "nodes": [],
    "edges": []
  }
}

4. Article has one author only

Nếu article chỉ có 1 author:

  • Vẫn có thể tạo author node
  • Không tạo author-author edge cho article đó

5. Missing author name

Nếu author thiếu display_name:

label = "Unknown Author"

hoặc bỏ qua author đó tùy convention BE.

Suggested phase 1:

Use "Unknown Author"

6. Missing institution data

Nếu chưa có Institution/Affiliation data:

{
  "code": 200,
  "message": "Fetch global collaboration network successfully",
  "data": {
    "nodes": [
      {
        "id": "auth_991",
        "label": "Dr. Helena Vance",
        "type": "AUTHOR",
        "size": 28,
        "color": "#FF6B00"
      }
    ],
    "edges": []
  }
}

Hoặc build author-author edges nếu có co-author data.


7. Invalid year range

Nếu from_year > to_year:

{
  "code": 400,
  "message": "Invalid year range",
  "data": null
}

8. Invalid limit_nodes

Nếu limit_nodes <= 0 hoặc không phải number:

{
  "code": 400,
  "message": "Invalid limit_nodes",
  "data": null
}

Suggested max:

150

9. Invalid min_weight

Nếu min_weight <= 0 hoặc không phải number:

{
  "code": 400,
  "message": "Invalid min_weight",
  "data": null
}

🧪 Acceptance Criteria

  • API nhận được project_id
  • API tự lấy được subject area / subject categories / keywords của project
  • API hỗ trợ filter thêm bằng subject_area
  • API hỗ trợ filter thêm bằng keywords
  • API hỗ trợ filter theo from_yearto_year
  • API hỗ trợ limit_nodes
  • API hỗ trợ min_weight
  • Response có object nodes
  • Response có object edges
  • Node có đủ id, label, type, size, color
  • Edge có đủ from, to, weight, label
  • Node type chỉ gồm AUTHOR, INSTITUTION
  • Author node color là #FF6B00
  • Institution node color là #1A202C
  • size luôn là number
  • weight luôn là number
  • Không có node trùng id
  • Không có edge trỏ tới node không tồn tại
  • Empty dataset trả về { nodes: [], edges: [] }
  • FE có thể render network graph trực tiếp không cần transform thêm
  • Response time với dữ liệu nhỏ dưới 100ms

🧪 Test Cases

TC-01: Fetch collaboration network by project_id only

Request

GET /analytics/network/collaboration?project_id=1

Expected

  • API lấy project scope
  • Filter bài báo theo project scope
  • Build nodes từ authors / institutions nếu có
  • Build edges từ quan hệ collaboration
  • Response đúng contract

TC-02: Fetch network with subject_area filter

Request

GET /analytics/network/collaboration?project_id=1&subject_area=Artificial Intelligence

Expected

  • Chỉ lấy dữ liệu thuộc subject area được truyền vào
  • Không trả dữ liệu ngoài project scope
  • Nodes và edges vẫn hợp lệ

TC-03: Fetch network with keywords filter

Request

GET /analytics/network/collaboration?project_id=1&keywords=LLM,RAG

Expected

  • Chỉ lấy dữ liệu có keyword match với LLM hoặc RAG
  • Không có node null
  • Không có edge trỏ tới node không tồn tại

TC-04: Fetch network with year range

Request

GET /analytics/network/collaboration?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
  • Không trả data ngoài khoảng năm
  • Response đúng contract

TC-05: Article has multiple authors

Input

Một article có 3 authors:

A, B, C

Expected

Tạo author nodes:

A
B
C

Tạo edges:

A-B
A-C
B-C

TC-06: Duplicate collaboration edge

Input

Author A và Author B cùng viết 3 bài.

Expected

Chỉ trả 1 edge:

{
  "from": "auth_A",
  "to": "auth_B",
  "weight": 3,
  "label": "3 joint ventures"
}

TC-07: Article has one author only

Input

Một article chỉ có Author A.

Expected

  • Có node Author A
  • Không tạo author-author edge cho article đó

TC-08: Project not found

Request

GET /analytics/network/collaboration?project_id=999

Expected

{
  "code": 404,
  "message": "Project not found",
  "data": null
}

TC-09: Empty filtered dataset

Request

GET /analytics/network/collaboration?project_id=1&keywords=unknown-keyword

Expected

{
  "code": 200,
  "message": "Fetch global collaboration network successfully",
  "data": {
    "nodes": [],
    "edges": []
  }
}

TC-10: Invalid year range

Request

GET /analytics/network/collaboration?project_id=1&from_year=2026&to_year=2021

Expected

{
  "code": 400,
  "message": "Invalid year range",
  "data": null
}

TC-11: Invalid limit_nodes

Request

GET /analytics/network/collaboration?project_id=1&limit_nodes=-1

Expected

{
  "code": 400,
  "message": "Invalid limit_nodes",
  "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 authors and institutions
  -> build nodes
  -> build edges
  -> merge duplicate edges
  -> apply min_weight
  -> apply node limit
  -> build response contract

Gợi ý function:

getProjectTrackingScope(projectId)
buildArticleScopeFilter(scope, queryParams)
getFilteredArticles(scope, filters)
getAuthorNodes(filteredArticles)
getInstitutionNodes(filteredArticles)
buildAuthorCollaborationEdges(filteredArticles)
buildAuthorInstitutionEdges(filteredArticles)
buildInstitutionCollaborationEdges(filteredArticles)
mergeDuplicateEdges(edges)
applyNetworkLimits(nodes, edges, limitNodes, minWeight)
buildCollaborationNetworkResponse(nodes, edges)

📦 Suggested Mock Data

const mockCollaborationNetwork = {
  nodes: [
    {
      id: "auth_991",
      label: "Dr. Helena Vance",
      type: "AUTHOR",
      size: 28,
      color: "#FF6B00"
    },
    {
      id: "auth_992",
      label: "Prof. Marcus Lee",
      type: "AUTHOR",
      size: 22,
      color: "#FF6B00"
    },
    {
      id: "inst_001",
      label: "Stanford Bio-Dynamics Lab",
      type: "INSTITUTION",
      size: 24,
      color: "#1A202C"
    }
  ],
  edges: [
    {
      from: "auth_991",
      to: "auth_992",
      weight: 3,
      label: "3 joint ventures"
    },
    {
      from: "auth_991",
      to: "inst_001",
      weight: 5,
      label: "5 joint ventures"
    }
  ]
};

🚀 Future Improvements

  • Add real Institution/Affiliation schema if not available
  • Add country code for institution nodes
  • Add cross-country flag for edges
  • Add node coordinates if FE wants fixed layout
  • Add edge type:
    • CO_AUTHOR
    • AUTHOR_INSTITUTION
    • INSTITUTION_COLLABORATION
  • Add community detection / clustering
  • Add collaboration strength score
  • Add filters by country / region
  • Add node_type=authors|institutions|all
  • Add Redis caching:
analytics:network:collaboration:{project_id}:{subject_area}:{keywords}:{from_year}:{to_year}:{limit_nodes}:{min_weight}
  • Optimize query with indexes on:
    • project_id
    • publication_year
    • author_id
    • article_id
    • keyword_id
    • subject_category_id

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions