All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.lucene.sandbox.search.QueryProfilerTree Maven / Gradle / Ivy

There is a newer version: 10.0.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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 org.apache.lucene.sandbox.search;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.List;
import org.apache.lucene.search.Query;

/**
 * This class tracks the dependency tree for queries (scoring and rewriting) and generates {@link
 * QueryProfilerBreakdown} for each node in the tree.
 */
class QueryProfilerTree {

  private final ArrayList breakdowns;
  /** Maps the Query to it's list of children. This is basically the dependency tree */
  private final ArrayList> tree;
  /** A list of the original queries, keyed by index position */
  private final ArrayList queries;
  /** A list of top-level "roots". Each root can have its own tree of profiles */
  private final ArrayList roots;
  /** A temporary stack used to record where we are in the dependency tree. */
  private final Deque stack;

  private int currentToken = 0;

  /** Rewrite time */
  private long rewriteTime;

  private long rewriteScratch;

  public QueryProfilerTree() {
    breakdowns = new ArrayList<>(10);
    stack = new ArrayDeque<>(10);
    tree = new ArrayList<>(10);
    queries = new ArrayList<>(10);
    roots = new ArrayList<>(10);
  }

  /**
   * Returns a {@link QueryProfilerBreakdown} for a scoring query. Scoring queries (e.g. those that
   * are past the rewrite phase and are now being wrapped by createWeight() ) follow a recursive
   * progression. We can track the dependency tree by a simple stack
   *
   * 

The only hiccup is that the first scoring query will be identical to the last rewritten * query, so we need to take special care to fix that * * @param query The scoring query we wish to profile * @return A ProfileBreakdown for this query */ public QueryProfilerBreakdown getProfileBreakdown(Query query) { int token = currentToken; boolean stackEmpty = stack.isEmpty(); // If the stack is empty, we are a new root query if (stackEmpty) { // We couldn't find a rewritten query to attach to, so just add it as a // top-level root. This is just a precaution: it really shouldn't happen. // We would only get here if a top-level query that never rewrites for some reason. roots.add(token); // Increment the token since we are adding a new node, but notably, do not // updateParent() because this was added as a root currentToken += 1; stack.add(token); return addDependencyNode(query, token); } updateParent(token); // Increment the token since we are adding a new node currentToken += 1; stack.add(token); return addDependencyNode(query, token); } /** * Helper method to add a new node to the dependency tree. * *

Initializes a new list in the dependency tree, saves the query and generates a new {@link * QueryProfilerBreakdown} to track the timings of this query. * * @param query The query to profile * @param token The assigned token for this query * @return A {@link QueryProfilerBreakdown} to profile this query */ private QueryProfilerBreakdown addDependencyNode(Query query, int token) { // Add a new slot in the dependency tree tree.add(new ArrayList<>(5)); // Save our query for lookup later queries.add(query); QueryProfilerBreakdown breakdown = createProfileBreakdown(); breakdowns.add(token, breakdown); return breakdown; } private QueryProfilerBreakdown createProfileBreakdown() { return new QueryProfilerBreakdown(); } /** Removes the last (e.g. most recent) value on the stack */ public void pollLast() { stack.pollLast(); } /** * After the query has been run and profiled, we need to merge the flat timing map with the * dependency graph to build a data structure that mirrors the original query tree * * @return a hierarchical representation of the profiled query tree */ public List getTree() { ArrayList results = new ArrayList<>(roots.size()); for (Integer root : roots) { results.add(doGetTree(root)); } return results; } /** * Recursive helper to finalize a node in the dependency tree * * @param token The node we are currently finalizing * @return A hierarchical representation of the tree inclusive of children at this level */ private QueryProfilerResult doGetTree(int token) { Query query = queries.get(token); QueryProfilerBreakdown breakdown = breakdowns.get(token); List children = tree.get(token); List childrenProfileResults = Collections.emptyList(); if (children != null) { childrenProfileResults = new ArrayList<>(children.size()); for (Integer child : children) { QueryProfilerResult childNode = doGetTree(child); childrenProfileResults.add(childNode); } } // TODO this would be better done bottom-up instead of top-down to avoid // calculating the same times over and over...but worth the effort? String type = getTypeFromQuery(query); String description = getDescriptionFromQuery(query); return new QueryProfilerResult( type, description, breakdown.toBreakdownMap(), breakdown.toTotalTime(), childrenProfileResults); } private String getTypeFromQuery(Query query) { // Anonymous classes won't have a name, // we need to get the super class if (query.getClass().getSimpleName().isEmpty()) { return query.getClass().getSuperclass().getSimpleName(); } return query.getClass().getSimpleName(); } private String getDescriptionFromQuery(Query query) { return query.toString(); } /** * Internal helper to add a child to the current parent node * * @param childToken The child to add to the current parent */ private void updateParent(int childToken) { Integer parent = stack.peekLast(); ArrayList parentNode = tree.get(parent); parentNode.add(childToken); tree.set(parent, parentNode); } /** Begin timing a query for a specific Timing context */ public void startRewriteTime() { assert rewriteScratch == 0; rewriteScratch = System.nanoTime(); } /** * Halt the timing process and add the elapsed rewriting time. startRewriteTime() must be called * for a particular context prior to calling stopAndAddRewriteTime(), otherwise the elapsed time * will be negative and nonsensical * * @return The elapsed time */ public long stopAndAddRewriteTime() { long time = Math.max(1, System.nanoTime() - rewriteScratch); rewriteTime += time; rewriteScratch = 0; return time; } public long getRewriteTime() { return rewriteTime; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy