Skip to content
1 change: 1 addition & 0 deletions conf/solr/schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,7 @@
<field name="fileRestricted" type="boolean" stored="true" indexed="false" multiValued="false"/>
<field name="canDownloadFile" type="boolean" stored="true" indexed="false" multiValued="false"/>
<field name="versionNote" type="string" stored="true" indexed="false" multiValued="false"/>
<field name="isLinked" type="boolean" stored="true" indexed="false" multiValued="false"/>

<!-- Added for Dataverse 4.0 alpha 1. This is a required field so we don't have to go to the database to get the database id of the entity. On cards we use the id in links -->
<field name="entityId" type="plong" stored="true" indexed="true" multiValued="false"/>
Expand Down
2 changes: 2 additions & 0 deletions doc/release-notes/11845-extend-search-api-return-islinked.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
## Feature Request ##
Search API now returns "isLinked" in the JSON response for datasets and collections that are linked. "isLinked" will also be returned in direct dataset and collection lookups. This attribute will only be included when the value is true.
43 changes: 13 additions & 30 deletions src/main/java/edu/harvard/iq/dataverse/Dataset.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,38 +8,17 @@
import edu.harvard.iq.dataverse.makedatacount.DatasetExternalCitations;
import edu.harvard.iq.dataverse.makedatacount.DatasetMetrics;
import edu.harvard.iq.dataverse.settings.FeatureFlags;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import jakarta.persistence.CascadeType;
import jakarta.persistence.ColumnResult;
import jakarta.persistence.Entity;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedNativeQuery;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.OrderBy;
import jakarta.persistence.SqlResultSetMapping;
import jakarta.persistence.Table;
import jakarta.persistence.Temporal;
import jakarta.persistence.TemporalType;

import edu.harvard.iq.dataverse.settings.JvmSettings;
import edu.harvard.iq.dataverse.storageuse.StorageUse;
import edu.harvard.iq.dataverse.util.StringUtil;
import edu.harvard.iq.dataverse.util.SystemConfig;
import jakarta.persistence.*;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;

