diff --git a/Kitodo-API/src/main/java/org/kitodo/config/KitodoConfig.java b/Kitodo-API/src/main/java/org/kitodo/config/KitodoConfig.java index cec59ad1286..015d4ead277 100644 --- a/Kitodo-API/src/main/java/org/kitodo/config/KitodoConfig.java +++ b/Kitodo-API/src/main/java/org/kitodo/config/KitodoConfig.java @@ -12,7 +12,9 @@ package org.kitodo.config; import java.io.File; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; import java.nio.file.Paths; import java.util.NoSuchElementException; import java.util.Optional; @@ -237,6 +239,27 @@ public static Optional getOptionalString(ParameterInterface key) { } } + /** + * Returns the URL of the search server. + * + * @return the URL + * @throws MalformedURLException + * if an unknown protocol, or the port is a negative number + * other than -1 + */ + public static URL getSearchServerUrl() throws MalformedURLException { + String host = getParameter("elasticsearch.host", "localhost"); + int port = getIntParameter(new ParameterInterface() { + @Override + public String getName() { + return "elasticsearch.port"; + } + }, 9200); + String protocol = getParameter("elasticsearch.protocol", "http"); + String path = getParameter("elasticsearch.path", "/"); + return new URL(protocol, host, port, path); + } + /** * Returns the selected URI from the configuration file. Throws a * {@code NoSuchElementException} if no such parameter exists. diff --git a/Kitodo-DataManagement/hibernate.properties b/Kitodo-DataManagement/hibernate.properties new file mode 100644 index 00000000000..bbd8d7c534a --- /dev/null +++ b/Kitodo-DataManagement/hibernate.properties @@ -0,0 +1,3 @@ +hibernate.search.enabled=true +hibernate.search.backend.hosts=localhost:9200 +hibernate.search.backend.protocol=http diff --git a/Kitodo-DataManagement/pom.xml b/Kitodo-DataManagement/pom.xml index d9e6a26760b..5bc990a8619 100644 --- a/Kitodo-DataManagement/pom.xml +++ b/Kitodo-DataManagement/pom.xml @@ -83,6 +83,16 @@ org.hibernate hibernate-jcache + + org.opensearch + opensearch + test + + + org.opensearch.plugin + transport-netty4-client + test + com.github.spotbugs spotbugs-annotations @@ -121,6 +131,17 @@ org.junit.vintage junit-vintage-engine + + + org.hibernate.search + hibernate-search-mapper-orm + ${hibernate-search.version} + + + org.hibernate.search + hibernate-search-backend-elasticsearch + ${hibernate-search.version} + diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseBean.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseBean.java index d6414c28101..ce23b7b021c 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseBean.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseBean.java @@ -22,6 +22,7 @@ import javax.persistence.MappedSuperclass; import org.hibernate.Hibernate; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import org.kitodo.data.database.persistence.BaseDAO; /** @@ -34,6 +35,7 @@ public abstract class BaseBean implements Serializable { @Id @Column(name = "id") + @GenericField @GeneratedValue(strategy = GenerationType.IDENTITY) protected Integer id; diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseTemplateBean.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseTemplateBean.java index 2ed4a6ca721..e136775d1dd 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseTemplateBean.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/BaseTemplateBean.java @@ -17,18 +17,22 @@ import javax.persistence.Column; import javax.persistence.MappedSuperclass; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; + /** * This bean contains properties common for Template and Process. */ @MappedSuperclass public abstract class BaseTemplateBean extends BaseBean { + @GenericField @Column(name = "title") protected String title; @Column(name = "creationDate") protected Date creationDate; + @GenericField @Column(name = "sortHelperStatus") private String sortHelperStatus; diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Batch.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Batch.java index 1577c6b0e39..93fb527049d 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Batch.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Batch.java @@ -27,6 +27,9 @@ import javax.persistence.ManyToMany; import javax.persistence.Table; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded; import org.kitodo.data.database.enums.BatchType; import org.kitodo.data.database.persistence.BatchDAO; @@ -38,6 +41,7 @@ * multi-journal binding unit. */ @Entity +@Indexed(index = "kitodo-batch") @Table(name = "batch") public class Batch extends BaseBean { @@ -45,6 +49,7 @@ public class Batch extends BaseBean { * The batch title. Using titles for batches is optional, the field may be * {@code null}. If so, the ID will be shown to the user instead. */ + @GenericField @Column(name = "title") private String title; diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Client.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Client.java index efdc9cf95df..62b3f0fcecd 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Client.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Client.java @@ -25,12 +25,14 @@ import javax.persistence.OneToMany; import javax.persistence.Table; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import org.kitodo.data.database.persistence.ClientDAO; @Entity @Table(name = "client") public class Client extends BaseBean { + @GenericField @Column(name = "name") private String name; diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Comment.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Comment.java index 4aac5ceef6f..17c03f09dcb 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Comment.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Comment.java @@ -22,6 +22,7 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import org.kitodo.data.database.enums.CommentType; @Entity @@ -31,6 +32,7 @@ public class Comment extends BaseBean { * The field message holds the comment message. */ @Column(name = "message", columnDefinition = "longtext") + @GenericField private String message; /** @@ -38,6 +40,7 @@ public class Comment extends BaseBean { */ @Column(name = "type") @Enumerated(EnumType.STRING) + @GenericField private CommentType type; /** diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Docket.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Docket.java index 981c4218a54..d8506ecf97f 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Docket.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Docket.java @@ -20,20 +20,32 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; +import org.hibernate.search.mapper.pojo.automaticindexing.ReindexOnUpdate; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexingDependency; + @Entity +@Indexed(index = "kitodo-docket") @Table(name = "docket") public class Docket extends BaseBean { + @GenericField @Column(name = "title") private String title; + @GenericField @Column(name = "file") private String file; + @GenericField @Column(name = "active") private Boolean active = true; @ManyToOne + @IndexedEmbedded(includePaths = {"id", "name"}) + @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW) @JoinColumn(name = "client_id", foreignKey = @ForeignKey(name = "FK_docket_client_id")) private Client client; diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Filter.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Filter.java index 3fe328e4734..90ea446254f 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Filter.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Filter.java @@ -21,20 +21,28 @@ import javax.persistence.ManyToOne; import javax.persistence.Table; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded; + /** * Filter bean. */ @Entity +@Indexed(index = "kitodo-filter") @Table(name = "filter") public class Filter extends BaseBean { + @GenericField @Column(name = "value", columnDefinition = "longtext") private String value; + @GenericField @Column(name = "creationDate") private Date creationDate; @ManyToOne + @IndexedEmbedded(includePaths = {"id"}) @JoinColumn(name = "user_id", foreignKey = @ForeignKey(name = "FK_filter_user_id")) private User user; diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Folder.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Folder.java index 961fa32ef4a..63f831e8321 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Folder.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Folder.java @@ -29,6 +29,7 @@ import javax.persistence.Table; import javax.persistence.Transient; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; import org.kitodo.api.imagemanagement.ImageManagementInterface; import org.kitodo.config.ConfigMain; import org.kitodo.data.database.enums.LinkingMode; @@ -104,6 +105,7 @@ public class Folder extends BaseBean { * contents of this folder will be linked. */ @Column(name = "fileGroup") + @GenericField private String fileGroup; /** @@ -130,12 +132,14 @@ public class Folder extends BaseBean { * @see org.kitodo.config.xml.fileformats.FileFormatsConfig */ @Column(name = "mimeType") + @GenericField private String mimeType = "image/jpeg"; /** * The path to the folder in the process directory of each processes. */ @Column(name = "path") + @GenericField private String path = ""; /** @@ -151,6 +155,7 @@ public class Folder extends BaseBean { * replaced before concatenation. */ @Column(name = "urlStructure") + @GenericField private String urlStructure; /** diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Process.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Process.java index fbdc529ef6c..a8c476ffc45 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Process.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Process.java @@ -38,6 +38,12 @@ import org.hibernate.LazyInitializationException; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; +import org.hibernate.search.mapper.pojo.automaticindexing.ReindexOnUpdate; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexingDependency; import org.kitodo.data.database.converter.ProcessConverter; import org.kitodo.data.database.enums.CorrectionComments; import org.kitodo.data.database.enums.TaskStatus; @@ -45,47 +51,62 @@ import org.kitodo.data.database.persistence.ProcessDAO; @Entity +@Indexed(index = "kitodo-process") @Table(name = "process") public class Process extends BaseTemplateBean { + @GenericField @Column(name = "sortHelperImages") private Integer sortHelperImages; + @GenericField @Column(name = "sortHelperArticles") private Integer sortHelperArticles; + @GenericField @Column(name = "sortHelperMetadata") private Integer sortHelperMetadata; + @GenericField @Column(name = "sortHelperDocstructs") private Integer sortHelperDocstructs; + @FullTextField @Column(name = "wikiField", columnDefinition = "longtext") private String wikiField = ""; + @GenericField @Column(name = "processBaseUri") private String processBaseUri; + @GenericField @Column(name = "ordering") private Integer ordering; @ManyToOne + @IndexedEmbedded(includePaths = {"title", "id"}) + @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW) @JoinColumn(name = "docket_id", foreignKey = @ForeignKey(name = "FK_process_docket_id")) private Docket docket; @ManyToOne + @IndexedEmbedded(includePaths = {"title", "active", "id", "client.id"}) @JoinColumn(name = "project_id", foreignKey = @ForeignKey(name = "FK_process_project_id")) private Project project; @ManyToOne + @IndexedEmbedded(includePaths = {"title", "id"}) + @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW) @JoinColumn(name = "ruleset_id", foreignKey = @ForeignKey(name = "FK_process_ruleset_id")) private Ruleset ruleset; @ManyToOne + @IndexedEmbedded(includePaths = {"title", "id"}) @JoinColumn(name = "template_id", foreignKey = @ForeignKey(name = "FK_process_template_id")) private Template template; @ManyToOne + @IndexedEmbedded(includePaths = {"title", "id"}) @JoinColumn(name = "parent_id", foreignKey = @ForeignKey(name = "FK_process_parent_id")) private Process parent; @@ -95,40 +116,56 @@ public class Process extends BaseTemplateBean { @Transient private boolean hasChildren = true; + @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(mappedBy = "process", cascade = CascadeType.ALL, orphanRemoval = true) + @IndexedEmbedded(includePaths = {"title", "id"}) @OrderBy("ordering") private List tasks; @LazyCollection(LazyCollectionOption.FALSE) @OneToMany(mappedBy = "process", cascade = CascadeType.PERSIST, orphanRemoval = true) + @IndexedEmbedded(includePaths = {"message"}) private List comments; + @LazyCollection(LazyCollectionOption.FALSE) @ManyToMany(cascade = CascadeType.ALL) + @IndexedEmbedded(includePaths = {"id", "title", "value"}) + @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW) @JoinTable(name = "process_x_property", joinColumns = { @JoinColumn(name = "process_id", foreignKey = @ForeignKey(name = "FK_process_x_property_process_id")) }, inverseJoinColumns = { @JoinColumn(name = "property_id", foreignKey = @ForeignKey(name = "FK_process_x_property_property_id")) }) private List properties; + @LazyCollection(LazyCollectionOption.FALSE) @ManyToMany(cascade = CascadeType.ALL) + @IndexedEmbedded(includePaths = {"id", "title", "value"}) + @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW) @JoinTable(name = "template_x_property", joinColumns = { @JoinColumn(name = "process_id", foreignKey = @ForeignKey(name = "FK_template_x_property_process_id")) }, inverseJoinColumns = { @JoinColumn(name = "property_id", foreignKey = @ForeignKey(name = "FK_template_x_property_property_id")) }) private List templates; + @LazyCollection(LazyCollectionOption.FALSE) @ManyToMany(cascade = CascadeType.ALL) + @IndexedEmbedded(includePaths = {"id", "title", "value"}) + @IndexingDependency(reindexOnUpdate = ReindexOnUpdate.SHALLOW) @JoinTable(name = "workpiece_x_property", joinColumns = { @JoinColumn(name = "process_id", foreignKey = @ForeignKey(name = "FK_workpiece_x_property_process_id")) }, inverseJoinColumns = { @JoinColumn(name = "property_id", foreignKey = @ForeignKey(name = "FK_workpiece_x_property_property_id")) }) private List workpieces; + @LazyCollection(LazyCollectionOption.FALSE) @ManyToMany(mappedBy = "processes") + @IndexedEmbedded(includePaths = {"title", "id"}) private List batches = new ArrayList<>(); @Column(name = "exported") + @GenericField private boolean exported; @Column(name = "inChoiceListShown") + @GenericField Boolean inChoiceListShown; @Column(name = "ocrd_workflow_id") @@ -1010,4 +1047,9 @@ public void setHasComments(boolean hasComments) { throw new UnsupportedOperationException("cannot insert comments"); } } + + @Override + public String toString() { + return title + " [" + id + "]"; + } } diff --git a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java index da9b33d94e4..ef12b210d93 100644 --- a/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java +++ b/Kitodo-DataManagement/src/main/java/org/kitodo/data/database/beans/Project.java @@ -39,65 +39,91 @@ import org.hibernate.LazyInitializationException; import org.hibernate.annotations.LazyCollection; import org.hibernate.annotations.LazyCollectionOption; +import org.hibernate.search.mapper.pojo.automaticindexing.ReindexOnUpdate; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.FullTextField; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded; +import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexingDependency; import org.kitodo.data.database.enums.PreviewHoverMode; import org.kitodo.data.database.persistence.ProjectDAO; @Entity +@Indexed(index = "kitodo-project") @Table(name = "project") public class Project extends BaseBean implements Comparable { + @GenericField @Column(name = "title", nullable = false, unique = true) private String title; + @FullTextField @Column(name = "dmsImportRootPath") private String dmsImportRootPath; + @FullTextField @Column(name = "metsRightsOwner") private String metsRightsOwner = ""; + @FullTextField @Column(name = "metsRightsOwnerLogo") private String metsRightsOwnerLogo = ""; + @FullTextField @Column(name = "metsRightsOwnerSite") private String metsRightsOwnerSite = ""; + @FullTextField @Column(name = "metsRightsOwnerMail") private String metsRightsOwnerMail = ""; + @FullTextField @Column(name = "metsDigiprovReference") private String metsDigiprovReference = ""; + @FullTextField @Column(name = "metsDigiprovPresentation") private String metsDigiprovPresentation = ""; + @FullTextField @Column(name = "metsPointerPath") private String metsPointerPath = ""; + @FullTextField @Column(name = "metsPurl") private String metsPurl = ""; + @FullTextField @Column(name = "metsContentId") private String metsContentIDs = ""; @Column(name = "startDate") + @GenericField private Date startDate; @Column(name = "endDate") + @GenericField private Date endDate; @Column(name = "numberOfPages") + @GenericField private Integer numberOfPages; @Column(name = "numberOfVolumes") + @GenericField private Integer numberOfVolumes; @Column(name = "active") + @GenericField private Boolean active = true; + @LazyCollection(LazyCollectionOption.FALSE) @ManyToMany(mappedBy = "projects", cascade = CascadeType.PERSIST) + @IndexedEmbedded(includePaths = {"surname", "name", "id", "login"}) private List users; @OneToMany(mappedBy = "project", cascade = CascadeType.ALL, orphanRemoval = true) + @IndexedEmbedded(includePaths = {"id", "title"}) private List processes; @Transient @@ -105,6 +131,7 @@ public class Project extends BaseBean implements Comparable { @LazyCollection(LazyCollectionOption.FALSE) @ManyToMany(mappedBy = "projects", cascade = CascadeType.PERSIST) + @IndexedEmbedded(includePaths = {"id", "title"}) private List