Merge "Fix up com.fasterxml.jackson.jaxrs from [2.9.8,] to 2.11.3"
diff --git a/.gitignore b/.gitignore
index 4e95513..95d91a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,7 @@
*.iml
dependency-reduced-pom.xml
admin_token
-/sandbox/
+/sandbox
/bin/
/db.sqlite
/lite/liteDB.sqlite
diff --git a/core/Changes b/core/Changes
index 8700115..611af1f 100644
--- a/core/Changes
+++ b/core/Changes
@@ -5,6 +5,8 @@
- Removed salt from config and updated config files.
05/02/2020
- Added welcome page.
+01/04/2020
+ - Support expand query parameter for match retrieval (diewald)
11/05/2020
- Added tool to create VC from list (diewald)
29/05/2020
diff --git a/core/pom.xml b/core/pom.xml
index 958595a..f617a48 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -241,7 +241,7 @@
<dependency>
<groupId>de.ids_mannheim.korap</groupId>
<artifactId>Koral</artifactId>
- <version>[0.36,)</version>
+ <version>[0.37,)</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jetty</groupId>
@@ -299,7 +299,7 @@
<dependency>
<groupId>de.ids_mannheim.korap</groupId>
<artifactId>Krill</artifactId>
- <version>[0.59.2,)</version>
+ <version>[0.59.3,)</version>
<exclusions>
<exclusion>
<groupId>org.glassfish.jersey.containers</groupId>
diff --git a/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java b/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
index 2d414f9..18a349c 100644
--- a/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
+++ b/core/src/main/java/de/ids_mannheim/korap/service/SearchService.java
@@ -360,13 +360,14 @@
public String retrieveMatchInfo (String corpusId, String docId,
String textId, String matchId, Set<String> foundries,
String username, HttpHeaders headers, Set<String> layers,
- boolean spans, boolean highlights) throws KustvaktException {
+ boolean spans, boolean sentenceExpansion,
+ boolean highlights) throws KustvaktException {
String matchid =
searchKrill.getMatchId(corpusId, docId, textId, matchId);
User user = createUser(username, headers);
Pattern p = determineAvailabilityPattern(user);
-
+
boolean match_only = foundries == null || foundries.isEmpty();
String results;
// try {
@@ -387,7 +388,7 @@
}
results = searchKrill.getMatch(matchid, foundryList, layerList,
- spans, highlights, true, p);
+ spans, highlights, sentenceExpansion, p);
}
else {
results = searchKrill.getMatch(matchid, p);
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java b/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
index 8096c77..27665a2 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/SearchKrill.java
@@ -33,10 +33,6 @@
private static final boolean DEBUG = false;
- // Temporary - shouldn't be here.
- String indexDir = "/data/prep_corpus/index/";
- String i = "/Users/hanl/Projects/prep_corpus";
- String klinux10 = "/vol/work/hanl/indices";
public static KrillIndex index;
/**
diff --git a/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java b/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
index 128a0de..e3967b4 100644
--- a/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
+++ b/core/src/main/java/de/ids_mannheim/korap/web/controller/SearchController.java
@@ -238,7 +238,7 @@
@QueryParam("hls") Boolean highlights) throws KustvaktException {
return retrieveMatchInfo(ctx, headers, locale, corpusId, docId, textId,
- matchId, foundries, layers, spans, highlights);
+ matchId, foundries, layers, spans, "sentence", highlights);
}
@GET
@@ -253,9 +253,15 @@
@QueryParam("foundry") Set<String> foundries,
@QueryParam("layer") Set<String> layers,
@QueryParam("spans") Boolean spans,
+ @QueryParam("expand") String expansion,
// Highlights may also be a list of valid highlight classes
@QueryParam("hls") Boolean highlights) throws KustvaktException {
+ Boolean expandToSentence = true;
+ if (expansion != null && (expansion.equals("false") || expansion.equals("null"))) {
+ expandToSentence = false;
+ }
+
TokenContext tokenContext = (TokenContext) ctx.getUserPrincipal();
scopeService.verifyScope(tokenContext, OAuth2Scope.MATCH_INFO);
spans = spans != null ? spans : false;
@@ -265,7 +271,7 @@
try{
String results = searchService.retrieveMatchInfo(corpusId, docId,
textId, matchId, foundries, tokenContext.getUsername(),
- headers, layers, spans, highlights);
+ headers, layers, spans, expandToSentence, highlights);
return Response.ok(results).build();
}
catch (KustvaktException e) {
diff --git a/full/Changes b/full/Changes
index 49be3dd..f4efc95 100644
--- a/full/Changes
+++ b/full/Changes
@@ -1,6 +1,10 @@
# version 0.63
26/10/2020
- Updated dependency of nimbus-jose-jwt and oauth2-oidc-sdk (diewald)
+29/10/2020
+ - Introduction of a query reference rewrite mechanism (diewald)
+30/10/2020
+ - Added database methods for storing query references (diewald)
# version 0.62.4
24/01/2020
diff --git a/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java b/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
index 3d6b4fa..0aea814 100644
--- a/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
+++ b/full/src/main/java/de/ids_mannheim/korap/constant/VirtualCorpusType.java
@@ -7,6 +7,11 @@
* @author margaretha
*
*/
+/*
+ * TODO (nd):
+ * This should probably be renamed to something like RessourceType,
+ * as QueryReferences will use the same types.
+ */
public enum VirtualCorpusType {
/**
* available for all
diff --git a/full/src/main/java/de/ids_mannheim/korap/dao/QueryReferenceDao.java b/full/src/main/java/de/ids_mannheim/korap/dao/QueryReferenceDao.java
new file mode 100644
index 0000000..2d849b6
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/dao/QueryReferenceDao.java
@@ -0,0 +1,126 @@
+package de.ids_mannheim.korap.dao;
+
+import org.springframework.stereotype.Repository;
+import org.springframework.transaction.annotation.Transactional;
+
+import javax.persistence.EntityManager;
+import javax.persistence.NoResultException;
+import javax.persistence.NonUniqueResultException;
+import javax.persistence.PersistenceContext;
+import javax.persistence.Query;
+import javax.persistence.criteria.CriteriaBuilder;
+import javax.persistence.criteria.CriteriaQuery;
+import javax.persistence.criteria.Join;
+import javax.persistence.criteria.Predicate;
+import javax.persistence.criteria.Root;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+
+import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+import de.ids_mannheim.korap.entity.QueryReference;
+import de.ids_mannheim.korap.entity.QueryReference_;
+
+/**
+ * QueryReferenceDao manages database queries and transactions
+ * regarding query fragments, e.g. retrieving and storing queries
+ * for embedding in more complex queries.
+ *
+ * Based on VirtualCorpusDao.
+ *
+ * @author diewald
+ *
+ */
+
+@Transactional
+@Repository
+public class QueryReferenceDao {
+
+ @PersistenceContext
+ private EntityManager entityManager;
+
+ /**
+ * Create query reference and return ID.
+ * Does not support any access management yet
+ */
+ public int createQuery(
+ String qName,
+ VirtualCorpusType type,
+ // CorpusAccess requiredAccess,
+ String koralQuery,
+ String definition,
+ String description,
+ String status,
+ String createdBy
+ ) throws KustvaktException {
+
+ QueryReference q = new QueryReference();
+ q.setName(qName);
+ q.setType(type);
+ q.setKoralQuery(koralQuery);
+ q.setDefinition(definition);
+ q.setDescription(description);
+ q.setStatus(status);
+ q.setCreatedBy(createdBy);
+
+ // Maybe unused
+ q.setRequiredAccess("");
+
+ entityManager.persist(q);
+ return q.getId();
+ };
+
+
+ /**
+ * Retrieve a single query reference based on its name.
+ */
+ public QueryReference retrieveQueryByName (String qName, String createdBy)
+ throws KustvaktException {
+ ParameterChecker.checkStringValue(createdBy, "createdBy");
+ ParameterChecker.checkStringValue(qName, "q");
+
+ CriteriaBuilder builder = entityManager.getCriteriaBuilder();
+ CriteriaQuery<QueryReference> query =
+ builder.createQuery(QueryReference.class);
+
+ Root<QueryReference> qref = query.from(QueryReference.class);
+
+ Predicate condition = builder.and(
+ builder.equal(qref.get(QueryReference_.createdBy),
+ createdBy),
+ builder.equal(qref.get(QueryReference_.name), qName));
+
+ query.select(qref);
+ query.where(condition);
+
+ Query q = entityManager.createQuery(query);
+ QueryReference qr = null;
+ try {
+ qr = (QueryReference) q.getSingleResult();
+ }
+ catch (NoResultException e) {
+ return null;
+ }
+ catch (NonUniqueResultException e) {
+ String refCode = createdBy + "/" + qName;
+ throw new KustvaktException(StatusCodes.NON_UNIQUE_RESULT_FOUND,
+ "Non unique result found for query: retrieve virtual corpus by name "
+ + refCode,
+ String.valueOf(refCode), e);
+ }
+ return qr;
+ };
+
+ /**
+ * Remove a query reference from the database.
+ */
+ public void deleteQueryReference (QueryReference q)
+ throws KustvaktException {
+ if (!entityManager.contains(q)) {
+ q = entityManager.merge(q);
+ }
+ entityManager.remove(q);
+ }
+};
diff --git a/full/src/main/java/de/ids_mannheim/korap/entity/QueryReference.java b/full/src/main/java/de/ids_mannheim/korap/entity/QueryReference.java
new file mode 100644
index 0000000..2a6ecff
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/entity/QueryReference.java
@@ -0,0 +1,100 @@
+package de.ids_mannheim.korap.entity;
+
+import java.util.List;
+
+import javax.persistence.CascadeType;
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.OneToMany;
+import javax.persistence.Table;
+
+import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * Describes the query reference table.
+ *
+ * It is yet not as complete as the Virtual Corpus implementation,
+ * as it has no mechanism for sharing any query references.
+ *
+ * @author diewald
+ *
+ * @see VirtualCorpus
+ */
+@Setter
+@Getter
+@Entity
+@Table(name = "query_reference")
+public class QueryReference implements Comparable<QueryReference> {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private int id;
+ private String name;
+ @Enumerated(EnumType.STRING)
+ private VirtualCorpusType type; // TODO (nd): This should be named RessourceType
+ private String status;
+ private String description;
+ // @Enumerated(EnumType.STRING)
+ @Column(name = "required_access")
+ private String requiredAccess;
+ //private CorpusAccess requiredAccess;
+ @Column(name = "query")
+ private String koralQuery;
+ private String definition;
+ @Column(name = "created_by")
+ private String createdBy;
+
+ /*
+ @OneToMany(mappedBy = "queryReference", fetch = FetchType.LAZY,
+ cascade = CascadeType.REMOVE)
+ private List<VirtualCorpusAccess> virtualCorpusAccess;
+ */
+
+ @Override
+ public String toString () {
+ return "id=" + id +
+ ", name= " + name +
+ ", type= " + type +
+ ", status= " + status +
+ ", description=" + description +
+ // ", requiredAccess=" + requiredAccess +
+ ", query= " + koralQuery +
+ ", definition= " + definition +
+ ", createdBy= " + createdBy;
+ }
+
+ @Override
+ public int hashCode () {
+ int prime = 37;
+ int result = 1;
+ result = prime * result + id;
+ result = prime * result + name.hashCode();
+ result = prime * result + createdBy.hashCode();
+ return result;
+ }
+
+ @Override
+ public boolean equals (Object obj) {
+ QueryReference q = (QueryReference) obj;
+ return (this.id == q.getId()) ? true : false;
+ }
+
+ @Override
+ public int compareTo (QueryReference o) {
+ if (this.getId() > o.getId()) {
+ return 1;
+ }
+ else if (this.getId() < o.getId()) {
+ return -1;
+ }
+ return 0;
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java b/full/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java
new file mode 100644
index 0000000..c4193ed
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/rewrite/QueryReferenceRewrite.java
@@ -0,0 +1,110 @@
+package de.ids_mannheim.korap.rewrite;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+import de.ids_mannheim.korap.config.KustvaktConfiguration;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.rewrite.KoralNode.RewriteIdentifier;
+import de.ids_mannheim.korap.service.QueryReferenceService;
+import de.ids_mannheim.korap.user.User;
+import de.ids_mannheim.korap.util.StatusCodes;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+/**
+ * Rewrites query reference with the corresponding koral
+ * query describing the actual query fragment.
+ *
+ * Based on VirtualCorpusRewrite.
+ *
+ * @author diewald, margaretha
+ *
+ */
+@Component
+public class QueryReferenceRewrite implements RewriteTask.RewriteQuery {
+
+ @Autowired
+ private KustvaktConfiguration config;
+
+ @Autowired
+ private QueryReferenceService qService;
+
+ @Override
+ public KoralNode rewriteQuery (KoralNode node,
+ KustvaktConfiguration config,
+ User user) throws KustvaktException {
+ if (node.has("query")) {
+ node = node.at("/query");
+ findQueryRef(user.getUsername(), node);
+ }
+ return node;
+ }
+
+ private void findQueryRef (String username, KoralNode koralNode)
+ throws KustvaktException {
+ if (koralNode.has("@type")
+ && koralNode.get("@type").equals("koral:queryRef")) {
+ if (!koralNode.has("ref")) {
+ throw new KustvaktException(
+ StatusCodes.MISSING_QUERY_REFERENCE,
+ "ref is not found"
+ );
+ }
+ else {
+ String queryRefName = koralNode.get("ref");
+ String queryRefOwner = "system";
+ boolean ownerExist = false;
+ if (queryRefName.contains("/")) {
+ String[] names = queryRefName.split("/");
+ if (names.length == 2) {
+ queryRefOwner = names[0];
+ queryRefName = names[1];
+ ownerExist = true;
+ }
+ }
+
+ JsonNode qref = qService.searchQueryByName(
+ username,
+ queryRefName,
+ queryRefOwner);
+
+ rewriteQuery(qref,koralNode);
+ }
+ }
+
+ else if (koralNode.has("operands")) {
+ KoralNode operands = koralNode.at("/operands");
+
+ for (int i = 0; i < operands.size(); i++) {
+ KoralNode operand = operands.get(i);
+ this.findQueryRef(username, operand);
+ operand.buildRewrites();
+ }
+ }
+ }
+
+
+ private void removeOwner (String koralQuery,
+ String queryRefOwner,
+ KoralNode koralNode) throws KustvaktException {
+ JsonNode jsonNode = koralNode.rawNode();
+ String ref = jsonNode.at("/ref").asText();
+ koralNode.remove("ref", new RewriteIdentifier("ref", ref));
+
+ ref = ref.substring(queryRefOwner.length() + 1, ref.length());
+ koralNode.set("ref", ref, new RewriteIdentifier("ref", ref));
+ }
+
+ private void rewriteQuery (JsonNode qref, KoralNode koralNode)
+ throws KustvaktException {
+ JsonNode jsonNode = koralNode.rawNode();
+ koralNode.remove("@type",
+ new RewriteIdentifier("@type", jsonNode.at("/@type").asText()));
+ koralNode.remove("ref",
+ new RewriteIdentifier("ref", jsonNode.at("/ref").asText()));
+ koralNode.setAll((ObjectNode) qref);
+ }
+}
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java b/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java
new file mode 100644
index 0000000..04d7b67
--- /dev/null
+++ b/full/src/main/java/de/ids_mannheim/korap/service/QueryReferenceService.java
@@ -0,0 +1,183 @@
+package de.ids_mannheim.korap.service;
+
+import java.sql.SQLException;
+
+import java.util.regex.Pattern;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.exceptions.StatusCodes;
+import de.ids_mannheim.korap.utils.ParameterChecker;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+import de.ids_mannheim.korap.dao.AdminDao;
+import de.ids_mannheim.korap.dao.QueryReferenceDao;
+
+import de.ids_mannheim.korap.constant.VirtualCorpusType;
+
+import de.ids_mannheim.korap.entity.QueryReference;
+
+
+/**
+ * This service is similar to VirtualCorpusService,
+ * while not as complete.
+ * For the moment, e.g., there is no mechanism supported to
+ * share a query reference with other users or groups.
+ * Only private queries are supported.
+ *
+ * @author diewald
+ */
+@Service
+public class QueryReferenceService {
+
+ public static Pattern qNamePattern = Pattern.compile("[-\\w.]+");
+
+ @Autowired
+ private AdminDao adminDao;
+
+ @Autowired
+ private QueryReferenceDao qDao;
+
+
+ /**
+ * Serch for a query by its name.
+ */
+ public JsonNode searchQueryByName (String username,
+ String qName,
+ String createdBy) throws KustvaktException {
+
+ QueryReference qr = qDao.retrieveQueryByName(qName, createdBy);
+ if (qr == null) {
+ String refCode = createdBy + "/" + qName;
+ throw new KustvaktException(
+ StatusCodes.NO_RESOURCE_FOUND,
+ "Query reference " + refCode + " is not found.",
+ String.valueOf(refCode));
+ }
+
+ // TODO:
+ // checkVCAcess(q, username);
+ return JsonUtils.readTree(qr.getKoralQuery());
+ };
+
+
+ /**
+ * Store a query in the database.
+ */
+ public void storeQuery (String qJson,
+ String qName,
+ String createdBy)
+ throws KustvaktException {
+
+ // TODO:
+ // This doesn't support a whole bunch of applicable
+ // information from VCs, like 'definition', 'description',
+ // 'status' etc.
+
+ storeQuery(
+ qJson,
+ qName,
+ "",
+ createdBy
+ );
+ }
+
+
+ /**
+ * Store a query in the database.
+ */
+ public void storeQuery (String qJson,
+ String qName,
+ String desc,
+ String username)
+ throws KustvaktException {
+ ParameterChecker.checkStringValue(qJson, "q");
+ ParameterChecker.checkNameValue(qName, "qName");
+
+ if (!qNamePattern.matcher(qName).matches()) {
+ throw new KustvaktException(
+ StatusCodes.INVALID_ARGUMENT,
+ "Query name must only contain letters, numbers, "
+ + "underscores, hypens and spaces",
+ qName);
+ }
+
+ if (username.equals("system") && !adminDao.isAdmin(username)) {
+ throw new KustvaktException(
+ StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username);
+ };
+
+ int qId = 0;
+ try {
+ qId = qDao.createQuery(
+ qName,
+ VirtualCorpusType.PRIVATE,
+ qJson,
+ "", // TODO: definition,
+ desc,
+ "", // TODO: status,
+ username);
+
+ }
+ catch (Exception e) {
+ Throwable cause = e;
+ Throwable lastCause = null;
+ while ((cause = cause.getCause()) != null
+ && !cause.equals(lastCause)) {
+ if (cause instanceof SQLException) {
+ break;
+ }
+ lastCause = cause;
+ }
+ throw new KustvaktException(
+ StatusCodes.DB_INSERT_FAILED,
+ cause.getMessage()
+ );
+ };
+
+ // TODO:
+ // This doesn't publish the query, if it is meant to be published
+ // based on its type.
+ };
+
+
+ /**
+ * Only admin and the owner of the virtual corpus are allowed to
+ * delete a virtual corpus.
+ */
+ public void deleteQueryByName (
+ String username,
+ String qName,
+ String createdBy
+ ) throws KustvaktException {
+
+ QueryReference q = qDao.retrieveQueryByName(qName, createdBy);
+
+ if (q == null) {
+ String refCode = createdBy + "/" + qName;
+ throw new KustvaktException(
+ StatusCodes.NO_RESOURCE_FOUND,
+ "Query reference " + refCode + " is not found.",
+ String.valueOf(refCode));
+ }
+
+ // Check if the user created the qr or is admin
+ else if (q.getCreatedBy().equals(username)
+ || adminDao.isAdmin(username)) {
+ // TODO:
+ // Here checks for publication status is missing
+ qDao.deleteQueryReference(q);
+ }
+
+ else {
+ throw new KustvaktException(
+ StatusCodes.AUTHORIZATION_FAILED,
+ "Unauthorized operation for user: " + username, username);
+ };
+ };
+};
diff --git a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
index 4532991..1163573 100644
--- a/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
+++ b/full/src/main/java/de/ids_mannheim/korap/service/VirtualCorpusService.java
@@ -331,7 +331,7 @@
if (!vcNamePattern.matcher(vcName).matches()) {
throw new KustvaktException(StatusCodes.INVALID_ARGUMENT,
- "Virtual corpus name must only contains letters, numbers, "
+ "Virtual corpus name must only contain letters, numbers, "
+ "underscores, hypens and spaces",
vcName);
}
diff --git a/full/src/main/resources/db/mysql/V1.7__query_references.sql b/full/src/main/resources/db/mysql/V1.7__query_references.sql
new file mode 100644
index 0000000..6d636da
--- /dev/null
+++ b/full/src/main/resources/db/mysql/V1.7__query_references.sql
@@ -0,0 +1,14 @@
+CREATE TABLE IF NOT EXISTS query_refernce (
+ id INTEGER PRIMARY KEY AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ type VARCHAR(100) NOT NULL,
+ required_access VARCHAR(100) NOT NULL,
+ created_by VARCHAR(100) NOT NULL,
+ description VARCHAR(255) DEFAULT NULL,
+ status VARCHAR(100) DEFAULT NULL,
+ query TEXT NOT NULL,
+ definition VARCHAR(255) DEFAULT NULL,
+ UNIQUE INDEX unique_index (name,created_by),
+ INDEX owner_index (created_by),
+ INDEX type_index (type)
+);
diff --git a/full/src/main/resources/db/sqlite/V1.7__query_references.sql b/full/src/main/resources/db/sqlite/V1.7__query_references.sql
new file mode 100644
index 0000000..0ebdbc1
--- /dev/null
+++ b/full/src/main/resources/db/sqlite/V1.7__query_references.sql
@@ -0,0 +1,16 @@
+CREATE TABLE IF NOT EXISTS query_reference (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name VARCHAR(255) NOT NULL,
+ type VARCHAR(100) NOT NULL,
+ required_access VARCHAR(100) NOT NULL,
+ created_by VARCHAR(100) NOT NULL,
+ description VARCHAR(255) DEFAULT NULL,
+ status VARCHAR(100) DEFAULT NULL,
+ query TEXT NOT NULL,
+ definition VARCHAR(255) DEFAULT NULL
+);
+
+CREATE INDEX query_reference_owner_index ON query_reference(created_by);
+CREATE INDEX query_reference_type_index ON query_reference(type);
+CREATE UNIQUE INDEX query_reference_unique_name
+ ON query_reference(name,created_by);
diff --git a/full/src/main/resources/db/test/V3.7__insert_query_references.sql b/full/src/main/resources/db/test/V3.7__insert_query_references.sql
new file mode 100644
index 0000000..e13b13b
--- /dev/null
+++ b/full/src/main/resources/db/test/V3.7__insert_query_references.sql
@@ -0,0 +1,4 @@
+-- query references
+INSERT INTO query_reference(name, type, required_access, created_by, description, status, query)
+ VALUES ("dory-q", "PRIVATE", "FREE", "dory", "test query", "experimental",
+ '{ "@type": "koral:token" }');
diff --git a/full/src/main/resources/default-config.xml b/full/src/main/resources/default-config.xml
index bd25718..c1da94b 100644
--- a/full/src/main/resources/default-config.xml
+++ b/full/src/main/resources/default-config.xml
@@ -220,12 +220,14 @@
<bean id="foundryRewrite" class="de.ids_mannheim.korap.rewrite.FoundryRewrite"/>
<bean id="collectionRewrite" class="de.ids_mannheim.korap.rewrite.CollectionRewrite"/>
<bean id="virtualCorpusRewrite" class="de.ids_mannheim.korap.rewrite.VirtualCorpusRewrite"/>
+ <bean id="queryReferenceRewrite" class="de.ids_mannheim.korap.rewrite.QueryReferenceRewrite"/>
<util:list id="rewriteTasks"
value-type="de.ids_mannheim.korap.rewrite.RewriteTask">
<ref bean="foundryRewrite" />
<ref bean="collectionRewrite" />
<ref bean="virtualCorpusRewrite" />
+ <ref bean="queryReferenceRewrite" />
</util:list>
<bean id="rewriteHandler" class="de.ids_mannheim.korap.rewrite.RewriteHandler">
@@ -389,4 +391,4 @@
</props>
</constructor-arg>
</bean>
-</beans>
\ No newline at end of file
+</beans>
diff --git a/full/src/test/java/de/ids_mannheim/korap/rewrite/QueryRewriteTest.java b/full/src/test/java/de/ids_mannheim/korap/rewrite/QueryRewriteTest.java
new file mode 100644
index 0000000..fd2b0ff
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/rewrite/QueryRewriteTest.java
@@ -0,0 +1,59 @@
+package de.ids_mannheim.korap.rewrite;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.net.HttpHeaders;
+import com.sun.jersey.api.client.ClientResponse;
+
+import de.ids_mannheim.korap.authentication.http.HttpAuthorizationHandler;
+import de.ids_mannheim.korap.config.Attributes;
+import de.ids_mannheim.korap.config.SpringJerseyTest;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.util.QueryException;
+import de.ids_mannheim.korap.utils.JsonUtils;
+
+/**
+ * @author diewald
+ *
+ */
+public class QueryRewriteTest extends SpringJerseyTest {
+
+ @Test
+ public void testRewriteRefNotFound ()
+ throws KustvaktException, Exception {
+
+ ClientResponse response = resource().path(API_VERSION).path("search")
+ .queryParam("q", "[orth=der]{%23examplequery} Baum")
+ .queryParam("ql", "poliqarp")
+ .get(ClientResponse.class);
+
+ String ent = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(ent);
+ assertEquals(node.at("/errors/0/1").asText(), "Query reference system/examplequery is not found.");
+ }
+
+ @Test
+ public void testRewriteRefRewrite ()
+ throws KustvaktException, Exception {
+
+ // Added in the database migration sql for tests
+ ClientResponse response = resource().path(API_VERSION).path("search")
+ .queryParam("q", "[orth=der]{%23dory/dory-q} Baum")
+ .queryParam("ql", "poliqarp")
+ .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+ .createBasicAuthorizationHeaderValue("dory", "pass"))
+ .get(ClientResponse.class);
+
+ String ent = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(ent);
+ assertEquals("koral:token", node.at("/query/operands/1/@type").asText());
+ assertEquals("@type(koral:queryRef)",
+ node.at("/query/operands/1/rewrites/0/scope").asText());
+ }
+}
diff --git a/full/src/test/java/de/ids_mannheim/korap/service/QueryReferenceServiceTest.java b/full/src/test/java/de/ids_mannheim/korap/service/QueryReferenceServiceTest.java
new file mode 100644
index 0000000..93d1133
--- /dev/null
+++ b/full/src/test/java/de/ids_mannheim/korap/service/QueryReferenceServiceTest.java
@@ -0,0 +1,53 @@
+package de.ids_mannheim.korap.service;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.util.List;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import de.ids_mannheim.korap.constant.UserGroupStatus;
+import de.ids_mannheim.korap.constant.VirtualCorpusType;
+import de.ids_mannheim.korap.dto.VirtualCorpusAccessDto;
+import de.ids_mannheim.korap.dto.VirtualCorpusDto;
+import de.ids_mannheim.korap.entity.UserGroup;
+import de.ids_mannheim.korap.entity.VirtualCorpus;
+import de.ids_mannheim.korap.exceptions.KustvaktException;
+import de.ids_mannheim.korap.web.input.VirtualCorpusJson;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@ContextConfiguration("classpath:test-config.xml")
+public class QueryReferenceServiceTest {
+
+ @Autowired
+ private QueryReferenceService qService;
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+ @Test
+ public void createQuery () throws KustvaktException {
+ qService.storeQuery("{\"@type\":\"koral:token\"}", "new-query", "me" );
+ JsonNode json = qService.searchQueryByName("me", "new-query", "me");
+ assertEquals("koral:token", json.at("/@type").asText());
+ qService.deleteQueryByName("me", "new-query", "me");
+ };
+
+ @Test
+ public void testCreateNonUniqueQuery () throws KustvaktException {
+ qService.storeQuery("{\"@type\":\"koral:token\"}", "new-query", "me" );
+ thrown.expect(KustvaktException.class);
+ qService.storeQuery("{\"@type\":\"koral:token\"}", "new-query", "me" );
+ qService.deleteQueryByName("me", "new-query", "me");
+ };
+};
diff --git a/full/src/test/resources/test-config.xml b/full/src/test/resources/test-config.xml
index 82b314e..b9f5e07 100644
--- a/full/src/test/resources/test-config.xml
+++ b/full/src/test/resources/test-config.xml
@@ -203,6 +203,7 @@
<bean id="collectionCleanRewrite" class="de.ids_mannheim.korap.rewrite.CollectionCleanRewrite"/>
<bean id="virtualCorpusRewrite" class="de.ids_mannheim.korap.rewrite.VirtualCorpusRewrite"/>
<bean id="collectionConstraint" class="de.ids_mannheim.korap.rewrite.CollectionConstraint"/>
+ <bean id="queryReferenceRewrite" class="de.ids_mannheim.korap.rewrite.QueryReferenceRewrite"/>
<util:list id="rewriteTasks"
value-type="de.ids_mannheim.korap.rewrite.RewriteTask">
@@ -211,6 +212,7 @@
<ref bean="foundryRewrite" />
<ref bean="collectionRewrite" />
<ref bean="virtualCorpusRewrite" />
+ <ref bean="queryReferenceRewrite" />
</util:list>
<bean id="rewriteHandler" class="de.ids_mannheim.korap.rewrite.RewriteHandler">
@@ -375,4 +377,4 @@
</props>
</constructor-arg>
</bean>
-</beans>
\ No newline at end of file
+</beans>
diff --git a/lite/pom.xml b/lite/pom.xml
index d80b3e4..b40e638 100644
--- a/lite/pom.xml
+++ b/lite/pom.xml
@@ -129,6 +129,18 @@
</execution>
</executions>
</plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ <version>2.7</version>
+ <configuration>
+ <formats>
+ <format>html</format>
+ <format>xml</format>
+ </formats>
+ <check/>
+ </configuration>
+ </plugin>
</plugins>
</build>
@@ -160,5 +172,18 @@
<version>1.18.16</version>
<scope>provided</scope>
</dependency>
+ <!-- https://mvnrepository.com/artifact/org.codehaus.mojo/cobertura-maven-plugin -->
+ <dependency>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>cobertura-maven-plugin</artifactId>
+ <version>2.7</version>
+ <scope>test</scope>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/backport-util-concurrent/backport-util-concurrent -->
+ <dependency>
+ <groupId>backport-util-concurrent</groupId>
+ <artifactId>backport-util-concurrent</artifactId>
+ <version>3.1</version>
+ </dependency>
</dependencies>
</project>
diff --git a/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteSearchControllerTest.java b/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteSearchControllerTest.java
index 4c2dd28..479ea57 100644
--- a/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteSearchControllerTest.java
+++ b/lite/src/test/java/de/ids_mannheim/korap/web/service/LiteSearchControllerTest.java
@@ -223,6 +223,27 @@
};
@Test
+ public void testMatchInfoWithoutExtension () throws KustvaktException {
+ ClientResponse response = resource().path(API_VERSION)
+ .path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42")
+ .queryParam("foundry", "-").queryParam("spans", "false")
+ .queryParam("expand","false")
+ .get(ClientResponse.class);
+ assertEquals(ClientResponse.Status.OK.getStatusCode(),
+ response.getStatus());
+ String ent = response.getEntity(String.class);
+ JsonNode node = JsonUtils.readTree(ent);
+ assertNotNull(node);
+ assertEquals("GOE/AGA/01784", node.at("/textSigle").asText());
+ assertEquals("match-GOE/AGA/01784-p36-46(5)37-45(2)38-42",
+ node.at("/matchID").asText());
+ assertEquals("<span class=\"context-left\"><span class=\"more\"></span></span><span class=\"match\"><mark>gefüttert; der Ort ist sehr zerschossen; dann über die Schiffbrücke</mark></span><span class=\"context-right\"><span class=\"more\"></span></span>",
+ node.at("/snippet").asText());
+ assertEquals("Belagerung von Mainz", node.at("/title").asText());
+ };
+
+
+ @Test
public void testMatchInfoGetWithHighlights () throws KustvaktException {
ClientResponse response = resource().path(API_VERSION)
.path("corpus/GOE/AGA/01784/p36-46(5)37-45(2)38-42/matchInfo")