diff --git a/pkg/services/searchV2/bluge.go b/pkg/services/searchV2/bluge.go
index c65624c7f16..93658636fb0 100644
--- a/pkg/services/searchV2/bluge.go
+++ b/pkg/services/searchV2/bluge.go
@@ -429,7 +429,8 @@ func doSearchQuery(
 		hasConstraints = true
 	}
 
-	if q.Query == "*" || q.Query == "" {
+	isMatchAllQuery := q.Query == "*" || q.Query == ""
+	if isMatchAllQuery {
 		if !hasConstraints {
 			fullQuery.AddShould(bluge.NewMatchAllQuery())
 		}
@@ -600,7 +601,11 @@ func doSearchQuery(
 		}
 
 		if q.Explain {
-			fScore.Append(match.Score)
+			if isMatchAllQuery {
+				fScore.Append(float64(fieldLen + q.From))
+			} else {
+				fScore.Append(match.Score)
+			}
 			if match.Explanation != nil {
 				js, _ := json.Marshal(&match.Explanation)
 				jsb := json.RawMessage(js)
diff --git a/public/app/features/search/page/components/ExplainScorePopup.tsx b/public/app/features/search/page/components/ExplainScorePopup.tsx
new file mode 100644
index 00000000000..5f14e7f7947
--- /dev/null
+++ b/public/app/features/search/page/components/ExplainScorePopup.tsx
@@ -0,0 +1,57 @@
+import React, { useState } from 'react';
+
+import { DataFrame } from '@grafana/data';
+import { CodeEditor, Modal, ModalTabsHeader, TabContent } from '@grafana/ui';
+import { DataHoverView } from 'app/plugins/panel/geomap/components/DataHoverView';
+
+export interface Props {
+  name: string;
+  explain: {};
+  frame: DataFrame;
+  row: number;
+}
+
+const tabs = [
+  { label: 'Score', value: 'score' },
+  { label: 'Fields', value: 'fields' },
+];
+
+export function ExplainScorePopup({ name, explain, frame, row }: Props) {
+  const [isOpen, setOpen] = useState<boolean>(true);
+  const [activeTab, setActiveTab] = useState('score');
+
+  const modalHeader = (
+    <ModalTabsHeader
+      title={name}
+      icon={'info'}
+      tabs={tabs}
+      activeTab={activeTab}
+      onChangeTab={(t) => {
+        setActiveTab(t.value);
+      }}
+    />
+  );
+
+  return (
+    <Modal title={modalHeader} isOpen={isOpen} onDismiss={() => setOpen(false)} closeOnBackdropClick closeOnEscape>
+      <TabContent>
+        {activeTab === tabs[0].value && (
+          <CodeEditor
+            width="100%"
+            height="70vh"
+            language="json"
+            showLineNumbers={false}
+            showMiniMap={true}
+            value={JSON.stringify(explain, null, 2)}
+            readOnly={false}
+          />
+        )}
+        {activeTab === tabs[1].value && (
+          <div>
+            <DataHoverView data={frame} rowIndex={row} />
+          </div>
+        )}
+      </TabContent>
+    </Modal>
+  );
+}
diff --git a/public/app/features/search/page/components/SearchResultsTable.tsx b/public/app/features/search/page/components/SearchResultsTable.tsx
index eb88361da35..8f30e9c1671 100644
--- a/public/app/features/search/page/components/SearchResultsTable.tsx
+++ b/public/app/features/search/page/components/SearchResultsTable.tsx
@@ -282,6 +282,11 @@ const getColumnStyles = (theme: GrafanaTheme2) => {
       text-align: right;
       padding: ${theme.spacing(1)} ${theme.spacing(3)} ${theme.spacing(1)} ${theme.spacing(1)};
     `,
+    explainItem: css`
+      text-align: right;
+      padding: ${theme.spacing(1)} ${theme.spacing(3)} ${theme.spacing(1)} ${theme.spacing(1)};
+      cursor: pointer;
+    `,
     locationCellStyle: css`
       padding-top: ${theme.spacing(1)};
       padding-right: ${theme.spacing(1)};
diff --git a/public/app/features/search/page/components/SearchView.tsx b/public/app/features/search/page/components/SearchView.tsx
index ce7377a15b2..adddc05973b 100644
--- a/public/app/features/search/page/components/SearchView.tsx
+++ b/public/app/features/search/page/components/SearchView.tsx
@@ -64,6 +64,7 @@ export const SearchView = ({
       ds_uid: query.datasource as string,
       location: folderDTO?.uid, // This will scope all results to the prefix
       sort: query.sort?.value,
+      explain: query.explain,
     };
 
     // Only dashboards have additional properties
diff --git a/public/app/features/search/page/components/columns.tsx b/public/app/features/search/page/components/columns.tsx
index 12219de34ab..9381df6b15b 100644
--- a/public/app/features/search/page/components/columns.tsx
+++ b/public/app/features/search/page/components/columns.tsx
@@ -5,11 +5,14 @@ import SVG from 'react-inlinesvg';
 import { Field, FieldType, formattedValueToString, getDisplayProcessor, getFieldDisplayName } from '@grafana/data';
 import { config, getDataSourceSrv } from '@grafana/runtime';
 import { Checkbox, Icon, IconButton, IconName, TagList } from '@grafana/ui';
+import appEvents from 'app/core/app_events';
 import { PluginIconName } from 'app/features/plugins/admin/types';
+import { ShowModalReactEvent } from 'app/types/events';
 
 import { QueryResponse, SearchResultMeta } from '../../service';
 import { SelectionChecker, SelectionToggle } from '../selection';
 
+import { ExplainScorePopup } from './ExplainScorePopup';
 import { TableColumn } from './SearchResultsTable';
 
 const TYPE_COLUMN_WIDTH = 175;
@@ -40,6 +43,10 @@ export const generateColumns = (
     availableWidth -= sortFieldWith; // pre-allocate the space for the last column
   }
 
+  if (access.explain && access.score) {
+    availableWidth -= 100; // pre-allocate the space for the last column
+  }
+
   let width = 50;
   if (selection && selectionToggle) {
     width = 30;
@@ -107,7 +114,8 @@ export const generateColumns = (
       let classNames = cx(styles.nameCellStyle);
       let name = access.name.values.get(p.row.index);
       if (!name?.length) {
-        name = 'Missing title'; // normal for panels
+        const loading = p.row.index >= response.view.dataFrame.length;
+        name = loading ? 'Loading...' : 'Missing title'; // normal for panels
         classNames += ' ' + styles.missingTitleText;
       }
       return (
@@ -196,6 +204,37 @@ export const generateColumns = (
     });
   }
 
+  if (access.explain && access.score) {
+    const vals = access.score.values;
+    const showExplainPopup = (row: number) => {
+      appEvents.publish(
+        new ShowModalReactEvent({
+          component: ExplainScorePopup,
+          props: {
+            name: access.name.values.get(row),
+            explain: access.explain.values.get(row),
+            frame: response.view.dataFrame,
+            row: row,
+          },
+        })
+      );
+    };
+
+    columns.push({
+      Header: () => <div className={styles.sortedHeader}>Score</div>,
+      Cell: (p) => {
+        return (
+          <div {...p.cellProps} className={styles.explainItem} onClick={() => showExplainPopup(p.row.index)}>
+            {vals.get(p.row.index)}
+          </div>
+        );
+      },
+      id: `column-score-field`,
+      field: access.score,
+      width: 100,
+    });
+  }
+
   return columns;
 };
 
diff --git a/public/app/features/search/reducers/searchQueryReducer.ts b/public/app/features/search/reducers/searchQueryReducer.ts
index 9def0ce0de1..84b42beed07 100644
--- a/public/app/features/search/reducers/searchQueryReducer.ts
+++ b/public/app/features/search/reducers/searchQueryReducer.ts
@@ -17,9 +17,6 @@ export const defaultQuery: DashboardQuery = {
   query: '',
   tag: [],
   starred: false,
-  skipRecent: false,
-  skipStarred: false,
-  folderIds: [],
   sort: null,
   layout: SearchLayout.Folders,
   prevSort: null,
diff --git a/public/app/features/search/service/bluge.ts b/public/app/features/search/service/bluge.ts
index 6d7e252fec7..e66f73d45a7 100644
--- a/public/app/features/search/service/bluge.ts
+++ b/public/app/features/search/service/bluge.ts
@@ -121,15 +121,12 @@ async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
     }
   }
 
-  const view = new DataFrameView<DashboardQueryResult>(first);
-  return {
-    totalRows: meta.count ?? first.length,
-    view,
-    loadMoreItems: async (startIndex: number, stopIndex: number): Promise<void> => {
-      console.log('LOAD NEXT PAGE', { startIndex, stopIndex, length: view.dataFrame.length });
+  let loadMax = 0;
+  let pending: Promise<void> | undefined = undefined;
+  const getNextPage = async () => {
+    while (loadMax > view.dataFrame.length) {
       const from = view.dataFrame.length;
-      const limit = stopIndex - from;
-      if (limit < 0) {
+      if (from >= meta.count) {
         return;
       }
       const frame = (
@@ -141,7 +138,7 @@ async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
                 search: {
                   ...(target?.search ?? {}),
                   from,
-                  limit: Math.max(limit, nextPageSizes),
+                  limit: nextPageSizes,
                 },
                 refId: 'Page',
                 facet: undefined,
@@ -175,7 +172,20 @@ async function doSearchQuery(query: SearchQuery): Promise<QueryResponse> {
           meta.locationInfo[key] = value;
         }
       }
-      return;
+    }
+    pending = undefined;
+  };
+
+  const view = new DataFrameView<DashboardQueryResult>(first);
+  return {
+    totalRows: meta.count ?? first.length,
+    view,
+    loadMoreItems: async (startIndex: number, stopIndex: number): Promise<void> => {
+      loadMax = Math.max(loadMax, stopIndex);
+      if (!pending) {
+        pending = getNextPage();
+      }
+      return pending;
     },
     isItemLoaded: (index: number): boolean => {
       return index < view.dataFrame.length;
diff --git a/public/app/features/search/service/types.ts b/public/app/features/search/service/types.ts
index e0a9eaf6aa9..adea9ba357d 100644
--- a/public/app/features/search/service/types.ts
+++ b/public/app/features/search/service/types.ts
@@ -33,6 +33,10 @@ export interface DashboardQueryResult {
   tags: string[];
   location: string; // url that can be split
   ds_uid: string[];
+
+  // debugging fields
+  score: number;
+  explain: {};
 }
 
 export interface LocationInfo {
diff --git a/public/app/features/search/types.ts b/public/app/features/search/types.ts
index 5d16446bb9d..47068c37f34 100644
--- a/public/app/features/search/types.ts
+++ b/public/app/features/search/types.ts
@@ -61,9 +61,7 @@ export interface DashboardQuery {
   query: string;
   tag: string[];
   starred: boolean;
-  skipRecent: boolean;
-  skipStarred: boolean;
-  folderIds: number[];
+  explain?: boolean; // adds debug info
   datasource?: string;
   sort: SelectableValue | null;
   // Save sorting data between layouts