From cddc4918044eb9504396bb315b511c19f09f256c Mon Sep 17 00:00:00 2001 From: Nico Gulden Date: Tue, 17 Oct 2023 15:51:19 +0200 Subject: [PATCH 1/4] Add plugin for Matomo to track site search keywords Use the plugins API for autocomplete, hook into the state change, retrieve the query term from the use, and send the query term together with the search results from the global context to Matomo. * autocomplete plugins: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/plugins/ * autocomplete state: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/state/ * autocomplete access data with context: https://www.algolia.com/doc/ui-libraries/autocomplete/core-concepts/context/ https://developer.matomo.org/guides/tracking-javascript-guide#internal-search-tracking Relates to https://github.com/typesense/typesense-docsearch.js/issues/16 --- .../docsearch-react/src/DocSearchModal.tsx | 5 ++++ packages/docsearch-react/src/MatomoPlugin.tsx | 24 +++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 packages/docsearch-react/src/MatomoPlugin.tsx diff --git a/packages/docsearch-react/src/DocSearchModal.tsx b/packages/docsearch-react/src/DocSearchModal.tsx index 56f7bb98..a1c89a92 100644 --- a/packages/docsearch-react/src/DocSearchModal.tsx +++ b/packages/docsearch-react/src/DocSearchModal.tsx @@ -2,6 +2,8 @@ import type { AutocompleteState } from '@algolia/autocomplete-core'; import { createAutocomplete } from '@algolia/autocomplete-core'; import React from 'react'; +import { createMatomoPlugin } from './MatomoPlugin'; + import { MAX_QUERY_SIZE } from './constants'; import type { DocSearchProps } from './DocSearch'; import type { FooterTranslations } from './Footer'; @@ -125,6 +127,8 @@ export function DocSearchModal({ [favoriteSearches, recentSearches, disableUserPersonalization] ); + const matomoPlugin = createMatomoPlugin(); + const autocomplete = React.useMemo( () => createAutocomplete< @@ -282,6 +286,7 @@ export function DocSearchModal({ ); }); }, + plugins: [matomoPlugin], }), [ typesenseCollectionName, diff --git a/packages/docsearch-react/src/MatomoPlugin.tsx b/packages/docsearch-react/src/MatomoPlugin.tsx new file mode 100644 index 00000000..7f562692 --- /dev/null +++ b/packages/docsearch-react/src/MatomoPlugin.tsx @@ -0,0 +1,24 @@ +import debounce from 'lodash/debounce'; + +declare global { + interface Window { + _paq: any; + } +} + +function _matomoSiteSearch(query: string, hits: string) { + var _paq = window._paq = window._paq || []; + if (query.length > 0) { + _paq.push(['trackSiteSearch', query, false, hits]); + } +} + +var matomoSiteSearch_debounced = debounce(_matomoSiteSearch, 400); + +export function createMatomoPlugin() { + return { + onStateChange({ state }) { + matomoSiteSearch_debounced(state.query, state.context.nbHits); + } + }; +}; \ No newline at end of file From 4df8dff89edffba135dcd8362d3ff0c21ce04fb0 Mon Sep 17 00:00:00 2001 From: Nico Gulden Date: Fri, 20 Oct 2023 13:18:55 +0200 Subject: [PATCH 2/4] Matomo plugin: Add conditions for sending search analytics data The Matomo plugin only sends data under the following conditions: * The modal UI is opened * The query string has length greater than 0 * The query term wasn't used before Increase debounce limit to 500 ms. --- packages/docsearch-react/src/MatomoPlugin.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/packages/docsearch-react/src/MatomoPlugin.tsx b/packages/docsearch-react/src/MatomoPlugin.tsx index 7f562692..5a2eaf8f 100644 --- a/packages/docsearch-react/src/MatomoPlugin.tsx +++ b/packages/docsearch-react/src/MatomoPlugin.tsx @@ -6,19 +6,22 @@ declare global { } } +var query_cache = ""; + function _matomoSiteSearch(query: string, hits: string) { + query_cache = query; var _paq = window._paq = window._paq || []; - if (query.length > 0) { - _paq.push(['trackSiteSearch', query, false, hits]); - } + _paq.push(['trackSiteSearch', query, false, hits]); } -var matomoSiteSearch_debounced = debounce(_matomoSiteSearch, 400); +var matomoSiteSearch_debounced = debounce(_matomoSiteSearch, 500); export function createMatomoPlugin() { return { onStateChange({ state }) { - matomoSiteSearch_debounced(state.query, state.context.nbHits); - } + if ( state.isOpen && state.query.length > 0 && query_cache !== state.query ) { + matomoSiteSearch_debounced(state.query, state.context.nbHits); + } + }, }; }; \ No newline at end of file From a546796fceab232d6ba8b20c49fe45e95a7a3e8d Mon Sep 17 00:00:00 2001 From: Nico Gulden Date: Fri, 20 Oct 2023 13:25:37 +0200 Subject: [PATCH 3/4] Matomo plugin: Add configuration switch To activate the Matomo plugin, add `matomoSearchAnalytics: true` to the properties in the call to the docsearch function. Per default, the plugin is deactivated. --- packages/docsearch-react/src/DocSearch.tsx | 1 + packages/docsearch-react/src/DocSearchModal.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/packages/docsearch-react/src/DocSearch.tsx b/packages/docsearch-react/src/DocSearch.tsx index c769c657..9106b55d 100644 --- a/packages/docsearch-react/src/DocSearch.tsx +++ b/packages/docsearch-react/src/DocSearch.tsx @@ -43,6 +43,7 @@ export interface DocSearchProps { navigator?: AutocompleteOptions['navigator']; translations?: DocSearchTranslations; getMissingResultsUrl?: ({ query }: { query: string }) => string; + matomoSearchAnalytics?: boolean; } export function DocSearch(props: DocSearchProps) { diff --git a/packages/docsearch-react/src/DocSearchModal.tsx b/packages/docsearch-react/src/DocSearchModal.tsx index a1c89a92..dbfc710b 100644 --- a/packages/docsearch-react/src/DocSearchModal.tsx +++ b/packages/docsearch-react/src/DocSearchModal.tsx @@ -52,6 +52,7 @@ export function DocSearchModal({ initialQuery: initialQueryFromProp = '', translations = {}, getMissingResultsUrl, + matomoSearchAnalytics = false, }: DocSearchModalProps) { const { footer: footerTranslations, @@ -127,7 +128,12 @@ export function DocSearchModal({ [favoriteSearches, recentSearches, disableUserPersonalization] ); - const matomoPlugin = createMatomoPlugin(); + var plugins_to_load: any = []; + + if (matomoSearchAnalytics) { + const matomoPlugin = createMatomoPlugin(); + plugins_to_load.push(matomoPlugin); + }; const autocomplete = React.useMemo( () => @@ -286,7 +292,7 @@ export function DocSearchModal({ ); }); }, - plugins: [matomoPlugin], + plugins: plugins_to_load, }), [ typesenseCollectionName, From 1a73cc39d92c94eb102a57acd8249cf2a0889ecc Mon Sep 17 00:00:00 2001 From: Nico Gulden Date: Mon, 15 Jan 2024 14:02:18 +0100 Subject: [PATCH 4/4] feat(MatomoPlugin): At least 3 characters for Search analytics Only send search terms when they have a least three characters of length. Otherwise, search analytics is cluttered with strings of no further use. Refs #4 --- packages/docsearch-react/src/MatomoPlugin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/docsearch-react/src/MatomoPlugin.tsx b/packages/docsearch-react/src/MatomoPlugin.tsx index 5a2eaf8f..350cd54b 100644 --- a/packages/docsearch-react/src/MatomoPlugin.tsx +++ b/packages/docsearch-react/src/MatomoPlugin.tsx @@ -19,7 +19,7 @@ var matomoSiteSearch_debounced = debounce(_matomoSiteSearch, 500); export function createMatomoPlugin() { return { onStateChange({ state }) { - if ( state.isOpen && state.query.length > 0 && query_cache !== state.query ) { + if ( state.isOpen && state.query.length > 2 && query_cache !== state.query ) { matomoSiteSearch_debounced(state.query, state.context.nbHits); } },