diff --git a/documentation/projects/proposals/additional_search_views/20230719-implementation_plan_additional_search_views.md b/documentation/projects/proposals/additional_search_views/20230719-implementation_plan_additional_search_views.md index 060294a5c8b..d636995da0c 100644 --- a/documentation/projects/proposals/additional_search_views/20230719-implementation_plan_additional_search_views.md +++ b/documentation/projects/proposals/additional_search_views/20230719-implementation_plan_additional_search_views.md @@ -18,134 +18,200 @@ - [Project Thread](https://github.com/WordPress/openverse/issues/410) - [Project Proposal](https://docs.openverse.org/projects/proposals/additional_search_views/20230424-project_proposal_additional_search_views.html) +- [Milestone](https://github.com/WordPress/openverse/milestone/17) -## Overview +## Expected Outcomes - + -This plan describes the changes that need to be added on the frontend: the new -components, pages and store. +API endpoints return all media with the selected tag, from the selected source +or by the selected creator, sorted by date added to Openverse. -## Expected Outcomes +Frontend allows to browse media items by a selected creator, source, or with a +selected tag. - +The single result pages link to these collection views; the external links are +also updated to clearly show that they are external. -Three collection pages are added to Openverse: +## Step-by-step plan -- items with the selected tag -- items from the selected source (provider) -- items by the selected creator. +1. Update the Elasticsearch index to enable exact matching of the `tag`, + `source` and `creator` fields (both the query analyzer and the index + analyzer). This will require reindexing. +2. Add API endpoints for exact matching of the `tag`, `source` and `creator` + fields. +3. Create the new components: `VCollectionHeader`, `VCollectionLink` and `VTag`. +4. Update the store and utils used to construct the API query to allow for + searching by `tag`, `creator` or `source`, in addition to the current search + by title/description/tags combination. +5. Add a switchable "additional_search_views" feature flag. +6. Create a page for `tag` / `creator` /`source` collections. The page should + handle fetching and updating the search store state. +7. Update the single result pages: tags area, the "creator" and "source" area + under the main media item. +8. Add the Analytics event `VISIT_SOURCE_LINK` and update where + `VISIT_CREATOR_LINK` is sent. +9. Cleanup after the feature flag is removed: + - Remove conditional rendering on the single result pages. + - Remove the `additional_search_views` feature flag and `VMediaTag` + component. -The pages re-use the existing image grid and audio collection views and can load -more images or audio items on the Load more button click. The single result -pages are updated to add the links to these pages, as seen in the Figma mockups: +## Step details -- [Image single result](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-63284&mode=dev) -- [Audio single result](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-63285&mode=dev) +### 1. Elasticsearch updates -For the creator pages, a separate API media view is added to get the items that -match the creator and source pair exactly. +Currently, when filtering the search results, the API matches some query +parameters in a fuzzy way: an item matches the query if the field value contains +the query string as a separate word. When indexing the items, we "analyze" them, +which means that we split the field values by whitespace and stem them. We also +do the same to the query string. This means that the query string "bike" will +match the field value "bikes", "biking", or "bike shop". -## Step-by-step plan +For these pages, however, we need an exact match. -The work on adding the new components can be done independently because it does -not affect the existing pages. This includes the following steps: +One alternative implementation considered when writing this plan was to use the +database instead of the Elasticsearch to get the results. This would make it +easy to get the exact matches. However, there are some problems with using the +database rather than ES to access anything: -- Extract the `VAudioCollection` component -- Add the `VCollectionHeader` component -- Add the `VCollectionLink` component -- Add the new `VTag` component -- Update links in the "information" section -- Add an analytics event for visiting source `VISIT_SOURCE_LINK` that is similar - to `VISIT_CREATOR_LINK`. +- The database does not cache queries in the same way that ES does. Repeated + queries will not necessarily be as efficient as from ES. +- The database does not score documents at all, so the order will different + dramatically to the way that ES would order the documents. That's an issue + with respect to popularity data today already, but will become even more of an + issue if we start to score documents based on other metrics as theorised by + our search relevancy discussions. +- `creator` is not indexed in the API database, so a query against it will be + very slow. -This work can be done in parallel with the API changes. It would be useful to -have the API changes done before the frontend changes, so that we can test the -new pages with the real data and query parameters. +This is why we should update the existing Elasticsearch index to enable exact +and add new endpoints for the `tag`, `creator` and `source` matches. -Other frontend changes will affect the existing pages, so they will need to be -done under a feature flag. This includes the updates to the single result pages, -additional pages and updating the search query. +The updates to the Elasticsearch index should: -- Add a `additional_search_views` feature flag -- Update the `searchBy` filter -- Create a page for `tag` / `creator` /`source` collections. -- Update the `VCollectionLink` area on the single result page -- Use `VTag` component in the search results +- use + [`keyword` field type](https://www.elastic.co/guide/en/elasticsearch/reference/current/keyword.html) + for mapping and + [`term` query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html) + for `creator`, `source` and `tags` fields. This will allow for exact matching + of the values (e.g. `bike` will not match `bikes` or `biking`), and will + probably make the search more performant since the fields and the query won't + need to be analyzed and/or stemmed. -Cleanup after the feature flag is removed: +We will need to run the data refresh to reindex the data. -- Remove the `additional_search_views` feature flag -- Remove `VMediaTag` component +### 2. New API endpoints -## Step details +The new routes should use path parameters instead of query parameters for the +`tag`, `creator` and `source` values. This will make the URLs more readable, +easier to share, will be easier to cache or perform cache invalidation required +by #1969. The path parameters should be URL encoded to preserve special +characters and spaces. - +Instead of using query strings, we can describe the resource via the path: +`//source//creator/` is very clean, easy to read +and understand, and very easy to manage the cache for because it is a static +path. The source page can use the same route by leaving off the creator. This +removes the need to manage specific query params as well and would allow us to +add querying within these routes more easily in the future behind the regular q +parameter if we wanted. -### API changes +For the tag route, the singular `tag` rather than plural `tags` should be used +for legibility since we are presenting a single tag. -Currently, when filtering the search results, the API matches some query -parameters in a fuzzy way: an item matches the query if the field value contains -the query string as a separate word. When indexing the items, we "analyze" them, -which means that we split the field values by whitespace and stem them. We also -do the same to the query string. This means that the query string "bike" will -match the field value "bikes", "biking", or "bike shop". +The new views should use the same pagination and dead link cleanup as the search +views. -For most of these pages, we need an exact match instead. This section will -describe each page and the necessary changes. +### 3. New and updated components -#### Source page +#### Extract the `VAudioCollection` component + +Currently, it is not possible to reuse the audio collection from the audio +search result page because it is a part of the `audio.vue` page. We should +extract the part that shows the loading skeleton, the column of `VAudioTrack` +rows and the Load more section into `VAudioCollection` component. This component +will be reused in the audio search page and on the Additional search views. + +#### Add a `VCollectionHeader` component -To get the items from the selected source, we can use the `source` query -parameter: -[https://api.openverse.engineering/v1/images/?source=met](https://api.openverse.engineering/v1/images/?source=met) +The header should have an icon (tag, creator or source) and the name of the +tag/creator/source. For source and the creator, there should be an external link +button if it's available (not all creators have urls). -There is a limited number of sources, and we validate the `source` parameter -against the full list. This means that the `source` parameter will be matched -exactly. +The header should also display the number of results, "251 audio files with the +selected tag", "604 images provided by this source", "37 images by this creator +in Hirshhorn Museum and Sculpture Garden". -#### Creator page +_Note_: There are sources that only have works by one creator. In this case, we +should probably still have two separate pages for the source and the creator, +but we might want to add a note that the creator is the only one associated with +this source. -We could update the Elasticsearch index and the search controller to allow for -exact matching by the `creator` field. However, I think there is an easier way -that does not require any changes to the indexes: use the Django ORM to filter -the items by the creator name and the provider name. +**Figma links**: **creator** +[desktop](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56323&mode=design&t=pN0PPAzlKQEKT9tJ-4) +and +[mobile](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56362&mode=design&t=suLIyJHNmZrM0mPH-4), +**source** +[desktop](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56381&mode=design&t=suLIyJHNmZrM0mPH-4) +and +[mobile](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56421&mode=design&t=suLIyJHNmZrM0mPH-4), +**tag** +[desktop](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1216-58402&mode=design&t=suLIyJHNmZrM0mPH-4) +and +[mobile](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56457&mode=design&t=suLIyJHNmZrM0mPH-4). -We can add a media view at -`/api/v1//creator/?q=&provider=`. It would -use `self.get_queryset().filter(creator=creator, provider=provider)` to get all -the items by the creator from the provider. This view would also need to filter -out dead links, and return a paginated response. +#### Add `VCollectionLink` component -#### Tag page +This component should be a `VButton` with `as="VLink"`, should have an icon, and +should accept a localized link to the creator or source page. -To get the items matching a tag, we can use the `tags` query parameter with a -quoted value. -[https://api.openverse.engineering/v1/images/?tags=%22black%20cat%22](https://api.openverse.engineering/v1/images/?source=met) -This query returns all images with tags that contain the phrase "black cat". +**Figma link**: +[creator and source buttons](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56972&mode=dev) -This would not match the tags exactly, but tags page is supposed to show all -items that are associated with the same topic or category. +#### Create a new `VTag` component to be a `VButton` wrapping a `VLink` -We could also add a media view at `/api/v1//tags/?q=`, but it -is difficult to implement because the tags are stored as a list JSONField model. +The +[`VMediaTag` component](https://github.com/WordPress/openverse/blob/c7b76139d5a001ce43bde27805be5394e5732d1a/frontend/src/components/VMediaTag/VMediaTag.vue) +should be updated to be a `VButton` wrapping a `VLink`, and should match the +design in the +[Figma mockups](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56515&mode=design&t=nCX20BtJYqMOFAQm-4). +The component should link to the localized page for the tag collection. -### Nuxt store changes +#### Update links in the "information" section -We can re-use the search store as is for these pages. Currently, the store uses -the `searchBy` filter to determine the shape of the API query. If `searchBy` -value is set, then the `q` parameter is replaced with the -`=` query parameter. API query parameters are constructed -from the search store state in the -[`prepare-search-query-params` method](https://github.com/WordPress/openverse/blob/b4b46b903731870015c475d2c08eebef7ec6b25b/frontend/src/utils/prepare-search-query-params.ts#L22-L25) +The links to creator in the +[image](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/pages/image/_id/index.vue#L62-L73) +and +[audio](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/components/VAudioTrack/layouts/VFullLayout.vue#L32-L41) +single result pages Information section should have an "external link" icon. -We can update the `searchBy` filter to have one of the possible values: -`{ searchBy: }`. Then, this value would be used to -construct the API query. +Audio creator link should also be updated to match the image creator link. It +should be a conditional component: `VLink` if the `creator_url` is truthy and +`span` if the `creator_url` is falsy. + +Currently, the `foreign_landing_url` is linked to the "source" in the +[image](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/components/VImageDetails/VImageDetails.vue#L26-L28) +page and "provider" in the +[audio page](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/components/VAudioDetails/VAudioDetails.vue#L61C1-L63). +The audio page should be updated to match the image page: the +`foreign_landing_url` link should be added to the "source", not provider. + +### 4. Nuxt store and API request changes + +We can re-use the search store as is for these pages. Currently, the frontend +can perform searches by `source` parameter. If `searchBy` value is set, then the +`q` parameter is replaced with the `=` query parameter. + +For this project, we should add new values to the `searchBy` filter. + +The API request URL is constructed from the search store state in the +[`prepare-search-query-params` method](https://github.com/WordPress/openverse/blob/b4b46b903731870015c475d2c08eebef7ec6b25b/frontend/src/utils/prepare-search-query-params.ts#L22-L25). +We will need to update this method to use the `searchBy` filter value to +construct the API request path as described in the "API Changes" section. #### Update the `searchBy` filter -The `searchBy` filter will be used to determine the shape of the API query. +The `searchBy` filter will be used to determine the shape of the API request. While currently these parameters will be mutually exclusive (we can only search by one of them), we might want to allow searching by multiple parameters in the future. @@ -154,47 +220,53 @@ For other filters, we only use `toggle` method to update the value. However, for `searchBy`, we need to be able to check one of the `searchBy` parameters, and uncheck the others. To enable that, we should add a new `search` store method. -This change should also update `prepare-search-query-params` to use the -`searchBy` filter value to construct the API query: +If `searchBy` is set to `tag`, `creator` or `source`, then the media store +should create search path instead of the search query. So, instead of calling +`prepareSearchQuery` to create the query parameters, it should call +`prepareSearchPath` to create the path. ```typescript -const prepareSearchQuery = ( +const searchPathOrQuery = searchBy + ? prepareSearchPath(searchParams, mediaType) + : prepareSearchQuery(searchParams, mediaType) + +const prepareSearchPath = ( searchParams: Record, mediaType: SupportedMediaType ) => { - if (searchParams.searchBy) { + let path + if (searchBy === "tag") { + path = `${mediaType}/tag/${searchTerm}` + } else { + path = `${mediaType}/source/${searchParams[`${mediaType}Provider`]}` if (searchBy === "creator") { - // the name of the source is stored in the `Provider` filter - return { - creator: searchTerm, - source: searchParams[`${mediaType}Provider`], - } - } else if (searchBy === "source") { - return { source: searchParams[`${mediaType}Provider`] } - } else if (searchBy === "tag") { - // The parameter is plural in the API - return { tags: searchTerm } + path += `/creator/${searchTerm}` } - } else { - // Current search query transform } + return path } ``` -### Page changes +### 5. Add the `additional_search_views` feature flag + +The flag should be switchable, and off by default. -#### Create a page for `tag` / `creator` /`source` collections. +### 6. Create a page for `tag` / `creator` /`source` collections. Nuxt allows creating nested dynamic routes like `/pages/_collection/_mediaType/_term`. -Here, the collection can be `tag`, `creator` or `source`. `mediaType` can be one -of the supported media types (currently, `image` and `audio`). `term` refers to -the actual value of the tag, creator or source name. +We should add the following pages: -This page should use the +- `/pages/_mediaType/tag/_tag` +- `/pages/_mediaType/source/_source` +- `/pages/_mediaType/source/_source/creator/_creator` (this page might not be + needed as it might be handled by the source page) + +To make sure that the `mediaType`, `source` and `creator` parameters are valid, +this page should use the [`validate` method](https://v2.nuxt.com/docs/components-glossary/validate/) to -make sure that the `collection`, `mediaType` and `term` `params` are valid. +make sure that and show an error page if necessary. ```typescript function validate({ params, $pinia }): boolean { @@ -209,111 +281,59 @@ function validate({ params, $pinia }): boolean { This page should also update the state (`searchType`, `searchTerm` and `searchBy` and `provider` filters) in the `search` store and handle fetching -using `mediaStore`'s `fetchMedia` method in the `useFetch` hook. Since it is not -possible to change the path or query parameters from this page client-side, -fetching can be much simpler than on the current search page (that has to watch -for changes in the route and fetch if necessary). +using `mediaStore`'s `fetchMedia` method in the `useFetch` hook. -This page should have the common header (title, button - link to the source -external URL or the creator URL, and the summary of item counts in the -collection), and a nested component: the image grid or the audio collection. +Since it is not possible to change the path or query parameters from this page +client-side, fetching can be much simpler than on the current search page (that +has to watch for changes in the route and fetch if necessary). -### New and updated components +This page should use `VCollectionHeader` and the image grid or the audio +collection. -#### Extract the `VAudioCollection` component +### 7. Update the single result pages -Currently, it is not possible to reuse the audio collection from the audio -search result page because it is a part of the `audio.vue` page. We should -extract the part that shows the loading skeleton, the column of `VAudioTrack` -rows and the Load more section. This component will be reused in the audio -search page and on the Additional search views. One thing that I'm not sure -about here is whether the analytics events should stay the same and be sent from -all pages - or if we should somehow differentiate the events coming from -different search views. - -#### Add a `VCollectionHeader` component - -Figma links: - -- creator - [desktop](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56323&mode=design&t=pN0PPAzlKQEKT9tJ-4) - and - [mobile](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56362&mode=design&t=suLIyJHNmZrM0mPH-4) -- source - [desktop](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56381&mode=design&t=suLIyJHNmZrM0mPH-4) - and - [mobile](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56421&mode=design&t=suLIyJHNmZrM0mPH-4) -- tag - [desktop](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1216-58402&mode=design&t=suLIyJHNmZrM0mPH-4) - and - [mobile](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56457&mode=design&t=suLIyJHNmZrM0mPH-4) - -The header should have an icon (tag, creator or source), the name of the -tag/creator/source. For source and the creator, there should be an external link -button. - -The header should also display the number of results, "251 audio files with the -selected tag", "604 images provided by this source", "37 images by this creator -in Hirshhorn Museum and Sculpture Garden". +All of these changes should be conditional on whether the +`additional_search_views` feature flag is enabled. -Note: There are sources that only have works by one creator. In this case, we -should probably still have two separate pages for the source and the creator, -but we might want to add a note that the creator is the only one associated with -this source. +The Figma links for new designs: -#### Add `VCollectionLink` component - -This components should be a `VButton` with `as="VLink"` and should link to the -localized page for the creator or the source pages. +- [Image single result](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-63284&mode=dev) +- [Audio single result](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-63285&mode=dev) -### Update the `VCollectionLink` area on the single result page +#### Update the `VCollectionLink` area on the single result page -The "by creator" line under the main item on the single result page should be +The content info line under the main item on the single result page should be replaced with a section that has two buttons: one for a creator link and a source link. This section should be horizontally scrollable on mobile. It should implement a scroll-snap (example: https://play.tailwindcss.com/AbfA33Za50) -#### Update `VMediaTag` component to be a `VButton` wrapping a `VLink` - -The -[`VMediaTag` component](https://github.com/WordPress/openverse/blob/c7b76139d5a001ce43bde27805be5394e5732d1a/frontend/src/components/VMediaTag/VMediaTag.vue) -should be updated to be a `VButton` wrapping a `VLink`, and should match the -design in the -[Figma mockups](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56515&mode=design&t=nCX20BtJYqMOFAQm-4). -The component should link to the localized page for the tag collection. +#### Use `VTag` with links for the tags -### Other interface changes +The tags should be rendered using the `VTag` component with links to the tag +collection page. -#### Update links in the "information" section +#### Update the tags area on the single result page -The links to creator in the -[image](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/pages/image/_id/index.vue#L62-L73) -and -[audio](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/components/VAudioTrack/layouts/VFullLayout.vue#L32-L41) -single result pages Information section should have an "external link" icon. +The tags area should be collapsible to make long lists of tags collapsible: +https://github.com/WordPress/openverse/issues/2589 -Audio creator link should also be updated to match the image creator link. It -should be a conditional component: `VLink` if the `creator_url` is truthy and -`span` if the `creator_url` is falsy. +#### Add the information popover next to source and provider links -Currently, the `foreign_landing_url` is linked to the "source" in the -[image](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/components/VImageDetails/VImageDetails.vue#L26-L28) -page and "provider" in the -[audio page](https://github.com/WordPress/openverse/blob/35f08e5710d59f6c10f0cf54103fcce151adfefe/frontend/src/components/VAudioDetails/VAudioDetails.vue#L61C1-L63). -The audio page should be updated to match the image page: the -`foreign_landing_url` link should be added to the "source", not provider. +The information popover should be added next to the source and provider links +that explains the difference between the source and provider. -### Analytics changes +[**Figma link**](https://www.figma.com/file/niWnCgB7K0Y4e4mgxMrnRC/Additional-search-views?type=design&node-id=1200-56521&mode=dev) -The views can be tracked as page views, so no separate event is necessary. The -only way to access the pages is directly or via links on the single results, -which will all be captured by standard page visits. +### 8. Additional analytics events -Clicking on the items will be tracked as `SELECT_SEARCH_RESULT` events. These -events can be narrowed by pathname (`/search` or `/tag\*`, for example) to -determine where the event occurred. +Some existing events will already track the new views events. The views can be +tracked as page views, so no separate event is necessary. The only way to access +the pages is directly or via links on the single results, which will all be +captured by standard page visits. Clicking on the items will be tracked as +`SELECT_SEARCH_RESULT` events. These events can be narrowed by pathname +(`/search` or `/tag\*`, for example) to determine where the event occurred. -Two analytics events should be added: +Two analytics events should be added or updated: - The clicks on external creator link in the `VCollectionHeader` should be tracked as `VISIT_CREATOR_LINK` events. @@ -321,6 +341,12 @@ Two analytics events should be added: - We should also a special event for visiting source `VISIT_SOURCE_LINK`, similar to `VISIT_CREATOR_LINK`. +### 9. Cleanup after the feature flag is enabled in production + +After the feature flag is enabled in production, we should remove the +conditional rendering on the single result pages and remove the +`additional_search_views` feature flag and (old) `VMediaTag` component. + ### Tests We should add visual-regression tests for the new views. To minimize flakiness @@ -361,13 +387,15 @@ Not applicable. -The work on components (`VCollectionLink`, `VMediaTag`), and on the search store -changes can be parallelized. +The API changes can be done independently of the frontend changes, although they +should be finished before the final testing of the frontend changes. + +Adding the new components (step 3), Nuxt store update (step 4) and the +`additional_search_views` feature flag (step 5) can be done in parallel, and are +not dependent on anything. -We should create a `additional_search_views` feature flag for this work because -adding the links to the collection pages on the single result pages -(`VCollectionLink`, `VMediaTag`) before the collection pages are ready is not -possible. +The work on the single result pages (step 7) can be done in parallel with the +work on the collection pages (step 6), but should follow the previous steps. ## Blockers @@ -386,7 +414,7 @@ indicate the change of context. -To rollback the changes, we would need to set the feature flag to `OFF`. +To roll back the changes, we would need to set the feature flag to `OFF`. ## Risks