Package com.psddev.cms.db

Source Code of com.psddev.cms.db.Search$OptionalTerms

package com.psddev.cms.db;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.codec.language.Metaphone;

import com.psddev.dari.db.ComparisonPredicate;
import com.psddev.dari.db.CompoundPredicate;
import com.psddev.dari.db.ObjectField;
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.Recordable;
import com.psddev.dari.util.CollectionUtils;
import com.psddev.dari.util.ObjectUtils;

@Deprecated
public class Search extends Record {

    private static final Metaphone METAPHONE = new Metaphone();

    @Required
    private String displayName;

    @Indexed(unique = true)
    @Required
    private String internalName;

    private Set<ObjectType> types;
    private List<Rule> rules = new ArrayList<Rule>(Arrays.asList(new StopWords()));

    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public String getInternalName() {
        return internalName;
    }

    public void setInternalName(String internalName) {
        this.internalName = internalName;
    }

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

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

    public List<Rule> getRules() {
        if (rules == null) {
            setRules(new ArrayList<Rule>());
        }
        return rules;
    }

    public void setRules(List<Rule> rules) {
        this.rules = rules;
    }

    // --- Fluent methods ---

    public static Search named(String name) {
        return Query.from(Search.class).where("internalName = ?", name).first();
    }

    public Search addTypes(ObjectType... types) {
        if (types != null) {
            for (ObjectType type : types) {
                getTypes().add(type);
            }
        }
        return this;
    }

    public Search addTypes(Class<?>... classes) {
        if (classes != null) {
            for (Class<?> c : classes) {
                getTypes().add(ObjectType.getInstance(c));
            }
        }
        return this;
    }

    public Search addRule(Rule rule) {
        getRules().add(rule);
        return this;
    }

    public Search addStopWords(String... stopWords) {
        if (stopWords != null) {
            StopWords rule = null;

            for (Rule r : getRules()) {
                if (r instanceof StopWords) {
                    rule = (StopWords) r;
                    break;
                }
            }

            if (rule == null) {
                rule = new StopWords();
                addRule(rule);
            }

            Collections.addAll(rule.getStopWords(), stopWords);
        }

        return this;
    }

    public Search boostType(double boost, ObjectType type) {
        BoostType rule = new BoostType();
        rule.setBoost(boost);
        rule.setType(type);
        return addRule(rule);
    }

    public Search boostType(double boost, Class<?> objectClass) {
        return boostType(boost, ObjectType.getInstance(objectClass));
    }

    public Search boostLabels(double boost) {
        if (boost != 1.0) {
            for (Rule rule : getRules()) {
                if (rule instanceof BoostLabels) {
                    ((BoostLabels) rule).setBoost(boost);
                    return this;
                }
            }

            BoostLabels rule = new BoostLabels();
            rule.setBoost(boost);
            addRule(rule);

        } else {
            for (Iterator<Rule> i = getRules().iterator(); i.hasNext();) {
                Rule rule = i.next();
                if (rule instanceof BoostLabels) {
                    i.remove();
                }
            }
        }

        return this;
    }

    public Search boostFields(double boost, ObjectType type, String... fields) {
        BoostFields rule = new BoostFields();
        rule.setBoost(boost);
        rule.setType(type);
        Collections.addAll(rule.getFields(), fields);
        return addRule(rule);
    }

    public Search boostFields(double boost, Class<?> objectClass, String... fields) {
        return boostFields(boost, ObjectType.getInstance(objectClass), fields);
    }

    public Search boostPhrase(double boost, String pattern, ObjectType type, String predicate) {
        BoostPhrase rule = new BoostPhrase();
        rule.setBoost(boost);
        rule.setPattern(pattern);
        rule.setType(type);
        rule.setPredicate(predicate);
        return addRule(rule);
    }

