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;
019
020import org.apache.commons.lang3.StringUtils;
021
022/**
023 * Represents a {@link Query} corresponding to the logical negation of another {@link Query}.
024 */
025public class NotQuery extends AbstractWrapperQuery
026{
027    /** The prefix for making a query negative. */
028    public static final String NEGATION_QUERY_PREFIX = "*:* -";
029    
030    /**
031     * Build a NotQuery object.
032     * @param query the negated query.
033     */
034    public NotQuery(Query query)
035    {
036        super(query);
037    }
038    
039    /**
040     * Appends a negation to the query being built.
041     * <br>This method just does <code>sb.append({@value #NEGATION_QUERY_PREFIX});</code>
042     * @param query The query builder
043     * @return The given query builder
044     */
045    public static StringBuilder appendNegation(StringBuilder query)
046    {
047        query.append(NEGATION_QUERY_PREFIX);
048        return query;
049    }
050    
051    @Override
052    public String build() throws QuerySyntaxException
053    {
054        String queryString = getSubQuery().build();
055        if (queryString.isEmpty())
056        {
057            return "";
058        }
059        else
060        {
061            return NEGATION_QUERY_PREFIX + "(" + queryString + ")";
062        }
063    }
064    
065    public Object buildAsJson() throws QuerySyntaxException
066    {
067        Query rewrittenQuery = getSubQuery().rewrite();
068        
069        if (rewrittenQuery instanceof NotQuery nq)
070        {
071            return nq.getSubQuery().buildAsJson();
072        }
073        else
074        {
075            return Map.of("bool", Map.of("must", "*:*", "must_not", rewrittenQuery.buildAsJson()));
076        }
077    }
078    
079    public Query rewrite()
080    {
081        Query subQuery = getSubQuery();
082        Query rewrittenQuery = subQuery.rewrite();
083        
084        if (rewrittenQuery == subQuery)
085        {
086            return this;
087        }
088        else if (rewrittenQuery instanceof NotQuery nq)
089        {
090            return nq.getSubQuery();
091        }
092        else
093        {
094            return new NotQuery(rewrittenQuery);
095        }
096    }
097    
098    @Override
099    public String toString(int indent)
100    {
101        final String notLineIndent = StringUtils.repeat(' ', indent);
102        final String subq = getSubQuery().toString(indent + 2);
103        return notLineIndent + "[NOT]\n" +  subq + "\n" + notLineIndent + "[/NOT]";
104    }
105}