Package com.psddev.cms.tool

Source Code of com.psddev.cms.tool.Search

package com.psddev.cms.tool;

import java.awt.Color;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import com.psddev.cms.db.Content;
import com.psddev.cms.db.Directory;
import com.psddev.cms.db.Site;
import com.psddev.cms.db.ToolUi;
import com.psddev.cms.db.Workflow;
import com.psddev.cms.db.WorkflowState;
import com.psddev.dari.db.CompoundPredicate;
import com.psddev.dari.db.Database;
import com.psddev.dari.db.DatabaseEnvironment;
import com.psddev.dari.db.ObjectField;
import com.psddev.dari.db.ObjectIndex;
import com.psddev.dari.db.ObjectStruct;
import com.psddev.dari.db.ObjectType;
import com.psddev.dari.db.Predicate;
import com.psddev.dari.db.PredicateParser;
import com.psddev.dari.db.Query;
import com.psddev.dari.db.Record;
import com.psddev.dari.db.Singleton;
import com.psddev.dari.db.Sorter;
import com.psddev.dari.util.HuslColorSpace;
import com.psddev.dari.util.ObjectUtils;
import com.psddev.dari.util.PaginatedResult;
import com.psddev.dari.util.StringUtils;
import com.psddev.dari.util.UuidUtils;

public class Search extends Record {

    public static final String ADDITIONAL_PREDICATE_PARAMETER = "aq";
    public static final String ADVANCED_QUERY_PARAMETER = "av";
    public static final String GLOBAL_FILTER_PARAMETER_PREFIX = "gf.";
    public static final String FIELD_FILTER_PARAMETER_PREFIX = "f.";
    public static final String LIMIT_PARAMETER = "l";
    public static final String MISSING_FILTER_PARAMETER_SUFFIX = ".m";
    public static final String NAME_PARAMETER = "n";
    public static final String OFFSET_PARAMETER = "o";
    public static final String COLOR_PARAMETER = "c";
    public static final String ONLY_PATHED_PARAMETER = "p";
    public static final String PARENT_PARAMETER = "pt";
    public static final String PARENT_TYPE_PARAMETER = "py";
    public static final String QUERY_STRING_PARAMETER = "q";
    public static final String SELECTED_TYPE_PARAMETER = "st";
    public static final String SHOW_DRAFTS_PARAMETER = "d";
    public static final String VISIBILITIES_PARAMETER = "v";
    public static final String SHOW_MISSING_PARAMETER = "m";
    public static final String SORT_PARAMETER = "s";
    public static final String SUGGESTIONS_PARAMETER = "sg";
    public static final String TYPES_PARAMETER = "rt";

    public static final String NEWEST_SORT_LABEL = "Newest";
    public static final String NEWEST_SORT_VALUE = "_newest";
    public static final String RELEVANT_SORT_LABEL = "Relevant";
    public static final String RELEVANT_SORT_VALUE = "_relevant";

    public static final double RELEVANT_SORT_LABEL_BOOST = 10.0;

    private String name;
    private Set<ObjectType> types;
    private ObjectType selectedType;
    private String queryString;
    private String color;
    private boolean onlyPathed;
    private String additionalPredicate;
    private String advancedQuery;
    private UUID parentId;
    private UUID parentTypeId;
    private Map<String, String> globalFilters;
    private Map<String, Map<String, String>> fieldFilters;
    private String sort;
    private boolean showDrafts;
    private List<String> visibilities;
    private boolean showMissing;
    private boolean suggestions;
    private long offset;
    private int limit;

    public Search() {
    }

    public Search(ObjectField field) {
        getTypes().addAll(field.getTypes());
        setOnlyPathed(ToolUi.isOnlyPathed(field));
        setAdditionalPredicate(field.getPredicate());
    }

