VoteStore.java

/*
 * Copyright © 2024-2025 The CTAN Team and individual authors
 *
 * This file is distributed under the 3-clause BSD license.
 * See file LICENSE for details.
 */
package org.ctan.site.stores;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.ctan.site.domain.account.User;
import org.ctan.site.domain.catalogue.Pkg;
import org.ctan.site.domain.site.Vote;
import org.ctan.site.stores.base.AbstractStore;
import org.hibernate.SessionFactory;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.Root;
import lombok.Builder;
import lombok.Data;

/**
 * The class <code>VoteStore</code> contains the repository for votes.
 *
 * @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
 */
public class VoteStore extends AbstractStore<Vote> {

    /**
     * The class <code>CountInfo</code> contains the transport object for the
     * statistics data.
     */
    @Data
    @Builder
    public static class CountInfo {

        /**
         * The field <code>sum</code> contains the rating.
         */
        private int sum;

        /**
         * The field <code>count</code> contains the number of items.
         */
        private int count;
    }

    /**
     * This is the constructor for the <code>VoteStore</code>.
     *
     * @param sessionFactory the session factory
     */
    public VoteStore(SessionFactory sessionFactory) {

        super(sessionFactory);
    }

    /**
     * The method <code>count</code> provides means to retrieve a mean and
     * number of votes for a package by its key.
     *
     * @param key the key of the package
     * @return the summary
     */
    public CountInfo count(String key) {

        var query = criteriaQuery();
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        Root<Vote> topic = query.from(Vote.class);
        query.where(cb.equal(topic.get("pkg").get("key"), key));
        var lst = list(query);
        var sum = 0;
        for (var v : lst) {
            sum += v.getRating();
        }
        return CountInfo.builder()
            .sum(sum)
            .count(lst.size())
            .build();
    }

    /**
     * The method <code>findByAccount</code> provides means to retrieve the
     * votes of a user.
     *
     * @param user the user
     */
    public List<Vote> findByAccount(User user) {

        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        var query = criteriaQuery();
        Root<Vote> root = query.from(Vote.class);
        query.where(cb.equal(root.get("user"), user));

        return list(query);
    }

    /**
     * The method <code>findByPkg</code> provides means to retrieve a list of
     * votes for a package.
     *
     * @param pkg the package key
     *
     * @return the list of votes ordered by modification time
     */
    public List<Vote> findByPkg(Pkg pkg) {

        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        var query = criteriaQuery();
        Root<Vote> root = query.from(Vote.class);
        query.where(cb.equal(root.get("pkg"), pkg));
        query.orderBy(cb.desc(root.get("lastModified")));

        return list(query);
    }

    /**
     * The method <code>getByAccountAndPkg</code> provides means to retrieve the
     * vote of a user for a package.
     *
     * @param user the user
     * @param pkg the package
     */
    public Vote getByAccountAndPkg(User user, Pkg pkg) {

        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        var query = criteriaQuery();
        Root<Vote> root = query.from(Vote.class);
        query.where(cb.and(cb.equal(root.get("pkg"), pkg),
            cb.equal(root.get("user"), user)));

        return uniqueResult(query);
    }

    /**
     * {@inheritDoc}
     *
     * @see org.ctan.site.stores.base.AbstractStore#listQuery(java.lang.String,
     *     jakarta.persistence.criteria.CriteriaBuilder,
     *     jakarta.persistence.criteria.CriteriaQuery)
     */
    @Override
    protected Root<Vote> listQuery(String term, CriteriaBuilder cb,
        CriteriaQuery<Vote> query) {

        Root<Vote> root = query.from(Vote.class);
        if (term != null && !term.isBlank()) {
            var t = "%" + term.toLowerCase() + "%";
            query.where(cb.or(
                cb.like(cb.lower(root.get("pkg")), t),
                cb.like(cb.lower(root.get("user")), t),
                cb.like(cb.lower(root.get("comment")), t)));
        }
        return root;
    }

    /**
     * {@inheritDoc}
     *
     * @see org.ctan.site.stores.base.AbstractStore#map(java.util.List)
     */
    @Override
    protected List<Map<String, Object>> map(List<Vote> list) {

        return list.stream()
            .map(it -> it.toMap())
            .collect(Collectors.toList());
    }
}