elaborate.publication.solr.SearchService.java Source code

Java tutorial

Introduction

Here is the source code for elaborate.publication.solr.SearchService.java

Source

package elaborate.publication.solr;

/*
 * #%L
 * elab4-publication-backend
 * =======
 * Copyright (C) 2013 - 2016 Huygens ING
 * =======
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>.
 * #L%
 */

import static nl.knaw.huygens.facetedsearch.SolrUtils.EMPTYVALUE_SYMBOL;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.inject.Singleton;

import org.joda.time.DateTime;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import nl.knaw.huygens.Log;
import nl.knaw.huygens.facetedsearch.ElaborateQueryComposer;
import nl.knaw.huygens.facetedsearch.ElaborateSearchParameters;
import nl.knaw.huygens.facetedsearch.LocalSolrServer;
import nl.knaw.huygens.facetedsearch.RangeField;
import nl.knaw.huygens.facetedsearch.SearchData;
import nl.knaw.huygens.facetedsearch.SolrServerWrapper;
import nl.knaw.huygens.facetedsearch.SolrUtils;
import nl.knaw.huygens.jaxrstools.exceptions.InternalServerErrorException;
import nl.knaw.huygens.solr.FacetInfo;
import nl.knaw.huygens.solr.FacetType;

@Singleton
public class SearchService {
    private static final SearchService instance = new SearchService();

    private final Map<Long, SearchData> searchDataIndex = Maps.newHashMap();
    private SolrServerWrapper solrServer;
    private String solrDir;
    private Map<String, FacetInfo> facetInfoMap;
    private List<RangeField> rangeFields;
    private String[] facetFields;
    private String[] defaultSortOrder;

    private String hostname;

    private SearchService() {
        super();
        loadConfig();
    }

    public static SearchService instance() {
        return instance;
    }

    public SearchData createSearch(ElaborateSearchParameters elaborateSearchParameters) {
        elaborateSearchParameters//
                .setFacetFields(getFacetFields())//
                .setFacetInfoMap(getFacetInfoMap())//
                .setRanges(getRangeFields())//
                .setLevelFields(defaultSortOrder[0], defaultSortOrder[1], defaultSortOrder[2]);
        try {
            Log.info("searchParameters={}", elaborateSearchParameters);
            Map<String, Object> result = getSolrServer().search(elaborateSearchParameters);
            Log.info("result={}", result);
            SearchData searchData = new SearchData().setResults(result);
            searchDataIndex.put(searchData.getId(), searchData);
            return searchData;

        } catch (Exception e) {
            Log.error(e.getMessage());
            Log.error("e={}", e);
            e.printStackTrace();
            throw new InternalServerErrorException(e.getMessage());
        }
    }

    public Map<String, Object> getSearchResult(long searchId, int start, int rows) {
        Map<String, Object> resultsMap = Maps.newHashMap();
        SearchData searchData = searchDataIndex.get(searchId);
        //      Map<String, String> fieldnameMap = getFieldnameMap();

        if (searchData != null) {
            List<String> sortableFields = Lists.newArrayList("id", "name");
            sortableFields.addAll(ImmutableList.copyOf(getFacetFields()));

            resultsMap = searchData.getResults();

            List<String> ids = (List<String>) resultsMap.remove("ids");
            List<Map<String, Object>> results = (List<Map<String, Object>>) resultsMap.remove("results");

            Log.info("start={}, rows={}", start, rows);
            int lo = toRange(start, 0, ids.size());
            int hi = toRange(lo + rows, 0, ids.size());
            Log.info("lo={}, hi={}", lo, hi);
            results = results.subList(lo, hi);
            Log.info("results={}", results);
            groupMetadata(results);
            Log.info("after groupMetadata: results={}", results);

            resultsMap.put("ids", ids);
            resultsMap.put("results", results);
            resultsMap.put("start", lo);
            resultsMap.put("rows", hi - lo);

            resultsMap.put("sortableFields", sortableFields);
        }
        return resultsMap;
    }