    public Search boostPhrase(double boost, String pattern, Class<?> objectClass, String predicate) {
        return boostPhrase(boost, pattern, ObjectType.getInstance(objectClass), predicate);
    }

    public Search addTypeKeywords(double boost, ObjectType type, String... keywords) {
        if (keywords != null) {
            TypeKeywords rule = new TypeKeywords();
            rule.setBoost(boost);
            rule.setType(type);
            Collections.addAll(rule.getKeywords(), keywords);
            addRule(rule);
        }
        return this;
    }

    public Search addTypeKeywords(double boost, Class<?> objectClass, String... keywords) {
        return addTypeKeywords(boost, ObjectType.getInstance(objectClass), keywords);
    }

    public Search boostDirectoryItems(final double boost) {
        return addRule(new Rule() {
            public void apply(Search search, SearchQuery query, List<String> queryTerms) {
                query.sortRelevant(boost, Directory.Static.hasPathPredicate());
            }
        });
    }

    private List<String> normalizeTerms(Object... terms) {
        List<String> normalized = new ArrayList<String>();

        for (Object term : CollectionUtils.recursiveIterable(terms)) {
            if (term == null) {
                continue;

            } else if (term instanceof Recordable) {
                normalized.add(((Recordable) term).getState().getId().toString());

            } else if (term != null) {
                String termString = term.toString();
                char[] letters = termString.toCharArray();
                int lastEnd = 0;

                for (int i = 0, length = letters.length; i < length; ++ i) {
                    char letter = letters[i];

                    if (Character.isWhitespace(letter)) {
                        int end = i;
                        for (++ i; i < length && Character.isWhitespace(letters[i]);) {
                            ++ i;
                        }

                        String word = termString.substring(lastEnd, end);
                        lastEnd = i;
                        normalized.add(word);
                    }
                }

                normalized.add(termString.substring(lastEnd));
            }
        }

        return normalized;
    }

    public Search addOptionalTerms(double boost, Object... terms) {
        OptionalTerms rule = new OptionalTerms();
        rule.setBoost(boost);
        rule.setTerms(new HashSet<String>(normalizeTerms(terms)));
        return addRule(rule);
    }

    @Deprecated
    public SearchQuery toQuery(Object... terms) {
        List<String> queryTerms = normalizeTerms(terms);
        SearchQuery query = new SearchQuery();

        for (Rule rule : getRules()) {
            rule.apply(this, query, queryTerms);
        }

        if (!queryTerms.isEmpty()) {
            query.or("_any matchesAll ?", queryTerms);
        }

        Set<ObjectType> allTypes = new HashSet<ObjectType>();
        for (ObjectType type : getTypes()) {
            allTypes.addAll(type.as(ToolUi.class).findDisplayTypes());
        }

        query.and("_type = ?", allTypes);
        return query;
    }

    @Deprecated
    @Embedded
    public abstract static class Rule extends Record {

        @Deprecated
        public abstract void apply(Search search, SearchQuery query, List<String> queryTerms);
    }

    public static class StopWords extends Rule {

        @CollectionMinimum(1)
        private Set<String> stopWords = new LinkedHashSet<String>(Arrays.asList(
                "a", "about", "an", "and", "are", "as", "at", "be", "but",
                "by", "com", "do", "for", "from", "he", "her", "him", "his",
                "her", "hers", "how", "I", "if", "in", "is", "it", "its", "me",
                "my", "of", "on", "or", "our", "ours", "that", "the", "they",
                "this", "to", "too", "us", "she", "was", "what", "when",
                "where", "who", "will", "with", "why", "www"));

        public String getLabel() {
            return "Common words that may be omitted from the search query to provide higher quality results";
        }

        public Set<String> getStopWords() {
            if (stopWords == null) {
                setStopWords(new LinkedHashSet<String>());
            }
            return stopWords;
        }

        public void setStopWords(Set<String> stopWords) {
            this.stopWords = stopWords;
        }

