Package controllers

Source Code of controllers.SearchController

/*
* Copyright 2013 TORCH UG
*
* This file is part of Graylog2.
*
* Graylog2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Graylog2 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Graylog2.  If not, see <http://www.gnu.org/licenses/>.
*/
package controllers;

import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.net.MediaType;
import lib.SearchTools;
import lib.security.RestPermissions;
import org.graylog2.restclient.lib.APIException;
import org.graylog2.restclient.lib.ApiClient;
import org.graylog2.restclient.lib.Field;
import org.graylog2.restclient.lib.ServerNodes;
import org.graylog2.restclient.lib.timeranges.InvalidRangeParametersException;
import org.graylog2.restclient.lib.timeranges.RelativeRange;
import org.graylog2.restclient.lib.timeranges.TimeRange;
import org.graylog2.restclient.models.MessagesService;
import org.graylog2.restclient.models.SavedSearch;
import org.graylog2.restclient.models.SavedSearchService;
import org.graylog2.restclient.models.SearchSort;
import org.graylog2.restclient.models.Stream;
import org.graylog2.restclient.models.UniversalSearch;
import org.graylog2.restclient.models.api.responses.system.indices.IndexRangeSummary;
import org.graylog2.restclient.models.api.results.DateHistogramResult;
import org.graylog2.restclient.models.api.results.SearchResult;
import org.joda.time.DateTime;
import org.joda.time.Minutes;
import play.libs.Json;
import play.mvc.Result;
import views.helpers.Permissions;