    void groupMetadata(List<Map<String, Object>> results) {
        for (Map<String, Object> resultmap : results) {
            Map<String, String> metadata = Maps.newHashMap();
            List<String> keys = ImmutableList.copyOf(resultmap.keySet());
            for (String key : keys) {
                if (key.startsWith(SolrUtils.METADATAFIELD_PREFIX)) {
                    Object valueObject = resultmap.remove(key);
                    FacetInfo facetInfo = facetInfoMap.get(key);
                    if (facetInfo != null) {
                        String name = facetInfo.getTitle();
                        if (valueObject == null) {
                            metadata.put(name, EMPTYVALUE_SYMBOL);
                        } else if (valueObject instanceof List) {
                            List<String> values = (List<String>) valueObject;
                            if (values.isEmpty()) {
                                metadata.put(name, EMPTYVALUE_SYMBOL);
                            } else if (values.size() == 1) {
                                metadata.put(name, values.get(0));
                            } else if (values.size() > 1) {
                                Log.warn("unexpected: multiple values: {}", values);
                                metadata.put(name, values.get(0));
                            }
                        }
                    }
                }
            }
            Log.info("metadata:{}", metadata);
            resultmap.put("metadata", metadata);
        }
    }

    public void removeExpiredSearches() {
        long cutoffDate = new DateTime().minusDays(1).getMillis();
        Set<Long> keySet = searchDataIndex.keySet();
        for (Long key : keySet) {
            if (key < cutoffDate) {
                searchDataIndex.remove(key);
            }
        }
    }

    public int toRange(int value, int minValue, int maxValue) {
        return Math.min(Math.max(value, minValue), maxValue);
    }

    public String getSolrDir() {
        return solrDir;
    }

    public void setSolrDir(String solrDir) {
        this.solrDir = solrDir;
    }

    private SolrServerWrapper getSolrServer() {
        if (solrServer == null) {
            solrServer = new LocalSolrServer(getSolrDir(), "entries", new ElaborateQueryComposer());
        }
        return solrServer;
    }

    private String[] getFacetFields() {
        return facetFields;
    }

    private Map<String, FacetInfo> getFacetInfoMap() {
        return facetInfoMap;
    }

    private List<RangeField> getRangeFields() {
        return rangeFields;
    }

    void setFacetInfoMap(Map<String, FacetInfo> _facetInfoMap) {
        facetInfoMap = _facetInfoMap;
    }

    void setRangeFields(List<RangeField> rangeFields) {
        this.rangeFields = rangeFields;
    }

    void loadConfig() {
        //      Log.info("{}", Thread.currentThread().getContextClassLoader().getResource(".").getPath());
        try {
            InputStream inputStream = Thread.currentThread().getContextClassLoader()
                    .getResourceAsStream("config.json");
            Map<String, Object> configMap = readConfigMap(inputStream);
            setFacetInfoMap(toMap(configMap.get("facetInfoMap")));
            setRangeFields(toRangeFieldList(configMap.get("rangeFields")));
            facetFields = toStringArray(configMap.get("facetFields"));
            defaultSortOrder = toStringArray(configMap.get("defaultSortOrder"));
            hostname = (String) configMap.get("baseURL");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @SuppressWarnings("unchecked")
    static List<RangeField> toRangeFieldList(Object object) {
        List<RangeField> list = Lists.newArrayList();
        List<Map<String, Object>> mapList = (List<Map<String, Object>>) object;
        for (Map<String, Object> map : mapList) {
            list.add(new RangeField((String) map.get("name"), (String) map.get("lowerField"),
                    (String) map.get("upperField")));
        }
        return list;
    }

    @SuppressWarnings("unchecked")
    static String[] toStringArray(Object object) {
        return ((List<String>) object).toArray(new String[] {});
    }

    @SuppressWarnings("unchecked")
    static Map<String, FacetInfo> toMap(Object object) {
        Map<String, Map<String, String>> inMap = (Map<String, Map<String, String>>) object;
        Map<String, FacetInfo> outMap = Maps.newHashMapWithExpectedSize(inMap.size());
        for (Entry<String, Map<String, String>> entry : inMap.entrySet()) {
            String key = entry.getKey();
            Map<String, String> value = entry.getValue();
            outMap.put(key, new FacetInfo().setName(value.get("name")).setTitle(value.get("title"))
                    .setType(FacetType.valueOf(value.get("type"))));
        }
        return outMap;
    }

    static Map<String, Object> readConfigMap(InputStream inputStream)
            throws IOException, JsonParseException, JsonMappingException {
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        Map<String, Object> configMap = new ObjectMapper().readValue(inputStreamReader, Map.class);
        if (configMap == null) {
            configMap = Maps.newHashMap();
        }
        return configMap;
    }

    public List<String> getAllSearchResultIds(long searchId) {
        try {
            SearchData searchData = searchDataIndex.get(searchId);
            if (searchData != null) {
                Map<String, Object> resultsMap = searchData.getResults();
                List<String> list = (List<String>) resultsMap.remove("ids");
                return list;
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return ImmutableList.of();
    }

    public String getBaseURL() {
        return hostname;
    }

}