        @Deprecated
        @Override
        public void apply(Search search, SearchQuery query, List<String> queryTerms) {
            Set<String> stopWords = getStopWords();
            Set<String> removed = null;

            for (Iterator<String> i = queryTerms.iterator(); i.hasNext();) {
                String word = i.next();
                if (stopWords.contains(word)) {
                    i.remove();
                    if (removed == null) {
                        removed = new HashSet<String>();
                    }
                    removed.add(word);
                }
            }

            if (removed != null && !removed.isEmpty()) {
                query.sortRelevant(1.0, "_any matchesAll ?", removed);
            }
        }
    }

    public abstract static class BoostRule extends Rule {

        private double boost;

        public double getBoost() {
            return boost;
        }

        public void setBoost(double boost) {
            this.boost = boost;
        }
    }

    public static class BoostType extends BoostRule {

        private ObjectType type;

        public ObjectType getType() {
            return type;
        }

        public void setType(ObjectType type) {
            this.type = type;
        }

        @Deprecated
        @Override
        public void apply(Search search, SearchQuery query, List<String> queryTerms) {
            query.sortRelevant(getBoost(), "_type = ?", type.as(ToolUi.class).findDisplayTypes());
        }
    }

    public static class BoostLabels extends BoostRule {

        @Deprecated
        @Override
        public void apply(Search search, SearchQuery query, List<String> queryTerms) {
            double boost = getBoost();
            for (ObjectType type : search.getTypes()) {
                String prefix = type.getInternalName() + "/";
                for (String fieldName : type.getLabelFields()) {
                    query.sortRelevant(boost, prefix + fieldName + " matchesAll ?", queryTerms);
                }
            }
        }
    }

    public static class BoostFields extends BoostRule {

        private ObjectType type;
        private Set<String> fields;

        public ObjectType getType() {
            return type;
        }

        public void setType(ObjectType type) {
            this.type = type;
        }

        public Set<String> getFields() {
            if (fields == null) {
                setFields(new LinkedHashSet<String>());
            }
            return fields;
        }

        public void setFields(Set<String> fields) {
            this.fields = fields;
        }

        @Deprecated
        @Override
        public void apply(Search search, SearchQuery query, List<String> queryTerms) {
            double boost = getBoost();
            String prefix = getType().getInternalName() + "/";
            for (String field : getFields()) {

                List<UUID> uuids = new ArrayList<UUID>();
                List<String> texts = new ArrayList<String>();

                if (queryTerms != null) {
                    for (String queryTerm : queryTerms) {
                        UUID uuid = ObjectUtils.to(UUID.class, queryTerm);
                        if (uuid != null) {
                            uuids.add(uuid);
                        } else {
                            texts.add(queryTerm);
                        }
                    }
                }

                if (ObjectField.RECORD_TYPE.equals(type.getField(field).getInternalItemType())) {
                    if (!uuids.isEmpty()) {
                        query.sortRelevant(boost, prefix + field + " matchesAll ?", uuids);
                    }

                } else {
                    if (!texts.isEmpty()) {
                        query.sortRelevant(boost, prefix + field + " matchesAll ?", texts);
                    }
                }
            }
        }
    }

    public static class BoostPhrase extends BoostRule {

        public String pattern;
        public ObjectType type;
        public String predicate;

        public String getPattern() {
            return pattern;
        }

        public void setPattern(String pattern) {
            this.pattern = pattern;
        }

        public ObjectType getType() {
            return type;
        }

        public void setType(ObjectType type) {
            this.type = type;
        }

        public String getPredicate() {
            return predicate;
        }

        public void setPredicate(String predicate) {
            this.predicate = predicate;
        }