import javax.inject.Inject;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class SearchController extends AuthenticatedController {
    // guess high, so we never have a bad resolution
    private static final int DEFAULT_ASSUMED_GRAPH_RESOLUTION = 4000;

    @Inject
    protected UniversalSearch.Factory searchFactory;
    @Inject
    protected MessagesService messagesService;
    @Inject
    protected SavedSearchService savedSearchService;
    @Inject
    private ServerNodes serverNodes;

    public Result globalSearch() {
        // User would not be allowed to do any global searches anyway, so we can redirect him to the streams page to avoid confusion.
        if (Permissions.isPermitted(RestPermissions.SEARCHES_ABSOLUTE)
                || Permissions.isPermitted(RestPermissions.SEARCHES_RELATIVE)
                || Permissions.isPermitted(RestPermissions.SEARCHES_KEYWORD)) {
            return ok(views.html.search.global.render(currentUser()));
        } else {
            return redirect(routes.StreamsController.index());
        }
    }

    public Result index(String q,
                        String rangeType,
                        int relative,
                        String from, String to,
                        String keyword,
                        String interval,
                        int page,
                        String savedSearchId,
                        String sortField, String sortOrder,
                        String fields,
                        int displayWidth) {
        SearchSort sort = buildSearchSort(sortField, sortOrder);

        return renderSearch(q, rangeType, relative, from, to, keyword, interval, page, savedSearchId, fields, displayWidth, sort, null, null);
    }

    protected Result renderSearch(String q, String rangeType, int relative, String from, String to, String keyword, String interval, int page, String savedSearchId, String fields, int displayWidth, SearchSort sort, Stream stream, String filter) {
        UniversalSearch search;
        try {
            search = getSearch(q, filter, rangeType, relative, from, to, keyword, page, sort);
        } catch (InvalidRangeParametersException e2) {
            return status(400, views.html.errors.error.render("Invalid range parameters provided.", e2, request()));
        } catch (IllegalArgumentException e1) {
            return status(400, views.html.errors.error.render("Invalid range type provided.", e1, request()));
        }

        SearchResult searchResult;
        DateHistogramResult histogramResult;
        SavedSearch savedSearch;
        Set<String> selectedFields = getSelectedFields(fields);
        String formattedHistogramResults;

        try {
            if (savedSearchId != null && !savedSearchId.isEmpty()) {
                savedSearch = savedSearchService.get(savedSearchId);
            } else {
                savedSearch = null;
            }

            searchResult = search.search();
            if (searchResult.getError() != null) {
                return ok(views.html.search.queryerror.render(currentUser(), q, searchResult, savedSearch, fields, stream));
            }
            searchResult.setAllFields(getAllFields());

            // histogram resolution (strangely aka interval)
            if (interval == null || interval.isEmpty() || !SearchTools.isAllowedDateHistogramInterval(interval)) {
                interval = determineHistogramResolution(searchResult);
            }
            histogramResult = search.dateHistogram(interval);
            formattedHistogramResults = formatHistogramResults(histogramResult.getResults(), displayWidth);
        } catch (IOException e) {
            return status(504, views.html.errors.error.render(ApiClient.ERROR_MSG_IO, e, request()));
        } catch (APIException e) {
            String message = "There was a problem with your search. We expected HTTP 200, but got a HTTP " + e.getHttpCode() + ".";
            return status(504, views.html.errors.error.render(message, e, request()));
        }

        if (searchResult.getTotalResultCount() > 0) {
            return ok(views.html.search.results.render(currentUser(), search, searchResult, histogramResult, formattedHistogramResults, q, page, savedSearch, selectedFields, serverNodes.asMap(), stream));
        } else {
            return ok(views.html.search.noresults.render(currentUser(), q, searchResult, savedSearch, selectedFields, stream));
        }
    }

    protected String determineHistogramResolution(final SearchResult searchResult) {
        final String interval;
        final int HOUR = 60;
        final int DAY = HOUR * 24;
        final int WEEK = DAY * 7;
        final int MONTH = HOUR * 24 * 30;
        final int YEAR = MONTH * 12;

        // Return minute as default resolution if search from and to DateTimes are not available
        if (searchResult.getFromDateTime() == null && searchResult.getToDateTime() == null) {
            return "minute";
        }

        int queryRangeInMinutes;

        // We don't want to use fromDateTime coming from the search query if the user asked for all messages
        if (isEmptyRelativeRange(searchResult.getTimeRange())) {
            List<IndexRangeSummary> usedIndices = searchResult.getUsedIndices();
            IndexRangeSummary oldestIndex = usedIndices.get(usedIndices.size() - 1);
            queryRangeInMinutes = Minutes.minutesBetween(DateTime.parse(oldestIndex.starts), searchResult.getToDateTime()).getMinutes();
        } else {
            queryRangeInMinutes = Minutes.minutesBetween(searchResult.getFromDateTime(), searchResult.getToDateTime()).getMinutes();
        }

        if (queryRangeInMinutes < DAY / 2) {
            interval = "minute";
        } else if (queryRangeInMinutes < DAY * 2) {
            interval = "hour";
        } else if (queryRangeInMinutes < MONTH) {
            interval = "day";
        } else if (queryRangeInMinutes < MONTH * 6) {
            interval = "week";
        } else if (queryRangeInMinutes < YEAR * 2) {
            interval = "month";
        } else if (queryRangeInMinutes < YEAR * 10) {
            interval = "quarter";
        } else {
            interval = "year";
        }
        return interval;
    }

    private boolean isEmptyRelativeRange(TimeRange timeRange) {
        return (timeRange.getType() == TimeRange.Type.RELATIVE) && (((RelativeRange) timeRange).isEmptyRange());
    }

    /**
     * [{ x: -1893456000, y: 92228531 }, { x: -1577923200, y: 106021568 }]
     *
     * @return A JSON string representation of the result, suitable for Rickshaw data graphing.
     */
    protected String formatHistogramResults(Map<String, Long> histogramResults, int displayWidth) {
        final int saneDisplayWidth = (displayWidth == -1 || displayWidth < 100 || displayWidth > DEFAULT_ASSUMED_GRAPH_RESOLUTION) ? DEFAULT_ASSUMED_GRAPH_RESOLUTION : displayWidth;
        final List<Map<String, Long>> points = Lists.newArrayList();

        // using the absolute value guarantees, that there will always be enough values for the given resolution
        final int factor = (saneDisplayWidth != -1 && histogramResults.size() > saneDisplayWidth) ? histogramResults.size() / saneDisplayWidth : 1;

        int index = 0;
        for (Map.Entry<String, Long> result : histogramResults.entrySet()) {
            // TODO: instead of sampling we might consider interpolation (compare DashboardsApiController)
            if (index % factor == 0) {
                Map<String, Long> point = Maps.newHashMap();
                point.put("x", Long.parseLong(result.getKey()));
                point.put("y", result.getValue());

                points.add(point);
            }
            index++;
        }

        return Json.stringify(Json.toJson(points));
    }

    protected Set<String> getSelectedFields(String fields) {
        Set<String> selectedFields = Sets.newLinkedHashSet();
        if (fields != null && !fields.isEmpty()) {
            Iterables.addAll(selectedFields, Splitter.on(',').split(fields));
        } else {
            selectedFields.addAll(Field.STANDARD_SELECTED_FIELDS);
        }
        return selectedFields;
    }

    public Result exportAsCsv(String q, String filter, String rangeType, int relative, String from, String to, String keyword, String fields) {
        UniversalSearch search;
        try {
            search = getSearch(q, filter.isEmpty() ? null : filter, rangeType, relative, from, to, keyword, 0, UniversalSearch.DEFAULT_SORT);
        } catch (InvalidRangeParametersException e2) {
            return status(400, views.html.errors.error.render("Invalid range parameters provided.", e2, request()));
        } catch (IllegalArgumentException e1) {
            return status(400, views.html.errors.error.render("Invalid range type provided.", e1, request()));
        }

        final String s;
        try {
            Set<String> selectedFields = getSelectedFields(fields);
            s = search.searchAsCsv(selectedFields);
        } catch (IOException e) {
            return status(504, views.html.errors.error.render(ApiClient.ERROR_MSG_IO, e, request()));
        } catch (APIException e) {
            String message = "There was a problem with your search. We expected HTTP 200, but got a HTTP " + e.getHttpCode() + ".";
            return status(504, views.html.errors.error.render(message, e, request()));
        }

        // TODO streaming the result
        response().setContentType(MediaType.CSV_UTF_8.toString());
        response().setHeader("Content-Disposition", "attachment; filename=graylog2-searchresult.csv");
        return ok(s);
    }

    protected List<Field> getAllFields() {
        List<Field> allFields = Lists.newArrayList();
        for (String f : messagesService.getMessageFields()) {
            allFields.add(new Field(f));
        }
        return allFields;
    }

    protected UniversalSearch getSearch(String q, String filter, String rangeType, int relative, String from, String to, String keyword, int page, SearchSort order)
            throws InvalidRangeParametersException, IllegalArgumentException {
        if (q == null || q.trim().isEmpty()) {
            q = "*";
        }

        // Determine timerange type.
        TimeRange timerange = TimeRange.factory(rangeType, relative, from, to, keyword);

        UniversalSearch search;
        if (filter == null) {
            search = searchFactory.queryWithRangePageAndOrder(q, timerange, page, order);
        } else {
            search = searchFactory.queryWithFilterRangePageAndOrder(q, filter, timerange, page, order);
        }

        return search;
    }

    protected SearchSort buildSearchSort(String sortField, String sortOrder) {
        if (sortField == null || sortOrder == null || sortField.isEmpty() || sortOrder.isEmpty()) {
            return UniversalSearch.DEFAULT_SORT;
        }

        try {
            return new SearchSort(sortField, SearchSort.Direction.valueOf(sortOrder.toUpperCase()));
        } catch (IllegalArgumentException e) {
            return UniversalSearch.DEFAULT_SORT;
        }
    }
}
TOP

Related Classes of controllers.SearchController

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.