de.otto.mongodb.profiler.op.Explanation.java Source code

Java tutorial

Introduction

Here is the source code for de.otto.mongodb.profiler.op.Explanation.java

Source

/*
 *  Copyright 2013 Robert Gacki <robert.gacki@cgi.com>
 *
 *  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 de.otto.mongodb.profiler.op;

import com.mongodb.DBObject;
import org.joda.time.DateTime;

import java.util.*;

import static de.otto.mongodb.profiler.util.MongoSupport.require;

/**
 * An explanation of a query.
 *
 * @author Robert Gacki
 */
public class Explanation {

    private static DBObject query(final QueryProfile profile) {

        if (profile.getQuery().containsField("$query")) {
            return require("$query", DBObject.class, profile.getQuery());
        }

        if (profile.isSnapshot() && profile.getQuery().containsField("query")) {
            return require("query", DBObject.class, profile.getQuery());
        }

        return profile.getQuery();
    }

    private static Collection<ExplanationIssue> findIssuesInExplanation(final DBObject explanationResult,
            final QueryProfile profile) {

        final List<ExplanationIssue> result = new ArrayList<>(1);

        final String cursor = ((String) explanationResult.get("cursor"));
        if (cursor != null && !cursor.toLowerCase(Locale.ENGLISH).startsWith("btreecursor ")) {

            final DBObject query = query(profile);

            // Does the query simply finds all documents
            if (query.keySet().isEmpty()) {
                // If it is a count, it will just ask for the document number without a scan
                if (profile instanceof CountProfile == false) {
                    result.add(ExplanationIssue.FULL_TABLE_SCAN);
                }
            }
            // ...or is the index missing
            else {
                result.add(ExplanationIssue.MISSING_INDEX);
            }
        }

        final Boolean scanAndOrder = ((Boolean) explanationResult.get("scanAndOrder"));
        if (scanAndOrder != null && scanAndOrder.booleanValue()) {
            result.add(ExplanationIssue.RESULTS_ORDERED_AFTER_SCAN);
        }

        return Collections.unmodifiableList(result);
    }

    private final DBObject data;
    private final DateTime ceatedAt;
    private final Collection<ExplanationIssue> issues;

    /**
     * Creates a new explanation.
     *
     * @param data     the explanation data
     * @param profile  the profile the explanation was created for
     * @param ceatedAt the date and time when the explanation was created
     */
    public Explanation(final DBObject data, final QueryProfile profile, final DateTime ceatedAt) {
        this.data = data;
        this.ceatedAt = ceatedAt;
        this.issues = findIssuesInExplanation(data, profile);
    }

    /**
     * Returns the explanation data.
     *
     * @return the explanation data
     */
    public DBObject getData() {
        return data;
    }

    /**
     * Returns the date and time the explanation was created.
     *
     * @return the date and time the explanation was created
     */
    public DateTime getCreatedAt() {
        return ceatedAt;
    }

    /**
     * Returns a collection of issues that were found in this explanation. Returns an empty collection if there are no
     * issues found.
     *
     * @return the collection of issues which may be empty
     */
    public Collection<ExplanationIssue> getIssues() {
        return issues;
    }
}