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

Searching Bar #5

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c80f8e3
Toggle button.
danielchungara1 Sep 13, 2021
97f3b81
Dark card styling.
danielchungara1 Sep 13, 2021
70eaf2a
Page buttons & root height fixes.
danielchungara1 Sep 13, 2021
4c79f3d
Dark Mode Reducer.
danielchungara1 Sep 13, 2021
3022bb7
Link container style.
danielchungara1 Sep 13, 2021
622d6ae
Checkout, Order, Payment & Shipping dark mode.
danielchungara1 Sep 14, 2021
c071a84
Dinamyc dark mode for tables.
danielchungara1 Sep 14, 2021
3d89e8e
Fix background color.
danielchungara1 Sep 14, 2021
099c22e
Merge dark mode.
danielchungara1 Sep 14, 2021
1ffca3e
Adding search component.
danielchungara1 Sep 14, 2021
8213821
Api rest support for searching.
danielchungara1 Sep 15, 2021
bca75d0
Fix header styles.
danielchungara1 Sep 15, 2021
bdd808d
Merge search bar.
danielchungara1 Sep 15, 2021
ffb2621
Price filter form.
danielchungara1 Sep 16, 2021
914c024
Api rest supports price filter.
danielchungara1 Sep 16, 2021
1e36c00
Price filters form fixes.
danielchungara1 Sep 17, 2021
fcb5304
Script for run microservices with docker.
danielchungara1 Sep 18, 2021
982a86d
Rating filter component.
danielchungara1 Sep 18, 2021
f7e7b17
Adding support for average rating filter in catalog microservice.
danielchungara1 Sep 19, 2021
19a3ddb
Average Rating filter fixes.
danielchungara1 Sep 20, 2021
1b450c2
Product Availability Filter.
danielchungara1 Sep 20, 2021
172e858
Merge filters.
danielchungara1 Sep 20, 2021
461c3fa
Sorting catalog.
danielchungara1 Sep 22, 2021
650f5c0
Merge sorting catalog.
danielchungara1 Sep 22, 2021
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
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
package com.devd.spring.bookstorecatalogservice.controller;

import com.devd.spring.bookstorecatalogservice.service.ProductService;
import com.devd.spring.bookstorecatalogservice.web.CreateProductRequest;
import com.devd.spring.bookstorecatalogservice.web.ProductResponse;
import com.devd.spring.bookstorecatalogservice.web.ProductsPagedResponse;
import com.devd.spring.bookstorecatalogservice.web.UpdateProductRequest;
import com.devd.spring.bookstorecatalogservice.web.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.web.PagedResourcesAssembler;
Expand All @@ -25,6 +22,7 @@
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import javax.validation.Valid;
import java.math.BigDecimal;
import java.net.URI;

