-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbfs.py
More file actions
63 lines (53 loc) · 2.29 KB
/
Copy pathbfs.py
File metadata and controls
63 lines (53 loc) · 2.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
from collections import defaultdict, deque
from typing import List
from data_access.models import Book
class GraphRecommender:
"""
Graph-based recommender using BFS.
Nodes: books; edges: same author or same category.
"""
@staticmethod
def get_recommendations(
start_book_id: str, max_depth: int = 2, max_results: int = 10
) -> List[Book]:
# Build feature maps: author -> books, category -> books
author_map = defaultdict(set)
category_map = defaultdict(set)
all_books = list(Book.objects.only("id", "authors", "categories")) # O(n)
for b in all_books: # O(n * f)
if b.authors:
for author in b.authors.split(","):
author_map[author.strip().lower()].add(b.id)
if b.categories:
for cat in b.categories.split(","):
category_map[cat.strip().lower()].add(b.id)
visited = set([start_book_id])
queue = deque([(start_book_id, 0)])
recommendations = []
while queue and len(recommendations) < max_results:
current_id, depth = queue.popleft()
if depth >= max_depth:
continue
# neighbors by author
b = Book.objects.only("id", "authors", "categories").get(pk=current_id)
neighbors = set()
if b.authors:
for author in b.authors.split(","):
neighbors |= author_map[author.strip().lower()]
if b.categories:
for cat in b.categories.split(","):
neighbors |= category_map[cat.strip().lower()]
for nb_id in neighbors:
if nb_id not in visited:
visited.add(nb_id)
queue.append((nb_id, depth + 1))
if nb_id != start_book_id:
recommendations.append(nb_id)
if len(recommendations) >= max_results:
break
# Fetch Book instances preserving order
books = list(Book.objects.filter(id__in=recommendations))
# maintain recommendation order
id_to_book = {book.id: book for book in books}
ordered = [id_to_book[rid] for rid in recommendations if rid in id_to_book]
return ordered