        @Deprecated
        @Override
        public void apply(Search search, SearchQuery query, List<String> queryTerms) {
            StringBuilder queryTermsString = new StringBuilder();

            for (String term : queryTerms) {
                queryTermsString.append(term);
                queryTermsString.append(' ');
            }

            Pattern pattern = Pattern.compile(getPattern(), Pattern.CANON_EQ | Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE);
            Matcher matcher = pattern.matcher(queryTermsString.toString());

            while (matcher.find()) {
                int groupCount = matcher.groupCount();
                Object[] parameters = new Object[groupCount];

                for (int i = 0; i < groupCount; ++ i) {
                    parameters[i] = matcher.group(i + 1);
                }

                Predicate predicate = PredicateParser.Static.parse(getPredicate(), parameters);
                predicate = addPrefix(getType().getInternalName() + "/", predicate);
                query.sortRelevant(getBoost(), predicate);
            }
        }

        private Predicate addPrefix(String prefix, Predicate predicate) {
            if (predicate instanceof CompoundPredicate) {
                CompoundPredicate compound = (CompoundPredicate) predicate;
                List<Predicate> children = new ArrayList<Predicate>();
                for (Predicate child : compound.getChildren()) {
                    children.add(addPrefix(prefix, child));
                }
                return new CompoundPredicate(compound.getOperator(), children);

            } else if (predicate instanceof ComparisonPredicate) {
                ComparisonPredicate comparison = (ComparisonPredicate) predicate;
                return new ComparisonPredicate(
                        comparison.getOperator(),
                        comparison.isIgnoreCase(),
                        prefix + comparison.getKey(),
                        comparison.getValues());

            } else {
                return predicate;
            }
        }
    }

    public static class TypeKeywords extends BoostRule {

        private ObjectType type;
        private Set<String> keywords;

        public ObjectType getType() {
            return type;
        }

        public void setType(ObjectType type) {
            this.type = type;
        }

        public Set<String> getKeywords() {
            if (keywords == null) {
                setKeywords(new LinkedHashSet<String>());
            }
            return keywords;
        }

        public void setKeywords(Set<String> keywords) {
            this.keywords = keywords;
        }

        @Deprecated
        @Override
        public void apply(Search search, SearchQuery query, List<String> queryTerms) {
            List<ObjectType> types = getType().as(ToolUi.class).findDisplayTypes();

            for (Iterator<String> i = queryTerms.iterator(); i.hasNext();) {
                String word = i.next();
                String similar = findSimilar(word);

                if (similar != null) {
                    i.remove();
                    query.and("_type = ? or _any matchesAll ?", types, word);
                    query.sortRelevant(getBoost(), "_type = ?", types);

                    if (!similar.equalsIgnoreCase(word)) {
                        query.getSubstitutions().put(word, similar);
                    }
                }
            }
        }

        private String findSimilar(String term) {
            ObjectType type = getType();
            String encodedTerm = METAPHONE.encode(term);

            String displayName = type.getDisplayName();
            if (encodedTerm.equals(METAPHONE.encode(displayName))) {
                return displayName;
            }

            for (String keyword : getKeywords()) {
                if (encodedTerm.equals(METAPHONE.encode(keyword))) {
                    return keyword;
                }
            }

            return null;
        }
    }

    public static class OptionalTerms extends BoostRule {

        private Set<String> terms;

        public Set<String> getTerms() {
            if (terms == null) {
                terms = new HashSet<String>();
            }
            return terms;
        }

        public void setTerms(Set<String> terms) {
            this.terms = terms;
        }

        @Deprecated
        @Override
        public void apply(Search search, SearchQuery query, List<String> queryTerms) {
            Set<String> terms = getTerms();

            for (Iterator<String> i = queryTerms.iterator(); i.hasNext();) {
                String queryTerm = i.next();

                if (terms.contains(queryTerm)) {
                    i.remove();
                }
            }

            query.or("_any matchesAny ?", terms);
            query.sortRelevant(getBoost(), "_any matchesAny ?", terms);
        }
    }
}
TOP

Related Classes of com.psddev.cms.db.Search$OptionalTerms

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.