/**
Expand Down Expand Up @@ -82,9 +80,11 @@ public ResponseEntity<?> updateProduct(@RequestBody @Valid UpdateProductRequest
public ResponseEntity<?> getAllProducts(@RequestParam(value = "sort", required = false) String sort,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size,
@RequestParam(value = "searchText", required = false) String searchText,
ProductFiltersRequest filters,
PagedResourcesAssembler<ProductResponse> assembler) {

Page<ProductResponse> list = productService.getAllProducts(sort, page, size);
Page<ProductResponse> list = productService.getAllProducts(sort, page, size, searchText, filters);

Link link = new Link(ServletUriComponentsBuilder.fromCurrentRequest().build()
.toUriString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

import com.devd.spring.bookstorecatalogservice.repository.dao.Product;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.stereotype.Repository;

/**
* @author: Devaraj Reddy,
* Date : 2019-06-06
*/
@Repository
public interface ProductRepository extends JpaRepository<Product, String> {
public interface ProductRepository extends JpaRepository<Product, String>,
JpaSpecificationExecutor<Product> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import java.math.BigDecimal;

/**
* @author: Devaraj Reddy,
Expand Down Expand Up @@ -55,6 +56,12 @@ public class Product extends DateAudit {
@Column(name = "AVAILABLE_ITEM_COUNT")
private int availableItemCount;

@Column(name = "AVERAGE_RATING")
private BigDecimal averageRating;

@Column(name = "NO_OF_RATINGS")
private Integer noOfRatings;

public String getProductCategory() {
return productCategory.getProductCategoryName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.devd.spring.bookstorecatalogservice.repository.dao.Product;
import com.devd.spring.bookstorecatalogservice.web.CreateProductRequest;
import com.devd.spring.bookstorecatalogservice.web.ProductFiltersRequest;
import com.devd.spring.bookstorecatalogservice.web.ProductResponse;
import com.devd.spring.bookstorecatalogservice.web.UpdateProductRequest;
import javax.validation.Valid;
Expand All @@ -23,5 +24,6 @@ public interface ProductService {

Page<Product> findAllProducts(Pageable pageable);

Page<ProductResponse> getAllProducts(String sort, Integer page, Integer size);
Page<ProductResponse> getAllProducts(String sort, Integer page, Integer size, String searchText, ProductFiltersRequest filters);

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import com.devd.spring.bookstorecatalogservice.service.ProductService;
import com.devd.spring.bookstorecatalogservice.service.ReviewService;
import com.devd.spring.bookstorecatalogservice.web.CreateProductRequest;
import com.devd.spring.bookstorecatalogservice.web.ProductFiltersRequest;
import com.devd.spring.bookstorecatalogservice.web.ProductResponse;
import com.devd.spring.bookstorecatalogservice.web.UpdateProductRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand All @@ -17,9 +18,13 @@
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.persistence.criteria.Predicate;
import javax.validation.Valid;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

Expand Down Expand Up @@ -84,7 +89,7 @@ private void populateRatingForProduct(String productId, ProductResponse productR
if (reviewsForProduct.size() > 0) {
double sum = reviewsForProduct.stream().mapToDouble(Review::getRatingValue).sum();
double rating = sum / reviewsForProduct.size();
productResponse.setAverageRating(rating);
productResponse.setAverageRating(BigDecimal.valueOf(rating));
}

productResponse.setNoOfRatings(Math.toIntExact(reviewRepository.countAllByProductId(productId)));
Expand Down Expand Up @@ -148,7 +153,7 @@ public Page<Product> findAllProducts(Pageable pageable) {
}

@Override
public Page<ProductResponse> getAllProducts(String sort, Integer page, Integer size) {
public Page<ProductResponse> getAllProducts(String sort, Integer page, Integer size, String searchText, ProductFiltersRequest filters) {

//set defaults
if (size == null || size == 0) {
Expand All @@ -162,7 +167,7 @@ public Page<ProductResponse> getAllProducts(String sort, Integer page, Integer s

Pageable pageable;

if (sort == null) {
if (sort == null || sort.isEmpty()) {
pageable = PageRequest.of(page, size);
} else {
Sort.Order order;
Expand All @@ -179,10 +184,61 @@ public Page<ProductResponse> getAllProducts(String sort, Integer page, Integer s
}

}
Page<Product> allProducts = productRepository.findAll(pageable);
Page<ProductResponse> allProductsResponse = allProducts.map(Product::fromEntity);
allProductsResponse.forEach(productResponse -> populateRatingForProduct(productResponse.getProductId(), productResponse));

return allProductsResponse;
Specification<Product> specification = Specification.where(
(root, criteriaQuery, criteriaBuilder) -> {

List<Predicate> predicates = new ArrayList<>();

if (searchText != null) {
List<Predicate> predicateList = new ArrayList<>();
predicateList.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("productName")), "%" + searchText.toLowerCase() + "%"));
predicateList.add(criteriaBuilder.like(criteriaBuilder.lower(root.get("description")), "%" + searchText.toLowerCase() + "%"));

Predicate[] array = new Predicate[predicateList.size()];
predicates.add(criteriaBuilder.or(predicateList.toArray(array)));
}

if (filters.getMinPrice() != null) {
predicates.add(criteriaBuilder.greaterThanOrEqualTo(root.get("price"), filters.getMinPrice()));
}

if (filters.getMaxPrice() != null) {
predicates.add(criteriaBuilder.lessThanOrEqualTo(root.get("price"), filters.getMaxPrice()));
}

if (filters.getMinRating() != null) {
List<Predicate> predicateList = new ArrayList<>();
predicateList.add(criteriaBuilder.greaterThanOrEqualTo(root.get("averageRating"), filters.getMinRating()));
if (filters.getMinRating().equals(BigDecimal.ZERO)) {
predicateList.add(criteriaBuilder.isNull(root.get("averageRating"))); // Include no rating products
}

Predicate[] array = new Predicate[predicateList.size()];
predicates.add(criteriaBuilder.or(predicateList.toArray(array)));
}

if (filters.getMaxRating() != null) {
List<Predicate> predicateList = new ArrayList<>();
predicateList.add(criteriaBuilder.lessThanOrEqualTo(root.get("averageRating"), filters.getMaxRating()));
predicateList.add(criteriaBuilder.isNull(root.get("averageRating"))); // Include no rating products

Predicate[] array = new Predicate[predicateList.size()];
predicates.add(criteriaBuilder.or(predicateList.toArray(array)));
}

if (filters.getAvailability() != null && filters.getAvailability().equals(true)) {
predicates.add(criteriaBuilder.greaterThan(root.get("availableItemCount"), 0));
}

return criteriaBuilder.and(predicates.toArray(new Predicate[]{}));

}
);

Page<Product> allProducts = productRepository.findAll(specification, pageable);

return allProducts.map(Product::fromEntity);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.devd.spring.bookstorecatalogservice.web;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

/**
* @author: Daniel Chungara,
* Date : 2021-09-16
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ProductFiltersRequest {

private BigDecimal minPrice;

private BigDecimal maxPrice;

private BigDecimal minRating;

private BigDecimal maxRating;

private Boolean availability;

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;

/**
* @author Devaraj Reddy, Date : 08-Nov-2020
*/
Expand All @@ -18,7 +20,7 @@ public class ProductResponse {
private double price;
private String productCategory;
private int availableItemCount;
private Double averageRating;
private BigDecimal averageRating;
private int noOfRatings;
private String imageId;

Expand Down
14 changes: 7 additions & 7 deletions bookstore-catalog-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ logging:
spring:
profiles: local
jpa:
database-platform: org.hibernate.dialect.H2Dialect
database: h2
database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
database: mysql
open-in-view: true
hibernate:
ddl-auto: none
ddl-auto: validate
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
properties:
Expand All @@ -52,10 +52,10 @@ spring:
use_sql_comments: true
format_sql: true
datasource:
driver-class-name: org.h2.Driver
url: jdbc:h2:mem:bookstore_db;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;DB_CLOSE_ON_EXIT=FALSE
username: sa
password:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/bookstore_db?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
username: bookstoreDBA
password: PaSSworD
h2:
console:
enabled: true
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
-- Adding average rating column into product.
alter table PRODUCT
add column average_rating decimal(2,1) default null;

-- Function that returns the average rating of a product.
DELIMITER $$
CREATE FUNCTION averageRating(product_id varchar(255))
RETURNS DECIMAL(2,1) READS SQL DATA DETERMINISTIC
BEGIN
DECLARE average DECIMAL(2,1) DEFAULT NULL;

SELECT avg (rating_value)
INTO average
FROM PRODUCT p
INNER JOIN REVIEW r
ON p.product_id = r.product_id
WHERE p.product_id = product_id;

RETURN average;
END
$$

-- Procedure that updates the average rating of a product.
DELIMITER $$
CREATE PROCEDURE updateAverageRatingByProductId(productID varchar(255))
BEGIN
UPDATE PRODUCT p
SET p.average_rating = averageRating(productID)
WHERE p.product_id = productID ;
END $$
DELIMITER ;

-- Procedure that update the average rating of all products
DELIMITER $$
CREATE PROCEDURE updateAllAverageRating()
BEGIN
DECLARE finished INTEGER DEFAULT 0;
DECLARE productID varchar(255) DEFAULT NULL;

-- declare cursor
DEClARE curProduct
CURSOR FOR
SELECT product_id FROM PRODUCT;

-- declare NOT FOUND handler
DECLARE CONTINUE HANDLER
FOR NOT FOUND SET finished = 1;

OPEN curProduct;

readLoop: LOOP
FETCH curProduct INTO productID;
IF finished = 1 THEN
LEAVE readLoop;
END IF;
CALL updateAverageRatingByProductId(productID);
END LOOP;

CLOSE curProduct;

END
$$

-- Call procedure for sets the average rating of all products
DELIMITER $$
CALL updateAllAverageRating()
$$

-- Trigger that updates the average rating after insert a new review.
DELIMITER $$
CREATE TRIGGER updateAverageRatingAfterInsertReviewTrigger
AFTER INSERT ON REVIEW
FOR EACH ROW

BEGIN
CALL updateAverageRatingByProductId(NEW.product_id);
END
$$

-- Trigger that updates the average rating after update a review.
DELIMITER $$
CREATE TRIGGER updateAverageRatingAfterUpdateReviewTrigger
AFTER UPDATE ON REVIEW
FOR EACH ROW

BEGIN
CALL updateAverageRatingByProductId(NEW.product_id);

-- In case product_id changes then update old
IF OLD.product_id <> NEW.product_id THEN
CALL updateAverageRatingByProductId(OLD.product_id);
END IF;

END
$$

-- Trigger that updates the average rating after delete a review.
DELIMITER $$
CREATE TRIGGER updateAverageRatingAfterDeleteReviewTrigger
AFTER DELETE ON REVIEW
FOR EACH ROW

BEGIN
CALL updateAverageRatingByProductId(OLD.product_id);

END
$$
DELIMITER ;
Loading