diff --git a/data/reusables/repositories/rulesets-commit-regex.md b/data/reusables/repositories/rulesets-commit-regex.md index 506e491e77e9..3c73ac70a3e9 100644 --- a/data/reusables/repositories/rulesets-commit-regex.md +++ b/data/reusables/repositories/rulesets-commit-regex.md @@ -2,7 +2,7 @@ When you add metadata restrictions, you can use regular expression syntax to def Rulesets support RE2 syntax. For more information, see Google's [syntax guide](https://github.com/google/re2/wiki/Syntax). To validate your expressions, you can use the validator on [regex101.com](https://regex101.com/), selecting the "Golang" flavor in the left sidebar. -Regular expressions consider multiple lines of text by default. For example, if you have a multiline commit message, the pattern `^ABC` will be a match if any line in the message starts with `ABC`. To match the start of the message specifically, you can start your expression with `\A`. +By default, regular expressions in metadata restrictions do not consider multiple lines of text. For example, if you have a multiline commit message, the pattern `^ABC` will be a match if the first line of the message starts with `ABC`. To match multiple lines of the message, start your expression with `(?m)`. The negative lookahead assertion, denoted `?!`, is not supported. However, for cases where you need to look for a given string that is not followed by another given string, you can use the positive lookahead assertion, denoted `?`, combined with the "Must not match a given regex pattern" requirement. diff --git a/src/search/components/SearchResults.tsx b/src/search/components/SearchResults.tsx index e005f8e73456..a6e5aa46c482 100644 --- a/src/search/components/SearchResults.tsx +++ b/src/search/components/SearchResults.tsx @@ -3,32 +3,30 @@ import { SearchIcon } from '@primer/octicons-react' import { useRouter } from 'next/router' import cx from 'classnames' -import type { SearchResultsT, SearchResultHitT } from './types' +import type { SearchResultsT, SearchResultHitT, SearchQueryT } from './types' import { useTranslation } from 'components/hooks/useTranslation' import { Link } from 'components/Link' -import { useQuery } from 'src/search/components/useQuery' import { sendEvent, EventType } from 'src/events/components/events' import styles from './SearchResults.module.scss' type Props = { results: SearchResultsT - query: string + search: SearchQueryT } -export function SearchResults({ results, query }: Props) { +export function SearchResults({ results, search }: Props) { const pages = Math.ceil(results.meta.found.value / results.meta.size) const { page } = results.meta return (
- + {pages > 1 && }
) } -function SearchResultHits({ hits, query }: { hits: SearchResultHitT[]; query: string }) { - const { debug } = useQuery() +function SearchResultHits({ hits, search }: { hits: SearchResultHitT[]; search: SearchQueryT }) { return (
{hits.length === 0 && } @@ -36,10 +34,10 @@ function SearchResultHits({ hits, query }: { hits: SearchResultHitT[]; query: st ))}
@@ -117,7 +115,7 @@ function SearchResultHit({ function ResultsPagination({ page, totalPages }: { page: number; totalPages: number }) { const router = useRouter() - const [asPathRoot, asPathQuery = ''] = router.asPath.split('?') + const [asPathRoot, asPathQuery = ''] = router.asPath.split('#')[0].split('?') function hrefBuilder(page: number) { const params = new URLSearchParams(asPathQuery) diff --git a/src/search/components/index.tsx b/src/search/components/index.tsx index 03b918ebc11c..ed7997de141a 100644 --- a/src/search/components/index.tsx +++ b/src/search/components/index.tsx @@ -63,7 +63,7 @@ export function Search({ search }: Props) { ) : null} - {results ? : null} + {results ? : null} ) } diff --git a/src/search/components/types.ts b/src/search/components/types.ts index 4d23eeb91a17..f7ce0c5f458c 100644 --- a/src/search/components/types.ts +++ b/src/search/components/types.ts @@ -32,21 +32,12 @@ export type SearchResultsT = { export type SearchQueryT = { query: string - version: string - language: string - size: number - page: number - sort: string - highlights: string[] - autocomplete: boolean debug: boolean - include: string[] - indexName: string } export type SearchValidationErrorT = { error: string - key: string + // key: string } export type SearchT = { diff --git a/src/search/middleware/contextualize.js b/src/search/middleware/contextualize.js index 358b78032fb6..9784f1eb5f1e 100644 --- a/src/search/middleware/contextualize.js +++ b/src/search/middleware/contextualize.js @@ -1,5 +1,6 @@ import got from 'got' import { errors } from '@elastic/elasticsearch' +import statsd from '#src/observability/lib/statsd.js' import { getPathWithoutVersion, getPathWithoutLanguage } from '../../../lib/path-utils.js' import { getSearchFromRequest } from './get-search-request.js' @@ -61,8 +62,10 @@ export default async function contextualizeSearch(req, res, next) { // In local dev, you get to see the error. In production, // you get a "Oops! Something went wrong" which involves a Failbot // send. + const tags = [`indexName:${search.indexName}`] + const timed = statsd.asyncTimer(getSearchResults, 'contextualize.search', tags) try { - req.context.search.results = await getSearchResults(search) + req.context.search.results = await timed(search) } catch (error) { // If the error coming from the Elasticsearch client is any sort // of 4xx error, it will be bubbled up to the next middleware diff --git a/src/search/pages/search.tsx b/src/search/pages/search.tsx index 650a48a5f814..37da6bb1b05d 100644 --- a/src/search/pages/search.tsx +++ b/src/search/pages/search.tsx @@ -42,7 +42,31 @@ export const getServerSideProps: GetServerSideProps = async (context) => // This should have been done by the middleware. throw new Error('Expected req.context to be populated with .search') } - const search: SearchT = req.context.search + + // The `req.context.search` is similar to what's needed to React + // render the search result page. + // But it contains information (from the contextualizing) that is + // not needed to display search results. + // For example, the `req.context.search.search` contains things like + // `page` and `indexName` which was useful when it made the actual + // Elasticsearch query. But it's not needed to render the results. + // We explicitly pick out the parts that are needed, only. + const search: SearchT = { + search: { + query: req.context.search.search.query, + debug: req.context.search.search.debug, + }, + validationErrors: req.context.search.validationErrors, + } + // If there are no results (e.g. /en/search?query=) from the + // contextualizing, then `req.context.search.results` will + // be `undefined` which can't be serialized as a prop, using JSON.stringify. + if (req.context.search.results) { + search.results = { + meta: req.context.search.results.meta, + hits: req.context.search.results.hits, + } + } return { props: {