Java tutorial
/* * Copyright (c) 2007-2014 by Public Library of Science * * http://plos.org * http://ambraproject.org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.ambraproject.service.article; import org.ambraproject.ApplicationException; import org.ambraproject.models.Issue; import org.ambraproject.models.Journal; import org.ambraproject.models.Volume; import org.ambraproject.service.cache.Cache; import org.ambraproject.service.hibernate.HibernateServiceImpl; import org.ambraproject.service.journal.JournalService; import org.ambraproject.service.search.SolrServerFactory; import org.ambraproject.service.search.SolrServiceUtil; import org.ambraproject.views.BrowseResult; import org.ambraproject.views.IssueInfo; import org.ambraproject.views.SearchHit; import org.ambraproject.views.TOCArticle; import org.ambraproject.views.TOCArticleGroup; import org.ambraproject.views.VolumeInfo; import org.ambraproject.views.article.ArticleInfo; import org.ambraproject.views.article.ArticleType; import org.ambraproject.views.article.Years; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.StringUtils; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.FacetField; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.hibernate.Criteria; import org.hibernate.FetchMode; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.criterion.DetachedCriteria; import org.hibernate.criterion.Projections; import org.hibernate.criterion.Restrictions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Required; import org.springframework.orm.hibernate3.HibernateCallback; import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.net.URI; import java.sql.SQLException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Class to get all Articles in system and organize them by date and by category * from the SOLR index * * @author Jen Song * @author Joe Osowski */ public class BrowseServiceImpl extends HibernateServiceImpl implements BrowseService { private static final Logger log = LoggerFactory.getLogger(BrowseServiceImpl.class); private static final String ARTBYCAT_LIST_KEY = "ArtByCat-"; private static final String DATE_LIST_KEY = "DateList-"; private static final String ARTBYDATE_LIST_KEY = "ArtByDate-"; // sort option possible values (sort direction is optional) // field desc|asc // sum(field1, field2) desc|asc // break up the option string on comma: "," private static final Pattern SORT_OPTION_PATTERN = Pattern.compile(",(?![^\\(\\)]*\\))"); private Cache browseSolrCache; private JournalService journalService; private ArticleService articleService; private SolrServerFactory serverFactory; private int cacheTimeToLive = 15; private Boolean useCache = true; //We have two collections here, as list supports ordering //And we want to keep the sorts in the order in which they are defined private List displaySorts = null; private Map validSorts = null; private static final Integer getYear(String date) { return Integer.valueOf(date.substring(0, 4)); } private static final Integer getMonth(String date) { return Integer.valueOf(date.substring(5, 7)); } private static final Integer getDay(String date) { return Integer.valueOf(date.substring(8, 10)); } /** * @param journalService The journal-service to use. */ @Required public void setJournalService(JournalService journalService) { this.journalService = journalService; } /** * @param articleService The articleService to use. */ @Required public void setArticleService(ArticleService articleService) { this.articleService = articleService; } /** * @param browseSolrCache The browse solr cache to use. */ @Required public void setBrowseSolrCache(Cache browseSolrCache) { this.browseSolrCache = browseSolrCache; // the time to live will be short enough that we are not going to worry about invalidator logic } /** * Set the configuration class * * @param config the configuration class to use * @throws ApplicationException if required configuration settings are missing */ @Required public void setConfiguration(Configuration config) throws ApplicationException { this.cacheTimeToLive = config.getInt("ambra.services.browse.time", 15) * 60; this.useCache = config.getBoolean("ambra.services.browse.cache", true); } /** * The map of sorts that are valid for this provider * @return */ public List getSorts() { return this.displaySorts; } /** * Get the dates of all articles with a <code>state</code> of <code>ACTIVE</code> (meaning the articles have been * published). The outer map is a map of years, the next inner map a map of months, and finally the innermost is a * list of days. <br/> * * @param journalKey the current journal * * @return the article dates. */ @Transactional(readOnly = true) public Years getArticleDatesForJournal(final String journalKey) { if (this.useCache) { String cacheKey = DATE_LIST_KEY + journalKey; return browseSolrCache.get(cacheKey, this.cacheTimeToLive, new Cache.SynchronizedLookup<Years, RuntimeException>(cacheKey.intern()) { @SuppressWarnings("synthetic-access") @Override public Years lookup() throws RuntimeException { return loadArticleDates(journalKey); } }); } else { return loadArticleDates(journalKey); } } /** * Get articles in the given category. One "page" of articles will be returned, i.e. articles pageNum * pageSize .. * (pageNum + 1) * pageSize - 1 . Note that less than a pageSize articles may be returned, either because it's the end * of the list or because some articles are not accessible. * * @param params A collection filters / parameters to browse by * @return the articles. */ @Transactional(readOnly = true) public BrowseResult getArticlesBySubject(final BrowseParameters params) { BrowseResult result; if (this.useCache) { final String cacheKey = ARTBYCAT_LIST_KEY + params.getJournalKey() + "-" + StringUtils.join(params.getSubjects(), "-") + "-" + params.getSort() + "-" + "-" + params.getPageNum() + "-" + params.getPageSize(); result = browseSolrCache.get(cacheKey, this.cacheTimeToLive, new Cache.SynchronizedLookup<BrowseResult, RuntimeException>(cacheKey.intern()) { @Override public BrowseResult lookup() throws RuntimeException { return getArticlesBySubjectViaSolr(params); } }); } else { result = getArticlesBySubjectViaSolr(params); } return result; } /** * Get articles in the given date range, from newest to oldest, of the given article type(s). One "page" of articles * will be returned, i.e. articles pageNum * pageSize .. (pageNum + 1) * pageSize - 1 . Note that less than a pageSize * articles may be returned, either because it's the end of the list or because some articles are not accessible. * <p/> * Note: this method assumes the dates are truly just dates, i.e. no hours, minutes, etc. * <p/> * If the <code>articleTypes</code> parameter is null or empty, then all types of articles are returned. * <p/> * This method should never return null. * * @param params A collection filters / parameters to browse by * @return the articles. */ @Transactional(readOnly = true) public BrowseResult getArticlesByDate(final BrowseParameters params) { BrowseResult result; if (this.useCache) { String mod = params.getJournalKey() + "-" + params.getStartDate().getTimeInMillis() + "-" + params.getEndDate().getTimeInMillis() + "-" + params.getSort(); String cacheKey = ARTBYDATE_LIST_KEY + mod + "-" + params.getPageNum() + "-" + params.getPageSize(); result = browseSolrCache.get(cacheKey, this.cacheTimeToLive, new Cache.SynchronizedLookup<BrowseResult, RuntimeException>(cacheKey.intern()) { @Override public BrowseResult lookup() throws RuntimeException { return getArticlesByDateViaSolr(params); } }); } else { result = getArticlesByDateViaSolr(params); } return result; } /** * Get a list of article-counts for each category. * * @param journalKey The current journal * @return the category infos. */ @Transactional(readOnly = true) public SortedMap<String, Long> getSubjectsForJournal(final String journalKey) { if (this.useCache) { final String cacheKey = ARTBYCAT_LIST_KEY + journalKey; return browseSolrCache.get(cacheKey, this.cacheTimeToLive, new Cache.SynchronizedLookup<SortedMap<String, Long>, RuntimeException>(cacheKey.intern()) { @Override public SortedMap<String, Long> lookup() throws RuntimeException { return getSubjectsForJournalViaSolr(journalKey); } }); } else { return getSubjectsForJournalViaSolr(journalKey); } } /** * Get Issue information from the new data model * * @param issueUri DOI of Issue. * @return the Issue information. */ @Override @Transactional(readOnly = true) public IssueInfo getIssueInfo(final String issueUri) { final Issue issue = getIssue(issueUri); if (issue == null) { log.error("Failed to retrieve Issue for doi='" + issueUri + "'"); return null; } return createIssueInfo(issue); } @SuppressWarnings("unchecked") @Override public IssueInfo createIssueInfo(Issue issue) { Volume parentVolume = null; List<Long> results = (List<Long>) hibernateTemplate.findByCriteria( DetachedCriteria.forClass(Volume.class).createAlias("issues", "i") .add(Restrictions.eq("i.id", issue.getID())).setProjection(Projections.property("ID")), 0, 1); if (results.size() != 0) { parentVolume = (Volume) hibernateTemplate.findByCriteria(DetachedCriteria.forClass(Volume.class) .add(Restrictions.eq("ID", results.get(0))).setFetchMode("issues", FetchMode.JOIN) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)).get(0); } return createIssueInfo(issue, parentVolume); } private IssueInfo createIssueInfo(Issue issue, Volume parentVolume) { // derive prev/next Issue, "parent" Volume String prevIssueURI = null; String nextIssueURI = null; if (parentVolume != null) { final java.util.List<Issue> parentIssues = parentVolume.getIssues(); final int issuePos = parentIssues.indexOf(issue); prevIssueURI = (issuePos == 0) ? null : parentIssues.get(issuePos - 1).getIssueUri(); nextIssueURI = (issuePos == parentIssues.size() - 1) ? null : parentIssues.get(issuePos + 1).getIssueUri(); } else { log.warn("Issue: " + issue.getID() + ", not contained in any Volumes"); } IssueInfo issueInfo = new IssueInfo(issue.getIssueUri(), issue.getDisplayName(), prevIssueURI, nextIssueURI, issue.getImageUri(), issue.getDescription(), parentVolume == null ? null : parentVolume.getVolumeUri(), issue.isRespectOrder()); issueInfo.setArticleUriList(issue.getArticleDois()); if (issueInfo.getDescription() != null) { String results[] = extractInfoFromIssueDesc(issue.getDescription()); issueInfo.setIssueTitle(results[0]); issueInfo.setIssueImageCredit(results[1]); issueInfo.setIssueDescription(results[2]); } return issueInfo; } /** * Get issue by issue URI * @param issueUri * @return */ @SuppressWarnings("unchecked") private Issue getIssue(final String issueUri) { // get the Issue final java.util.List<Issue> issues = (List<Issue>) hibernateTemplate .findByCriteria(DetachedCriteria.forClass(Issue.class).add(Restrictions.eq("issueUri", issueUri)) .setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)); if (issues.size() == 0) { return null; } else { return issues.get(0); } } /** * Return the ID of the latest issue from the latest volume. If no issue exists in the latest volume, then look at the * previous volume and so on. The Current Issue for each Journal should be configured via the admin console. This * method is a reasonable way to get the most recent Issue if Current Issue was not set. * * @param journal The journal in which to seek the most recent Issue * @return The most recent Issue from the most recent Volume, or null if there are no Issues */ @Override @Transactional(readOnly = true) public String getLatestIssueFromLatestVolume(Journal journal) { List<VolumeInfo> vols = getVolumeInfosForJournal(journal); if (vols.size() > 0) { for (VolumeInfo volInfo : vols) { IssueInfo latestIssue = null; List<IssueInfo> issuesInVol = volInfo.getIssueInfos(); if (issuesInVol.size() > 0) { latestIssue = issuesInVol.get(issuesInVol.size() - 1); } if (latestIssue != null) { return latestIssue.getIssueURI(); } } } return null; } /** * Get a VolumeInfo for the given id. This only works if the volume is in the current journal. * * @param volumeUri Volume ID * @return VolumeInfo */ @Transactional(readOnly = true) @Override public VolumeInfo getVolumeInfo(String volumeUri, String journalKey) { List<VolumeInfo> volumes = getVolumeInfosForJournal(journalService.getJournal(journalKey)); for (VolumeInfo vol : volumes) { if (volumeUri.equals(vol.getVolumeUri())) { return vol; } } return null; } /** * Returns a list of VolumeInfos for the given Journal. VolumeInfos are sorted in reverse order to reflect most common * usage. Uses the pull-through cache. * * @param journal To find VolumeInfos for. * @return VolumeInfos for journal in reverse order. */ @Transactional(readOnly = true) @Override public List<VolumeInfo> getVolumeInfosForJournal(final Journal journal) { //get journal volumes in a session since they're lazy return hibernateTemplate.execute(new HibernateCallback<List<VolumeInfo>>() { @Override public List<VolumeInfo> doInHibernate(Session session) throws HibernateException, SQLException { List<Volume> volumes = ((Journal) session.get(Journal.class, journal.getID())).getVolumes(); List<VolumeInfo> volumeInfos = loadVolumeInfos(volumes); Collections.reverse(volumeInfos); return volumeInfos; } }); } /** * Get VolumeInfos. Note that the IssueInfos contained in the volumes have not been instantiated with the * ArticleInfos. * * @param volumes to look up. * @return volumeInfos. */ private List<VolumeInfo> loadVolumeInfos(final List<Volume> volumes) { List<VolumeInfo> volumeInfos = new ArrayList<VolumeInfo>(); // get the Volumes for (int curVolume = 0; curVolume < volumes.size(); curVolume++) { final Volume volume = volumes.get(curVolume); List<IssueInfo> issueInfos = new ArrayList<IssueInfo>(); if (volume.getIssues() != null) { for (final Issue issue : volume.getIssues()) { issueInfos.add(createIssueInfo(issue, volume)); } } // calculate prev/next final String prevVolumeDoi = (curVolume == 0) ? null : volumes.get(curVolume - 1).getVolumeUri(); final String nextVolumeDoi = (curVolume == volumes.size() - 1) ? null : volumes.get(curVolume + 1).getVolumeUri(); final VolumeInfo volumeInfo = new VolumeInfo(volume.getVolumeUri(), volume.getDisplayName(), prevVolumeDoi, nextVolumeDoi, volume.getImageUri(), volume.getDescription(), issueInfos); volumeInfos.add(volumeInfo); } return volumeInfos; } private Years loadArticleDates(String journalKey) { Years dates = new Years(); SolrQuery query = createCommonQuery(journalKey); query.setFacet(true); query.addFacetField("publication_date"); query.setFacetLimit(-1); query.setFacetMinCount(1); query.setRows(0); try { QueryResponse response = this.serverFactory.getServer().query(query); FacetField ff = response.getFacetField("publication_date"); List<FacetField.Count> counts = ff.getValues(); for (FacetField.Count count : counts) { String publicationDate = count.getName(); Integer y = getYear(publicationDate); Integer m = getMonth(publicationDate); Integer d = getDay(publicationDate); dates.getMonths(y).getDays(m).add(d); } } catch (SolrServerException e) { log.error("Unable to execute a query on the Solr Server.", e); } return dates; } /** * Given a list of Article Groups with correctly ordered articles create a CSV string of article URIs. The URIs will * be in the order that they show up on the TOC. * * @param articleGroups the list of TOCArticleGroup to process. * @return a string of a comma separated list of article URIs */ public String articleGrpListToCSV(List<TOCArticleGroup> articleGroups) { StringBuilder articleList = new StringBuilder(); Iterator i = articleGroups.listIterator(); // Group Loop while (i.hasNext()) { TOCArticleGroup ag = (TOCArticleGroup) i.next(); Iterator y = ag.articles.listIterator(); // Article Loop while (y.hasNext()) { TOCArticle ai = (TOCArticle) y.next(); articleList.append(ai.getDoi()); if (y.hasNext()) articleList.append(','); } if (i.hasNext()) articleList.append(','); } return articleList.toString(); } /** * */ @Override public List<TOCArticleGroup> getArticleGrpList(String issueURI, String authId) { IssueInfo issueInfo = getIssueInfo(issueURI); // If the issue does not exist then return an empty list if (issueInfo == null) return new ArrayList<TOCArticleGroup>(); return getArticleGrpList(issueInfo, authId); } /** * */ @Override @Transactional(readOnly = true) public List<TOCArticleGroup> getArticleGrpList(IssueInfo issue, String authId) { List<TOCArticleGroup> groupList = new ArrayList<TOCArticleGroup>(); for (ArticleType at : ArticleType.getOrderedListForDisplay()) { TOCArticleGroup newArticleGroup = new TOCArticleGroup(at); groupList.add(newArticleGroup); } return buildArticleGroups(issue, groupList, authId); } /** * */ @Override public List<TOCArticleGroup> buildArticleGroups(IssueInfo issue, List<TOCArticleGroup> articleGroups, String authId) { //There are some pretty big inefficiencies here. We load up complete article classes when //we only need doi/title/authors. A new TOCArticle class should probably be created once article lazy //loading is working correctly List<TOCArticle> articlesInIssue = articleService.getArticleTOCEntries(issue.getArticleUriList(), authId); /* * For every article that is of the same ArticleType as a TOCArticleGroup, add it to that group. * Articles can appear in multiple TOCArticleGroups. */ for (TOCArticle ai : articlesInIssue) for (TOCArticleGroup ag : articleGroups) for (ArticleType articleType : ai.getArticleTypes()) if (ag.getArticleType().equals(articleType)) { ag.addArticle(ai); break; } Iterator iter = articleGroups.listIterator(); Integer i = 0; while (iter.hasNext()) { TOCArticleGroup ag = (TOCArticleGroup) iter.next(); // remove the group if it has no articles if (ag.articles.size() == 0) { iter.remove(); continue; } // If we respect order then don't sort. if (!issue.isRespectOrder()) { ag.setId("tocGrp_" + (i++)); ag.sortArticles(); } } return articleGroups; } /** * Sets the solr server factory object * @param serverFactory solr server factory */ public void setServerFactory(SolrServerFactory serverFactory) { this.serverFactory = serverFactory; } @SuppressWarnings("unchecked") @Transactional(readOnly = true) private Volume getVolume(String volumeUri) { List<Volume> volumes = (List<Volume>) hibernateTemplate.findByCriteria( DetachedCriteria.forClass(Volume.class).add(Restrictions.eq("volumeUri", volumeUri)), 0, 1); if (volumes.size() == 0) { return null; } else { return volumes.get(0); } } /** * Get a list of article counts for each category * * @param journalKey the current journal * * @return category info */ private SortedMap<String, Long> getSubjectsForJournalViaSolr(String journalKey) { SortedMap<String, Long> categories = new TreeMap<String, Long>(); SolrQuery query = createCommonQuery(journalKey); query.setFacet(true); query.addFacetField("subject_level_1"); query.setFacetLimit(-1); query.setFacetMinCount(1); query.setRows(0); try { QueryResponse response = this.serverFactory.getServer().query(query); FacetField ff = response.getFacetField("subject_level_1"); List<FacetField.Count> counts = ff.getValues(); if (counts != null) { for (FacetField.Count count : counts) { categories.put(count.getName(), count.getCount()); } } } catch (SolrServerException e) { log.error("Unable to execute a query on the Solr Server.", e); } return categories; } /** * Returns a list of articles for a given category * @param params a collection filters / parameters to browse by * @return articles */ private BrowseResult getArticlesBySubjectViaSolr(BrowseParameters params) { BrowseResult result = new BrowseResult(); ArrayList<SearchHit> articles = new ArrayList<SearchHit>(); long total = 0; SolrQuery query = createCommonQuery(params.getJournalKey()); query.addField("title_display"); query.addField("author_display"); query.addField("article_type"); query.addField("publication_date"); query.addField("id"); query.addField("abstract_primary_display"); query.addField("eissn"); if (params.getSubjects() != null && params.getSubjects().length > 0) { StringBuffer subjectQuery = new StringBuffer(); for (String subject : params.getSubjects()) { subjectQuery.append("\"").append(subject).append("\"").append(" AND "); } // remove the last " AND " query.setQuery("subject_level_1:(" + subjectQuery.substring(0, subjectQuery.length() - 5) + ")"); } // we use subject_level_1 field instead of subject_facet field because // we are only interested in the top level subjects query.setFacet(true); query.addFacetField("subject_level_1"); query.setFacetMinCount(1); query.setFacetSort("index"); setSort(query, params); query.setStart(params.getPageNum() * params.getPageSize()); query.setRows(params.getPageSize()); try { QueryResponse response = this.serverFactory.getServer().query(query); SolrDocumentList documentList = response.getResults(); total = documentList.getNumFound(); for (SolrDocument document : documentList) { SearchHit sh = createArticleBrowseDisplay(document, query.toString()); articles.add(sh); } result.setSubjectFacet(facetCountsToHashMap(response.getFacetField("subject_level_1"))); } catch (SolrServerException e) { log.error("Unable to execute a query on the Solr Server.", e); } result.setTotal(total); result.setArticles(articles); return result; } /** * Sorting values stored in the config, this parses through those values and * sets up the correct parameters for SOLR * * @param query The SolrQuery which will have a <i>sort</i> clause attached * @param params The SearchParameters DTO which contains the <code>sort</code> field used by this method */ private void setSort(SolrQuery query, BrowseParameters params) throws RuntimeException { log.debug("SearchParameters.sort = {}", params.getSort()); String sortKey = params.getSort(); if (sortKey != null && sortKey.trim().length() > 0) { String sortValue = (String) validSorts.get(sortKey); if (sortValue == null) { throw new RuntimeException("Invalid sort of '" + params.getSort() + "' specified."); } String[] sortOptions = SORT_OPTION_PATTERN.split(sortValue); for (String sortOption : sortOptions) { sortOption = sortOption.trim(); int index = sortOption.lastIndexOf(" "); String fieldName = sortOption; String sortDirection = null; if (index != -1) { fieldName = sortOption.substring(0, index); sortDirection = sortOption.substring(index + 1).trim(); } if (sortDirection == null || !sortDirection.toLowerCase().equals("asc")) { query.addSortField(fieldName, SolrQuery.ORDER.desc); } else { query.addSortField(fieldName, SolrQuery.ORDER.asc); } } } else { //If no sort is specified default to publication_date query.addSortField("publication_date", SolrQuery.ORDER.desc); } //If everything else is equal, order by id query.addSortField("id", SolrQuery.ORDER.desc); } /** * Returns list of articles in a given date range, from newest to oldest * @param params the collection class of parameters. * @return the articles */ private BrowseResult getArticlesByDateViaSolr(BrowseParameters params) { BrowseResult result = new BrowseResult(); ArrayList<SearchHit> articles = new ArrayList<SearchHit>(); long totalSize = 0; SolrQuery query = createCommonQuery(params.getJournalKey()); query.addField("title_display"); query.addField("author_display"); query.addField("article_type"); query.addField("publication_date"); query.addField("id"); query.addField("abstract_primary_display"); query.addField("eissn"); query.addField("striking_image"); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); String sDate = sdf.format(params.getStartDate().getTime()); String eDate = sdf.format(params.getEndDate().getTime()); sDate = sDate + "T00:00:00Z"; eDate = eDate + "T00:00:00Z"; query.addFilterQuery("publication_date:[" + sDate + " TO " + eDate + "]"); StringBuffer sb = new StringBuffer(); if (params.getArticleTypes() != null && params.getArticleTypes().size() > 0) { for (URI uri : params.getArticleTypes()) { String path = uri.getPath(); int index = path.lastIndexOf("/"); if (index != -1) { String articleType = path.substring(index + 1); sb.append("\"").append(articleType).append("\"").append(" OR "); } } String articleTypesQuery = sb.substring(0, sb.length() - 4); if (articleTypesQuery.length() > 0) { query.addFilterQuery("article_type_facet:" + articleTypesQuery); } } setSort(query, params); query.setStart(params.getPageNum() * params.getPageSize()); query.setRows(params.getPageSize()); log.info("getArticlesByDate Solr Query:" + query.toString()); try { QueryResponse response = this.serverFactory.getServer().query(query); SolrDocumentList documentList = response.getResults(); totalSize = documentList.getNumFound(); for (SolrDocument document : documentList) { SearchHit sh = createArticleBrowseDisplay(document, query.toString()); articles.add(sh); } } catch (SolrServerException e) { log.error("Unable to execute a query on the Solr Server.", e); } result.setArticles(articles); result.setTotal(totalSize); return result; } /** * Creates a commonly used SolrQuery object * @return pre-populated SolrQuery object */ private SolrQuery createCommonQuery(String journalKey) { SolrQuery query = new SolrQuery("*:*"); query.addFilterQuery("doc_type:full"); query.addFilterQuery("!article_type_facet:\"Issue Image\""); query.addFilterQuery("cross_published_journal_key:" + journalKey); return query; } /** * Populates the SearchHit object using SolrDocument object (from search result) * @param document one search result * @param query query * @return populated SearchHit object */ private SearchHit createArticleBrowseDisplay(SolrDocument document, String query) { String id = SolrServiceUtil.getFieldValue(document, "id", String.class, query); String message = id == null ? query : id; String title = SolrServiceUtil.getFieldValue(document, "title_display", String.class, message); Date publicationDate = SolrServiceUtil.getFieldValue(document, "publication_date", Date.class, message); String eissn = SolrServiceUtil.getFieldValue(document, "eissn", String.class, message); String articleType = SolrServiceUtil.getFieldValue(document, "article_type", String.class, message); String strikingImage = SolrServiceUtil.getFieldValue(document, "striking_image", String.class, message); List<String> abstractDisplayList = SolrServiceUtil.getFieldMultiValue(document, "abstract_primary_display", String.class, message); List<String> authorList = SolrServiceUtil.getFieldMultiValue(document, "author_display", String.class, message); SearchHit hit = SearchHit.builder().setUri(id).setTitle(title).setListOfCreators(authorList) .setDate(publicationDate).setIssn(eissn).setArticleTypeForDisplay(articleType) .setArticleTypeForDisplay(StringUtils.join(abstractDisplayList, ", ")) .setAbstractText(StringUtils.join(abstractDisplayList, ", ")).setStrikingImage(strikingImage) .build(); return hit; } /** * Checks to see if solr is up or not * @throws org.apache.solr.client.solrj.SolrServerException * @throws java.io.IOException */ public void pingSolr() throws SolrServerException, IOException { this.serverFactory.getServer().ping(); } //TODO: refactor this somehow, this method (for the most part) is also in SOLRSearchService private Map<String, Long> facetCountsToHashMap(FacetField field) { List<FacetField.Count> counts = field.getValues(); TreeMap<String, Long> result = new TreeMap<String, Long>(); if (counts != null) { for (FacetField.Count count : counts) { result.put(count.getName(), count.getCount()); } return result; } else { return null; } } /** * Extract issue title, issue description, issue image credit from the full issue description * @param desc full issue description * @return issue title, issue image credit, issue description */ private String[] extractInfoFromIssueDesc(String desc) { String results[] = { "", "", "" }; int start = 0, end = 0; // get the title of the issue Pattern p1 = Pattern.compile("<title>(.*?)</title>"); Matcher m1 = p1.matcher(desc); if (m1.find()) { // there should be one title results[0] = m1.group(1); // title seems to be surround by <bold> element results[0] = results[0].replaceAll("<.*?>", ""); start = m1.start(); end = m1.end(); // remove the title from the total description String descBefore = desc.substring(0, start); String descAfter = desc.substring(end); desc = descBefore + descAfter; } // get the image credit Pattern p2 = Pattern.compile("<italic>Image Credit: (.*?)</italic>"); Matcher m2 = p2.matcher(desc); if (m2.find()) { // there should be one image credit results[1] = m2.group(1); start = m2.start(); end = m2.end(); // remove the image credit from the total description String descBefore = desc.substring(0, start); String descAfter = desc.substring(end); desc = descBefore + descAfter; } // once title and image credit have been removed, the rest of the content is the issue description results[2] = desc; return results; } }