org.loklak.api.server.SuggestServlet.java Source code

Java tutorial

Introduction

Here is the source code for org.loklak.api.server.SuggestServlet.java

Source

/**
 *  SuggestServlet
 *  Copyright 29.04.2015 by Michael Peter Christen, @0rb1t3r
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *  
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public License
 *  along with this program in the file lgpl21.txt
 *  If not, see <http://www.gnu.org/licenses/>.
 */

package org.loklak.api.server;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.elasticsearch.search.sort.SortOrder;
import org.loklak.data.DAO;
import org.loklak.harvester.SourceType;
import org.loklak.http.RemoteAccess;
import org.loklak.objects.AbstractIndexEntry;
import org.loklak.objects.QueryEntry;
import org.loklak.objects.ResultList;

import com.fasterxml.jackson.databind.ObjectMapper;

/*
 * - test suggestions -
 * 
 * most common queries
 * http://localhost:9000/api/suggest.json?q=beer&orderby=query_count&order=desc
 *
 * random list of re-load queries
 * http://localhost:9000/api/suggest.json?source=query&orderby=retrieval_next&order=asc&count=1000&until=now&random=3
 */

public class SuggestServlet extends HttpServlet {

    private static final long serialVersionUID = 8578478303032749879L;

    public static Map<Integer, Map<String, Object>> cache = new ConcurrentHashMap<>();

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        RemoteAccess.Post post = RemoteAccess.evaluate(request);

        // manage DoS
        if (post.isDoS_blackout()) {
            response.sendError(503, "your request frequency is too high");
            return;
        }

        String callback = post.get("callback", "");
        boolean jsonp = callback != null && callback.length() > 0;
        boolean minified = post.get("minified", false);

        int requestkey = post.hashCode();
        Map<String, Object> m = post.isDoS_servicereduction() ? cache.get(requestkey) : null;
        if (m == null) {
            boolean local = post.isLocalhostAccess();
            boolean delete = post.get("delete", false);
            int count = post.get("count", 10); // number of queries
            String query = post.get("q", ""); // to get a list of queries which match; to get all latest: leave q empty
            String source = post.get("source", "all"); // values: all,query,geo
            String orders = post.get("order", query.length() == 0 ? "desc" : "asc").toUpperCase();
            SortOrder order = SortOrder.valueOf(orders);
            String orderby = post.get("orderby", query.length() == 0 ? "retrieval_next" : "query_count");
            int timezoneOffset = post.get("timezoneOffset", 0);
            Date since = post.get("since", "").equals("now") ? new Date()
                    : post.get("since", (Date) null, timezoneOffset);
            Date until = post.get("until", "").equals("now") ? new Date()
                    : post.get("until", (Date) null, timezoneOffset);
            String selectby = post.get("selectby", "retrieval_next");
            ResultList<QueryEntry> queryList = new ResultList<>();

            if ((source.equals("all") || source.equals("query")) && query.length() >= 0) {
                long start = System.currentTimeMillis();
                queryList = DAO.SearchLocalQueries(query, count, orderby, "long", order, since, until, selectby);
                post.recordEvent("localqueries_time", System.currentTimeMillis() - start);
            }

            if (delete && local && queryList.size() > 0) {
                long start = System.currentTimeMillis();
                for (QueryEntry qe : queryList)
                    DAO.deleteQuery(qe.getQuery(), qe.getSourceType());
                queryList.clear();
                queryList = DAO.SearchLocalQueries(query, count, orderby, "long", order, since, until, selectby);
                post.recordEvent("localquerydelete_time", System.currentTimeMillis() - start);
            }

            if (source.equals("all") || source.equals("geo")) {
                long start = System.currentTimeMillis();
                LinkedHashSet<String> suggestions = DAO.geoNames.suggest(query, count, 0);
                if (suggestions.size() < count && query.length() > 2)
                    suggestions.addAll(DAO.geoNames.suggest(query, count, 1));
                if (suggestions.size() < count && query.length() > 5)
                    suggestions.addAll(DAO.geoNames.suggest(query, count, 2));
                for (String s : suggestions) {
                    QueryEntry qe = new QueryEntry(s, 0, Long.MAX_VALUE, SourceType.IMPORT, false);
                    queryList.add(qe);
                }
                post.recordEvent("suggestionsquery_time", System.currentTimeMillis() - start);
            }

            long start = System.currentTimeMillis();
            post.setResponse(response, "application/javascript");

            List<Object> queries = new ArrayList<>();
            if (queryList != null)
                for (QueryEntry t : queryList)
                    queries.add(t.toMap());

            int random = post.get("random", -1);
            if (random > 0 && random < queries.size()) {
                // take the given number from the result list and use random to choose
                List<Object> random_queries = new ArrayList<>();
                Random r = new Random(System.currentTimeMillis());
                while (random-- > 0) {
                    random_queries.add(queries.remove(r.nextInt(queries.size())));
                    int shrink = Math.max(queries.size() / 2, random * 10);
                    while (queries.size() > shrink)
                        queries.remove(queries.size() - 1); // prefer from top
                }
                queries = random_queries;
            }

            // generate json
            m = new LinkedHashMap<String, Object>();
            Map<String, Object> metadata = new LinkedHashMap<String, Object>();
            metadata.put("count", queryList == null ? "0" : Integer.toString(queries.size()));
            metadata.put("hits", queryList.getHits());
            metadata.put("query", query);
            metadata.put("order", orders);
            metadata.put("orderby", orderby);
            if (since != null)
                metadata.put("since", AbstractIndexEntry.utcFormatter.print(since.getTime()));
            if (until != null)
                metadata.put("until", AbstractIndexEntry.utcFormatter.print(until.getTime()));
            if (since != null || until != null)
                metadata.put("selectby", selectby);
            metadata.put("client", post.getClientHost());
            m.put("search_metadata", metadata);

            m.put("queries", queries);
            post.recordEvent("postprocessing_time", System.currentTimeMillis() - start);
        }

        // write json
        response.setCharacterEncoding("UTF-8");
        PrintWriter sos = response.getWriter();
        if (jsonp)
            sos.print(callback + "(");
        sos.print(minified ? new ObjectMapper().writer().writeValueAsString(m)
                : new ObjectMapper().writerWithDefaultPrettyPrinter().writeValueAsString(m));
        if (jsonp)
            sos.println(");");
        sos.println();
        post.finalize();
    }

}