com.ushahidi.swiftriver.core.api.service.DropIndexService.java Source code

Java tutorial

Introduction

Here is the source code for com.ushahidi.swiftriver.core.api.service.DropIndexService.java

Source

/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/agpl.html>
 * 
 * Copyright (C) Ushahidi Inc. All Rights Reserved.
 */
package com.ushahidi.swiftriver.core.api.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.dozer.Mapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ushahidi.swiftriver.core.api.dao.DropDao;
import com.ushahidi.swiftriver.core.api.dto.GetDropDTO;
import com.ushahidi.swiftriver.core.model.Drop;
import com.ushahidi.swiftriver.core.solr.DropDocument;
import com.ushahidi.swiftriver.core.solr.repository.DropDocumentRepository;

@Service
public class DropIndexService {

    @Resource
    private DropDocumentRepository repository;

    @Autowired
    private Mapper mapper;

    @Autowired
    private DropDao dropDao;

    private NamedParameterJdbcTemplate namedJdbcTemplate;

    final Logger logger = LoggerFactory.getLogger(DropIndexService.class);

    public void setRepository(DropDocumentRepository repository) {
        this.repository = repository;
    }

    public void setMapper(Mapper mapper) {
        this.mapper = mapper;
    }

    public void setDropDao(DropDao dropDao) {
        this.dropDao = dropDao;
    }

    @Autowired
    public void setDataSource(DataSource dataSource) {
        this.namedJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
    }

    /** Convenience method for testing purposes */
    public void setNamedJdbcTemplate(NamedParameterJdbcTemplate template) {
        this.namedJdbcTemplate = template;
    }

    /**
     * Creates a {@link DropDocument} from the {@link Drop} specified
     * in <code>drop</code>. It is the {@link DropDocument} instance
     * that is added to the search index
     * 
     * @param drop
     */
    @Transactional(readOnly = false)
    public void addToIndex(Drop drop) {
        List<Drop> drops = new ArrayList<Drop>();
        drops.add(drop);

        this.addAllToIndex(drops);
    }

    /**
     * Adds a collection of {@link Drop} entities specified in <code>drops</code>
     * to the search index en masse.
     * 
     * The {@link Drop} entities are transformed to {@link DropDocument} for
     * purposes of consumption by {@link DropDocumentRepository} 
     *  
     * @param drops
     */
    @Transactional(readOnly = false)
    public void addAllToIndex(List<Drop> drops) {
        // Set the places, bucket and river ids
        Map<Long, List<String>> dropPlaces = getDropPlaces(drops);
        populateRiverIds(drops);
        populateBucketIds(drops);

        logger.debug("Performing batch add of drop entities");

        List<DropDocument> documents = new ArrayList<DropDocument>();
        for (Drop drop : drops) {
            DropDocument document = mapper.map(drop, DropDocument.class);

            // Set the places
            document.setGeo(dropPlaces.get(drop.getId()));
            documents.add(document);
        }

        repository.save(documents);

        logger.debug("Successfully added {} drops to the search index", drops.size());
    }

    /**
     * Deletes the {@link Drop} with the ID specified in <code>dropId</code>
     * from the search index
     * 
     * @param dropId
     */
    @Transactional(readOnly = false)
    public void deleteFromIndex(Long dropId) {
        logger.debug("Removing drop {} from the search index", dropId);
        repository.delete(dropId.toString());
    }

    /**
     * Deletes the collection of {@link DropDocument} entities whose IDs are
     * in the {@link List} specified in <code>dropIds</code> from the search index
     * 
     * @param dropIds
     */
    @Transactional(readOnly = false)
    public void deleteAllFromIndex(List<Long> dropIds) {
        logger.debug("Deleting documents with IDs {} from the search index", dropIds);

        List<String> documentIds = new ArrayList<String>();
        for (Long dropId : dropIds) {
            documentIds.add(dropId.toString());
        }

        // Find all drop documents with the specified IDs
        List<DropDocument> dropDocuments = repository.findAll(documentIds);
        if (dropDocuments.isEmpty()) {
            logger.debug("No documents found in the search index");
            return;
        }

        repository.delete(dropDocuments);

        logger.debug("Successfully deleted {} documents from the search index", dropDocuments.size());
    }

    /**
     * Finds and returns a {@link List} of {@link GetDropDTO}
     * entities that contain the phrase specified in <code>searchTerm</code>
     * in their <code>title</code> or <code>content</code>
     * 
     * The search is performed against the search index and the returned
     * document IDs are used to retrieve the drops from the database.
     * 
     * @param searchTerm
     * @param count
     * @param page
     * @return
     */
    @Transactional
    public List<GetDropDTO> findDrops(String searchTerm, int count, int page) {
        // Pages are zero-indexed. Therefore, the first page is 0
        page = (page >= 1) ? page - 1 : page;

        // Search the index for drops containing searchTerm
        Pageable pageRequest = new PageRequest(page, count);
        List<DropDocument> dropDocuments = repository.find(searchTerm, pageRequest);

        List<GetDropDTO> drops = new ArrayList<GetDropDTO>();

        // Anything from the search?
        if (dropDocuments.isEmpty()) {
            // Log
            logger.debug(String.format("No documents found containing \"%s\" on page %d", searchTerm, count));

            // Return empty list
            return drops;
        }

        List<Long> dropIds = new ArrayList<Long>();
        for (DropDocument document : dropDocuments) {
            dropIds.add(Long.parseLong(document.getId()));
        }

        // Retrieve drops from the DB and transform to DTO
        for (Drop drop : dropDao.findAll(dropIds)) {
            drops.add(mapper.map(drop, GetDropDTO.class));
        }

        return drops;
    }

