package edu.harvard.iq.dataverse.search;
import edu.harvard.iq.dataverse.DataFile;
import edu.harvard.iq.dataverse.DataFileServiceBean;
import edu.harvard.iq.dataverse.DataTable;
import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.DatasetServiceBean;
import edu.harvard.iq.dataverse.DatasetVersionServiceBean;
import edu.harvard.iq.dataverse.Dataverse;
import edu.harvard.iq.dataverse.DataverseServiceBean;
import edu.harvard.iq.dataverse.DataverseSession;
import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.DvObjectServiceBean;
import edu.harvard.iq.dataverse.PermissionServiceBean;
import edu.harvard.iq.dataverse.PermissionsWrapper;
import edu.harvard.iq.dataverse.SettingsWrapper;
import edu.harvard.iq.dataverse.WidgetWrapper;
import edu.harvard.iq.dataverse.dataaccess.ImageThumbConverter;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import javax.ejb.EJB;
import javax.faces.context.FacesContext;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.lang.StringUtils;
@ViewScoped
@Named("SearchIncludeFragment")
public class SearchIncludeFragment implements java.io.Serializable {
private static final Logger logger = Logger.getLogger(SearchIncludeFragment.class.getCanonicalName());
@EJB
SearchServiceBean searchService;
@EJB
DataverseServiceBean dataverseService;
@EJB
DatasetServiceBean datasetService;
@EJB
DatasetVersionServiceBean datasetVersionService;
@EJB
DataFileServiceBean dataFileService;
@EJB
PermissionServiceBean permissionService;
@EJB
DvObjectServiceBean dvObjectService;
@Inject
DataverseSession session;
@Inject
SettingsWrapper settingsWrapper;
@Inject
PermissionsWrapper permissionsWrapper;
@Inject
WidgetWrapper widgetWrapper;
private String browseModeString = "browse";
private String searchModeString = "search";
private String mode;
private String query;
private List<String> filterQueries = new ArrayList<>();
private List<FacetCategory> facetCategoryList = new ArrayList<>();
private List<SolrSearchResult> searchResultsList = new ArrayList<>();
private int searchResultsCount;
private String fq0;
private String fq1;
private String fq2;
private String fq3;
private String fq4;
private String fq5;
private String fq6;
private String fq7;
private String fq8;
private String fq9;
private String dataverseAlias;
private Dataverse dataverse;
private String dataversePath = null;
// commenting out dataverseSubtreeContext. it was not well-loved in the GUI
// private String dataverseSubtreeContext;
private String selectedTypesString;
private List<String> selectedTypesList = new ArrayList<String>();
private String selectedTypesHumanReadable;
private String searchFieldType = SearchFields.TYPE;
private String searchFieldSubtree = SearchFields.SUBTREE;
// private String searchFieldHostDataverse = SearchFields.HOST_DATAVERSE;
private String searchFieldNameSort = SearchFields.NAME_SORT;
private String searchFieldRelevance = SearchFields.RELEVANCE;
// private String searchFieldReleaseDate = SearchFields.RELEASE_DATE_YYYY;
private String searchFieldReleaseOrCreateDate = SearchFields.RELEASE_OR_CREATE_DATE;
final private String ASCENDING = SortOrder.asc.toString();
final private String DESCENDING = SortOrder.desc.toString();
private String typeFilterQuery;
private Long facetCountDataverses = 0L;
private Long facetCountDatasets = 0L;
private Long facetCountFiles = 0L;
Map<String, Long> previewCountbyType = new HashMap<>();
private SolrQueryResponse solrQueryResponseAllTypes;
private String sortField;
private SortOrder sortOrder;
private String currentSort;
private String currentSortFriendly;
private int page = 1;
private int paginationGuiStart = 1;
private int paginationGuiEnd = 10;
private int paginationGuiRows = 10;
Map<String, String> datasetfieldFriendlyNamesBySolrField = new HashMap<>();
Map<String, String> staticSolrFieldFriendlyNamesBySolrField = new HashMap<>();
private boolean solrIsDown = false;
private Map<String, Integer> numberOfFacets = new HashMap<>();
private boolean debug = false;
// private boolean showUnpublished;
List<String> filterQueriesDebug = new ArrayList<>();
// private Map<String, String> friendlyName = new HashMap<>();
private String errorFromSolr;
private SearchException searchException;
private boolean rootDv = false;
private Map<Long, String> harvestedDatasetDescriptions = null;
/**
* @todo:
*
* better style and icons for facets
*
* replace * with watermark saying "Search this Dataverse"
*
* get rid of "_s" et al. (human eyeball friendly)
*
* pagination (previous/next links)
*
* test dataset cards
*
* test files cards
*
* test dataset cards when Solr is down
*
* make results sortable: https://redmine.hmdc.harvard.edu/issues/3482
*
* always show all types, even if zero count:
* https://redmine.hmdc.harvard.edu/issues/3488
*
* make subtree facet look like amazon widget (i.e. a tree)
*
* see also https://trello.com/c/jmry3BJR/28-browse-dataverses
*/
public String searchRedirect(String dataverseRedirectPage) {
/**
* These are our decided-upon search/browse rules, the way we expect
* users to search/browse and how we want the app behave:
*
* 1. When a user is browsing (i.e. hasn't entered a search term) we
* only show dataverses and datasets. Files are hidden. See
* https://redmine.hmdc.harvard.edu/issues/3573
*
* 2. A search is always brand new. Don't keep around old facets that
* were selected. Show page 1 of results. Make the results bookmarkable:
* https://redmine.hmdc.harvard.edu/issues/3664
*
* 3. When you add or remove a facet, you should always go to page 1 of
* search results. Search terms should be preserved. Sorting should be
* preserved.
*
* 4. After search terms have been entered and facets have been
* selected, we expect users to (optionally) page through search results
* and as they do so we will preserve the state of their search terms,
* their facet selections, and their sorting.
*
* 5. Someday the default sort order for browse mode will be by "release
* date" (newest first) but that functionality is not yet available in
* the system ( see https://redmine.hmdc.harvard.edu/issues/3628 and
* https://redmine.hmdc.harvard.edu/issues/3629 ) so for now the default
* sort order for browse mode will by alphabetical (sort by name,
* ascending). The default sort order for search mode will be by
* relevance. (We only offer ascending ordering for relevance since
* descending order is unlikely to be useful.) When you sort, facet
* selections and what page you are on should be preserved.
*
*/
dataverseRedirectPage = StringUtils.isBlank(dataverseRedirectPage) ? "dataverse.xhtml" : dataverseRedirectPage;
String optionalDataverseScope = "&alias=" + dataverse.getAlias();
String qParam = "";
if (query != null) {
qParam = "&q=" + query;
}
return widgetWrapper.wrapURL(dataverseRedirectPage + "?faces-redirect=true&q=" + qParam + optionalDataverseScope);
}
public void search() {
search(false);
}
public void search(boolean onlyDataRelatedToMe) {
logger.fine("search called");
// wildcard/browse (*) unless user supplies a query
String queryToPassToSolr = "*";
if (this.query == null) {
mode = browseModeString;
} else if (this.query.isEmpty()) {
mode = browseModeString;
} else {
mode = searchModeString;
}
if (mode.equals(browseModeString)) {
queryToPassToSolr = "*";
if (sortField == null) {
sortField = searchFieldReleaseOrCreateDate;
}
if (sortOrder == null) {
sortOrder = SortOrder.desc;
}
if (selectedTypesString == null || selectedTypesString.isEmpty()) {
selectedTypesString = "dataverses:datasets";
}
} else if (mode.equals(searchModeString)) {
queryToPassToSolr = query;
if (sortField == null) {
sortField = searchFieldRelevance;
}
if (sortOrder == null) {
sortOrder = SortOrder.desc;
}
if (selectedTypesString == null || selectedTypesString.isEmpty()) {
selectedTypesString = "dataverses:datasets:files";
}
}
filterQueries = new ArrayList<>();
for (String fq : Arrays.asList(fq0, fq1, fq2, fq3, fq4, fq5, fq6, fq7, fq8, fq9)) {
if (fq != null) {
filterQueries.add(fq);
}
}
SolrQueryResponse solrQueryResponse = null;
List<String> filterQueriesFinal = new ArrayList<>();
if (dataverseAlias != null) {
this.dataverse = dataverseService.findByAlias(dataverseAlias);
}
if (this.dataverse != null) {
dataversePath = dataverseService.determineDataversePath(this.dataverse);
String filterDownToSubtree = SearchFields.SUBTREE + ":\"" + dataversePath + "\"";
//logger.info("SUBTREE parameter: " + dataversePath);
if (!this.dataverse.equals(dataverseService.findRootDataverse())) {
/**
* @todo centralize this into SearchServiceBean
*/
filterQueriesFinal.add(filterDownToSubtree);
// this.dataverseSubtreeContext = dataversePath;
} else {
// this.dataverseSubtreeContext = "all";
this.setRootDv(true);
}
} else {
this.dataverse = dataverseService.findRootDataverse();
// this.dataverseSubtreeContext = "all";
this.setRootDv(true);
}
selectedTypesList = new ArrayList<>();
String[] parts = selectedTypesString.split(":");
// int count = 0;
for (String string : parts) {
selectedTypesList.add(string);
}
List<String> filterQueriesFinalAllTypes = new ArrayList<>();
String[] arr = selectedTypesList.toArray(new String[selectedTypesList.size()]);
selectedTypesHumanReadable = combine(arr, " OR ");
if (!selectedTypesHumanReadable.isEmpty()) {
typeFilterQuery = SearchFields.TYPE + ":(" + selectedTypesHumanReadable + ")";
}
filterQueriesFinal.addAll(filterQueries);
filterQueriesFinalAllTypes.addAll(filterQueriesFinal);
filterQueriesFinal.add(typeFilterQuery);
String allTypesFilterQuery = SearchFields.TYPE + ":(dataverses OR datasets OR files)";
filterQueriesFinalAllTypes.add(allTypesFilterQuery);
int paginationStart = (page - 1) * paginationGuiRows;
/**
* @todo
*
* design/make room for sort widget drop down:
* https://redmine.hmdc.harvard.edu/issues/3482
*
*/
try {
logger.fine("query from user: " + query);
logger.fine("queryToPassToSolr: " + queryToPassToSolr);
logger.fine("sort by: " + sortField);
/**
* @todo Number of search results per page should be configurable -
* https://github.com/IQSS/dataverse/issues/84
*/
int numRows = 10;
HttpServletRequest httpServletRequest = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
DataverseRequest dataverseRequest = new DataverseRequest(session.getUser(), httpServletRequest);
solrQueryResponse = searchService.search(dataverseRequest, dataverse, queryToPassToSolr, filterQueriesFinal, sortField, sortOrder.toString(), paginationStart, onlyDataRelatedToMe, numRows, false);
// This 2nd search() is for populating the facets: -- L.A.
// TODO: ...
solrQueryResponseAllTypes = searchService.search(dataverseRequest, dataverse, queryToPassToSolr, filterQueriesFinalAllTypes, sortField, sortOrder.toString(), paginationStart, onlyDataRelatedToMe, numRows, false);
} catch (SearchException ex) {
Throwable cause = ex;
StringBuilder sb = new StringBuilder();
sb.append(cause + " ");
while (cause.getCause() != null) {
cause = cause.getCause();
sb.append(cause.getClass().getCanonicalName() + " ");
sb.append(cause + " ");
}
String message = "Exception running search for [" + queryToPassToSolr + "] with filterQueries " + filterQueries + " and paginationStart [" + paginationStart + "]: " + sb.toString();
logger.info(message);
this.solrIsDown = true;
this.searchException = ex;
}
if (!solrIsDown) {
this.facetCategoryList = solrQueryResponse.getFacetCategoryList();
this.searchResultsList = solrQueryResponse.getSolrSearchResults();
this.searchResultsCount = solrQueryResponse.getNumResultsFound().intValue();
this.datasetfieldFriendlyNamesBySolrField = solrQueryResponse.getDatasetfieldFriendlyNamesBySolrField();
this.staticSolrFieldFriendlyNamesBySolrField = solrQueryResponse.getStaticSolrFieldFriendlyNamesBySolrField();
this.filterQueriesDebug = solrQueryResponse.getFilterQueriesActual();
this.errorFromSolr = solrQueryResponse.getError();
paginationGuiStart = paginationStart + 1;
paginationGuiEnd = Math.min(page * paginationGuiRows, searchResultsCount);
List<SolrSearchResult> searchResults = solrQueryResponse.getSolrSearchResults();
/**
* @todo consider creating Java objects called DatasetCard,
* DatasetCart, and FileCard since that's what we call them in the
* UI. These objects' fields (affiliation, citation, etc.) would be
* populated from Solr if possible (for performance, to avoid extra
* database calls) or by a database call (if it's tricky or doesn't
* make sense to get the data in and out of Solr). We would continue
* to iterate through all the SolrSearchResult objects as we build
* up the new card objects. Think about how we have a
* solrSearchResult.setCitation method but only the dataset card in
* the UI (currently) shows this "citation" field.
*/
for (SolrSearchResult solrSearchResult : searchResults) {
if (solrSearchResult.getEntityId() == null) {
// avoiding EJBException a la https://redmine.hmdc.harvard.edu/issues/3809
logger.warning(SearchFields.ENTITY_ID + " was null for Solr document id:" + solrSearchResult.getId() + ", skipping. Bad Solr data?");
break;
}
// going to assume that this is NOT a linked object, for now:
solrSearchResult.setIsInTree(true);
// (we'll review this later!)
if (solrSearchResult.getType().equals("dataverses")) {
//logger.info("XXRESULT: dataverse: "+solrSearchResult.getEntityId());
dataverseService.populateDvSearchCard(solrSearchResult);
/*
Datasets cannot be harvested yet.
if (isHarvestedDataverse(solrSearchResult.getEntityId())) {
solrSearchResult.setHarvested(true);
}*/
} else if (solrSearchResult.getType().equals("datasets")) {
//logger.info("XXRESULT: dataset: "+solrSearchResult.getEntityId());
datasetVersionService.populateDatasetSearchCard(solrSearchResult);
// @todo - the 3 lines below, should they be moved inside
// searchServiceBean.search()?
String deaccesssionReason = solrSearchResult.getDeaccessionReason();
if (deaccesssionReason != null) {
solrSearchResult.setDescriptionNoSnippet(deaccesssionReason);
}
} else if (solrSearchResult.getType().equals("files")) {
//logger.info("XXRESULT: datafile: "+solrSearchResult.getEntityId());
dataFileService.populateFileSearchCard(solrSearchResult);
/**
* @todo: show DataTable variables
*/
}
}
// populate preview counts: https://redmine.hmdc.harvard.edu/issues/3560
previewCountbyType.put("dataverses", 0L);
previewCountbyType.put("datasets", 0L);
previewCountbyType.put("files", 0L);
if (solrQueryResponseAllTypes != null) {
for (FacetCategory facetCategory : solrQueryResponseAllTypes.getTypeFacetCategories()) {
for (FacetLabel facetLabel : facetCategory.getFacetLabel()) {
previewCountbyType.put(facetLabel.getName(), facetLabel.getCount());
}
}
}
} else {
// if SOLR is down:
List contentsList = dataverseService.findByOwnerId(dataverse.getId());
contentsList.addAll(datasetService.findByOwnerId(dataverse.getId()));
// directChildDvObjectContainerList.addAll(contentsList);
}
/**
* @todo: pull values from datasetField.getTitle() rather than hard
* coding them here
*/
// friendlyName.put(SearchFields.SUBTREE, "Dataverse Subtree");
// friendlyName.put(SearchFields.HOST_DATAVERSE, "Original Dataverse");
// friendlyName.put(SearchFields.AUTHOR_STRING, "Author");
// friendlyName.put(SearchFields.AFFILIATION, "Affiliation");
// friendlyName.put(SearchFields.KEYWORD, "Keyword");
// friendlyName.put(SearchFields.DISTRIBUTOR, "Distributor");
// friendlyName.put(SearchFields.FILE_TYPE, "File Type");
// friendlyName.put(SearchFields.PRODUCTION_DATE_YEAR_ONLY, "Production Date");
// friendlyName.put(SearchFields.DISTRIBUTION_DATE_YEAR_ONLY, "Distribution Date");
}
// public boolean isShowUnpublished() {
// return showUnpublished;
// }
//
// public void setShowUnpublished(boolean showUnpublished) {
// this.showUnpublished = showUnpublished;
// }
public String getBrowseModeString() {
return browseModeString;
}
public String getSearchModeString() {
return searchModeString;
}
public String getMode() {
// enum would be prefered but we can't reference enums from JSF:
// http://stackoverflow.com/questions/2524420/how-to-testing-for-enum-equality-in-jsf/2524901#2524901
return mode;
}
public int getNumberOfFacets(String name, int defaultValue) {
Integer numFacets = numberOfFacets.get(name);
if (numFacets == null) {
numberOfFacets.put(name, defaultValue);
numFacets = defaultValue;
}
return numFacets;
}
public void incrementFacets(String name, int incrementNum) {
Integer numFacets = numberOfFacets.get(name);
if (numFacets == null) {
numFacets = incrementNum;
}
numberOfFacets.put(name, numFacets + incrementNum);
}
// http://stackoverflow.com/questions/1515437/java-function-for-arrays-like-phps-join/1515548#1515548
String combine(String[] s, String glue) {
int k = s.length;
if (k == 0) {
return null;
}
StringBuilder out = new StringBuilder();
out.append(s[0]);
for (int x = 1; x < k; ++x) {
out.append(glue).append(s[x]);
}
return out.toString();
}
private Long findFacetCountByType(String type) {
return previewCountbyType.get(type);
}
public boolean isAllowedToClickAddData() {
/**
* @todo is this the right permission to check?
*/
// being explicit about the user, could just call permissionService.on(dataverse)
// TODO: decide on rules for this button and check actual permissions
return session.getUser() != null && session.getUser().isAuthenticated();
//return permissionService.userOn(session.getUser(), dataverse).has(Permission.UndoableEdit);
}
private String getCreatedOrReleasedDate(DvObject dvObject, Date date) {
// the hedge is for https://redmine.hmdc.harvard.edu/issues/3806
String hedge = "";
if (dvObject instanceof Dataverse) {
hedge = "";
} else if (dvObject instanceof Dataset) {
hedge = " maybe";
} else if (dvObject instanceof DataFile) {
hedge = " maybe";
} else {
hedge = " what object is this?";
}
if (dvObject.isReleased()) {
return date + " released" + hedge;
} else {
return date + " created" + hedge;
}
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
public List<String> getFilterQueries() {
return filterQueries;
}
public void setFilterQueries(List<String> filterQueries) {
this.filterQueries = filterQueries;
}
public List<FacetCategory> getFacetCategoryList() {
return facetCategoryList;
}
public void setFacetCategoryList(List<FacetCategory> facetCategoryList) {
this.facetCategoryList = facetCategoryList;
}
public List<SolrSearchResult> getSearchResultsList() {
return searchResultsList;
}
public void setSearchResultsList(List<SolrSearchResult> searchResultsList) {
this.searchResultsList = searchResultsList;
}
public int getSearchResultsCount() {
return searchResultsCount;
}
public void setSearchResultsCount(int searchResultsCount) {
this.searchResultsCount = searchResultsCount;
}
public String getFq0() {
return fq0;
}
public void setFq0(String fq0) {
this.fq0 = fq0;
}
public String getFq1() {
return fq1;
}
public void setFq1(String fq1) {
this.fq1 = fq1;
}
public String getFq2() {
return fq2;
}
public void setFq2(String fq2) {
this.fq2 = fq2;
}
public String getFq3() {
return fq3;
}
public void setFq3(String fq3) {
this.fq3 = fq3;
}
public String getFq4() {
return fq4;
}
public void setFq4(String fq4) {
this.fq4 = fq4;
}
public String getFq5() {
return fq5;
}
public void setFq5(String fq5) {
this.fq5 = fq5;
}
public String getFq6() {
return fq6;
}
public void setFq6(String fq6) {
this.fq6 = fq6;
}
public String getFq7() {
return fq7;
}
public void setFq7(String fq7) {
this.fq7 = fq7;
}
public String getFq8() {
return fq8;
}
public void setFq8(String fq8) {
this.fq8 = fq8;
}
public String getFq9() {
return fq9;
}
public void setFq9(String fq9) {
this.fq9 = fq9;
}
public Dataverse getDataverse() {
return dataverse;
}
public void setDataverse(Dataverse dataverse) {
this.dataverse = dataverse;
}
// public String getDataverseSubtreeContext() {
// return dataverseSubtreeContext;
// }
//
// public void setDataverseSubtreeContext(String dataverseSubtreeContext) {
// this.dataverseSubtreeContext = dataverseSubtreeContext;
// }
public String getSelectedTypesString() {
return selectedTypesString;
}
public void setSelectedTypesString(String selectedTypesString) {
this.selectedTypesString = selectedTypesString;
}
public List<String> getSelectedTypesList() {
return selectedTypesList;
}
public void setSelectedTypesList(List<String> selectedTypesList) {
this.selectedTypesList = selectedTypesList;
}
public String getSelectedTypesHumanReadable() {
return selectedTypesHumanReadable;
}
public void setSelectedTypesHumanReadable(String selectedTypesHumanReadable) {
this.selectedTypesHumanReadable = selectedTypesHumanReadable;
}
public String getSearchFieldType() {
return searchFieldType;
}
public void setSearchFieldType(String searchFieldType) {
this.searchFieldType = searchFieldType;
}
public String getSearchFieldSubtree() {
return searchFieldSubtree;
}
public void setSearchFieldSubtree(String searchFieldSubtree) {
this.searchFieldSubtree = searchFieldSubtree;
}
// public String getSearchFieldHostDataverse() {
// return searchFieldHostDataverse;
// }
//
// public void setSearchFieldHostDataverse(String searchFieldHostDataverse) {
// this.searchFieldHostDataverse = searchFieldHostDataverse;
// }
public String getTypeFilterQuery() {
return typeFilterQuery;
}
public void setTypeFilterQuery(String typeFilterQuery) {
this.typeFilterQuery = typeFilterQuery;
}
public Long getFacetCountDatasets() {
return findFacetCountByType("datasets");
}
public Long getFacetCountDataverses() {
return findFacetCountByType("dataverses");
}
public Long getFacetCountFiles() {
return findFacetCountByType("files");
}
public String getSearchFieldRelevance() {
return searchFieldRelevance;
}
public void setSearchFieldRelevance(String searchFieldRelevance) {
this.searchFieldRelevance = searchFieldRelevance;
}
public String getSearchFieldNameSort() {
return searchFieldNameSort;
}
public void setSearchFieldNameSort(String searchFieldNameSort) {
this.searchFieldNameSort = searchFieldNameSort;
}
public String getSearchFieldReleaseOrCreateDate() {
return searchFieldReleaseOrCreateDate;
}
public String getASCENDING() {
return ASCENDING;
}
public String getDESCENDING() {
return DESCENDING;
}
public String getSortField() {
return sortField;
}
public void setSortField(String sortField) {
this.sortField = sortField;
}
public String getSortOrder() {
if (sortOrder != null) {
return sortOrder.toString();
} else {
return null;
}
}
/**
* Allow only valid values to be set.
*
* Rather than passing in a String and converting it to an enum in this
* method we could write a converter:
* http://stackoverflow.com/questions/8609378/jsf-2-0-view-parameters-to-pass-objects
*/
public void setSortOrder(String sortOrderSupplied) {
if (sortOrderSupplied != null) {
if (sortOrderSupplied.equals(SortOrder.asc.toString())) {
this.sortOrder = SortOrder.asc;
}
if (sortOrderSupplied.equals(SortOrder.desc.toString())) {
this.sortOrder = SortOrder.desc;
}
}
}
/**
* @todo this method doesn't seem to be in use and can probably be deleted.
*/
@Deprecated
public String getCurrentSortFriendly() {
String friendlySortField = sortField;
String friendlySortOrder = sortOrder.toString();
if (sortField.equals(SearchFields.NAME_SORT)) {
friendlySortField = "Name";
if (sortOrder.equals(ASCENDING)) {
friendlySortOrder = " (A-Z)";
} else if (sortOrder.equals(DESCENDING)) {
friendlySortOrder = " (Z-A)";
}
} else if (sortField.equals(SearchFields.RELEVANCE)) {
friendlySortField = "Relevance";
friendlySortOrder = "";
}
return friendlySortField + friendlySortOrder;
}
public String getCurrentSort() {
return sortField + ":" + sortOrder;
}
public boolean isSortedByNameAsc() {
return getCurrentSort().equals(searchFieldNameSort + ":" + ASCENDING) ? true : false;
}
public boolean isSortedByNameDesc() {
return getCurrentSort().equals(searchFieldNameSort + ":" + DESCENDING) ? true : false;
}
public boolean isSortedByReleaseDateAsc() {
return getCurrentSort().equals(searchFieldReleaseOrCreateDate + ":" + ASCENDING) ? true : false;
}
public boolean isSortedByReleaseDateDesc() {
return getCurrentSort().equals(searchFieldReleaseOrCreateDate + ":" + DESCENDING) ? true : false;
}
public boolean isSortedByRelevance() {
return getCurrentSort().equals(searchFieldRelevance + ":" + DESCENDING) ? true : false;
}
public int getPage() {
return page;
}
public void setPage(int page) {
this.page = page;
}
// helper method
public int getTotalPages() {
return ((searchResultsCount - 1) / paginationGuiRows) + 1;
}
public int getPaginationGuiStart() {
return paginationGuiStart;
}
public void setPaginationGuiStart(int paginationGuiStart) {
this.paginationGuiStart = paginationGuiStart;
}
public int getPaginationGuiEnd() {
return paginationGuiEnd;
}
public void setPaginationGuiEnd(int paginationGuiEnd) {
this.paginationGuiEnd = paginationGuiEnd;
}
public int getPaginationGuiRows() {
return paginationGuiRows;
}
public void setPaginationGuiRows(int paginationGuiRows) {
this.paginationGuiRows = paginationGuiRows;
}
public boolean isSolrIsDown() {
return solrIsDown;
}
public void setSolrIsDown(boolean solrIsDown) {
this.solrIsDown = solrIsDown;
}
public boolean isRootDv() {
return rootDv;
}
public void setRootDv(boolean rootDv) {
this.rootDv = rootDv;
}
public boolean isDebug() {
return (debug && session.getUser().isSuperuser())
|| settingsWrapper.isTrueForKey(":Debug", false);
}
public void setDebug(boolean debug) {
this.debug = debug;
}
public List<String> getFilterQueriesDebug() {
return filterQueriesDebug;
}
public boolean userLoggedIn() {
return session.getUser().isAuthenticated();
}
public boolean publishedSelected() {
String expected = SearchFields.PUBLICATION_STATUS + ":\"" + getPUBLISHED() + "\"";
logger.fine("published expected: " + expected + " actual: " + selectedTypesList);
return filterQueries.contains(SearchFields.PUBLICATION_STATUS + ":\"" + getPUBLISHED() + "\"");
}
public boolean unpublishedSelected() {
String expected = SearchFields.PUBLICATION_STATUS + ":\"" + getUNPUBLISHED() + "\"";
logger.fine("unpublished expected: " + expected + " actual: " + selectedTypesList);
return filterQueries.contains(SearchFields.PUBLICATION_STATUS + ":\"" + getUNPUBLISHED() + "\"");
}
public String getPUBLISHED() {
return IndexServiceBean.getPUBLISHED_STRING();
}
public String getUNPUBLISHED() {
return IndexServiceBean.getUNPUBLISHED_STRING();
}
public String getDRAFT() {
return IndexServiceBean.getDRAFT_STRING();
}
public String getIN_REVIEW() {
return IndexServiceBean.getIN_REVIEW_STRING();
}
public String getDEACCESSIONED() {
return IndexServiceBean.getDEACCESSIONED_STRING();
}
public List<String> getFriendlyNamesFromFilterQuery(String filterQuery) {
String[] parts = filterQuery.split(":");
String key = parts[0];
String value = parts[1];
List<String> friendlyNames = new ArrayList<>();
String datasetfieldFriendyName = datasetfieldFriendlyNamesBySolrField.get(key);
if (datasetfieldFriendyName != null) {
friendlyNames.add(datasetfieldFriendyName);
} else {
String nonDatasetSolrField = staticSolrFieldFriendlyNamesBySolrField.get(key);
if (nonDatasetSolrField != null) {
friendlyNames.add(nonDatasetSolrField);
} else if (key.equals(SearchFields.PUBLICATION_STATUS)) {
/**
* @todo Refactor this quick fix for
* https://github.com/IQSS/dataverse/issues/618 . We really need
* to get rid of all the reflection that's happening with
* solrQueryResponse.getStaticSolrFieldFriendlyNamesBySolrField()
* and
*/
friendlyNames.add("Publication Status");
} else {
// meh. better than nuthin'
friendlyNames.add(key);
}
}
String noLeadingQuote = value.replaceAll("^\"", "");
String noTrailingQuote = noLeadingQuote.replaceAll("\"$", "");
String valueWithoutQuotes = noTrailingQuote;
friendlyNames.add(valueWithoutQuotes);
return friendlyNames;
}
public String getNewSelectedTypes(String typeClicked) {
List<String> newTypesSelected = new ArrayList<>();
for (String selectedType : selectedTypesList) {
if (selectedType.equals(typeClicked)) {
} else {
newTypesSelected.add(selectedType);
}
}
if (selectedTypesList.contains(typeClicked)) {
} else {
newTypesSelected.add(typeClicked);
}
String[] arr = newTypesSelected.toArray(new String[newTypesSelected.size()]);
return combine(arr, ":");
}
public String getErrorFromSolr() {
return errorFromSolr;
}
/**
* @return the dataverseAlias
*/
public String getDataverseAlias() {
return dataverseAlias;
}
/**
* @param dataverseAlias the dataverseAlias to set
*/
public void setDataverseAlias(String dataverseAlias) {
this.dataverseAlias = dataverseAlias;
}
public boolean isTabular(DataFile datafile) {
if (datafile == null) {
return false;
}
return datafile.isTabularData();
}
public SearchException getSearchException() {
return searchException;
}
public String tabularDataDisplayInfo(DataFile datafile) {
String ret = "";
if (datafile == null) {
return null;
}
if (datafile.isTabularData() && datafile.getDataTable() != null) {
DataTable datatable = datafile.getDataTable();
String unf = datatable.getUnf();
Long varNumber = datatable.getVarQuantity();
Long obsNumber = datatable.getCaseQuantity();
if (varNumber != null && varNumber.intValue() != 0) {
ret = ret.concat(varNumber + " Variables");
if (obsNumber != null && obsNumber.intValue() != 0) {
ret = ret.concat(", " + obsNumber + " Observations");
}
ret = ret.concat(" - ");
}
if (unf != null && !unf.equals("")) {
ret = ret.concat("UNF: " + unf);
}
}
return ret;
}
public String dataFileSizeDisplay(DataFile datafile) {
if (datafile == null) {
return "";
}
return datafile.getFriendlySize();
}
public String dataFileChecksumDisplay(DataFile datafile) {
if (datafile == null) {
return "";
}
if (datafile.getChecksumValue() != null && datafile.getChecksumValue() != "") {
if (datafile.getChecksumType() != null) {
return " " + datafile.getChecksumType() + ": " + datafile.getChecksumValue() + " ";
}
}
return "";
}
public void setDisplayCardValues() {
int i = 0;
dvobjectThumbnailsMap = new HashMap<>();
dvobjectViewMap = new HashMap<>();
Set<Long> harvestedDatasetIds = null;
for (SolrSearchResult result : searchResultsList) {
//logger.info("checking DisplayImage for the search result " + i++);
boolean valueSet = false;
if (result.getType().equals("dataverses") /*&& result.getEntity() instanceof Dataverse*/) {
result.setImageUrl(getDataverseCardImageUrl(result));
valueSet = true;
} else if (result.getType().equals("datasets") /*&& result.getEntity() instanceof Dataset*/) {
result.setImageUrl(getDatasetCardImageUrl(result));
valueSet = true;
if (result.isHarvested()) {
if (harvestedDatasetIds == null) {
harvestedDatasetIds = new HashSet<>();
}
harvestedDatasetIds.add(result.getEntityId());
}
} else if (result.getType().equals("files") /*&& result.getEntity() instanceof DataFile*/) {
// TODO:
// use permissionsWrapper? -- L.A. 4.2.1
// OK, done! (4.2.2; in the getFileCardImageUrl() method, below)
result.setImageUrl(getFileCardImageUrl(result));
valueSet = true;
if (result.isHarvested()) {
if (harvestedDatasetIds == null) {
harvestedDatasetIds = new HashSet<>();
}
harvestedDatasetIds.add(result.getParentIdAsLong());
}
}
if (valueSet) {
if (result.getImageUrl() != null) {
result.setDisplayImage(true);
}
} else {
logger.warning("Index result / entity mismatch (id:resultType) - " + result.getId() + ":" + result.getType());
}
}
dvobjectThumbnailsMap = null;
dvobjectViewMap = null;
// Now, make another pass, and add the remote archive descriptions to the
// harvested dataset and datafile cards (at the expense of one extra
// SQL query:
if (harvestedDatasetIds != null) {
Map<Long, String> descriptionsForHarvestedDatasets = datasetService.getArchiveDescriptionsForHarvestedDatasets(harvestedDatasetIds);
if (descriptionsForHarvestedDatasets != null && descriptionsForHarvestedDatasets.size() > 0) {
for (SolrSearchResult result : searchResultsList) {
if (result.isHarvested()) {
if (result.getType().equals("files")) {
if (descriptionsForHarvestedDatasets.containsKey(result.getParentIdAsLong())) {
result.setHarvestingDescription(descriptionsForHarvestedDatasets.get(result.getParentIdAsLong()));
}
} else if (result.getType().equals("datasets")) {
if (descriptionsForHarvestedDatasets.containsKey(result.getEntityId())) {
result.setHarvestingDescription(descriptionsForHarvestedDatasets.get(result.getEntityId()));
}
}
}
}
}
descriptionsForHarvestedDatasets = null;
harvestedDatasetIds = null;
}
// determine which of the objects are linked:
if (!this.isRootDv()) {
// (nothing is "linked" if it's the root DV!)
Set<Long> dvObjectParentIds = new HashSet<>();
for (SolrSearchResult result : searchResultsList) {
if (dataverse.getId().equals(result.getParentIdAsLong())) {
// definitely NOT linked:
result.setIsInTree(true);
} else if (result.getParentIdAsLong().longValue() == 1L) {
// the object's parent is the root Dv; and the current
// Dv is NOT root... definitely linked:
result.setIsInTree(false);
} else {
dvObjectParentIds.add(result.getParentIdAsLong());
}
}
if (dvObjectParentIds.size() > 0) {
Map<Long, String> treePathMap = dvObjectService.getObjectPathsByIds(dvObjectParentIds);
if (treePathMap != null) {
for (SolrSearchResult result : searchResultsList) {
Long objectId = result.getParentIdAsLong();
if (treePathMap.containsKey(objectId)) {
String objectPath = treePathMap.get(objectId);
if (!objectPath.startsWith(dataversePath)) {
result.setIsInTree(false);
}
}
}
}
treePathMap = null;
}
dvObjectParentIds = null;
}
}
private Map<Long, String> dvobjectThumbnailsMap = null;
private Map<Long, DvObject> dvobjectViewMap = null;
private String getAssignedDatasetImage(Dataset dataset) {
if (dataset == null) {
return null;
}
DataFile assignedThumbnailFile = dataset.getThumbnailFile();
if (assignedThumbnailFile != null) {
Long assignedThumbnailFileId = null;
if (this.dvobjectThumbnailsMap.containsKey(assignedThumbnailFileId)) {
// Yes, return previous answer
//logger.info("using cached result for ... "+assignedThumbnailFileId);
if (!"".equals(this.dvobjectThumbnailsMap.get(assignedThumbnailFileId))) {
return this.dvobjectThumbnailsMap.get(assignedThumbnailFileId);
}
return null;
}
String imageSourceBase64 = ImageThumbConverter.getImageThumbAsBase64(
assignedThumbnailFile,
ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE);
if (imageSourceBase64 != null) {
this.dvobjectThumbnailsMap.put(assignedThumbnailFileId, imageSourceBase64);
return imageSourceBase64;
}
// OK - we can't use this "assigned" image, because of permissions, or because
// the thumbnail failed to generate, etc... in this case we'll
// mark this dataset in the lookup map - so that we don't have to
// do all these lookups again...
this.dvobjectThumbnailsMap.put(assignedThumbnailFileId, "");
// TODO: (?)
// do we need to cache this datafile object in the view map?
// -- L.A., 4.2.2
}
return null;
}
// it's the responsibility of the user - to make sure the search result
// passed to this method is of the Datafile type!
private String getFileCardImageUrl(SolrSearchResult result) {
Long imageFileId = result.getEntity().getId();
if (imageFileId != null) {
if (this.dvobjectThumbnailsMap.containsKey(imageFileId)) {
// Yes, return previous answer
//logger.info("using cached result for ... "+datasetId);
if (!"".equals(this.dvobjectThumbnailsMap.get(imageFileId))) {
return this.dvobjectThumbnailsMap.get(imageFileId);
}
return null;
}
String cardImageUrl = null;
if ((!((DataFile)result.getEntity()).isRestricted()
|| permissionsWrapper.hasDownloadFilePermission(result.getEntity()))
&& dataFileService.isThumbnailAvailable((DataFile) result.getEntity())) {
cardImageUrl = ImageThumbConverter.getImageThumbAsBase64(
(DataFile) result.getEntity(),
ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE);
}
if (cardImageUrl != null) {
this.dvobjectThumbnailsMap.put(imageFileId, cardImageUrl);
//logger.info("datafile id " + imageFileId + ", returning " + cardImageUrl);
if (!(dvobjectViewMap.containsKey(imageFileId)
&& dvobjectViewMap.get(imageFileId).isInstanceofDataFile())) {
dvobjectViewMap.put(imageFileId, result.getEntity());
}
return cardImageUrl;
} else {
this.dvobjectThumbnailsMap.put(imageFileId, "");
}
}
return null;
}
// it's the responsibility of the user - to make sure the search result
// passed to this method is of the Dataset type!
private String getDatasetCardImageUrl(SolrSearchResult result) {
// harvested check!
String cardImageUrl = null;
if (result.getEntity() != null) {
cardImageUrl = this.getAssignedDatasetImage((Dataset) result.getEntity());
if (cardImageUrl != null) {
//logger.info("dataset id " + result.getEntity().getId() + " has a dedicated image assigned; returning " + cardImageUrl);
return cardImageUrl;
}
}
Long thumbnailImageFileId = datasetVersionService.getThumbnailByVersionId(result.getDatasetVersionId());
if (thumbnailImageFileId != null) {
//cardImageUrl = FILE_CARD_IMAGE_URL + thumbnailImageFileId;
if (this.dvobjectThumbnailsMap.containsKey(thumbnailImageFileId)) {
// Yes, return previous answer
//logger.info("using cached result for ... "+datasetId);
if (!"".equals(this.dvobjectThumbnailsMap.get(thumbnailImageFileId))) {
return this.dvobjectThumbnailsMap.get(thumbnailImageFileId);
}
return null;
}
DataFile thumbnailImageFile = null;
if (dvobjectViewMap.containsKey(thumbnailImageFileId)
&& dvobjectViewMap.get(thumbnailImageFileId).isInstanceofDataFile()) {
thumbnailImageFile = (DataFile) dvobjectViewMap.get(thumbnailImageFileId);
} else {
thumbnailImageFile = dataFileService.findCheapAndEasy(thumbnailImageFileId);
if (thumbnailImageFile != null) {
// TODO:
// do we need this file on the map? - it may not even produce
// a thumbnail!
dvobjectViewMap.put(thumbnailImageFileId, thumbnailImageFile);
} else {
this.dvobjectThumbnailsMap.put(thumbnailImageFileId, "");
return null;
}
}
if (dataFileService.isThumbnailAvailable(thumbnailImageFile)) {
cardImageUrl = ImageThumbConverter.getImageThumbAsBase64(
thumbnailImageFile,
ImageThumbConverter.DEFAULT_CARDIMAGE_SIZE);
}
if (cardImageUrl != null) {
this.dvobjectThumbnailsMap.put(thumbnailImageFileId, cardImageUrl);
} else {
this.dvobjectThumbnailsMap.put(thumbnailImageFileId, "");
}
}
//logger.info("dataset id " + result.getEntityId() + ", returning " + cardImageUrl);
return cardImageUrl;
}
// it's the responsibility of the user - to make sure the search result
// passed to this method is of the Dataverse type!
private String getDataverseCardImageUrl(SolrSearchResult result) {
return dataverseService.getDataverseLogoThumbnailAsBase64ById(result.getEntityId());
}
/*
These commented out methods below are old optimizations that are no longer
necessary, since there is now a more direct connection between a harvested
dataset and its HarvestingClient configuration. -- L.A. 4.5
*/
/*
private Map<Long, String> getHarvestedDatasetDescriptions() {
if (harvestedDatasetDescriptions != null) {
return harvestedDatasetDescriptions;
}
harvestedDatasetDescriptions = dataverseService.getAllHarvestedDataverseDescriptions();
return harvestedDataverseDescriptions;
}*/
/*private boolean isHarvestedDataverse(Long id) {
return this.getHarvestedDataverseDescriptions().containsKey(id);
}
private String getHarvestingDataverseDescription(Long id) {
if (this.isHarvestedDataverse(id)) {
return this.getHarvestedDataverseDescriptions().get(id);
}
return null;
}*/
public enum SortOrder {
asc, desc
};
}