/**
*
Expand Down Expand Up @@ -810,13 +789,17 @@
public void setHarvestedFrom(HarvestingClient harvestingClientConfig) {
this.harvestedFrom = harvestingClientConfig;
}

public boolean isHarvested() {
return this.harvestedFrom != null;
}

public boolean isLinked() {
return (datasetLinkingDataverses != null && datasetLinkingDataverses.size() > 0);

Check warning on line 798 in src/main/java/edu/harvard/iq/dataverse/Dataset.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use isEmpty() to check whether the collection is empty or not.

See more on https://sonarcloud.io/project/issues?id=IQSS_dataverse&issues=AZ_yjR7sl3_9oaUGpVfY&open=AZ_yjR7sl3_9oaUGpVfY&pullRequest=12485
}

private String harvestIdentifier;

public String getHarvestIdentifier() {
return harvestIdentifier;
}
Expand Down
42 changes: 9 additions & 33 deletions src/main/java/edu/harvard/iq/dataverse/Dataverse.java
Original file line number Diff line number Diff line change
@@ -1,44 +1,16 @@
package edu.harvard.iq.dataverse;

import edu.harvard.iq.dataverse.dataverse.featured.DataverseFeaturedItem;
import edu.harvard.iq.dataverse.harvest.client.HarvestingClient;
import edu.harvard.iq.dataverse.authorization.DataverseRole;
import edu.harvard.iq.dataverse.dataset.DatasetType;
import edu.harvard.iq.dataverse.harvest.client.HarvestingClient;
import edu.harvard.iq.dataverse.search.savedsearch.SavedSearch;
import edu.harvard.iq.dataverse.storageuse.StorageUse;
import edu.harvard.iq.dataverse.util.BundleUtil;
import edu.harvard.iq.dataverse.util.SystemConfig;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import jakarta.persistence.CascadeType;
import jakarta.persistence.CollectionTable;
import jakarta.persistence.Column;
import jakarta.persistence.ElementCollection;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.Index;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.NamedQueries;
import jakarta.persistence.NamedQuery;
import jakarta.persistence.OneToMany;
import jakarta.persistence.OneToOne;
import jakarta.persistence.OrderBy;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.validation.constraints.Size;
import java.util.*;

/**
*
Expand Down Expand Up @@ -327,7 +299,11 @@
public void setDatasetLinkingDataverses(List<DatasetLinkingDataverse> datasetLinkingDataverses) {
this.datasetLinkingDataverses = datasetLinkingDataverses;
}


public boolean isLinked() {
return (dataverseLinkingDataverses != null && dataverseLinkingDataverses.size() > 0);

Check warning on line 304 in src/main/java/edu/harvard/iq/dataverse/Dataverse.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use isEmpty() to check whether the collection is empty or not.

See more on https://sonarcloud.io/project/issues?id=IQSS_dataverse&issues=AZ_yjR7Kl3_9oaUGpVfX&open=AZ_yjR7Kl3_9oaUGpVfX&pullRequest=12485
}

public Set<ControlledVocabularyValue> getDataverseSubjects() {
return dataverseSubjects;
}
Expand Down
10 changes: 5 additions & 5 deletions src/main/java/edu/harvard/iq/dataverse/api/AbstractApiBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -418,19 +418,19 @@ protected Template findTemplateInDataverseOrParentsOrDie(Long templateId, Datave
return template;
}

protected DataverseLinkingDataverse findDataverseLinkingDataverseOrDie(String dataverseId, String linkedDataverseId) throws WrappedResponse {
protected DataverseLinkingDataverse findDataverseLinkingDataverseOrDie(String linkingDataverseId, String linkedDataverseId) throws WrappedResponse {
DataverseLinkingDataverse dvld;
Dataverse dataverse = findDataverseOrDie(dataverseId);
Dataverse linkingDataverse = findDataverseOrDie(linkingDataverseId);
Dataverse linkedDataverse = findDataverseOrDie(linkedDataverseId);
try {
dvld = dvLinkingService.findDataverseLinkingDataverse(dataverse.getId(), linkedDataverse.getId());
dvld = dvLinkingService.findDataverseLinkingDataverse(linkingDataverse.getId(), linkedDataverse.getId());
if (dvld == null) {
throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.dataverselinking.error.not.found.ids", Arrays.asList(dataverseId, linkedDataverseId))));
throw new WrappedResponse(notFound(BundleUtil.getStringFromBundle("find.dataverselinking.error.not.found.ids", Arrays.asList(linkingDataverseId, linkedDataverseId))));
}
return dvld;
} catch (NumberFormatException nfe) {
throw new WrappedResponse(
badRequest(BundleUtil.getStringFromBundle("find.dataverselinking.error.not.found.bad.ids", Arrays.asList(dataverseId, linkedDataverseId))));
badRequest(BundleUtil.getStringFromBundle("find.dataverselinking.error.not.found.bad.ids", Arrays.asList(linkingDataverseId, linkedDataverseId))));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,7 @@ public Response deleteDataverseLinkingDataverse(@Context ContainerRequestContext
@PathParam("linkedDataverseId") String linkedDataverseId) {
boolean index = true;
return response(req -> {
execCommand(new DeleteDataverseLinkingDataverseCommand(req, findDataverseOrDie(linkingDataverseId), findDataverseLinkingDataverseOrDie(linkingDataverseId, linkedDataverseId), index));
execCommand(new DeleteDataverseLinkingDataverseCommand(req, findDataverseOrDie(linkedDataverseId), findDataverseLinkingDataverseOrDie(linkingDataverseId, linkedDataverseId), index));
return ok("Link from Dataverse " + linkingDataverseId + " to linked Dataverse " + linkedDataverseId + " deleted");
}, getRequestUser(crc));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@
import edu.harvard.iq.dataverse.Dataverse;
import edu.harvard.iq.dataverse.DataverseLinkingDataverse;
import edu.harvard.iq.dataverse.authorization.Permission;
import edu.harvard.iq.dataverse.authorization.users.AuthenticatedUser;
import edu.harvard.iq.dataverse.batch.util.LoggingUtil;
import edu.harvard.iq.dataverse.engine.command.AbstractCommand;
import edu.harvard.iq.dataverse.engine.command.CommandContext;
import edu.harvard.iq.dataverse.engine.command.DataverseRequest;
import edu.harvard.iq.dataverse.engine.command.RequiredPermissions;
import edu.harvard.iq.dataverse.engine.command.exception.CommandException;
import edu.harvard.iq.dataverse.engine.command.exception.PermissionException;
import java.io.IOException;
import java.util.Collections;
import java.util.concurrent.Future;

import org.apache.solr.client.solrj.SolrServerException;

/**
Expand All @@ -30,40 +27,30 @@
public class DeleteDataverseLinkingDataverseCommand extends AbstractCommand<Dataverse> {

private final DataverseLinkingDataverse doomed;
private final Dataverse editedDv;
private final Dataverse linkedDataverse;
private final boolean index;

public DeleteDataverseLinkingDataverseCommand(DataverseRequest aRequest, Dataverse editedDv , DataverseLinkingDataverse doomed, boolean index) {
super(aRequest, editedDv);
this.editedDv = editedDv;
public DeleteDataverseLinkingDataverseCommand(DataverseRequest aRequest, Dataverse linkedDataverse , DataverseLinkingDataverse doomed, boolean index) {
super(aRequest, linkedDataverse);
this.linkedDataverse = linkedDataverse;
this.doomed = doomed;
this.index = index;
}

@Override
public Dataverse execute(CommandContext ctxt) throws CommandException {
Dataverse merged = ctxt.em().merge(editedDv);
DataverseLinkingDataverse doomedAndMerged = ctxt.em().merge(doomed);
ctxt.em().remove(doomedAndMerged);

if (index) {
//can only index merged in the onSuccess method so must index doomed linking dataverse here
try {
ctxt.index().indexDataverse(doomed.getLinkingDataverse());
} catch (IOException | SolrServerException e) {
String failureLogText = "Indexing failed for Linked Dataverse. You can kickoff a re-index of this datavese with: \r\n curl http://localhost:8080/api/admin/index/datasets/" + doomed.getLinkingDataverse().getId().toString();
failureLogText += "\r\n" + e.getLocalizedMessage();
LoggingUtil.writeOnSuccessFailureLog(this, failureLogText, doomed.getLinkingDataverse());
}
}
return merged;
ctxt.em().remove(doomed);
return linkedDataverse;
}

@Override
public boolean onSuccess(CommandContext ctxt, Object r) {

try {
Future<String> retVal = ctxt.index().indexDataverse((Dataverse) r);
if (index) {
ctxt.index().indexDataverse(doomed.getDataverse());
ctxt.index().indexDataverse(doomed.getLinkingDataverse());
}
} catch (IOException | SolrServerException e) {
Dataverse dv = (Dataverse) r;
String failureLogText = "Indexing failed for Dataverse delinking. You can kickoff a re-index of this datavese with: \r\n curl http://localhost:8080/api/admin/index/datasets/" + dv.getId().toString();
Expand Down
100 changes: 29 additions & 71 deletions src/main/java/edu/harvard/iq/dataverse/search/IndexServiceBean.java
Original file line number Diff line number Diff line change
@@ -1,37 +1,8 @@
package edu.harvard.iq.dataverse.search;

import edu.harvard.iq.dataverse.ControlledVocabularyValue;
import edu.harvard.iq.dataverse.CurationStatus;
import edu.harvard.iq.dataverse.DataFile;
import edu.harvard.iq.dataverse.DataFileServiceBean;
import edu.harvard.iq.dataverse.DataFileTag;
import edu.harvard.iq.dataverse.DataTable;
import edu.harvard.iq.dataverse.Dataset;
import edu.harvard.iq.dataverse.DatasetField;
import edu.harvard.iq.dataverse.DatasetFieldCompoundValue;
import edu.harvard.iq.dataverse.DatasetFieldConstant;
import edu.harvard.iq.dataverse.DatasetFieldServiceBean;
import edu.harvard.iq.dataverse.DatasetFieldType;
import edu.harvard.iq.dataverse.DatasetFieldValue;
import edu.harvard.iq.dataverse.DatasetFieldValueValidator;
import edu.harvard.iq.dataverse.DatasetLinkingServiceBean;
import edu.harvard.iq.dataverse.DatasetServiceBean;
import edu.harvard.iq.dataverse.DatasetVersion;
import edu.harvard.iq.dataverse.*;
import edu.harvard.iq.dataverse.DatasetVersion.VersionState;
import edu.harvard.iq.dataverse.DatasetVersionFilesServiceBean;
import edu.harvard.iq.dataverse.DatasetVersionServiceBean;
import edu.harvard.iq.dataverse.Dataverse;
import edu.harvard.iq.dataverse.DataverseLinkingServiceBean;
import edu.harvard.iq.dataverse.DataverseServiceBean;
import edu.harvard.iq.dataverse.DvObject;
import edu.harvard.iq.dataverse.DvObject.DType;
import edu.harvard.iq.dataverse.DvObjectServiceBean;
import edu.harvard.iq.dataverse.Embargo;
import edu.harvard.iq.dataverse.FileMetadata;
import edu.harvard.iq.dataverse.GlobalId;
import edu.harvard.iq.dataverse.PermissionServiceBean;
import edu.harvard.iq.dataverse.Retention;
import edu.harvard.iq.dataverse.TermsOfUseAndAccess;
import edu.harvard.iq.dataverse.authorization.AuthenticationServiceBean;
import edu.harvard.iq.dataverse.authorization.providers.builtin.BuiltinUserServiceBean;
import edu.harvard.iq.dataverse.batch.util.LoggingUtil;
Expand All @@ -50,51 +21,12 @@
import edu.harvard.iq.dataverse.util.FileUtil;
import edu.harvard.iq.dataverse.util.StringUtil;
import edu.harvard.iq.dataverse.util.SystemConfig;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import jakarta.ejb.AsyncResult;
import jakarta.ejb.Asynchronous;
import jakarta.ejb.EJB;
import jakarta.ejb.EJBException;
import jakarta.ejb.Stateless;
import jakarta.ejb.TransactionAttribute;

import static jakarta.ejb.TransactionAttributeType.REQUIRES_NEW;

import jakarta.ejb.*;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.json.JsonObject;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import jakarta.persistence.Query;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.util.Strings;
Expand All @@ -107,15 +39,36 @@
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CursorMarkParams;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.metadata.Metadata;
import org.apache.tika.parser.AutoDetectParser;
import org.apache.tika.parser.ParseContext;
import org.apache.tika.sax.BodyContentHandler;
import org.eclipse.microprofile.metrics.MetricUnits;
import org.eclipse.microprofile.metrics.Timer;
import org.eclipse.microprofile.metrics.annotation.Metric;
import org.xml.sax.ContentHandler;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static jakarta.ejb.TransactionAttributeType.REQUIRES_NEW;

@Stateless
@Named
public class IndexServiceBean {
Expand Down Expand Up @@ -248,6 +201,9 @@ public Future<String> indexDataverse(Dataverse dataverse, boolean processPaths)
solrInputDocument.addField(SearchFields.METADATA_SOURCE, rootDataverse.getName()); //rootDataverseName);
/*}*/

solrInputDocument.addField(SearchFields.IS_LINKED, dataverse.isLinked());


addDataverseReleaseDateToSolrDoc(solrInputDocument, dataverse);
// if (dataverse.getOwner() != null) {
// solrInputDocument.addField(SearchFields.HOST_DATAVERSE,
Expand Down Expand Up @@ -1063,6 +1019,8 @@ public SolrInputDocuments toSolrDocs(IndexableDataset indexableDataset, Set<Long
solrInputDocument.addField(SearchFields.METADATA_SOURCE, rdvName); //rootDataverseName);
}

solrInputDocument.addField(SearchFields.IS_LINKED, dataset.isLinked());

DatasetType datasetType = dataset.getDatasetType();
solrInputDocument.addField(SearchFields.DATASET_TYPE, datasetType.getName());

Expand Down
Loading
Loading