com.afspq.model.Results.java Source code

Java tutorial

Introduction

Here is the source code for com.afspq.model.Results.java

Source

/**
 * Copyright 2012 Booz Allen Hamilton. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  Booz Allen Hamilton licenses this file
 * to you 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.afspq.model;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import com.bah.bahdit.main.plugins.fulltextindex.data.EdgeLinks;
import com.bah.bahdit.main.plugins.fulltextindex.data.SearchResults;
import com.bah.bahdit.main.search.Search;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;

public class Results {

    private String query;
    private String resultStr;
    private String correction;
    private String tagCloudWords;
    private boolean corrected = false;

    private long timeElapsed;
    private int numberOfPages = 0;
    private int numberOfResults = 0;

    public JsonArray nodesArr;
    public JsonObject keywordsTreeJson;
    public JsonObject keywordsCloudJson;

    public Results() {
        resultStr = "";
        correction = "";
        tagCloudWords = "";
    }

    /**
     * Returns the time it took to process a query in milli seconds.
     * @return
     */
    public long getTimeElapsed() {
        return timeElapsed;
    }

    public void setTimeElapsed(long timeElapsed) {
        this.timeElapsed = timeElapsed;
    }

    /**
     * Returns the all the results of a single page as a string.
     * @return
     */
    public String getResult() {
        return resultStr;
    }

    /**
     * Returns the total number of results for a search query.
     * @return
     */
    public int getNumResults() {
        return numberOfResults;
    }

    /**
     * Returns the number of pages that the search results will need.
     * By default, the search result shows 8 results.  So, if there were a total
     * of 16 results for a search query, this method would return 2.
     * @return
     */
    public int getNumPages() {
        return numberOfPages;
    }

    /**
     * Returns the search term(s) used for the query.  If the user searched for
     * "apple fox," then it this method would return that string.
     * @return
     */
    public String getQuery() {
        return query;
    }

    /**
     * Returns the string of words used by the key word cloud visualization.
     * @return
     */
    public String getTagCloudWords() {
        return tagCloudWords;
    }

    /**
     * Creates the string of words for the cloud visualization. 
     * Also creates a Jsona object of the keywords with the following
     * structure:
     * {
      * "words": [
      *   {
      *       "word": "keyword",
      *       "color": "blue"
      *   }
     * }
     * 
     * This Json object can be used in other types of visualizations.
     * @param search
     * @param searchResults
     */
    @SuppressWarnings("unchecked")
    private void createKeywordsCloud(Search search, SearchResults searchResults) {

        /*
         * getVisualizations returns an ArrayList containing data for different
         * visualiztions.  The first index in this ArrayList contains data for
         * that can be used for the keyword cloud.  The second index of the list
         * contains data for the page rank graph and so on.
         */
        ArrayList<?> visualizationList = search.getVisualizations(searchResults);

        /*
         * Each element inside the ArrayList contains a HashMap.  In this case, 
         * it is a HashMap where the key is the keyword and the value is its
         * weight, i.e. the popularity of the word in the sample table.
         */
        HashMap<String, Double> keywordsCloudMap = (HashMap<String, Double>) visualizationList.get(0);
        keywordsCloudJson = new JsonObject();
        JsonArray jsonArray = new JsonArray();
        SecureRandom sr = new SecureRandom(); // Used to pick a random font.

        if (keywordsCloudMap == null) {
            tagCloudWords = tagCloudWords.concat("<ul><li></li></ul>");
            return;
        }

        /*
         * The keyword cloud framework requires the words to be contained inside
         * an HTML unordered list.  This is the reason for appending <ul> and
         * <li> to the string.  See http://www.goat1000.com/tagcanvas.php for
         * more details.
         */
        tagCloudWords = tagCloudWords.concat("<ul>");
        String[] fontStyles = { "Times New Roman", "Arial Black", "Sans-Serif" };
        int count = 0;

        for (String s : keywordsCloudMap.keySet()) {
            // Avoid overcrowding of words and limit to 25 words in the cloud.
            if (++count == 25) {
                break;
            }

            int i = sr.nextInt(3);
            JsonObject node = new JsonObject();

            node.addProperty("word", s);
            node.addProperty("color", "#ffffff");
            jsonArray.add(node);

            if (s.equals("[[TOTAL NUM DOCS]]")) {
                continue;
            }

            double weight = keywordsCloudMap.get(s);
            weight = (weight * 40) + 12;
            String fontSize = Double.toString(weight);

            tagCloudWords = tagCloudWords.concat("<li><a style='font-size: " + fontSize
                    + "pt; color: #ffffff; font-family: " + fontStyles[i] + ";'" + "href='ProcessQuery?query=" + s
                    + "&page=1&searchType=web'>" + s + "</a></li>");
        }
        tagCloudWords = tagCloudWords.concat("</ul>");
        keywordsCloudJson.add("words", jsonArray);
    }

    /**
     * Creates the Json object for the keywords space tree visualization.
     * 
     * @param query
     * @param search
     * @param searchResults
     */
    @SuppressWarnings("unchecked")
    private void createKeywordsTree(String query, Search search, SearchResults searchResults) {
        ArrayList<?> visualizationList = search.getVisualizations(searchResults);
        HashMap<String, HashSet<String>> keywordsTreeMap = (HashMap<String, HashSet<String>>) visualizationList
                .get(1);

        if (keywordsTreeMap == null) {
            return;
        }

        keywordsTreeJson = new JsonObject();
        keywordsTreeJson.addProperty("id", "query");
        keywordsTreeJson.addProperty("name", query);

        JsonArray children = new JsonArray();
        keywordsTreeJson.add("children", children);

        int i = 0;
        for (String title : keywordsTreeMap.keySet()) {
            if (!keywordsTreeMap.get(title).isEmpty()) {

                JsonArray grandChildren = new JsonArray();
                JsonObject resultTitle = new JsonObject();

                String truncatedTitle = "";

                // Long titles don't fit nicely in the visualization tree node
                // so if the title is longer than 20 characters truncate it and
                // add "..."
                if (title.length() > 20) {
                    truncatedTitle = title.substring(0, 21).concat("...");
                } else {
                    truncatedTitle = title;
                }

                resultTitle.addProperty("id", title);
                resultTitle.addProperty("name", truncatedTitle);
                resultTitle.add("children", grandChildren);

                HashSet<String> keywords = keywordsTreeMap.get(title);

                for (String keyword : keywords) {
                    JsonObject key = new JsonObject();
                    key.addProperty("id", "k" + i++);
                    key.addProperty("name", keyword);
                    grandChildren.add(key);
                }

                children.add(resultTitle);
            }
        }
    }

    /**
     * Creates the Json object for the page rank graph visualizaiton.
     * 
     * @param query
     * @param search
     * @param searchResults
     */
    @SuppressWarnings("unchecked")
    private void createPageRankGraph(String query, Search search, SearchResults searchResults) {
        ArrayList<?> visualizationList = search.getVisualizations(searchResults);
        HashMap<String, EdgeLinks> pageRankMap = (HashMap<String, EdgeLinks>) visualizationList.get(2);

        if (pageRankMap == null) {
            return;
        }

        nodesArr = new JsonArray();

        // Colors for the nodes.  Selected randomly.
        String[] colorsArr = { "#003DF5", "#B800F5", "#F500B8", "#00B8F5", "#3366FF", "#F5003D", "#00F5B8",
                "#FFCC33", "#F53D00", "#00F53D", "#B8F500", "#F5B800" };

        HashMap<Integer, String> colorMap = new HashMap<Integer, String>();
        for (int i = 0; i < colorsArr.length; i++) {
            colorMap.put(i, colorsArr[i]);
        }

        SecureRandom sr = new SecureRandom();
        for (Map.Entry<String, EdgeLinks> urlEntry : pageRankMap.entrySet()) {

            String key = urlEntry.getKey();
            EdgeLinks value = urlEntry.getValue();
            double dim = value.getPageRank() * 15000 / 23;

            // Adjusting the diameter of the node.
            dim = (dim > 70) ? dim / 15 : dim;

            JsonObject node = new JsonObject();
            JsonArray adjArr = new JsonArray();
            JsonObject data = new JsonObject();

            data.addProperty("$type", "circle");
            data.addProperty("$color", colorMap.get(sr.nextInt(colorsArr.length)));
            data.addProperty("$dim", dim);
            HashMap<String, Double> assocUrls = value.getEdgeUrls();

            int i = 0;
            int ranAdj = sr.nextInt(15);

            // Each node should have at least 3 edge connections if possible.
            ranAdj = (ranAdj < 3) ? 3 : ranAdj;

            for (Map.Entry<String, Double> urlEdge : assocUrls.entrySet()) {

                if (i < ranAdj) {
                    i++;
                    JsonObject adj = new JsonObject();
                    JsonObject empData = new JsonObject();

                    adj.addProperty("nodeTo", urlEdge.getKey());
                    adj.addProperty("nodeFrom", key);
                    adj.add("data", empData);
                    adjArr.add(adj);
                } else
                    break;
            }

            node.add("adjacencies", adjArr);
            node.add("data", data);
            node.addProperty("id", key);
            node.addProperty("name", key);
            nodesArr.add(node);
        }
    }

    /**
     * Gets the corrected string if one exists and returns true if a correction
     * was created and false otherwise.
     * 
     * @param searchResults
     * @return
     */
    private boolean createCorrection(SearchResults searchResults) {
        correction = searchResults.getCorrection();

        if (!correction.equals("")) {
            resultStr = resultStr.concat("Did you mean ");
            resultStr = resultStr.concat("<a class='correction' href='ProcessQuery?query=" + correction.trim()
                    + "&page=1&searchButton=Search&searchType=web'>" + correction.trim() + "</a>");

            resultStr = resultStr.concat("?<br><br>");
            return true;
        }

        return false;
    }

    /**
     * Calls the methods from the Search class to create the results for a
     * search query.  Also calls to the methods that make visualizations are 
     * made from here.
     * 
     * @param query
     * @param search
     * @param page
     * @param resultsPerPage
     * @return
     */
    @SuppressWarnings("unused")
    public Results getResults(String query, Search search, int page, int resultsPerPage) {
        this.query = query;
        SearchResults searchResults = search.search(query.toLowerCase(), page, resultsPerPage);
        ArrayList<String> results = searchResults.getResults();

        numberOfResults = searchResults.getNumResults();
        numberOfPages = (int) Math.ceil((double) numberOfResults / (double) resultsPerPage);

        String rank = "";
        String url = "";
        String title = "";

        /*
         * Attempt to get a spelling correction if no search results are
         * returned.  If a correction is returned, recursively called this
         * function and return the results if they exist.  Otherwise, return
         * suggestions.
         */
        if (results == null || results.isEmpty()) {

            if (correction.isEmpty()) {
                createCorrection(searchResults);
                corrected = true;

                if (!correction.isEmpty() && !correction.equals(query)) {
                    getResults(correction.trim(), search, page, resultsPerPage);
                } else {
                    resultStr = resultStr.concat("No results found for <b>" + query + "</b>.<br><br>"
                            + "Suggestions: <br>" + "<ul>" + "<li>Make sure all words are spelled correctly.</li>"
                            + "<li>Try different keywords.</li>" + "<li>Try more general keywords.</li>"
                            + "<li>Try fewer keywords.</li>" + "</ul>");
                }
            } else {
                resultStr = resultStr.concat("No results found for <b>" + query + "</b>.<br><br>"
                        + "Suggestions: <br>" + "<ul>" + "<li>Make sure all words are spelled correctly.</li>"
                        + "<li>Try different keywords.</li>" + "<li>Try more general keywords.</li>"
                        + "<li>Try fewer keywords.</li>" + "</ul>");
            }

        } else {

            int numResults = 0;
            timeElapsed = searchResults.getTime();
            createKeywordsCloud(search, searchResults);
            createKeywordsTree(query, search, searchResults);
            createPageRankGraph(query, search, searchResults);

            String range = "";
            int startRange = ((page - 1) * resultsPerPage) + 1;

            if (numberOfResults < (resultsPerPage * page)) {
                range = startRange + " - " + numberOfResults + " ";
            } else {
                range = startRange + " - " + (startRange + resultsPerPage - 1);
            }

            String instead = corrected ? " instead" : "";

            resultStr = resultStr.concat("Showing " + range + " of " + numberOfResults + " results " + " for <b>"
                    + query.trim() + "</b>" + instead + ".<br><br>");

            if (numberOfResults < resultsPerPage) {
                numResults = numberOfResults;
            } else {
                numResults = resultsPerPage;
            }

            for (String result : results) {
                String[] resultInfo = result.split("\\[ \\]");
                rank = resultInfo[0];
                url = resultInfo[1];
                title = resultInfo[2];
                String keywords = "";

                for (int i = 3; i < resultInfo.length; i++) {
                    String urlQuery = resultInfo[i].replaceAll(" ", "+");
                    keywords = keywords + "<a href='?query=" + urlQuery.trim() + "&page=1&searchType=web'>"
                            + resultInfo[i].trim() + "</a> <nbsp>";
                }

                // Append keywords associated with each result.
                String keyHTML = "";
                if (resultInfo.length > 3) {
                    keyHTML = "<span class='keyword'>" + keywords + "</span><br>";
                }

                // Create the final result string.
                resultStr = resultStr.concat("<a href='" + url + "'>" + title + "</a><br>" + "<span id='url'>" + url
                        + "</span><br>" + keyHTML + "<span class='rank'><b>Rank:</b> " + rank + "</span><br><br>");
            }
        }

        return this;
    }
}