com.apress.progwt.server.service.impl.SearchServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.apress.progwt.server.service.impl.SearchServiceImpl.java

Source

/*
 * Copyright 2008 Jeff Dwyer
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.apress.progwt.server.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.apache.commons.lang.ArrayUtils;
import org.apache.log4j.Logger;
import org.compass.core.Compass;
import org.compass.core.CompassCallback;
import org.compass.core.CompassException;
import org.compass.core.CompassHit;
import org.compass.core.CompassHits;
import org.compass.core.CompassHitsOperations;
import org.compass.core.CompassSession;
import org.compass.core.CompassTemplate;
import org.compass.core.CompassQuery.SortDirection;
import org.compass.core.CompassQuery.SortPropertyType;
import org.compass.gps.CompassGps;
import org.compass.gps.MirrorDataChangesGpsDevice;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.annotation.Transactional;

import com.apress.progwt.client.domain.ForumPost;
import com.apress.progwt.client.domain.School;
import com.apress.progwt.client.domain.User;
import com.apress.progwt.client.domain.dto.SearchResult;
import com.apress.progwt.client.exception.SiteException;
import com.apress.progwt.server.service.SearchService;
import com.apress.progwt.server.service.UserService;

@Transactional
public class SearchServiceImpl implements SearchService, InitializingBean {
    private static final Logger log = Logger.getLogger(SearchServiceImpl.class);

    private static final int DEFAULT_MAX_SEARCH_RESULTS = 10;

    private CompassTemplate compassTemplate;

    private Compass compass;

    private MirrorDataChangesGpsDevice mirrorGPS;
    private CompassGps compassGPS;

    private UserService userService;

    @Required
    public void setMirrorGPS(MirrorDataChangesGpsDevice mirrorGPS) {
        this.mirrorGPS = mirrorGPS;
    }

    @Required
    public void setCompass(Compass compass) {
        this.compass = compass;
    }

    @Required
    public void setCompassGPS(CompassGps compassGPS) {
        this.compassGPS = compassGPS;
    }

    @Required
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    public void afterPropertiesSet() throws Exception {
        if (compass == null) {
            throw new IllegalArgumentException("Must set compass property");
        }
        this.compassTemplate = new CompassTemplate(compass);

        // ensure that our gps is going to mirror all data changes from
        // here on out.
        // this will give us real time searchability of saved objects
        mirrorGPS.setMirrorDataChanges(true);
    }

    public SearchResult search(final String searchString) throws SiteException {
        return search(searchString, userService.getCurrentUser(), 0, DEFAULT_MAX_SEARCH_RESULTS);
    }

    /**
     * run two searches. one on schools only, one on forum posts so that
     * we can be sure to get some of each.
     * 
     * TODO. find a way to return a better smattering of each without
     * running two queries.
     * 
     * @param searchString
     * @param user
     * @param start
     * @param max_num_hits
     * @return
     * @throws SiteException
     */
    private SearchResult search(final String searchString, final User user, final int start, final int max_num_hits)
            throws SiteException {
        try {
            log.debug("-----" + searchString + "--------" + user + "-----");

            SearchResult res = new SearchResult();

            if (searchString == null || searchString.equals("")) {
                return res;
            }

            final String[] aliases = { "school" };
            CompassHitsOperations schUserHits = getHits(searchString, start, max_num_hits, aliases);

            final String[] aliases2 = { "schoolforumpost", "userforumpost" };
            CompassHitsOperations forumPostHits = getHits(searchString, start, max_num_hits, aliases2);

            addHitsToRes(res, schUserHits);
            addHitsToRes(res, forumPostHits);

            return res;
        } catch (Exception e) {
            throw new SiteException(e);
        }
    }

    private CompassHitsOperations getHits(final String searchString, final int start, final int max_num_hits,
            final String[] aliases) {

        CompassHitsOperations hits = compassTemplate.executeFind(new CompassCallback() {
            public Object doInCompass(CompassSession session) throws CompassException {

                CompassHits hits = session.queryBuilder().queryString(searchString).toQuery().setAliases(aliases)
                        .addSort("searchOrder", SortPropertyType.INT, SortDirection.REVERSE).hits();

                return hits.detach(start, max_num_hits);
            }
        });
        log.debug(
                "Search " + searchString + " alias " + ArrayUtils.toString(aliases) + " hits " + hits.getLength());
        return hits;
    }

    /**
     * classify the given hits into types and add them to the SearchResult
     * parameter
     * 
     * @param res
     * @param hits
     */
    private void addHitsToRes(SearchResult res, CompassHitsOperations hits) {
        //
        // Turn results into SearchResults
        //
        for (int i = 0; i < hits.length(); i++) {
            CompassHit defaultCompassHit = hits.hit(i);

            // log.debug(i + " score: " + defaultCompassHit.getScore());
            // log.debug("alias: " + defaultCompassHit.getAlias());

            Object obj = defaultCompassHit.getData();
            // log.debug("DATA: " + defaultCompassHit.getData());

            if (obj instanceof User) {
                User usr = (User) obj;
                res.getUsers().add(usr);
            }
            if (obj instanceof School) {
                School sch = (School) obj;
                res.getSchools().add(sch);
            }
            if (obj instanceof ForumPost) {
                ForumPost fp = (ForumPost) obj;
                res.getForumPosts().add(fp);
            }
            log.debug("Found: " + res);
        }
    }

    public List<String> searchForSchool(final String searchString) throws SiteException {

        return searchForSchool(searchString, 0, DEFAULT_MAX_SEARCH_RESULTS);

    }

    /**
     * parse search string into parts and search for '+string*' for each
     * part. eg "Cal Berk" -> "+Cal* +Berk*" which will both AND and
     * impose a startsWith restriction, which is good for autocomplete
     * 
     * @param searchStringP
     * @param start
     * @param max_num_hits
     * @return
     */
    public List<String> searchForSchool(final String searchStringP, final int start, final int max_num_hits)
            throws SiteException {
        try {
            final List<String> rtn = new ArrayList<String>();

            if (searchStringP == null || searchStringP.equals("")) {
                return rtn;
            }
            String[] split = searchStringP.split(" ");
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < split.length; i++) {
                sb.append("+");
                sb.append(split[i]);
                sb.append("* ");
            }

            final String[] aliases = { "school" };
            final String searchString = sb.toString();

            compassTemplate.execute(new CompassCallback() {
                public Object doInCompass(CompassSession session) throws CompassException {

                    CompassHits hits = session.queryBuilder().queryString(searchString).toQuery()
                            .setAliases(aliases)
                            .addSort("popularityCounter", SortPropertyType.INT, SortDirection.REVERSE).hits();
                    log.debug("search string " + searchString + " hits " + hits.length());
                    for (int i = start; i < hits.length() && i < max_num_hits; i++) {
                        String name = hits.resource(i).get("name");
                        // log.debug("search string " + searchString
                        // + " hit: " + name + " "
                        // + hits.resource(i));
                        if (name != null) {
                            rtn.add(name);
                        }
                    }
                    return true;
                }
            });

            // log.debug("search string " + searchString + " hits "
            // + hits.length());
            // for (int i = 0; i < hits.length(); i++) {
            // CompassHit defaultCompassHit = hits.hit(i);
            // Object obj = defaultCompassHit.getData();
            // log.debug("DATA: " + defaultCompassHit.getData());
            //
            // if (obj instanceof School) {
            // School result = (School) obj;
            // rtn.add(result);
            // }
            // }

            return rtn;
        } catch (Exception e) {
            throw new SiteException(e);
        }
    }

    /**
     * re-index search. GPS will take care of most things, but if we edit
     * the DB directly compass won't hear about it and we'll need to
     * re-index.
     */
    public void reindex() {
        compassGPS.index();
    }
}