001/* 002 * Copyright 2014 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.cms.search.query; 017 018import java.util.Map; 019import java.util.Optional; 020 021import org.apache.commons.lang3.StringUtils; 022 023/** 024 * Represents a {@link Query} corresponding to the logical negation of another {@link Query}. 025 */ 026public class NotQuery extends AbstractWrapperQuery 027{ 028 /** The prefix for making a query negative. */ 029 public static final String NEGATION_QUERY_PREFIX = "*:* -"; 030 031 /** 032 * Build a NotQuery object. 033 * @param query the negated query. 034 */ 035 public NotQuery(Query query) 036 { 037 super(query); 038 } 039 040 /** 041 * Appends a negation to the query being built. 042 * <br>This method just does <code>sb.append({@value #NEGATION_QUERY_PREFIX});</code> 043 * @param query The query builder 044 * @return The given query builder 045 */ 046 public static StringBuilder appendNegation(StringBuilder query) 047 { 048 query.append(NEGATION_QUERY_PREFIX); 049 return query; 050 } 051 052 @Override 053 public String build() throws QuerySyntaxException 054 { 055 String queryString = getSubQuery().build(); 056 if (queryString.isEmpty()) 057 { 058 return ""; 059 } 060 else 061 { 062 return NEGATION_QUERY_PREFIX + "(" + queryString + ")"; 063 } 064 } 065 066 public Optional<Object> buildAsJson() throws QuerySyntaxException 067 { 068 Optional<Query> rewrittenQueryOptional = getSubQuery().rewrite(); 069 if (rewrittenQueryOptional.isEmpty()) 070 { 071 return Optional.empty(); 072 } 073 074 Query rewrittenQuery = rewrittenQueryOptional.get(); 075 if (rewrittenQuery instanceof NotQuery nq) 076 { 077 return nq.getSubQuery().buildAsJson(); 078 } 079 else 080 { 081 return rewrittenQuery.buildAsJson() 082 .map(q -> Map.of("bool", Map.of("must", "*:*", "must_not", q))); 083 } 084 } 085 086 public Optional<Query> rewrite() 087 { 088 Query subQuery = getSubQuery(); 089 Optional<Query> rewrittenQueryOptional = subQuery.rewrite(); 090 if (rewrittenQueryOptional.isEmpty()) 091 { 092 return Optional.empty(); 093 } 094 095 Query rewrittenQuery = rewrittenQueryOptional.get(); 096 if (rewrittenQuery == subQuery) 097 { 098 return Optional.of(this); 099 } 100 else if (rewrittenQuery instanceof NotQuery nq) 101 { 102 return Optional.of(nq.getSubQuery()); 103 } 104 else 105 { 106 return Optional.of(new NotQuery(rewrittenQuery)); 107 } 108 } 109 110 @Override 111 public String toString(int indent) 112 { 113 final String notLineIndent = StringUtils.repeat(' ', indent); 114 final String subq = getSubQuery().toString(indent + 2); 115 return notLineIndent + "[NOT]\n" + subq + "\n" + notLineIndent + "[/NOT]"; 116 } 117}