    /**
     * Deletes all the documents from the search index
     */
    @Transactional(readOnly = false)
    public void deleteAllFromIndex() {
        logger.debug("Flushing search index");
        repository.deleteAll();
    }

    /**
     * Returns a <code>java.util.Map</code> that contains a list of
     * all [latitude, longitude] pairs for the place entities associated
     * with each of the {@link Drop} entities in the <code>drops</code>
     * parameter
     *  
     * @param drops
     * @return
     */
    private Map<Long, List<String>> getDropPlaces(List<Drop> drops) {
        Map<Long, List<String>> dropPlaces = new HashMap<Long, List<String>>();

        // Fetch the drop ids
        List<Long> dropIds = new ArrayList<Long>();
        for (Drop drop : drops) {
            dropIds.add(drop.getId());
        }

        // Query to fetch the places associated with the drops 
        String sql = "SELECT droplets_places.droplet_id, " + "places.longitude, places.latitude " + "FROM places "
                + "INNER JOIN droplets_places ON (droplets_places.place_id = places.id) "
                + "WHERE droplets_places.droplet_id IN (:dropIds)";

        MapSqlParameterSource paramMap = new MapSqlParameterSource();
        paramMap.addValue("dropIds", dropIds);

        for (Map<String, Object> row : namedJdbcTemplate.queryForList(sql, paramMap)) {
            Long dropId = ((Number) row.get("droplet_id")).longValue();

            Float longitude = ((Number) row.get("longitude")).floatValue();
            Float latitude = ((Number) row.get("latitude")).floatValue();

            List<String> places = dropPlaces.get(dropId);
            if (places == null) {
                places = new ArrayList<String>();
            }

            places.add(String.format("%s,%s", latitude, longitude));
            dropPlaces.put(dropId, places);
        }

        return dropPlaces;
    }

    /**
     * Sets the <code>riverIds</code> property for each {@link Drop}
     * in <code>drops</code>
     * 
     * @param drops
     */
    private void populateRiverIds(List<Drop> drops) {
        // Store the drop index
        Map<Long, Integer> dropIndex = new HashMap<Long, Integer>();
        List<Long> dropIds = new ArrayList<Long>();
        int index = 0;
        for (Drop drop : drops) {
            dropIds.add(drop.getId());
            dropIndex.put(drop.getId(), index);
            index++;
        }

        String sql = "SELECT `droplet_id`, `river_id` " + "FROM `rivers_droplets` WHERE `droplet_id` IN (:dropIds)";

        MapSqlParameterSource paramMap = new MapSqlParameterSource();
        paramMap.addValue("dropIds", dropIds);

        for (Map<String, Object> row : namedJdbcTemplate.queryForList(sql, paramMap)) {
            Long dropId = ((Number) row.get("droplet_id")).longValue();
            Long riverId = ((Number) row.get("river_id")).longValue();

            Drop drop = drops.get(dropIndex.get(dropId));
            if (drop.getRiverIds() == null) {
                drop.setRiverIds(new ArrayList<Long>());
            }
            drop.getRiverIds().add(riverId);
        }

    }

    /**
     * Sets the <code>bucketIds</code> property for each {@link Drop}
     * in <code>drops</code>
     * @param drops
     */
    private void populateBucketIds(List<Drop> drops) {
        // Store the drop index
        Map<Long, Integer> dropIndex = new HashMap<Long, Integer>();
        List<Long> dropIds = new ArrayList<Long>();
        int index = 0;
        for (Drop drop : drops) {
            dropIds.add(drop.getId());
            dropIndex.put(drop.getId(), index);
            index++;
        }

        String sql = "SELECT `droplet_id`, `bucket_id` "
                + "FROM `buckets_droplets` WHERE `droplet_id` IN (:dropIds)";

        MapSqlParameterSource paramMap = new MapSqlParameterSource();
        paramMap.addValue("dropIds", dropIds);

        for (Map<String, Object> row : namedJdbcTemplate.queryForList(sql, paramMap)) {
            Long dropId = ((Number) row.get("droplet_id")).longValue();
            Long bucketId = ((Number) row.get("bucket_id")).longValue();

            Drop drop = drops.get(dropIndex.get(dropId));
            if (drop.getBucketIds() == null) {
                drop.setBucketIds(new ArrayList<Long>());
            }
            drop.getBucketIds().add(bucketId);
        }
    }

}