LugStore.java

/*
 * Copyright © 2023-2026 The CTAN Team and individual authors
 *
 * This file is distributed under the 3-clause BSD lug.
 * 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.site.Lug;
import org.ctan.site.stores.base.GeneralPage;
import org.hibernate.SessionFactory;

import io.dropwizard.hibernate.AbstractDAO;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Root;

/**
 * The class <code>LugStore</code> contains the repository for LUGs.
 *
 * @author <a href="mailto:gene@ctan.org">Gerd Neugebauer</a>
 */
public class LugStore extends AbstractDAO<Lug> {

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

        super(sessionFactory);
    }

    /**
     * The method <code>drop</code> provides means to delete a list of lugs.
     *
     * @param lugs the LUGs to delete
     */
    public void drop(List<Lug> lugs) {

        lugs.forEach(x -> drop(x));
    }

    /**
     * The method <code>drop</code> provides means to remove the lug form the
     * database and the search index.
     *
     * @param lug the lug
     */
    public void drop(Lug lug) {

        currentSession().remove(lug);
    }

    /**
     * The method <code>findAll</code> provides means to retrieve all LUGs.
     *
     * @return the list of LUGs
     */
    public List<Lug> findAll() {

        var query = criteriaQuery();
        currentSession().getCriteriaBuilder();
        query.from(Lug.class);

        return list(query);
    }

    /**
     * The method <code>findAllBy</code> provides means to retrieve LUGs where
     * the short name is starting with a given string. The comparison is done
     * case-insensitive.
     *
     * @param pattern the pattern for the name
     * @return the list of LUGs ordered by the short name
     */
    public List<Lug> findAllBy(String pattern) {

        var query = criteriaQuery();
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        Root<Lug> lug = query.from(Lug.class);

        query.where(cb.like(lug.get("shortName"), pattern + "%"))
            .orderBy(cb.asc(lug.get("shortName")));

        return list(query);
    }

    /**
     * The method <code>findOrCreateByCode</code> provides means to retrieve a
     * LUG by its id.
     *
     * @param code the id code
     * @return the LUG or {@code null}
     */
    public Lug findOrCreateByCode(String code) {

        var lug = getByKey(code);

        if (lug == null) {
            lug = persist(Lug.builder()
                .code(code)
                .build());
        }
        return lug;
    }

    /**
     * The method <code>getByKey</code> provides means to find an lug by its
     * key.
     *
     * @param key the key
     * @return the lug or {@code null}
     */
    public Lug getByKey(String key) {

        var query = criteriaQuery();
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        Root<Lug> lug = query.from(Lug.class);
        query.where(cb.equal(lug.get("code"), key));

        return uniqueResult(query);
    }

    /**
     * The method <code>list</code> provides means to extract a page of items.
     *
     * @param term the search term
     * @param page the current page
     * @param pageSize the page size
     * @return the paged results
     */
    public GeneralPage list(String term, int page, int pageSize,
        String orderBy, boolean asc) {

        if (page < 0 || pageSize < 1) {
            return null;
        }
        CriteriaBuilder cb = currentSession().getCriteriaBuilder();
        var query = criteriaQuery();
        Root<Lug> pkg = query.from(Lug.class);
        if (term != null && !term.isBlank()) {
            var t = "%" + term.toLowerCase() + "%";
            query.where(
                cb.or(
                    cb.like(cb.lower(pkg.get("code")), t),
                    cb.like(cb.lower(pkg.get("shortName")), t)));
        }
        if (orderBy != null && !orderBy.isBlank()) {
            if (asc) {
                query.orderBy(cb.asc(pkg.get(orderBy)));
            } else {
                query.orderBy(cb.desc(pkg.get(orderBy)));
            }
        }
        List<Lug> hits = list(query);
        var hitCount = hits.size();
        List<Map<String, Object>> list = hits
            .subList(Math.min(page * pageSize, hitCount),
                Math.min((page + 1) * pageSize, hitCount))
            .stream()
            .map(it -> it.toMap())
            .collect(Collectors.toList());
        return GeneralPage.builder()
            .size(hitCount)
            .list(list)
            .build();
    }

    /**
     * The method <code>save</code> provides means to persist an lug. As a side
     * effect the search index is updated.
     *
     * @param lug the lug
     * @return the updated lug
     */
    public Lug save(Lug lug) {

        return persist(lug);
    }

}