Implement a search box
Implement a search box
This is for:
DeveloperA search interface typically includes a text input from which users can enter and submit queries. This article provides guidelines for implementing a search box on your own, assuming that you can’t use the Coveo JavaScript Search Framework or the Atomic or Headless libraries in your custom search integration with the Coveo Platform.
Note
As a reference, you may want to look at the source code of the |
Standard search box actions
The following table lists some actions that you may want to make available in a search box implementation.
Each entry indicates the specific Coveo Usage Analytics (Coveo UA) event type, actionCause
, and customData
that must be logged (if applicable) when the action is performed, as well as a link to the corresponding implementation guidelines.
Search box action | Event category | actionCause |
customData |
Implementation guidelines |
---|---|---|---|---|
Submit a query |
Search |
|
||
Clear the search box |
Search |
|
||
Select a query correction suggestion |
Search |
|
||
Trigger an automatically corrected query |
Search |
|
||
Render Coveo ML suggestions |
Search |
|||
Select a Coveo ML suggestion |
Search |
|
|
|
Render field-based suggestions |
Search |
|||
Select a field-based suggestion |
Search |
|
Note
Using the proper event type,
|
Submit a query
Non-standalone search box
When the user submits a query from the search box (for example, by clicking Submit):
-
Retrieve the current value from the search box input.
-
Prepare a new query. Ensure that the
q
search request parameter is set to the value retrieved in step 1. -
Call the Search API to execute the query prepared in step 2. When the Search API returns:
-
Call the UA Write API to log the corresponding search event. In the request body:
-
Set the
actionCause
property tosearchboxSubmit
.NoteIf the user triggered a query by clearing the search box (for example, by clicking Clear), set the
actionCause
property tosearchboxClear
instead. -
Set other required or optional properties as needed.
-
-
Render the facets, if any.
-
Examples of submitting a query from a non-standalone search box
-
Executing the query after the expression
catcher rye
has been submitted from the search box:POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ ... "q": "catcher rye", "locale": "en-US", "searchHub": "BookstoreSearch", "tab": "All", ... }
200 OK response body (excerpt)
{ ... "duration": 145, "searchUid": "9862eba6-b30b-491b-94af-33ca2fd61547", "results": [ ...data to render query results... ], "groupByResults": [ ...data to render facets... ], ... }
-
Logging a
searchboxSubmit
search event after the Search API has returned:POST https://myorganizationid9sd8df7s.analytics.org.coveo.com/rest/ua/v15/analytics/search?visitor=28s6g49d-f81s-1435-2r5x153dle72 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ ... "actionCause": "searchboxSubmit", "queryText": "catcher rye", "language": "en", "originLevel1": "BookstoreSearch", "originLevel2": "All", "responseTime": 145, "searchQueryUid": "9862eba6-b30b-491b-94af-33ca2fd61547", ... }
NoteAs shown in this example, whenever logging a search event:
-
queryText
corresponds to q in the query request body. -
language
corresponds to the language part oflocale
in the query request body. -
originLevel1
corresponds tosearchHub
in the query request body. -
originLevel2
corresponds totab
in the query request body.
Moreover:
-
responseTime
corresponds to the elapsed time (in milliseconds) between the moment the search interface sent the query to the Search API, and the moment it received the results. -
searchQueryUid
corresponds tosearchUid
in the query response body.
-
From a standalone search box
When the user submits a query from the standalone search box (for example, by clicking Submit):
-
Retrieve the current value from the standalone search box input.
-
(Optional) If you want the standalone search box to be able to handle
redirect
query pipeline trigger rules:-
Get the execution plan of the query by sending a
GET
request tohttps://<orgId>.org.coveo.com/rest/search/v2/plan
, where<orgId>
is the unique identifier of your organization.authenticate the request using an access token that grants the privilege to execute queries. Set the q query parameter of the request to the value retrieved in step 1.
-
In the response body, if the
triggers
object of thepreprocessingOutput
property doesn’t contain an element whosetype
isredirect
, skip directly to step 3. Otherwise, retrieve thecontent
value from that element and then:-
Call the UA Write API to log a custom event.
In the request body:
-
Set
eventValue
toredirect
-
Set
eventType
toqueryPipelineTriggers
-
In the
customData
property, setredirectedTo
to thecontent
value retrieved in step 2.b. -
Set other required or optional properties as needed.
-
-
Skip to step 4, where you’ll redirect using the
content
value retrieved in step 2.b.
-
-
-
Call the UA Write API to log the corresponding search event. In the request body:
-
Set the
actionCause
property tosearchFromLink
. -
Set other required or optional properties as needed.
-
-
Redirect the browser.
If you’re redirecting to a full search interface that can display the results, you can forward the q to the interface by passing it as a query parameter in the URL and ensuring that the target interface is able to retrieve and parse it.
Correct queries
The DidYouMean query feature enables the Search API to check for a possible query correction if a query returns no results.
You can configure this feature to either suggest the correction, or automatically trigger a new query with the suggested term.
The DidYouMean feature involves the enableDidYouMean
(Boolean) search request parameter.
When enableDidYouMean
is set to true
, the Search API returns the queryCorrections
object in the query response body if no results are returned (for example, "totalCount": 0
).
To suggest the query correction in the search interface after a user has entered a query that returned no results:
-
Retrieve the value of the
correctedQuery
parameter from within thequeryCorrections
query response object. -
Use the value from Step 1 to output a message suggesting the corrected query. This message should include a link. When the user clicks that link:
-
Prepare a new query. Ensure that the
q
search request parameter is set to the value retrieved in step 1 (see Submit a query). -
Call the Search API to execute the query prepared in step 2. When the Search API returns:
-
Call the UA Write API to log the corresponding search event. In the request body:
-
Set the
actionCause
property todidyoumeanClick
. -
Set other required or optional properties as needed.
-
-
-
Note
You may want to use the |
Alternatively, you can enable your search interface to automatically correct queries and display results without requiring the user to interact more with the search interface. To do so, follow the preceding steps but make the following changes:
-
Automatically trigger the query from Step 2.a, rather than wait for a user to select a correction link.
-
Set the
actionCause
property todidyoumeanAutomatic
when logging the corresponding search event from Step 2.b.i.
Note
You should typically output a string to the user indicating that the query was automatically corrected. |
Examples of correcting queries
-
Executing a query after the expression
The Shning
has been submitted from the search box:POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2?organizationId=myorganizationid HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ "aq": "@source==\"MovieSource\"", "q": "Shning", "enableDidYouMean": true // additional parameters... }
200 OK response body (excerpt)
{ "totalCount": 0, ... "queryCorrections": [ { "correctedQuery": "shining", "wordCorrections": [ { "offset": 0, "length": 7, "originalWord": "shning", "correctedWord": "shining" } ] } ], ... }
-
Using the
correctedWord
value as the q parameter for the new query:POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2?organizationId=myorganizationid HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ "aq": "@source==\"MovieSource\"", "q": "shining", "enableDidYouMean": true // additional parameters... }
200 OK response body (excerpt)
{ "totalCount": 1, ... "results": [ { "title": "The Shining", ... } ], ... }
-
Logging a
didyoumeanAutomatic
custom event after the Search API has returned:POST https://myorganizationid9sd8df7s.analytics.org.coveo.com/rest/ua/v15/analytics/search?visitor=28s6g49d-f81s-1435-2r5x153dle72 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ ... "actionCause": "didyoumeanAutomatic", "queryText": "The Shining", "language": "en", "originLevel1": "MoviestoreSearch", "originLevel2": "Classics", "responseTime": 118, "searchQueryUid": "8c2f6897-203d-4d5c-db56-5b3e39831345", ... }
Provide Coveo Machine Learning query suggestions
Once you’ve logged enough usage analytics data in your Coveo organization, and created a Coveo Machine Learning Query Suggestions (QS) model, you can assist users by providing them with highly relevant, Coveo ML-powered QS as they’re typing in the search box.
Note
No UA event needs to be logged when requesting Coveo ML-based QS. |
To do so, on each valid keystroke in the search box:
-
Retrieve the current value from the search box input.
-
Resolve the current
searchHub
,{tab}
, andlocale
(and, optionally, thereferrer
andcontext
) search request parameters. -
Call the Search API to get query suggestions. In the request body:
-
Set the
q
property to the value retrieved in step 1. -
Set the
searchHub
,{tab}
, andlocale
(and, optionally,referrer
andcontext
for finer granularity) parameters to the values resolved in step 2 to get contextually relevant Coveo ML-based QS. -
Set the
enableWordCompletion
property totrue
if you want the top QS in the request response to be the one that best completes the last word being typed by the user in the search box. If you set this property tofalse
, the top query completion suggestion will instead be the one deemed most relevant by the QS model (even if this QS doesn’t actually complete the query).
When calling the Search API to get QS, the request must be routed to a query pipeline in which a Coveo ML QS model is configured and ready. Otherwise, the request will return an empty
completions
array. -
-
When the Search API returns, use the
expression
and/orhighlighted
properties of each element in the orderedcompletions
array of the response body to render QS in a dedicated list.NoteIn a
completions
array item:-
The
score
property value only has relative significance within the samecompletions
array.For example, a suggestion with a
score
of14.811407079917723
in thecompletions
array of response A isn’t necessarily less relevant than a suggestion with ascore
of24.325728875625282
in thecompletions
array of response B. -
The
highlighted
property uses the following logic:-
Characters between curly braces, such as
{cat}
, indicate an exact match with q. -
Characters between square brackets, such as
[cher]
, indicate completions. -
Characters between parentheses, such as
(act)
, indicate corrections to q.
-
-
The
executableConfidence
property contains a floating-point value between0
and1
indicating how "convinced" Coveo ML is that performing a query with this suggestion as a q will return relevant results. The threshold at which Coveo ML considers a QS executable is0.8
.You could use this property to include a reliable search-as-you-type feature in your search box implementation.
-
Example of providing Coveo ML query suggestions
Requesting Coveo ML-based QS:
POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2/querySuggest HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload
{
"q": "cat",
"locale": "en-US",
"searchHub": "BookstoreSearch",
"tab": "All",
"referrer": "https://example.com/books/classics/authors/Salinger_J_D/",
"context": {
"userType": "Premium"
},
"enableWordCompletion": false
}
Successful response - 200 OK
{
"completions": [
{
"expression": "catcher in the rye",
"score": 14.811407079917723,
"highlighted": "{cat}[cher] [in] [the] [rye]",
"executableConfidence": 1
},
{
"expression": "dream catcher",
"score": 14.135665512605295,
"highlighted": "[dream] {cat}[cher]",
"executableConfidence": 1.0
},
{
"expression": "catch-22",
"score": 13.576942132468472,
"highlighted": "{cat}[ch-22]",
"executableConfidence": 1
},
{
"expression": "cat in the hat",
"score": 12.879732037029243,
"highlighted": "{cat} [in] [the] [hat]",
"executableConfidence": 1.0
},
{
"expression": "the children act",
"score": 12.325728875625282,
"highlighted": "[the] [children] (act)",
"executableConfidence": 0.6666666666666666
},
]
}
Handle Coveo Machine Learning query suggestion selection
When the user selects a QS, such as by clicking one of the rendered suggestions:
-
Set the search box input value to the QS that the user has selected.
-
Prepare a new query. Ensure that the q search request parameter is set to the expression generated in step 1.
-
Call the Search API to execute the query prepared in step 2. When the Search API returns:
-
Call the UA Write API to log the corresponding search event.
In the request body:
-
Set the
actionCause
property toomniboxAnalytics
(oromniboxFromLink
in a standalone search box). -
Include the following key-value pairs in the
customData
property:-
"partialQueries": <partialQueries>
-
"partialQuery": <partialQuery>
-
"suggestionRanking": <suggestionRanking>
-
"suggestions": <suggestions>
where:
-
<partialQueries>
(semicolon-separated ordered list of strings) contains the q value of each Search API QS request that returned at least one suggestion, before a suggestion was selected. -
<partialQuery>
(string) is the q value of the last Search API QS request made before a suggestion was selected. -
<suggestionRanking>
(unsigned integer) is the 0-based index position of the suggestion that was selected in thecompletions
array. -
<suggestions>
(semicolon-separated ordered list of strings) contains theexpression
value of each item in the response of the last Search API QS request.
ExampleA user types
c
,i
,[backspace]
,o
, andv
in the search box. At this point, the following ordered list of QS is displayed:-
coveo for salesforce
-
coveo for sitecore
-
coveo
-
coveo for servicenow
-
coveo for zendesk
The user selects the third item in the list (
coveo
).In the
customData
property of the search event that gets logged when the triggered query returns:-
<partialQueries>
would have to be set toc;ci;c;co;cov
. -
<partialQuery>
would have to be set tocov
. -
<suggestionRanking>
would have to be set to2
. -
<suggestions>
would have to be set tocoveo for salesforce;coveo for sitecore;coveo;coveo for servicenow;coveo for zendesk
.
-
Set other required or optional properties as needed.
-
-
-
Render the facets, if any.
-
Examples of handling Coveo ML query suggestion selection
-
Executing a query after the
dream catcher
query completion suggestion has been selected:POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ ... "q": "dream catcher", "firstResult": 0, "locale": "en-US", "searchHub": "BookstoreSearch", "tab": "All", "referrer": "https://example.com/books/classics/authors/Salinger_J_D/", "context": { "userType": "Premium" }, ... }
-
Logging an
omniboxField
search event after the Search API has returned:POST https://myorganizationid9sd8df7s.analytics.org.coveo.com/rest/ua/v15/analytics/search?visitor=28s6g49d-f81s-1435-2r5x153dle72 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ ... "actionCause": "omniboxAnalytics", "customData": { ... "partialQueries": "c;ca;car;ca;cat", "partialQuery": "cat", "suggestionRanking": 1, "suggestions": "catcher in the rye;dream catcher;catch-22;cat in the hat;the children act", "context_userType": "Premium", ... }, "queryText": "dream catcher", "language": "en", "originLevel1": "BookstoreSearch", "originLevel2": "All", "responseTime": 97, "searchQueryUid": "153fdc38-8eb9-428a-942d-19a6ddb59fe9", ... }
NoteAs shown in this example, when logging a search event,
customData
must contain a correspondingcontext_
-prefixed key along with its value for each key-value pair in thecontext
object of the query request body.
Provide field-based query completion suggestions
Typically, you should only use Coveo ML QS in your search box implementation, as it’s both powerful and relatively easy to implement (see Provide Coveo Machine Learning query suggestions). However, it’s also possible (although not necessarily recommended) to assist users by providing them with field-based query completion suggestions as they’re typing in the search box.
In a bookstore search interface, you may want to suggest @author
field values that match what the user is typing.
Note
No UA event needs to be logged when requesting field values. |
To render field-based query completion suggestions, on each valid keystroke in the search box:
-
Retrieve the current value from the search box input.
-
Modify the retrieved value to a wildcard expression. For example, if the value retrieved in step 1 was
a
, set it to*a*
.NoteThese guidelines assume that you’re using the
wildcards
patternType
when requesting field values from the Search API, because this is how field suggestions are implemented in the Coveo JavaScript Search Framework (see theFieldsSuggestion
component). You can use any other validpatternType
value (regularExpression
,editDistance
, orphonetic
) in your own implementation if you so desire. -
If your search interface includes tabs, retrieve the filter expression enforced by the currently selected tab.
-
Call the Search API to get matching field values. In the request body:
-
Set the
patternType
property towildcards
. -
Set the
pattern
property to the value modified in step 2 (for example,*a*
). -
Set the
field
property to the@
-prefixed name of the field from which to retrieve query completion suggestions (for example,@author
).field-based query completion suggestions can only be retrieved from fields whose
facet
option is set totrue
(see Available Boolean field options). -
Set the
sortCriteria
property tooccurrences
to ensure that field suggestions are returned in a logical order. -
Set the
maximumNumberOfValues
property to a relatively low value (for example,5
) to ensure that only a useful number of field suggestions are requested. -
When users may use accented characters, set the
ignoreAccents
property totrue
to ensure that accented characters are treated as non-accented characters when retrieving field values. For example, whenignoreAccents
is set totrue
, using*élo*
or*elo*
as apattern
value is equivalent; both may return field values such asHello
orÉloïse
. -
If your search interface includes tabs, set the
queryOverride
property to the value retrieved in step 3 to ensure that only field suggestions matching the expression enforced by the currently selected tab are returned.
-
-
When the Search API returns, use the
value
property of each element in the orderedvalues
array of the response body to render field suggestions in a dedicated list.
Example of providing field-based query completion suggestions
Requesting query completion suggestions based on the @author
field:
POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2/values HTTP/1.1
Accept: application/json
Content-Type: application/json
Authorization: Bearer **********-****-****-****-************
Payload
{
"patternType": "wildcards",
"pattern": "*a*",
"field": "@author",
"sortCriteria": "occurrences",
"maximumNumberOfValues": 5,
"ignoreAccents": true,
"queryOverride": "@source==\"Classics\""
}
Successful response - 200 OK
{
"values": [
{
"value": "Mark Twain",
"lookupValue": "Mark Twain",
"numberOfResults": 25
},
{
"value": "Charles Dickens",
"lookupValue": "Charles Dickens",
"numberOfResults": 21
},
{
"value": "Jane Austen",
"lookupValue": "Jane Austen",
"numberOfResults": 12
},
{
"value": "Charlotte Brontë",
"lookupValue": "Charlotte Brontë",
"numberOfResults": 8
},
{
"value": "Mary Shelley",
"lookupValue": "Mary Shelly",
"numberOfResults": 5
}
]
}
Handle field-based query completion suggestion selection
When the user selects a specific field-based query completion suggestion (for example, by clicking one of the rendered values):
-
Generate an expression from the field value the user has selected (for example,
Alice Smith
). -
Set the search box input value to the query expression generated in step 1.
-
Prepare a new query. Ensure that the
q
search request parameter is set to the expression generated in step 1.NoteIf the expression generated in step 1 uses advanced query syntax (for example,
@author=="Alice Smith"
), also ensure that theenableQuerySyntax
search request parameter is set totrue
before the query is executed. -
Call the Search API to execute the query prepared in step 3. When the Search API returns:
-
Call the UA Write API to log the corresponding search event. In the request body:
-
Set the
actionCause
property toomniboxField
. -
Set other required or optional properties as needed.
-
-
Render the facets, if any.
-
Examples of handling field-based query completion suggestion selection
-
Executing a query after the
Charles Dickens
query completion suggestion has been selected:POST https://myorganizationid9sd8df7s.org.coveo.com/rest/search/v2 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ ... "q": "@author==\"Charles Dickens\"", "enableQuerySyntax": true, "firstResult": 0, "locale": "en-US", "searchHub": "BookstoreSearch", "tab": "Classics", ... }
NoteIn this example, the q generated from the selected field value uses advanced query syntax (
@author=="Charles Dickens"
). Therefore, theenableQuerySyntax
search request parameter is set totrue
to ensure that the Search API parses this expression correctly. -
Logging an
omniboxField
search event after the Search API has returned:POST https://myorganizationid9sd8df7s.analytics.org.coveo.com/rest/ua/v15/analytics/search?visitor=28s6g49d-f81s-1435-2r5x153dle72 HTTP/1.1 Accept: application/json Content-Type: application/json Authorization: Bearer **********-****-****-****-************
Payload (excerpt)
{ ... "actionCause": "omniboxField", "queryText": "@author==\"Charles Dickens\"", "language": "en", "originLevel1": "BookstoreSearch", "originLevel2": "Classics", "responseTime": 118, "searchQueryUid": "8c8f5897-233d-4b4c-bd56-5b3e39885345", ... }
Enable advanced search box features
Several search request parameters can have a direct and significant impact on the way the Search API and the index interpret the basic part of the query expression (q
).
Therefore, in a typical search interface, those search request parameters should logically be configurable through the search box.
You may want to ensure that your search box implementation includes options to enable and take advantage of the following features.
Partial match
The partial match feature can help the index find items that match the basic part of the query expression (q), even when the expression is fairly wordy. This feature involves the following search request parameters:
-
partialMatch
(Boolean) -
partialMatchKeywords
(unsigned integer) -
partialMatchThreshold
(unsigned integer or string [percentage])
When partialMatch
is set to true
and the q value contains at least partialMatchKeywords
(for example, 5
), items only need to match partialMatchThreshold
(for example, 50%
, rounded up) of those keywords to match the q.
Note
The partial match feature:
|
Partial match example
Enabling the partial match feature:
Query payload
{
"q": "the life and strange surprising adventures of robinson crusoe of york mariner",
"partialMatch": true,
"partialMatchKeywords": "3",
"partialMatchThreshold": "35%"
}
In this example, since partialMatch
is set to true
and q contains 12
keywords:
-
The q is converted to a partial match expression (
12
is greater than thepartialMatchKeywords
value of3
). -
Any item matching a minimum of
5
keywords in the partial match expression (that is, thepartialMatchThreshold
value of35%
multiplied by12
, and then rounded up) will match the expression.
This means that, for example, an item which contains the keywords the adventures of robinson crusoe
will match the expression.
Wildcards
The wildcards feature allows the index to recognize wildcards characters in the basic part of the query expression (q) and expand the expression accordingly. This feature involves the following search request parameters:
-
wildcards
(Boolean) -
questionMark
(Boolean)
When wildcards
is set to true
, *
characters in q are interpreted as wildcards.
When questionMark
is also set to true
, ?
characters in q are also treated as wildcards.
Note
Setting |
Wildcards example
Enabling and using the *
and ?
wildcards:
Query payload
{
"q": "?obi*",
"wildcards": true,
"questionMark": true,
}
In this example, since wildcards
and questionMark
are both set to true
, q is expanded so that items containing keywords such as robin
, robinson
, jobim
, or mobile
, will match the expression.
Advanced query syntax
The advanced query syntax feature allows the index to recognize Coveo query syntax in the basic part of the query expression (q) and interpret the expression accordingly. This feature involves the following search request parameters:
-
enableQuerySyntax
(Boolean) -
lowercaseOperators
(Boolean)
When enableQuerySyntax
is set to true
, Coveo query syntax in q is interpreted as such.
When lowercaseOperators
is also set to true
, Boolean operators (NEAR
, NOT
, AND
, and OR
) in q are always interpreted as such, even if they appear in lowercase (see Query syntax).
Notes
|
Advanced query syntax example
-
Enabling and using advanced query syntax:
Query payload
{ "q": "@title=prometheus NOT (@author=Shelley)", "enableQuerySyntax": true }
In this example, since
enableQuerySyntax
is set totrue
, q is parsed so that items whose title contains the keywordprometheus
and whose author name doesn’t contain the keywordShelley
will match the expression.This means that book items corresponding to Frankenstein or the Modern Prometheus (by Mary Shelley) and Prometheus Unbound (by Percy Bysshe Shelley) won’t match the expression. However, a book item such as The Prometheus Effect (by David Fleming) will match the expression.
-
Enabling and using lowercase operators:
Query payload
{ "q": "doctor strangelove or how i learned to stop worrying and love the bomb", "enableQuerySyntax": true, "lowercaseOperators": true }
In this example, because
enableQuerySyntax
andlowercaseOperators
are both set totrue
, the Search API parses the q as follows:(doctor strangelove) OR (how i learned to stop worrying) AND (love the bomb)
, which is likely not what the user wants. SettinglowercaseOperators
totrue
should only be done under specific circumstances and is typically not recommended.