Improve AvailabilityRewrite and implement operation:override (#781)

Change-Id: Ibceecb26109796addc06bf15a47a48a01d566084
diff --git a/Changes b/Changes
index 545f29b..dd162f0 100644
--- a/Changes
+++ b/Changes
@@ -12,6 +12,7 @@
 - Fixed availability regex for all access in the config (important to correctly 
   determine required access of stored VC)
 - Rename CollectionRewrite to AvailabilityRewrite  
+- Improve AvailabilityRewrite and implement operation:override (#781) 
   
 
 # version 0.75
diff --git a/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java b/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
index 75c6162..d606c76 100644
--- a/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
+++ b/src/main/java/de/ids_mannheim/korap/config/FullConfiguration.java
@@ -4,6 +4,7 @@
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Properties;
 import java.util.Set;
@@ -15,6 +16,7 @@
 
 import de.ids_mannheim.korap.constant.AuthenticationMethod;
 import de.ids_mannheim.korap.interfaces.EncryptionIface;
+import de.ids_mannheim.korap.user.User.CorpusAccess;
 import de.ids_mannheim.korap.utils.TimeUtils;
 
 /**
@@ -33,11 +35,15 @@
     private String freeOnlyRegex;
     private String publicOnlyRegex;
     private String allOnlyRegex;
+    
+    private String freeAvailabilityQuery;
+    private String publicAvailabilityQuery;
+    private String allAvailabilityQuery;
 
     private List<String> freeRegexList;
     private List<String> publicRegexList;
     private List<String> allRegexList;
-
+    
     private String authenticationScheme;
 
     private EncryptionIface.Encryption secureHashAlgorithm;
@@ -71,13 +77,9 @@
 
     @Override
     public void load (Properties properties) throws Exception {
-
         super.load(properties);
-        // EM: regex used for storing vc
+        
         setLicenseRegex(properties);
-
-        // EM: pattern for matching availability in Krill matches
-        setLicensePatterns(properties);
         ldapConfig = properties.getProperty("ldap.config");
 
         setSecurityConfiguration(properties);
@@ -141,38 +143,54 @@
                 .getProperty("oauth2.refresh.token.long.expiry", "365D")));
     }
 
-    private void setLicensePatterns (Properties properties) {
-        setFreeLicensePattern(compilePattern(getFreeOnlyRegex()));
-        setPublicLicensePattern(compilePattern(
-                getFreeOnlyRegex() + "|" + getPublicOnlyRegex()));
-        setAllLicensePattern(compilePattern(getFreeOnlyRegex() + "|"
-                + getPublicOnlyRegex() + "|" + getAllOnlyRegex()));
-    }
-
     private void setLicenseRegex (Properties properties) {
-        setFreeOnlyRegex(properties.getProperty("availability.regex.free", ""));
+        // EM: regex used for storing vc
+        freeOnlyRegex = properties.getProperty("availability.regex.free", "")
+                .trim();
         freeRegexList = splitAndAddToList(getFreeOnlyRegex());
+        freeAvailabilityQuery = createCorpusQuery(freeRegexList);
 
-        setPublicOnlyRegex(
-                properties.getProperty("availability.regex.public", ""));
-        publicRegexList = splitAndAddToList(getPublicOnlyRegex());
+        // EM: pattern for matching availability in Krill matches
+        freeLicensePattern = compilePattern(getFreeOnlyRegex());
+        
+        publicOnlyRegex = properties
+                .getProperty("availability.regex.public", "").trim();
+        publicRegexList = new ArrayList<>();
+        publicRegexList.addAll(freeRegexList);
+        publicRegexList.addAll(splitAndAddToList(getPublicOnlyRegex()));
+        publicAvailabilityQuery = createCorpusQuery(publicRegexList);
+        
+        publicLicensePattern = compilePattern(
+                getFreeOnlyRegex() + "|" + getPublicOnlyRegex());
 
-        setAllOnlyRegex(properties.getProperty("availability.regex.all", ""));
-        allRegexList = splitAndAddToList(getAllOnlyRegex());
+        allOnlyRegex = properties.getProperty("availability.regex.all", "")
+                .trim();
+        allRegexList = new ArrayList<>();
+        allRegexList.addAll(publicRegexList);
+        allRegexList.addAll(splitAndAddToList(getAllOnlyRegex()));
+        allAvailabilityQuery = createCorpusQuery(allRegexList);
+
+        allLicensePattern = compilePattern(getFreeOnlyRegex() + "|"
+                + getPublicOnlyRegex() + "|" + getAllOnlyRegex());
+        
     }
+    
+    private String createCorpusQuery (List<String> availabilitySet) {
+		String availabilityQuery = "";
+		for (String a : availabilitySet) {
+			availabilityQuery += "availability=/" + a + "/|";
+		}
+		return availabilityQuery.substring(0, availabilityQuery.length() - 1);
+	}
 
     private List<String> splitAndAddToList (String regex) {
         List<String> list;
         if (regex.contains("|")) {
             String[] regexes = regex.split("\\|");
-            list = new ArrayList<>(regexes.length);
-            for (String s : regexes) {
-                list.add(s.trim());
-            }
+            list = Arrays.asList(regexes);
         }
         else {
-            list = new ArrayList<>(1);
-            list.add(regex);
+            list = Arrays.asList(regex);
         }
         return list;
     }
@@ -270,7 +288,31 @@
         this.allOnlyRegex = allOnlyRegex;
     }
 
-    public EncryptionIface.Encryption getSecureHashAlgorithm () {
+    public String getFreeAvailabilityQuery () {
+		return freeAvailabilityQuery;
+	}
+
+	public void setFreeAvailabilityQuery (String freeAvailabilityQuery) {
+		this.freeAvailabilityQuery = freeAvailabilityQuery;
+	}
+
+	public String getPublicAvailabilityQuery () {
+		return publicAvailabilityQuery;
+	}
+
+	public void setPublicAvailabilityQuery (String publicAvailabilityQuery) {
+		this.publicAvailabilityQuery = publicAvailabilityQuery;
+	}
+
+	public String getAllAvailabilityQuery () {
+		return allAvailabilityQuery;
+	}
+
+	public void setAllAvailabilityQuery (String allAvailabilityQuery) {
+		this.allAvailabilityQuery = allAvailabilityQuery;
+	}
+
+	public EncryptionIface.Encryption getSecureHashAlgorithm () {
         return secureHashAlgorithm;
     }
 
diff --git a/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java b/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java
index 75e9094..e4306eb 100644
--- a/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java
+++ b/src/main/java/de/ids_mannheim/korap/rewrite/AvailabilityRewrite.java
@@ -1,7 +1,6 @@
 package de.ids_mannheim.korap.rewrite;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 import org.apache.logging.log4j.LogManager;
@@ -47,23 +46,13 @@
 
     public static Logger jlog = LogManager.getLogger(AvailabilityRewrite.class);
 
-    public static boolean DEBUG = false;
-
     public AvailabilityRewrite () {
         super();
     }
 
     private List<String> checkAvailability (JsonNode node,
-            List<String> originalAvailabilities,
-            List<String> updatedAvailabilities, boolean isOperationOr) {
-        if (DEBUG) {
-            try {
-                jlog.debug(JsonUtils.toJSON(node));
-            }
-            catch (KustvaktException e) {
-                e.printStackTrace();
-            }
-        }
+    		List<String> availabilityRules,
+    		List<String> actualAvailabilities, boolean isOperationOr) {
 
         if (node.has("operands")) {
             ArrayList<JsonNode> operands = Lists
@@ -72,10 +61,10 @@
             if (node.at("/operation").asText()
                     .equals(KoralOperation.AND.toString())) {
                 for (int i = 0; i < operands.size(); i++) {
-                    updatedAvailabilities = checkAvailability(operands.get(i),
-                            originalAvailabilities, updatedAvailabilities,
+                    actualAvailabilities = checkAvailability(operands.get(i),
+                            availabilityRules, actualAvailabilities,
                             false);
-                    if (updatedAvailabilities.isEmpty())
+                    if (actualAvailabilities.isEmpty())
                         break;
                 }
             }
@@ -86,14 +75,14 @@
                             .equals("availability")) {
                         jlog.debug("RESET availabilities 1, key="
                                 + node.at("/key").asText());
-                        updatedAvailabilities.clear();
-                        updatedAvailabilities.addAll(originalAvailabilities);
+                        actualAvailabilities.clear();
+                        actualAvailabilities.addAll(availabilityRules);
                         break;
                     }
                     else {
-                        updatedAvailabilities = checkAvailability(
-                                operands.get(i), originalAvailabilities,
-                                updatedAvailabilities, true);
+                        actualAvailabilities = checkAvailability(
+                                operands.get(i), availabilityRules,
+                                actualAvailabilities, true);
                     }
                 }
             }
@@ -103,126 +92,112 @@
             String queryAvailability = node.at("/value").asText();
             String matchOp = node.at("/match").asText();
 
-            if (originalAvailabilities.contains(queryAvailability)
+            if (availabilityRules.contains(queryAvailability)
                     && matchOp.equals(KoralMatchOperator.EQUALS.toString())) {
-                if (DEBUG) {
-                    jlog.debug("REMOVE " + queryAvailability);
-                }
-                updatedAvailabilities.remove(queryAvailability);
+                actualAvailabilities.remove(queryAvailability);
             }
             else if (isOperationOr) {
-                if (DEBUG) {
-                    jlog.debug("RESET availabilities 2");
-                }
-                updatedAvailabilities.clear();
-                updatedAvailabilities.addAll(originalAvailabilities);
-                return updatedAvailabilities;
+                actualAvailabilities.clear();
+                actualAvailabilities.addAll(availabilityRules);
+                return actualAvailabilities;
             }
         }
-        return updatedAvailabilities;
+        return actualAvailabilities;
     }
 
     @Override
-    public KoralNode rewriteQuery (KoralNode node, KustvaktConfiguration config,
+    public KoralNode rewriteQuery (KoralNode koralNode, KustvaktConfiguration config,
             User user) throws KustvaktException {
-        JsonNode jsonNode = node.rawNode();
+        JsonNode jsonNode = koralNode.rawNode();
 
         FullConfiguration fullConfig = (FullConfiguration) config;
+        CorpusAccess corpusAccess = user.getCorpusAccess();
+		List<String> availabilityRules = getAvailabilityRules(corpusAccess,
+				fullConfig);
 
-        List<String> userAvailabilities = new ArrayList<String>();
-        switch (user.getCorpusAccess()) {
-            case PUB:
-                userAvailabilities.addAll(fullConfig.getFreeRegexList());
-                userAvailabilities.addAll(fullConfig.getPublicRegexList());
-                break;
-            case ALL:
-                userAvailabilities.addAll(fullConfig.getFreeRegexList());
-                userAvailabilities.addAll(fullConfig.getPublicRegexList());
-                userAvailabilities.addAll(fullConfig.getAllRegexList());
-                break;
-            case FREE:
-                userAvailabilities.addAll(fullConfig.getFreeRegexList());
-                break;
-        }
-
-        KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
-        RewriteIdentifier identifier = new KoralNode.RewriteIdentifier(
-                Attributes.AVAILABILITY, user.getCorpusAccess().name());
-        JsonNode rewrittenNode;
-
+        String availabilityQuery = getCorpusQuery(corpusAccess, fullConfig);
+        
         if (jsonNode.has("collection")) {
-            List<String> avalabilityCopy = new ArrayList<String>(
-                    userAvailabilities.size());
-            avalabilityCopy.addAll(userAvailabilities);
-            if (DEBUG) {
-                jlog.debug("Availabilities: "
-                        + Arrays.toString(userAvailabilities.toArray()));
-            }
+            if (jsonNode.toString().contains("availability")) {
+            	List<String> actualAvalability = new ArrayList<>();
+                actualAvalability.addAll(availabilityRules);
 
-            userAvailabilities = checkAvailability(jsonNode.at("/collection"),
-                    avalabilityCopy, userAvailabilities, false);
-            if (!userAvailabilities.isEmpty()) {
-                builder.with(buildAvailability(avalabilityCopy));
-                if (DEBUG) {
-                    jlog.debug("corpus query: " + builder.toString());
+                actualAvalability = checkAvailability(jsonNode.at("/collection"),
+                		availabilityRules, actualAvalability, false);
+                if (!actualAvalability.isEmpty()) {
+                	createOperationAnd(availabilityQuery, jsonNode,
+    						corpusAccess.name(), koralNode);
+                	
+//                    builder.with(availabilityQuery);
+//                    builder.setBaseQuery(builder.toJSON());
+//                    rewrittenNode = builder.mergeWith(jsonNode).at("/collection");
+//                    koralNode.set("collection", rewrittenNode, identifier);
                 }
-                builder.setBaseQuery(builder.toJSON());
-                rewrittenNode = builder.mergeWith(jsonNode).at("/collection");
-                node.set("collection", rewrittenNode, identifier);
-            }
+			}
+			else {
+				createOperationAnd(availabilityQuery, jsonNode,
+						corpusAccess.name(), koralNode);
+			}
         }
-        else {
-            builder.with(buildAvailability(userAvailabilities));
-            if (DEBUG) {
-                jlog.debug("corpus query: " + builder.toString());
-            }
-            rewrittenNode = JsonUtils.readTree(builder.toJSON())
-                    .at("/collection");
-            node.set("collection", rewrittenNode, identifier);
-        }
+		else {
+			KoralCollectionQueryBuilder builder = 
+					new KoralCollectionQueryBuilder();
+			builder.with(availabilityQuery);
+			JsonNode rewrittenNode = JsonUtils.readTree(builder.toJSON())
+					.at("/collection");
+			RewriteIdentifier identifier = new KoralNode.RewriteIdentifier(
+					Attributes.AVAILABILITY, corpusAccess.name());
+			koralNode.set("collection", rewrittenNode, identifier);
+		}
 
-        node = node.at("/collection");
-        if (DEBUG) {
-            jlog.debug("REWRITES: " + node.toString());
-        }
-
-        return node;
+        koralNode = koralNode.at("/collection");
+        return koralNode;
     }
+    
+    private void createOperationAnd (String availabilityQuery,
+			JsonNode jsonNode, String corpusAccess, KoralNode node)
+			throws KustvaktException {
 
-    private String buildAvailability (List<String> userAvailabilities) {
-        StringBuilder sb = new StringBuilder();
-        for (int i = 0; i < userAvailabilities.size(); i++) {
-            parseAvailability(sb, userAvailabilities.get(i), "|");
-        }
-        String availabilities = sb.toString();
-        return availabilities.substring(0, availabilities.length() - 3);
-    }
+		KoralCollectionQueryBuilder availabilityBuilder = 
+				new KoralCollectionQueryBuilder();
+		availabilityBuilder.with(availabilityQuery);
+		JsonNode availabilityNode = JsonUtils
+				.readTree(availabilityBuilder.toJSON());
 
-    private void parseAvailability (StringBuilder sb, String availability,
-            String operator) {
-        String uaArr[] = null;
-        if (availability.contains("|")) {
-            uaArr = availability.split("\\|");
-            for (int j = 0; j < uaArr.length; j++) {
-                parseAvailability(sb, uaArr[j].trim(), "|");
-            }
-        }
-        // EM: not supported
-        //        else if (availability.contains("&")){
-        //            uaArr = availability.split("&");
-        //            for (int j=0; j < uaArr.length -1; j++){
-        //                parseAvailability(sb, uaArr[j], "&");
-        //            }
-        //            parseAvailability(sb, uaArr[uaArr.length-1], "|");
-        //        } 
-        else {
-            sb.append("availability=/");
-            sb.append(availability);
-            sb.append("/ ");
-            sb.append(operator);
-            sb.append(" ");
-        }
+		String source = jsonNode.at("/collection").toString();
+		JsonNode sourceNode = JsonUtils.readTree(source);
 
-    }
+		KoralCollectionQueryBuilder builder = new KoralCollectionQueryBuilder();
+		// Base query must contains collection
+		builder.setBaseQuery(availabilityNode);
+		JsonNode rewrittenNode = builder.mergeWith(jsonNode).at("/collection");
+		RewriteIdentifier identifier = new KoralNode.RewriteIdentifier(null,
+				corpusAccess, sourceNode);
+		node.replace("collection", rewrittenNode, identifier);
+	}
+    
+    private List<String> getAvailabilityRules (CorpusAccess access,
+			FullConfiguration fullConfig) {
+		switch (access) {
+			case PUB:
+				return fullConfig.getPublicRegexList();
+			case ALL:
+				return fullConfig.getAllRegexList();
+			default: // free
+				return fullConfig.getFreeRegexList();
+		}
+	}
 
+	private String getCorpusQuery (CorpusAccess access,
+			FullConfiguration fullConfig) {
+		switch (access) {
+			case PUB:
+				return fullConfig.getPublicAvailabilityQuery();
+			case ALL:
+				return fullConfig.getAllAvailabilityQuery();
+			default: // free
+				return fullConfig.getFreeAvailabilityQuery();
+		}
+
+	}
 }
diff --git a/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java b/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java
index ef7393f..d893540 100644
--- a/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java
+++ b/src/test/java/de/ids_mannheim/korap/rewrite/AvailabilityRewriteTest.java
@@ -211,8 +211,8 @@
                 "textClass");
         assertEquals(node.at("/collection/rewrites/0/@type").asText(),
                 "koral:rewrite");
-        assertEquals(node.at("/collection/rewrites/0/scope").asText(),
-                "availability(FREE)");
+//        assertEquals(node.at("/collection/rewrites/0/scope").asText(),
+//                "availability(FREE)");
     }
 
     @Test
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java
index 8cf0b2b..18dd10d 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/AvailabilityTest.java
@@ -25,102 +25,59 @@
     @Autowired
     public FullConfiguration config;    
 
-    private void checkAndFree (String json) throws KustvaktException {
-        JsonNode node = JsonUtils.readTree(json);
-        assertEquals(node.at("/collection/operands/0/key").asText(),
-                "availability");
-        assertEquals(node.at("/collection/operands/0/value").asText(),
-                "CC.*");
-        assertEquals(node.at("/collection/rewrites/0/operation").asText(),
-                "operation:injection");
-        assertEquals(node.at("/collection/rewrites/0/scope").asText(),
-                "availability(FREE)");
-    }
+	private void checkAndFree (String json, JsonNode source)
+			throws KustvaktException {
+		JsonNode node = JsonUtils.readTree(json).at("/collection");
+		assertEquals("availability", node.at("/operands/0/key").asText());
+		assertEquals("CC.*", node.at("/operands/0/value").asText());
+		assertEquals("operation:override",
+				node.at("/rewrites/0/operation").asText());
+		assertEquals(source, node.at("/rewrites/0/source"));
+		//        assertEquals(node.at("/collection/rewrites/0/scope").asText(),
+		//                "availability(FREE)");
+	}
 
-    private void checkAndPublic (String json) throws KustvaktException {
-        JsonNode node = JsonUtils.readTree(json);
-        assertNotNull(node);
-        assertEquals(node.at("/collection/operation").asText(),
-                "operation:and");
-        assertEquals(
-                node.at("/collection/operands/0/operands/0/match").asText(),
-                "match:eq");
-        assertEquals(node.at("/collection/operands/0/operands/0/type").asText(),
-                "type:regex");
-        assertEquals(node.at("/collection/operands/0/operands/0/key").asText(),
-                "availability");
-        assertEquals(
-                node.at("/collection/operands/0/operands/0/value").asText(),
-                "CC.*");
-        assertEquals(
-                node.at("/collection/operands/0/operands/1/operands/0/match")
-                        .asText(),
-                "match:eq");
-        assertEquals(
-                node.at("/collection/operands/0/operands/1/operands/0/value")
-                        .asText(),
-                "ACA.*");
-        assertEquals(
-                node.at("/collection/operands/0/operands/1/operands/1/match")
-                        .asText(),
-                "match:eq");
-        assertEquals(
-                node.at("/collection/operands/0/operands/1/operands/1/value")
-                        .asText(),
-                "QAO-NC");
-        assertEquals(node.at("/collection/rewrites/0/operation").asText(),
-                "operation:injection");
-        assertEquals(node.at("/collection/rewrites/0/scope").asText(),
-                "availability(PUB)");
-    }
+	private void checkAndPublic (String json, JsonNode source)
+			throws KustvaktException {
+		JsonNode node = JsonUtils.readTree(json).at("/collection");
+		assertNotNull(node);
+		System.out.println(node.at("/rewrites/0").toPrettyString());
+		assertEquals("operation:and", node.at("/operation").asText());
+		assertEquals("koral:rewrite", node.at("/rewrites/0/@type").asText());
+		assertEquals("Kustvakt", node.at("/rewrites/0/origin").asText());
+		assertEquals("operation:override", node.at("/rewrites/0/operation").asText());
+		assertEquals(source, node.at("/rewrites/0/source"));
+		
+		node = node.at("/operands/0");
+		assertEquals("match:eq", node.at("/operands/0/match").asText());
+		assertEquals("type:regex", node.at("/operands/0/type").asText());
+		assertEquals("availability", node.at("/operands/0/key").asText());
+		assertEquals("CC.*", node.at("/operands/0/value").asText());
+		assertEquals("match:eq",
+				node.at("/operands/1/operands/0/match").asText());
+		assertEquals("ACA.*", node.at("/operands/1/operands/0/value").asText());
+		assertEquals("match:eq",
+				node.at("/operands/1/operands/1/match").asText());
+		assertEquals("QAO-NC",
+				node.at("/operands/1/operands/1/value").asText());
+		
+	}
 
-    private void checkAndPublicWithACA (String json) throws KustvaktException {
-        JsonNode node = JsonUtils.readTree(json);
-        assertNotNull(node);
-        assertEquals(node.at("/collection/operation").asText(),
-                "operation:and");
-        assertEquals(node.at("/collection/rewrites/0/operation").asText(),
-                "operation:injection");
-        assertEquals(node.at("/collection/rewrites/0/scope").asText(),
-                "availability(PUB)");
-        assertEquals(node.at("/collection/operands/1/match").asText(),
-                "match:eq");
-        assertEquals(node.at("/collection/operands/1/type").asText(),
-                "type:regex");
-        assertEquals(node.at("/collection/operands/1/key").asText(),
-                "availability");
-        assertEquals(node.at("/collection/operands/1/value").asText(), "ACA.*");
-        node = node.at("/collection/operands/0");
-        assertEquals(node.at("/operands/0/match").asText(), "match:eq");
-        assertEquals(node.at("/operands/0/type").asText(), "type:regex");
-        assertEquals(node.at("/operands/0/key").asText(), "availability");
-        assertEquals(node.at("/operands/0/value").asText(), "CC.*");
-        assertEquals(node.at("/operands/1/operands/0/match").asText(),
-                "match:eq");
-        assertEquals(node.at("/operands/1/operands/0/type").asText(),
-                "type:regex");
-        assertEquals(node.at("/operands/1/operands/0/key").asText(),
-                "availability");
-        assertEquals(node.at("/operands/1/operands/0/value").asText(), "ACA.*");
-    }
+    private void checkAndAllWithACA (String json, JsonNode source)
+			throws KustvaktException {
+		JsonNode node = JsonUtils.readTree(json).at("/collection");
+		assertEquals("operation:and", node.at("/operation").asText());
+		assertEquals("operation:and", node.at("/operation").asText());
+		assertEquals("koral:rewrite", node.at("/rewrites/0/@type").asText());
+		assertEquals("Kustvakt", node.at("/rewrites/0/origin").asText());
+		assertEquals("operation:override", node.at("/rewrites/0/operation").asText());
+		assertEquals(source, node.at("/rewrites/0/source"));
 
-    private void checkAndAllWithACA (String json) throws KustvaktException {
-        JsonNode node = JsonUtils.readTree(json);
-        System.out.println(node.toPrettyString());
-        assertEquals("operation:and",
-                node.at("/collection/operation").asText());
-        assertEquals("operation:injection",
-                node.at("/collection/rewrites/0/operation").asText());
-        assertEquals("availability(ALL)",
-                node.at("/collection/rewrites/0/scope").asText());
-        assertEquals("match:eq",
-                node.at("/collection/operands/1/match").asText());
-        assertEquals("type:regex",
-                node.at("/collection/operands/1/type").asText());
-        assertEquals("availability",
-                node.at("/collection/operands/1/key").asText());
-        assertEquals("ACA.*", node.at("/collection/operands/1/value").asText());
-        node = node.at("/collection/operands/0");
+		assertEquals("match:eq", node.at("/operands/1/match").asText());
+		assertEquals("type:regex", node.at("/operands/1/type").asText());
+		assertEquals("availability", node.at("/operands/1/key").asText());
+        assertEquals("ACA.*", node.at("/operands/1/value").asText());
+        node = node.at("/operands/0");
         assertEquals("match:eq", node.at("/operands/0/match").asText());
         assertEquals("type:regex", node.at("/operands/0/type").asText());
         assertEquals("availability", node.at("/operands/0/key").asText());
@@ -147,45 +104,195 @@
         return target().path(API_VERSION).path("search")
                 .queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
                 .queryParam("cq", collectionQuery).request()
-                .header(Attributes.AUTHORIZATION,
-                        HttpAuthorizationHandler
-                                .createBasicAuthorizationHeaderValue("kustvakt",
-                                        "kustvakt2015"))
+                .header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+						.createBasicAuthorizationHeaderValue("user", "pass"))
                 .header(HttpHeaders.X_FORWARDED_FOR, ip).get();
     }
 
+    
     @Test
-    public void testAvailabilityFreeAuthorized () throws KustvaktException {
-        Response response = searchQuery("availability = CC-BY-SA");
+    public void testFreeWithoutCorpusQuery () throws KustvaktException {
+		Response response = target().path(API_VERSION).path("search")
+				.queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+				.request().get();
+		assertEquals(Status.OK.getStatusCode(), response.getStatus());
+		
+		String json = response.readEntity(String.class);
+		JsonNode node = JsonUtils.readTree(json).at("/collection");
+		assertEquals("availability", node.at("/key").asText());
+		assertEquals("CC.*", node.at("/value").asText());
+		assertEquals("operation:injection",
+				node.at("/rewrites/0/operation").asText());
+    }
+    
+    @Test
+	public void testPublicWithoutCorpusQuery () throws KustvaktException {
+		Response response = target().path(API_VERSION).path("search")
+				.queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+				.request()
+				.header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+						.createBasicAuthorizationHeaderValue("user", "pass"))
+				.header(HttpHeaders.X_FORWARDED_FOR, "149.27.0.32").get();
+		assertEquals(Status.OK.getStatusCode(), response.getStatus());
+		
+		String json = response.readEntity(String.class);
+		JsonNode node = JsonUtils.readTree(json).at("/collection");
+		
+		String expected = """
+			{
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "CC.*",
+                "key" : "availability"
+              }, {
+                "operands" : [ {
+                  "@type" : "koral:doc",
+                  "match" : "match:eq",
+                  "type" : "type:regex",
+                  "value" : "ACA.*",
+                  "key" : "availability"
+                }, {
+                  "@type" : "koral:doc",
+                  "match" : "match:eq",
+                  "type" : "type:regex",
+                  "value" : "QAO-NC",
+                  "key" : "availability"
+                } ],
+                "@type" : "koral:docGroup",
+                "operation" : "operation:or"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:or",
+              "rewrites" : [ {
+                "@type" : "koral:rewrite",
+                "src" : "Kustvakt",
+                "origin" : "Kustvakt",
+                "operation" : "operation:injection",
+                "scope" : "availability(PUB)"
+              } ]
+            }
+			""";
+		
+		assertEquals(JsonUtils.readTree(expected), node);
+    }
+    
+    
+    @Test
+   	public void testAllWithoutCorpusQuery () throws KustvaktException {
+   		Response response = target().path(API_VERSION).path("search")
+   				.queryParam("q", "[orth=das]").queryParam("ql", "poliqarp")
+   				.request()
+   				.header(Attributes.AUTHORIZATION, HttpAuthorizationHandler
+   						.createBasicAuthorizationHeaderValue("user", "pass"))
+   				.header(HttpHeaders.X_FORWARDED_FOR, "10.0.10.132").get();
+   		assertEquals(Status.OK.getStatusCode(), response.getStatus());
+   		
+   		String json = response.readEntity(String.class);
+   		JsonNode node = JsonUtils.readTree(json).at("/collection");
+   		String expected = """
+   			{
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "CC.*",
+                "key" : "availability"
+              }, {
+                "operands" : [ {
+                  "@type" : "koral:doc",
+                  "match" : "match:eq",
+                  "type" : "type:regex",
+                  "value" : "ACA.*",
+                  "key" : "availability"
+                }, {
+                  "operands" : [ {
+                    "@type" : "koral:doc",
+                    "match" : "match:eq",
+                    "type" : "type:regex",
+                    "value" : "QAO-NC",
+                    "key" : "availability"
+                  }, {
+                    "@type" : "koral:doc",
+                    "match" : "match:eq",
+                    "type" : "type:regex",
+                    "value" : "QAO-NC-LOC:ids.*",
+                    "key" : "availability"
+                  } ],
+                  "@type" : "koral:docGroup",
+                  "operation" : "operation:or"
+                } ],
+                "@type" : "koral:docGroup",
+                "operation" : "operation:or"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:or",
+              "rewrites" : [ {
+                "@type" : "koral:rewrite",
+                "src" : "Kustvakt",
+                "origin" : "Kustvakt",
+                "operation" : "operation:injection",
+                "scope" : "availability(ALL)"
+              } ]
+            }
+   			""";
+   		assertEquals(JsonUtils.readTree(expected), node);
+    }
+    
+    @Test
+    public void testFreeWithoutAvailabilityOr () throws KustvaktException {
+        Response response = searchQuery("corpusSigle=GOE | textClass=politik");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+            {
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "GOE",
+                "key" : "corpusSigle"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "politik",
+                "key" : "textClass"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:or"
+            }	
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testAvailabilityRegexFreeAuthorized ()
-            throws KustvaktException {
-        Response response = searchQuery("availability = /.*BY.*/");
+    public void testFreeWithoutAvailability () throws KustvaktException {
+        Response response = searchQuery("corpusSigle=GOE");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+            {
+              "@type" : "koral:doc",
+              "match" : "match:eq",
+              "value" : "GOE",
+              "key" : "corpusSigle"
+            }	
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
+    
     @Test
-    public void testAvailabilityFreeUnauthorized () throws KustvaktException {
-        Response response = searchQuery("availability = ACA-NC");
+    public void testFreeAvailabilityNoRewrite () throws KustvaktException {
+        Response response = searchQuery("availability = /CC.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String json = response.readEntity(String.class);
+        JsonNode node = JsonUtils.readTree(json);
+        assertTrue(node.at("/collection/rewrite").isMissingNode());
     }
-
+    
     @Test
-    public void testAvailabilityRegexFreeUnauthorized ()
-            throws KustvaktException {
-        Response response = searchQuery("availability = /ACA.*/");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testAvailabilityRegexNoRewrite () throws KustvaktException {
+    public void testFreeAvailabilityNoRewriteAnd () throws KustvaktException {
         Response response = searchQuery(
                 "availability = /CC.*/ & availability = /ACA.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
@@ -206,148 +313,301 @@
         assertEquals(node.at("/collection/operands/1/value").asText(), "ACA.*");
     }
 
+    
     @Test
-    public void testAvailabilityRegexFreeUnauthorized3 ()
+    public void testFreeAvailabilityAuthorized () throws KustvaktException {
+        Response response = searchQuery("availability = CC-BY-SA");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        
+        String source = """
+        	{
+              "@type" : "koral:doc",
+              "match" : "match:eq",
+              "value" : "CC-BY-SA",
+              "key" : "availability"
+            }
+        	""";
+        
+		checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+    
+    @Test
+    public void testFreeAvailabilityUnauthorized () throws KustvaktException {
+        Response response = searchQuery("availability = ACA-NC");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+        	{
+              "@type" : "koral:doc",
+              "match" : "match:eq",
+              "value" : "ACA-NC",
+              "key" : "availability"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+
+    @Test
+    public void testFreeAvailabilityRegexAuthorized ()
+            throws KustvaktException {
+        Response response = searchQuery("availability = /.*BY.*/");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+        	{
+              "@type" : "koral:doc",
+              "match" : "match:eq",
+              "type" : "type:regex",
+              "value" : ".*BY.*",
+              "key" : "availability"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+
+    
+    @Test
+    public void testFreeAvailabilityRegexUnauthorized ()
+            throws KustvaktException {
+        Response response = searchQuery("availability = /ACA.*/");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+        	{
+              "@type" : "koral:doc",
+              "match" : "match:eq",
+              "type" : "type:regex",
+              "value" : "ACA.*",
+              "key" : "availability"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+    
+    @Test
+    public void testFreeAvailabilityRegexUnauthorized2 ()
             throws KustvaktException {
         Response response = searchQuery("availability = /.*NC.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        // System.out.println(response.readEntity(String.class));
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+              "@type" : "koral:doc",
+              "match" : "match:eq",
+              "type" : "type:regex",
+              "value" : ".*NC.*",
+              "key" : "availability"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+    
+    @Test
+    public void testFreeAvailabilityOr () throws KustvaktException {
+        Response response = searchQuery(
+                "availability=/CC.*/ | availability=/ACA.*/");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+        	{
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "CC.*",
+                "key" : "availability"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "ACA.*",
+                "key" : "availability"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:or"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+    @Test
+    public void testFreeAvailabilityOrCorpusSigle () throws KustvaktException {
+        Response response = searchQuery(
+                "availability=/CC.*/ | corpusSigle=GOE");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+            {
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "CC.*",
+                "key" : "availability"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "GOE",
+                "key" : "corpusSigle"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:or"
+            }	
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
+    
     @Test
-    public void testNegationAvailabilityFreeUnauthorized ()
+    public void testFreeAvailabilityNegationUnauthorized ()
             throws KustvaktException {
         Response response = searchQuery("availability != /CC.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+              "@type" : "koral:doc",
+              "match" : "match:ne",
+              "type" : "type:regex",
+              "value" : "CC.*",
+              "key" : "availability"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testNegationAvailabilityFreeUnauthorized2 ()
+    public void testFreeAvailabilityNegationUnauthorized2 ()
             throws KustvaktException {
         Response response = searchQuery("availability != /.*BY.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+              "@type" : "koral:doc",
+              "match" : "match:ne",
+              "type" : "type:regex",
+              "value" : ".*BY.*",
+              "key" : "availability"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testNegationAvailabilityWithOperationOrUnauthorized ()
+    public void testFreeAvailabilityNegationOrUnauthorized ()
             throws KustvaktException {
         Response response = searchQuery(
                 "availability = /CC.*/ | availability != /CC.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "CC.*",
+                "key" : "availability"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:ne",
+                "type" : "type:regex",
+                "value" : "CC.*",
+                "key" : "availability"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:or"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testComplexNegationAvailabilityFreeUnauthorized ()
+    public void testFreeAvailabilityNegationAndUnauthorized ()
             throws KustvaktException {
         Response response = searchQuery(
                 "textClass=politik & availability != /CC.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "politik",
+                "key" : "textClass"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:ne",
+                "type" : "type:regex",
+                "value" : "CC.*",
+                "key" : "availability"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:and"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testComplexAvailabilityFreeUnauthorized ()
+    public void testFreeAvailabilityAndUnauthorized ()
             throws KustvaktException {
         Response response = searchQuery(
                 "textClass=politik & availability=ACA-NC");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "politik",
+                "key" : "textClass"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "ACA-NC",
+                "key" : "availability"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:and"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testComplexAvailabilityFreeUnauthorized3 ()
+    public void testFreeAvailabilityAndUnauthorized2 ()
             throws KustvaktException {
         Response response = searchQuery(
                 "textClass=politik & availability=/.*NC.*/");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "politik",
+                "key" : "textClass"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : ".*NC.*",
+                "key" : "availability"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:and"
+            }
+        	""";
+        checkAndFree(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
-
+    
     @Test
-    public void testAvailabilityPublicAuthorized () throws KustvaktException {
-        Response response = searchQueryWithIP("availability=ACA-NC",
-                "149.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndPublic(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testAvailabilityPublicUnauthorized () throws KustvaktException {
-        Response response = searchQueryWithIP("availability=QAO-NC-LOC:ids",
-                "149.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndPublic(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testAvailabilityRegexPublicAuthorized ()
-            throws KustvaktException {
-        Response response = searchQueryWithIP("availability= /ACA.*/",
-                "149.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndPublicWithACA(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testNegationAvailabilityPublicUnauthorized ()
-            throws KustvaktException {
-        Response response = searchQueryWithIP("availability != ACA-NC",
-                "149.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndPublic(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testNegationAvailabilityRegexPublicUnauthorized ()
-            throws KustvaktException {
-        Response response = searchQueryWithIP("availability != /ACA.*/",
-                "149.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndPublic(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testComplexAvailabilityPublicUnauthorized ()
-            throws KustvaktException {
-        Response response = searchQueryWithIP(
-                "textClass=politik & availability=QAO-NC-LOC:ids",
-                "149.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndPublic(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testNegationComplexAvailabilityPublicUnauthorized ()
-            throws KustvaktException {
-        Response response = searchQueryWithIP(
-                "textClass=politik & availability!=QAO-NC-LOC:ids",
-                "149.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndPublic(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testAvailabilityRegexAllAuthorized () throws KustvaktException {
-        Response response = searchQueryWithIP("availability= /ACA.*/",
-                "10.27.0.32");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndAllWithACA(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testAvailabilityOr () throws KustvaktException {
-        Response response = searchQuery(
-                "availability=/CC.*/ | availability=/ACA.*/");
-        assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
-    }
-
-    @Test
-    public void testRedundancyOrPub () throws KustvaktException {
+    public void testPublicAvailabilityNoRewrite () throws KustvaktException {
         Response response = searchQueryWithIP(
                 "availability=/CC.*/ | availability=/ACA.*/ | availability=/QAO-NC/",
                 "149.27.0.32");
@@ -359,24 +619,169 @@
     }
 
     @Test
-    public void testAvailabilityOrCorpusSigle () throws KustvaktException {
-        Response response = searchQuery(
-                "availability=/CC.*/ | corpusSigle=GOE");
+    public void testPublicAvailabilityAuthorized () throws KustvaktException {
+        Response response = searchQueryWithIP("availability=ACA-NC",
+                "149.27.0.32");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "ACA-NC",
+                "key" : "availability"
+            }
+        	""";
+        
+		checkAndPublic(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testOrWithoutAvailability () throws KustvaktException {
-        Response response = searchQuery("corpusSigle=GOE | textClass=politik");
+    public void testPublicAvailabilityUnauthorized () throws KustvaktException {
+        Response response = searchQueryWithIP("availability=QAO-NC-LOC:ids",
+                "149.27.0.32");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        
+        String source = """
+        	{
+                  "@type" : "koral:doc",
+                  "match" : "match:eq",
+                  "value" : "QAO-NC-LOC:ids",
+                  "key" : "availability"
+            }
+        	""";
+		checkAndPublic(response.readEntity(String.class),
+				JsonUtils.readTree(source));
     }
 
     @Test
-    public void testWithoutAvailability () throws KustvaktException {
-        Response response = searchQuery("corpusSigle=GOE");
+    public void testPublicAvailabilityRegexAuthorized ()
+            throws KustvaktException {
+        Response response = searchQueryWithIP("availability= /ACA.*/",
+                "149.27.0.32");
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
-        checkAndFree(response.readEntity(String.class));
+        String source = """
+        	{
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "ACA.*",
+                "key" : "availability"
+            }
+            	""";
+		checkAndPublic(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+
+    @Test
+    public void testPublicAvailabilityNegationUnauthorized ()
+            throws KustvaktException {
+        Response response = searchQueryWithIP("availability != ACA-NC",
+                "149.27.0.32");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+                {
+                  "@type" : "koral:doc",
+                  "match" : "match:ne",
+                  "value" : "ACA-NC",
+                  "key" : "availability"
+                }	
+        	""";
+		checkAndPublic(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+
+    @Test
+    public void testPublicAvailabilityNegationRegexUnauthorized ()
+            throws KustvaktException {
+        Response response = searchQueryWithIP("availability != /ACA.*/",
+                "149.27.0.32");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+            {
+              "@type" : "koral:doc",
+              "match" : "match:ne",
+              "type" : "type:regex",
+              "value" : "ACA.*",
+              "key" : "availability"
+            }	
+        	""";
+		checkAndPublic(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+
+    @Test
+    public void testPublicAvailabilityAndUnauthorized ()
+            throws KustvaktException {
+        Response response = searchQueryWithIP(
+                "textClass=politik & availability=QAO-NC-LOC:ids",
+                "149.27.0.32");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+            {
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "politik",
+                "key" : "textClass"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "QAO-NC-LOC:ids",
+                "key" : "availability"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:and"
+            }	
+        	""";
+		checkAndPublic(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+
+    @Test
+    public void testPublicAvailabilityNegationAndUnauthorized ()
+            throws KustvaktException {
+        Response response = searchQueryWithIP(
+                "textClass=politik & availability!=QAO-NC-LOC:ids",
+                "149.27.0.32");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+            {
+              "operands" : [ {
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "value" : "politik",
+                "key" : "textClass"
+              }, {
+                "@type" : "koral:doc",
+                "match" : "match:ne",
+                "value" : "QAO-NC-LOC:ids",
+                "key" : "availability"
+              } ],
+              "@type" : "koral:docGroup",
+              "operation" : "operation:and"
+            }	
+        	""";
+		checkAndPublic(response.readEntity(String.class),
+				JsonUtils.readTree(source));
+    }
+
+    @Test
+    public void testAllAvailabilityRegexAuthorized () throws KustvaktException {
+        Response response = searchQueryWithIP("availability= /ACA.*/",
+                "10.27.0.32");
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+        String source = """
+        	{
+                "@type" : "koral:doc",
+                "match" : "match:eq",
+                "type" : "type:regex",
+                "value" : "ACA.*",
+                "key" : "availability"
+              }
+            }
+        	""";
+        checkAndAllWithACA(response.readEntity(String.class),
+        		JsonUtils.readTree(source));
     }
 }
diff --git a/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java b/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
index 796dc14..a2ce70d 100644
--- a/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
+++ b/src/test/java/de/ids_mannheim/korap/web/controller/SearchControllerTest.java
@@ -329,10 +329,10 @@
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
         JsonNode node = JsonUtils.readTree(response.readEntity(String.class));
         assertNotNull(node);
-        assertEquals("operation:injection",
+        assertEquals("operation:override",
                 node.at("/collection/rewrites/0/operation").asText());
-        assertEquals("availability(FREE)",
-                node.at("/collection/rewrites/0/scope").asText());
+//        assertEquals("availability(FREE)",
+//                node.at("/collection/rewrites/0/scope").asText());
         // EM: double AND operations
         assertEquals("availability",
                 node.at("/collection/operands/0/key").asText());