    public Search(ToolPageContext page, Iterable<UUID> typeIds) {
        this.page = page;

        setName(page.param(String.class, NAME_PARAMETER));

        if (typeIds != null) {
            for (UUID typeId : typeIds) {
                ObjectType type = ObjectType.getInstance(typeId);

                if (type != null) {
                    getTypes().add(type);
                }
            }
        }

        for (String name : page.paramNamesList()) {
            if (name.startsWith(GLOBAL_FILTER_PARAMETER_PREFIX)) {
                getGlobalFilters().put(name.substring(GLOBAL_FILTER_PARAMETER_PREFIX.length()), page.param(String.class, name));

            } else if (name.startsWith(FIELD_FILTER_PARAMETER_PREFIX)) {
                String filterName = name.substring(FIELD_FILTER_PARAMETER_PREFIX.length());
                int dotAt = filterName.lastIndexOf('.');
                String filterValueKey;

                if (dotAt < 0) {
                    filterValueKey = "";

                } else {
                    filterValueKey = filterName.substring(dotAt + 1);

                    if (filterValueKey.length() > 1) {
                        filterValueKey = "";

                    } else {
                        filterName = filterName.substring(0, dotAt);
                    }
                }

                Map<String, String> filterValue = getFieldFilters().get(filterName);

                if (filterValue == null) {
                    filterValue = new HashMap<String, String>();
                    getFieldFilters().put(filterName, filterValue);
                }

                filterValue.put(filterValueKey, page.param(String.class, name));
            }
        }

        setSelectedType(ObjectType.getInstance(page.param(UUID.class, SELECTED_TYPE_PARAMETER)));
        setQueryString(page.paramOrDefault(String.class, QUERY_STRING_PARAMETER, "").trim());
        setColor(page.param(String.class, COLOR_PARAMETER));
        setOnlyPathed(page.param(boolean.class, IS_ONLY_PATHED));
        setAdditionalPredicate(page.param(String.class, ADDITIONAL_QUERY_PARAMETER));
        setAdvancedQuery(page.param(String.class, ADVANCED_QUERY_PARAMETER));
        setParentId(page.param(UUID.class, PARENT_PARAMETER));
        setParentTypeId(page.param(UUID.class, PARENT_TYPE_PARAMETER));
        setSort(page.param(String.class, SORT_PARAMETER));
        setShowDrafts(page.param(boolean.class, SHOW_DRAFTS_PARAMETER));
        setVisibilities(page.params(String.class, VISIBILITIES_PARAMETER));
        setShowMissing(page.param(boolean.class, SHOW_MISSING_PARAMETER));
        setSuggestions(page.param(boolean.class, SUGGESTIONS_PARAMETER));
        setOffset(page.param(long.class, OFFSET_PARAMETER));
        setLimit(page.paramOrDefault(int.class, LIMIT_PARAMETER, 10));

        for (Tool tool : Query.from(Tool.class).selectAll()) {
            tool.initializeSearch(this, page);
        }
    }

