Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BQGVFX] REFACT - Refatorar metodo updateRelatedBooks no serviço bookStore #22

Merged
merged 15 commits into from
Feb 2, 2025
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
144 changes: 92 additions & 52 deletions src/main/java/servico/bookstore/Bookstore.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import dominio.OrderLine;
import dominio.Review;
import dominio.Stock;
import servico.bookstore.utils.Counter;
import servico.bookstore.utils.BookstoreBookCounter;
import util.BookstoreConstants.Backing;
import util.BookstoreConstants.Subject;
import util.TPCW_Util;
Expand All @@ -35,6 +35,7 @@
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

Expand Down Expand Up @@ -703,66 +704,105 @@ public Stock getStock(int bId) {
public List<Stock> getStocks() {
return new ArrayList<>(stockByBook.values());
}

/**
* For all the clients that bought this book in the last 10000 orders, what
* are the five most sold books except this one.
* Updates the related books recommendations for a given book based on customer purchase history.
* The recommendations are generated by analyzing the last 10,000 orders and identifying
* books frequently purchased together with the target book.
*
* @param targetBook - O livro que terá os relacionados atualizados
* @param targetBook the book for which to update related recommendations
*/
public void updateRelatedBooks(Book targetBook) {
Set<Integer> clientIds = getClientIdsWhoBoughtTargetBook(targetBook);

Map<Integer, BookstoreBookCounter> purchaseFrequency = countBooksBoughtByClients(clientIds, targetBook);
Book[] topRelatedBooks = getTopFiveRelatedBooks(purchaseFrequency, targetBook);
setRelatedBooks(targetBook, topRelatedBooks);
}

/**
* Retrieves the IDs of customers who purchased the specified book in the last 10,000 orders.
*
* @param targetBook the book to search for in customer orders
* @return a set of customer IDs who purchased the target book
*/
private void updateRelatedBooks(Book targetBook) {
HashSet<Integer> clientIds = new HashSet<>();
int j = 0;
Iterator<Order> i = ordersByCreation.iterator();
while (i.hasNext() && j <= 10000) {
Order order = i.next();
for (OrderLine line : order.getLines()) {
Book book = line.getBook();
if (targetBook.getId() == book.getId()) {
clientIds.add(order.getCustomer().getId());
break;
}
}
j++;
private Set<Integer> getClientIdsWhoBoughtTargetBook(Book targetBook) {
Set<Integer> clientIds = new HashSet<>();
Iterator<Order> orderIterator = ordersByCreation.iterator();

int orderOldLimit = 10000;
for (int orderCount = 0; orderIterator.hasNext() && orderCount < orderOldLimit; orderCount++) {
Order order = orderIterator.next();
order.getLines().stream()
.filter(line -> line.getBook().getId() == targetBook.getId())
.findFirst()
.ifPresent(line -> clientIds.add(order.getCustomer().getId()));
}
HashMap<Integer, Counter> counters = new HashMap<>();
i = ordersByCreation.iterator();
while (i.hasNext()) {
Order order = i.next();
if (clientIds.contains(order.getCustomer().getId())) {
order.getLines().forEach((line) -> {

return clientIds;
}

**
* Counts the frequency of books purchased by the specified customers, excluding the target book.
*
* @param clientIds set of customer IDs to analyze purchases for
* @param targetBook the book to exclude from the counting
* @return a map of book IDs to their purchase frequency counters
*/
private Map<Integer, BookstoreBookCounter> countBooksBoughtByClients(Set<Integer> clientIds, Book targetBook) {
Map<Integer, BookstoreBookCounter> purchaseFrequency = new HashMap<>();

ordersByCreation.stream()
.filter(order -> clientIds.contains(order.getCustomer().getId()))
.forEach(order -> {
order.getLines().forEach(line -> {
Book book = line.getBook();
if (targetBook.getId() != book.getId()) {
Counter counter = counters.get(book.getId());
if (counter == null) {
counter = new Counter();
counter.book = book;
counter.count = 0;
counters.put(book.getId(), counter);
}
counter.count += line.getQty();
if (book.getId() != targetBook.getId()) {
purchaseFrequency.computeIfAbsent(book.getId(), id -> new BookstoreBookCounter(book))
.addQuantity(line.getQty()); //conta os outros livros comprados
}
});
}
}
Counter[] sorted = counters.values().toArray(new Counter[] {});
Arrays.sort(sorted, (Counter a, Counter b) -> {
if (b.count > a.count) {
return 1;
}
return b.count < a.count ? -1 : 0;
});
Book[] related = new Book[] { targetBook, targetBook, targetBook,
targetBook, targetBook };
for (j = 0; j < 5 && j < sorted.length; j++) {
related[j] = sorted[j].book;
});

return purchaseFrequency;
}

/**
* Identifies the top five most frequently purchased books from the frequency map.
* If fewer than five related books are found, the remaining slots are filled with the target book.
*
* @param purchaseFrequency map of book purchase frequencies
* @param targetBook the original book used to fill empty slots if necessary
* @return an array of the top five related books
*/
private Book[] getTopFiveRelatedBooks(Map<Integer, BookstoreBookCounter> purchaseFrequency, Book targetBook) {
// Ordena os livros mais vendidos e pega os cinco primeiros
List<BookstoreBookCounter> sortedFrequencies = purchaseFrequency.values().stream()
.sorted(Comparator.comparingInt(BookstoreBookCounter::getCount).reversed())
.collect(Collectors.toList()); // Ordena os livros mais vendidos e pega os cinco primeiro

int top = 5;
Book[] relatedBooks = new Book[top];
for (int i = 0; i < Math.min(top, sortedFrequencies.size()); i++) {
relatedBooks[i] = sortedFrequencies.get(i).getBook();
}
targetBook.setRelated1(related[0]);
targetBook.setRelated2(related[1]);
targetBook.setRelated3(related[2]);
targetBook.setRelated4(related[3]);
targetBook.setRelated5(related[4]);
Arrays.fill(relatedBooks, relatedBooks.length, top, targetBook);

return relatedBooks;
}

/**
* Sets the five related book references in the target book.
*
* @param targetBook the book to update with related recommendations
* @param relatedBooks array of five books to set as related items
*/
private void setRelatedBooks(Book targetBook, Book[] relatedBooks) {
targetBook.setRelated1(relatedBooks[0]);
targetBook.setRelated2(relatedBooks[1]);
targetBook.setRelated3(relatedBooks[2]);
targetBook.setRelated4(relatedBooks[3]);
targetBook.setRelated5(relatedBooks[4]);
}

/**
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/servico/bookstore/utils/BookstoreBookCounter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package servico.bookstore.utils;

import dominio.Book;

public class BookstoreBookCounter {
Book book;
int count;

public BookstoreBookCounter(Book book) {
this.book = book;
this.count = 0;
}

public void addQuantity(int qty) {
this.count += qty;
}

public int getCount() {
return this.count;
}

public Book getBook() {
return this.book;
}
}
21 changes: 21 additions & 0 deletions src/test/java/servico/BookstoreTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -525,4 +525,25 @@ public void testGetRecommendationByUsers() {
// TODO review the generated test code and remove the default call to fail.
fail("The test case is a prototype.");
}

@Test
public void shouldUpdateRelatedBooks() {
Book randomBook = instance.getABookAnyBook(new Random(0));

Book oldRelated1 = randomBook.getRelated1();
Book oldRelated2 = randomBook.getRelated2();
Book oldRelated3 = randomBook.getRelated3();
Book oldRelated4 = randomBook.getRelated4();
Book oldRelated5 = randomBook.getRelated5();

instance.updateRelatedBooks(randomBook);

Book updatedBook = instance.getBook(randomBook.getId()).get();

assertFalse(oldRelated1.getId() == updatedBook.getRelated1().getId());
assertFalse(oldRelated2.getId() == updatedBook.getRelated2().getId());
assertFalse(oldRelated3.getId() == updatedBook.getRelated3().getId());
assertFalse(oldRelated4.getId() == updatedBook.getRelated4().getId());
assertFalse(oldRelated5.getId() == updatedBook.getRelated5().getId());
}
}
Loading