    public Search(ToolPageContext page) {
        this(page, page.params(UUID.class, TYPES_PARAMETER));
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Set<ObjectType> getTypes() {
        if (types == null) {
            types = new HashSet<ObjectType>();
        }
        return types;
    }

    public void setTypes(Set<ObjectType> types) {
        this.types = types;
    }

    public ObjectType getSelectedType() {
        return selectedType;
    }

    public void setSelectedType(ObjectType selectedType) {
        this.selectedType = selectedType;
    }

    public String getQueryString() {
        return queryString;
    }

    public void setQueryString(String queryString) {
        this.queryString = queryString;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public boolean isOnlyPathed() {
        return onlyPathed;
    }

    public void setOnlyPathed(boolean onlyPathed) {
        this.onlyPathed = onlyPathed;
    }

    public String getAdditionalPredicate() {
        return additionalPredicate;
    }

    public void setAdditionalPredicate(String additionalPredicate) {
        this.additionalPredicate = additionalPredicate;
    }

    public String getAdvancedQuery() {
        return advancedQuery;
    }

    public void setAdvancedQuery(String advancedQuery) {
        this.advancedQuery = advancedQuery;
    }

    public UUID getParentId() {
        return parentId;
    }

    public void setParentId(UUID parentId) {
        this.parentId = parentId;
    }

    public UUID getParentTypeId() {
        return parentTypeId;
    }

    public void setParentTypeId(UUID parentTypeId) {
        this.parentTypeId = parentTypeId;
    }

    public Map<String, String> getGlobalFilters() {
        if (globalFilters == null) {
            globalFilters = new HashMap<String, String>();
        }
        return globalFilters;
    }

    public void setGlobalFilters(Map<String, String> globalFilters) {
        this.globalFilters = globalFilters;
    }

    public Map<String, Map<String, String>> getFieldFilters() {
        if (fieldFilters == null) {
            fieldFilters = new HashMap<String, Map<String, String>>();
        }
        return fieldFilters;
    }

    public void setFieldFilters(Map<String, Map<String, String>> fieldFilters) {
        this.fieldFilters = fieldFilters;
    }

    public String getSort() {
        return sort;
    }

    public void setSort(String sort) {
        this.sort = sort;
    }

    public boolean isShowDrafts() {
        return showDrafts;
    }

    public void setShowDrafts(boolean showDrafts) {
        this.showDrafts = showDrafts;
    }

    public List<String> getVisibilities() {
        if (visibilities == null) {
            visibilities = new ArrayList<String>();
        }
        return visibilities;
    }

    public void setVisibilities(List<String> visibilities) {
        this.visibilities = visibilities;
    }

    public boolean isShowMissing() {
        return showMissing;
    }

    public void setShowMissing(boolean showMissing) {
        this.showMissing = showMissing;
    }

    public boolean isSuggestions() {
        return suggestions;
    }

    public void setSuggestions(boolean suggestions) {
        this.suggestions = suggestions;
    }

    public long getOffset() {
        return offset;
    }

    public void setOffset(long offset) {
        this.offset = offset;
    }

    public int getLimit() {
        return limit;
    }

    public void setLimit(int limit) {
        this.limit = limit;
    }

    public Set<ObjectType> findValidTypes() {
        Set<ObjectType> types = getTypes();
        List<ObjectType> validTypes = new ArrayList<ObjectType>();

        if (types.size() == 1) {
            ObjectType type = types.iterator().next();

            if (type != null && Content.class.equals(type.getObjectClass())) {
                types.clear();
            }
        }

        for (ObjectType type : types) {
            validTypes.addAll(type.as(ToolUi.class).findDisplayTypes());
        }

        Collections.sort(validTypes);
        return new LinkedHashSet<ObjectType>(validTypes);
    }

    /**
     * Finds all sorters that can be applied to this search.
     *
     * @return Never {@code null}. The key is the unique ID, and the value
     * is the label. Sorted by the label.
     */
    public Map<String, String> findSorts() {
        Map<String, String> sorts = new LinkedHashMap<String, String>();
        ObjectType selectedType = getSelectedType();

        if (!ObjectUtils.isBlank(getQueryString())) {
            sorts.put(RELEVANT_SORT_VALUE, RELEVANT_SORT_LABEL);
        }

        addSorts(sorts, selectedType);
        addSorts(sorts, Database.Static.getDefault().getEnvironment());

        List<Map.Entry<String, String>> sortsList = new ArrayList<Map.Entry<String, String>>(sorts.entrySet());

        Collections.sort(sortsList, new Comparator<Map.Entry<String, String>>() {

            @Override
            public int compare(Map.Entry<String, String> x, Map.Entry<String, String> y) {
                return ObjectUtils.compare(x.getValue(), y.getValue(), false);
            }
        });

        sorts.clear();

        for (Map.Entry<String, String> entry : sortsList) {
            sorts.put(entry.getKey(), entry.getValue());
        }

        return sorts;
    }

    private void addSorts(Map<String, String> sorts, ObjectStruct struct) {
        if (struct != null) {
            for (ObjectField field : ObjectStruct.Static.findIndexedFields(struct)) {
                if (field.as(ToolUi.class).isEffectivelySortable()) {
                    sorts.put(field.getInternalName(), field.getDisplayName());
                }
            }
        }
    }

    public static Predicate getVisibilitiesPredicate(ObjectType selectedType, Collection<String> visibilities, Set<UUID> validTypeIds, boolean showDrafts) {

        Set<UUID> visibilityTypeIds = new HashSet<UUID>();

        Predicate visibilitiesPredicate = null;

        for (String visibility : visibilities) {
            if ("p".equals(visibility)) {
                Set<String> comparisonKeys = new HashSet<String>();
                DatabaseEnvironment environment = Database.Static.getDefault().getEnvironment();

                addVisibilityFields(comparisonKeys, environment);

                for (ObjectType type : environment.getTypes()) {
                    addVisibilityFields(comparisonKeys, type);
                }

                for (String key : comparisonKeys) {
                    if (showDrafts) {
                        visibilitiesPredicate = CompoundPredicate.combine(
                                PredicateParser.OR_OPERATOR,
                                visibilitiesPredicate,
                                PredicateParser.Static.parse(key + " = missing or " + key + " != missing or " + key + " = true"));

                    } else {
                        visibilitiesPredicate = CompoundPredicate.combine(
                                PredicateParser.OR_OPERATOR,
                                visibilitiesPredicate,
                                PredicateParser.Static.parse(key + " = missing"));
                    }
                }

            } else if ("w".equals(visibility)) {
                Set<String> ss = new HashSet<String>();

                for (Workflow w : (selectedType == null ?
                        Query.from(Workflow.class) :
                        Query.from(Workflow.class).where("contentTypes = ?", selectedType)).
                        selectAll()) {
                    for (WorkflowState s : w.getStates()) {
                        String value = s.getName();

                        ss.add(value);
                        addVisibilityTypeIds(visibilityTypeIds, validTypeIds, "cms.workflow.currentState", value);
                    }
                }

                visibilitiesPredicate = CompoundPredicate.combine(
                        PredicateParser.OR_OPERATOR,
                        visibilitiesPredicate,
                        PredicateParser.Static.parse("cms.workflow.currentState = ?", ss));

            } else if (visibility.startsWith("w.")) {
                String value = visibility.substring(2);
                visibilitiesPredicate = CompoundPredicate.combine(
                        PredicateParser.OR_OPERATOR,
                        visibilitiesPredicate,
                        PredicateParser.Static.parse("cms.workflow.currentState = ?", value));

                addVisibilityTypeIds(visibilityTypeIds, validTypeIds, "cms.workflow.currentState", value);

            } else if (visibility.startsWith("b.")) {
                String field = visibility.substring(2);
                visibilitiesPredicate = CompoundPredicate.combine(
                        PredicateParser.OR_OPERATOR,
                        visibilitiesPredicate,
                        PredicateParser.Static.parse(field + " = true"));

                addVisibilityTypeIds(visibilityTypeIds, validTypeIds, field, "true");

            } else if (visibility.startsWith("t.")) {
                visibility = visibility.substring(2);
                int equalAt = visibility.indexOf('=');

                if (equalAt > -1) {
                    String field = visibility.substring(0, equalAt);
                    String value = visibility.substring(equalAt + 1);
                    visibilitiesPredicate = CompoundPredicate.combine(
                            PredicateParser.OR_OPERATOR,
                            visibilitiesPredicate,
                            PredicateParser.Static.parse(field + " = ?", value));

                    addVisibilityTypeIds(visibilityTypeIds, validTypeIds, field, value);
                }
            }
        }

        if (validTypeIds != null) {
            validTypeIds.addAll(visibilityTypeIds);
        }

        return visibilitiesPredicate;

    }

    public Query<?> toQuery(Site site) {

        // If the query string is an URL, hit it to find the ID.
        String queryString = getQueryString();

        if (!ObjectUtils.isBlank(queryString)) {
            try {
                URL qsUrl = new URL(queryString.trim());
                URLConnection qsConnection = qsUrl.openConnection();

                if (qsConnection instanceof HttpURLConnection) {
                    HttpURLConnection qsHttp = (HttpURLConnection) qsConnection;

                    qsHttp.setConnectTimeout(1000);
                    qsHttp.setReadTimeout(1000);
                    qsHttp.setRequestMethod("HEAD");
                    qsHttp.setRequestProperty("Brightspot-Main-Object-Id-Query", "true");

                    InputStream qsInput = qsHttp.getInputStream();

                    try {
                        UUID mainObjectId = ObjectUtils.to(UUID.class, qsHttp.getHeaderField("Brightspot-Main-Object-Id"));

                        if (mainObjectId != null) {
                            return Query.
                                    fromAll().
                                    or("_id = ?", mainObjectId).
                                    or("* matches ?", mainObjectId).
                                    sortRelevant(100.0, "_id = ?", mainObjectId);
                        }

                    } finally {
                        qsInput.close();
                    }
                }

            } catch (IOException error) {
                // Can't connect to the URL in the query string to get the main
                // object ID, but that's OK to ignore and move on.
            }
        }

        Query<?> query = null;
        Set<ObjectType> types = getTypes();
        ObjectType selectedType = getSelectedType();
        Set<ObjectType> validTypes = findValidTypes();
        Set<UUID> validTypeIds = null;
        boolean isAllSearchable = true;

        if (selectedType != null) {
            isAllSearchable = Content.Static.isSearchableType(selectedType);
            query = Query.fromType(selectedType);

        } else {
            for (ObjectType type : validTypes) {
                if (!Content.Static.isSearchableType(type)) {
                    isAllSearchable = false;
                }
            }

            if (types.size() == 1) {
                for (ObjectType type : types) {
                    query = Query.fromType(type);
                    break;
                }

            } else {
                query = isAllSearchable ? Query.fromGroup(Content.SEARCHABLE_GROUP) : Query.fromAll();

                if (!validTypes.isEmpty()) {
                    validTypeIds = new HashSet<UUID>();

                    for (ObjectType t : validTypes) {
                        validTypeIds.add(t.getId());
                    }
                }
            }
        }

        String sort = getSort();
        boolean metricSort = false;

        if (RELEVANT_SORT_VALUE.equals(sort)) {
            if (isAllSearchable) {
                query.sortRelevant(100.0, "_label = ?", queryString);
                query.sortRelevant(10.0, "_label matches ?", queryString);
            }

        } else if (sort != null) {
            ObjectField sortField = selectedType != null ?
                    selectedType.getFieldGlobally(sort) :
                    Database.Static.getDefault().getEnvironment().getField(sort);

            if (sortField != null) {
                if (sortField.isMetric()) {
                    metricSort = true;
                }

                String sortName = selectedType != null ?
                        selectedType.getInternalName() + "/" + sort :
                        sort;

                if (ObjectField.TEXT_TYPE.equals(sortField.getInternalType())) {
                    query.sortAscending(sortName);

                } else {
                    query.sortDescending(sortName);
                }

                if (!isShowMissing()) {
                    query.and(sortName + " != missing");
                }
            }
        }

        if (metricSort) {
            // Skip Solr-related operations if sorting by metrics.

        } else if (ObjectUtils.isBlank(queryString)) {
            if (isAllSearchable) {
                query.and("* ~= *");
            }

        } else {

            // Strip http: or https: from the query for search by path below.
            if (queryString.length() > 8 &&
                    StringUtils.matches(queryString, "(?i)https?://.*")) {
                int slashAt = queryString.indexOf("/", 8);

                if (slashAt > -1) {
                    queryString = queryString.substring(slashAt);
                }
            }

            // Search by path.
            if (isAllSearchable && queryString.startsWith("/") && queryString.length() > 1) {
                List<String> paths = new ArrayList<String>();

                for (Directory directory : Query.
                        from(Directory.class).
                        where("path ^=[c] ?", queryString).
                        selectAll()) {
                    paths.add(directory.getRawPath());
                }

                int lastSlashAt = queryString.lastIndexOf("/");

                if (lastSlashAt > 0) {
                    for (Directory directory : Query.
                            from(Directory.class).
                            where("path ^=[c] ?", queryString.substring(0, lastSlashAt)).
                            selectAll()) {
                        paths.add(directory.getRawPath() + queryString.substring(lastSlashAt + 1));
                    }
                }

                query.and(Directory.PATHS_FIELD + " ^= ?", paths);

            // Full text search.
            } else if (isAllSearchable) {
                int lastSpaceAt = queryString.lastIndexOf(" ");

                if (lastSpaceAt > -1) {
                    query.and("* ~= ?", Arrays.asList(queryString, queryString.substring(0, lastSpaceAt)));

                } else {
                    query.and("* ~= ?", queryString);
                }

            } else if (selectedType != null) {
                for (String field : selectedType.getLabelFields()) {
                    if (selectedType.getIndex(field) != null) {
                        query.and(selectedType.getInternalName() + "/" + field + " contains[c] ?", queryString);
                    }
                    break;
                }

            } else {
                Predicate predicate = null;

                for (ObjectType type : validTypes) {
                    String prefix = type.getInternalName() + "/";

                    for (String field : type.getLabelFields()) {
                        if (type.getIndex(field) != null) {
                            predicate = CompoundPredicate.combine(
                                    PredicateParser.OR_OPERATOR,
                                    predicate,
                                    PredicateParser.Static.parse(prefix + field + " ^=[c] ?", queryString));
                        }
                        break;
                    }
                }

                query.and(predicate);
            }
        }

        if (isOnlyPathed()) {
            query.and(Directory.Static.hasPathPredicate());
        }

        if (isAllSearchable && selectedType == null) {
            DatabaseEnvironment environment = Database.Static.getDefault().getEnvironment();
            String q = getQueryString();

            if (!ObjectUtils.isBlank(q)) {
                q = q.replaceAll("\\s+", "").toLowerCase(Locale.ENGLISH);

                for (ObjectType t : environment.getTypes()) {
                    String name = t.getDisplayName();

                    if (!ObjectUtils.isBlank(name) &&
                            q.contains(name.replaceAll("\\s+", "").toLowerCase(Locale.ENGLISH))) {
                        query.sortRelevant(20.0, "_type = ?", t.as(ToolUi.class).findDisplayTypes());
                    }
                }
            }

            query.sortRelevant(10.0, "_type = ?", environment.getTypeByClass(Singleton.class).as(ToolUi.class).findDisplayTypes());
        }

        String additionalPredicate = getAdditionalPredicate();

        if (!ObjectUtils.isBlank(additionalPredicate)) {
            Object parent = Query.
                    from(Object.class).
                    where("_id = ?", getParentId()).
                    first();
            if (parent == null && getParentTypeId() != null) {
                ObjectType parentType = ObjectType.getInstance(getParentTypeId());
                parent = parentType.createObject(null);
            }
            query.and(additionalPredicate, parent);
        }

        String advancedQuery = getAdvancedQuery();

        if (!ObjectUtils.isBlank(advancedQuery)) {
            query.and(advancedQuery);
        }

        for (String filter : getGlobalFilters().values()) {
            if (filter != null) {
                query.and("* matches ?", filter);
            }
        }

        for (Map.Entry<String, Map<String, String>> entry : getFieldFilters().entrySet()) {
            Map<String, String> value = entry.getValue();

            if (value == null) {
                continue;
            }

            String fieldName = selectedType != null ?
                    selectedType.getInternalName() + "/" + entry.getKey() :
                    entry.getKey();

            if (ObjectUtils.to(boolean.class, value.get("m"))) {
                query.and(fieldName + " = missing");

            } else {
                String fieldValue = value.get("");
                String queryType = value.get("t");

                if ("d".equals(queryType)) {
                    Date start = ObjectUtils.to(Date.class, fieldValue);
                    Date end = ObjectUtils.to(Date.class, value.get("x"));

                    if (start != null) {
                        query.and(fieldName + " >= ?", start);
                    }

                    if (end != null) {
                        query.and(fieldName + " <= ?", end);
                    }

                } else if ("n".equals(queryType)) {
                    Double minimum = ObjectUtils.to(Double.class, fieldValue);
                    Double maximum = ObjectUtils.to(Double.class, value.get("x"));

                    if (minimum != null) {
                        query.and(fieldName + " >= ?", fieldValue);
                    }

                    if (maximum != null) {
                        query.and(fieldName + " <= ?", maximum);
                    }

                } else {
                    if (fieldValue == null) {
                        continue;
                    }

                    if ("t".equals(queryType)) {
                        query.and(fieldName + " matches ?", fieldValue);

                    } else if ("b".equals(queryType)) {
                        query.and(fieldName + ("true".equals(fieldValue) ? " = true" : " != true"));

                    } else {
                        query.and(fieldName + " = ?", fieldValue);
                    }
                }
            }
        }

        if (site != null &&
                !site.isAllSitesAccessible()) {
            Set<ObjectType> globalTypes = new HashSet<ObjectType>();

            if (selectedType != null) {
                addGlobalTypes(globalTypes, selectedType);

            } else {
                for (ObjectType type : validTypes) {
                    addGlobalTypes(globalTypes, type);
                }
            }

            query.and(CompoundPredicate.combine(
                    PredicateParser.OR_OPERATOR,
                    site.itemsPredicate(),
                    PredicateParser.Static.parse("_type = ?", globalTypes)));
        }

        Collection<String> visibilities = getVisibilities();

        if (!visibilities.isEmpty()) {

            Predicate visibilitiesPredicate = getVisibilitiesPredicate(selectedType, visibilities, validTypeIds, isShowDrafts());

            query.and(visibilitiesPredicate);

        } else if (selectedType == null &&
                isAllSearchable) {
            Set<String> comparisonKeys = new HashSet<String>();
            DatabaseEnvironment environment = Database.Static.getDefault().getEnvironment();

            addVisibilityFields(comparisonKeys, environment);

            for (ObjectType type : environment.getTypes()) {
                addVisibilityFields(comparisonKeys, type);
            }

            for (String key : comparisonKeys) {
                if (isShowDrafts()) {
                    query.and(key + " = missing or " + key + " != missing or " + key + " = true");

                } else {
                    query.and(key + " = missing");
                }
            }

        } else if (isShowDrafts()) {
            Set<String> comparisonKeys = new HashSet<String>();
            DatabaseEnvironment environment = Database.Static.getDefault().getEnvironment();

            addVisibilityFields(comparisonKeys, environment);
            addVisibilityFields(comparisonKeys, selectedType);

            for (String key : comparisonKeys) {
                query.and(key + " = missing or " + key + " != missing or " + key + " = true");
            }
        }

        if (validTypeIds != null) {
            query.and("_type = ?", validTypeIds);
        }

        String color = getColor();

        if (color != null && color.startsWith("#")) {
            int[] husl = HuslColorSpace.Static.toHUSL(new Color(Integer.parseInt(color.substring(1), 16)));
            int normalized0 = husl[0];
            int normalized1 = husl[1];
            int normalized2 = husl[2];

            if (normalized1 < 30 || normalized2 < 15 || normalized2 > 85) {
                normalized0 = 0;
                normalized1 = 0;
            }

            Set<String> fieldNames = new HashSet<String>();
            int[] binSizes = { 24, 20, 20 };
            int[] normalized = new int[] {
                    (((int) Math.round(normalized0 / 24.0)) * 24) % 360,
                    ((int) Math.round(normalized1 / 20.0)) * 20,
                    ((int) Math.round(normalized2 / 20.0)) * 20 };

            for (int i = -1; i <= 1; i += 1) {
                for (int j = -1; j <= 1; j += 1) {
                    for (int k = -1; k <= 1; k += 1) {
                        int h = (normalized[0] + i * binSizes[0]) % 360;
                        int s = normalized[1] + j * binSizes[1];
                        int l = normalized[2] + k * binSizes[2];

                        if ((i != 0 || j != 0 || k != 0) &&
                                (h >= 0 && h < 360) &&
                                (s >= 0 && s <= 100) &&
                                s != 20 &&
                                (s != 0 || h == 0) &&
                                (l >= 0 && l <= 100) &&
                                (l < 100 && l > 0 || s == 0)) {

                            fieldNames.add("color.distribution/n_" + h + "_" + s + "_" + l);
                        }
                    }
                }
            }

            String originFieldName =
                    "color.distribution/n_" + normalized[0] +
                    "_" + normalized[1] +
                    "_" + normalized[2];

            fieldNames.add(originFieldName);
            query.and(StringUtils.join(new ArrayList<String>(fieldNames), " > 0 || ") + " > 0");
            query.sortDescending(originFieldName);

            List<Sorter> sorters = query.getSorters();

            sorters.add(0, sorters.get(sorters.size() - 1));
        }

        for (Tool tool : Query.from(Tool.class).selectAll()) {
            tool.updateSearchQuery(this, query);
        }

        return query;
    }

    private static void addVisibilityTypeIds(Set<UUID> visibilityTypeIds, Set<UUID> validTypeIds, String field, String value) {
        if (validTypeIds == null) {
            return;
        }

        byte[] md5 = StringUtils.md5(field + "/" + value.toString().trim().toLowerCase(Locale.ENGLISH));

        for (UUID validTypeId : validTypeIds) {
            byte[] typeId = UuidUtils.toBytes(validTypeId);

            for (int i = 0, length = typeId.length; i < length; ++ i) {
                typeId[i] ^= md5[i];
            }

            visibilityTypeIds.add(UuidUtils.fromBytes(typeId));
        }
    }

    private void addGlobalTypes(Set<ObjectType> globalTypes, ObjectType type) {
        if (type != null && type.getGroups().contains(ObjectType.class.getName())) {
            globalTypes.add(type);
        }
    }

    private static void addVisibilityFields(Set<String> comparisonKeys, ObjectStruct struct) {
        if (struct == null) {
            return;
        }

        for (ObjectIndex index : struct.getIndexes()) {
            if (index.isVisibility()) {
                for (String fieldName : index.getFields()) {
                    ObjectField field = struct.getField(fieldName);

                    if (field != null) {
                        comparisonKeys.add(field.getUniqueName());
                    }
                }
            }
        }
    }

    /** @deprecated Use {@link #toQuery(Site)} instead. */
    @Deprecated
    public Query<?> toQuery() {
        return toQuery(null);
    }

    // --- Deprecated ---

    /** @deprecated Use {@link #ADDITIONA_PREDICATE_PARAMETER} instead. */
    @Deprecated
    public static final String ADDITIONAL_QUERY_PARAMETER = ADDITIONAL_PREDICATE_PARAMETER;

    /** @deprecated Use {@link #ONLY_PATHED_PARAMETER} instead. */
    @Deprecated
    public static final String IS_ONLY_PATHED = ONLY_PATHED_PARAMETER;

    /** @deprecated Use {@link #TYPES_PARAMETER} instead. */
    @Deprecated
    public static final String REQUESTED_TYPES_PARAMETER = TYPES_PARAMETER;

    /** @deprecated Use {@link #Search(ToolPageContext, Collection)} instead. */
    @Deprecated
    public Search(ToolPageContext page, UUID... typeIds) {
        this(page, typeIds != null ? Arrays.asList(typeIds) : null);
    }

    /** @deprecated Use {@link #getTypes} instead. */
    @Deprecated
    public Set<ObjectType> getRequestedTypes() {
        return getTypes();
    }

    /** @deprecated Use {@link #findValidTypes} instead. */
    public Set<ObjectType> getValidTypes() {
        return findValidTypes();
    }

    /** @deprecated Use {@link #toQuery} instead. */
    @Deprecated
    public Query<?> getQuery() {
        return toQuery();
    }

    @Deprecated
    private transient ToolPageContext page;

    @Deprecated
    private transient PaginatedResult<?> result;

    /** @deprecated Use {@link #toQuery} instead. */
    @Deprecated
    public PaginatedResult<?> getResult() {
        if (result == null) {
            result = toQuery(page != null ? page.getSite() : null).select(getOffset(), getLimit());
        }
        return result;
    }
}
TOP

Related Classes of com.psddev.cms.tool.Search

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.