Tuesday, August 4, 2020

How to Enable Proximity Search in Adobe Search and Promote | Location-aware search with Adobe Search and Promote

How to Enable Proximity Search in Adobe Search and Promote | Location-aware search with Adobe Search and Promote


This tutorial explains the details on enabling Proximity Search in Adobe Search and Promote.

Proximity Search

Proximity Search lets you associate a unique location with any page on your website, and then search and sort results by proximity (distance) from a given location.

For example, suppose you have populated pages on your website with United States postal ZIP code metadata such as the following:

<meta name="zipcode" content="84057">

After indexing your site, you perform the following search:

...&sp_q_location_1=84057&sp_x_1=zip&sp_q_max_1=100&sp_s=zip_proximity

The result set contains any documents located within 100 miles of ZIP code 84057, sorted in ascending order of distance from this ZIP code.

The telephone area codes also can be used for United States locations. Or, you can use latitude/longitude pairs to specify locations in your site metadata and in your search criteria. The location type is automatically determined from the form of the data that is provided.

Three-digit location values (“DDD”, where each “D” represents a decimal digit from 0–9) are treated as a United States telephone area code.

Five or five-dash-four digit location values (“DDDDD” or “DDDDD-DDDD”) are treated as a United States postal ZIP code.

Location values in the exact form of “±DD.DDDD±DDD.DDDD” are treated as a latitude/longitude pair. The first signed numeric value specifies latitude and the second signed numeric value represents longitude.

If you specify a positive latitude value, or positive longitude value, or both, the “+” character in the URL must be encoded as %2b. Otherwise, the “+” is interpreted as space, and the value is not recognized as a valid location.

When you search by proximity there is a special “proximity output field” created for that search. The field is populated with the relative distance between the location that is specified in the search criteria, and the location that is associated with each search result. This special field is named for the location-type field that is used in the search criteria with “_proximity” added to the end.

In the example search above, the results are sorted in ascending order of “zip_proximity.” That is the distance between the specified ZIP code (84057) and each result’s “zip” field location. You can also use this special “proximity output field” to display the relative distance for each search result, in either kilo meters or miles, using the Search template tag.

Configure Proximity Search

Let us now enable the proximity search in Adobe Search and Promote, I am going to use the IndexConnector to index the documents. Refer to the below URL for details on enabling IndexConnector and custom presentation and transport templates.

Proximity Search with zip code

Let us now enable the proximity search with zip proximity, I am going to index the below sample feed data through Index Connector

<feed xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<channel>
<title>Product Feed</title>
<Item>
<link>https://qa.example.com/product-title/p/prod1</link>
<title>
<![CDATA[Java Title1]]>
</title>
<description>
<![CDATA[<p>Prod1 description</p>]]>
</description>
<productType>Java</productType>
<ProductId>prod1</ProductId>
<zipcode>55123</zipcode>
<imageUrl>/content/dam/Images/product/prod1.jpg</imageUrl>
</Item>
<Item>
<link>https://qa.example.com/product-title/p/prod2</link>
<title>
<![CDATA[Java Title2]]>
</title>
<description>
<![CDATA[<p>Prod2 description</p>]]>
</description>
<productType>java</productType>
<ProductId>prod2</ProductId>
<zipcode>92307</zipcode>
<imageUrl>/content/dam/Images/product/prod2.jpg</imageUrl>
</Item>
<Item>
<link>https://qa.example.com/product-title/p/prod3</link>
<title>
<![CDATA[Java Title3]]>
</title>
<description>
<![CDATA[<p>Prod3 description</p>]]>
</description>
<productType>java</productType>
<ProductId>prod3</ProductId>
<zipcode>55103</zipcode>
<imageUrl>/content/dam/Images/product/prod3.jpg</imageUrl>
</Item>
<Item>
<link>https://qa.example.com/product-title/p/prod5</link>
<title>
<![CDATA[Lava Title]]>
</title>
<description>
<![CDATA[<p>Lava description</p>]]>
</description>
<productType>Lava</productType>
<zipcode>55109</zipcode>
<ProductId>prod5</ProductId>
<imageUrl>/content/dam/Images/product/prod5.jpg</imageUrl>
</Item>
</channel>
</feed>

Enable the metadata configuration for new zipcode field — Settings →Metadata →Definitions

proximity-search-in-adobe-search-and-promote

Configure the new field in IndexConnector — Settings →Crawling —>Index Connector

proximity-search-in-adobe-search-and-promote

Modify the presentation and Transports templates to include the “proximity output field" and if required metadata field zip code to the response, sample custom presentation & transports templates below (modify the corresponding templates used)

custom_backend_json.tpl (Transport)

<search-content-type-header charset="UTF-8">
{
"general": {
"query" : "<search-query />",
"total" : "<search-total />",
"lower" : "<search-lower />",
"upper" : "<search-upper />"
},

"facets" : [
{
"name" : "n1",
"values" : [<search-field-value-list name="n1" quotes="yes" data="values" sortby="values" encoding="json" />],
"counts" : [<search-field-value-list name="n1" quotes="no" data="results" sortby="values" />]
}
],
"results" : [
<search-results>
{
"fields" :
[
{
"name" : "mdi",
"value" : "<search-display-field name="mdi" length="500" encoding="json" />"
},
{
"name" : "title",
"value" : "<search-display-field name="title" encoding="json" />"
},
{
"name" : "productType",
"value" : "<search-display-field name="productType" encoding="json" />"
},
{
"name" : "zip",
"value" : "<search-display-field name="zip" encoding="json" />"
},
{
"name" : "relative_distance",
"value" : "<search-display-field name="zip_proximity" encoding="json" />"
}

]
}
<search-if-not-last>,</search-if-not-last>
</search-results>
]
}

custom_presentation_json.tmpl (Presentation)

<guided-content-type-header content="application/json" />
<guided-if-query-param-defined gsname="callback" /><guided-query-param gsname="callback" />(</guided-if-query-param-defined>
{
"general" :
{
"query" : "<guided-query-param gsname='q' />",
"total" : "<guided-results-total />",
"page_lower" : "<guided-results-lower>",
"page_upper" : "<guided-results-upper>",
"page_total": "<guided-page-total/>"
},
"facets" :
[

],
"results" :
[
<guided-results gsname="default">
{
"index" : "<guided-result-index />",
"title" : "<guided-result-field gsname="title" escape="ijson" />",
"productType" : "<guided-result-field gsname="productType" escape="ijson" />",
"zip" : "<guided-result-field gsname="zip" escape="ijson" />",
"relative_distance" : "<guided-result-field gsname="relative_distance" escape="ijson" />"

}<guided-if-not-last>,</guided-if-not-last>
</guided-results>
]
}
<guided-if-query-param-defined gsname="callback">)</guided-if-query-param-defined>

Run the Full Index now and execute the query by specifying the distance

http://xxxxxxxx.guided.ss-omtrdc.net/?do=json&sp_q_location_1=55123&sp_x_1=zip&sp_q_max_1=10&sp_s=zip_proximity

The result contains any documents located within 10 miles of ZIP code 55123, sorted in ascending order of distance from this ZIP code.

{
"general": {
"query": "",
"total": "1",
"page_lower": "1",
"page_upper": "1",
"page_total": "1"
},
"facets": [],
"results": [
{
"index": "",
"title": "Java Title1",
"productType": "Java",
"zip": "55123",
"relative_distance": "0.00"

}
]
}

http://xxxxxxxx.guided.ss-omtrdc.net/?do=json&sp_q_location_1=55123&sp_x_1=zip&sp_q_max_1=20&sp_s=zip_proximity

The result contains any documents located within 20 miles of ZIP code 55123, sorted in ascending order of distance from this ZIP code.

{
"general": {
"query": "",
"total": "3",
"page_lower": "1",
"page_upper": "3",
"page_total": "1"
},
"facets": [],
"results": [
{
"index": "",
"title": "Java Title1",
"productType": "Java",
"zip": "55123",
"relative_distance": "0.00"

},
{
"index": "",
"title": "Java Title3",
"productType": "java",
"zip": "55103",
"relative_distance": "10.98"

},
{
"index": "",
"title": "Lava Title",
"productType": "Lava",
"zip": "55109",
"relative_distance": "15.51"

}
]
}

http://xxxxxxxx.guided.ss-omtrdc.net/?do=json&sp_q_location_1=55123&sp_x_1=zip&sp_q_max_1=1500&sp_s=zip_proximity

The result contains any documents located within 1500 miles of ZIP code 55123, sorted in ascending order of distance from this ZIP code.

{
"general": {
"query": "",
"total": "4",
"page_lower": "1",
"page_upper": "4",
"page_total": "1"
},
"facets": [],
"results": [
{
"index": "",
"title": "Java Title1",
"productType": "Java",
"zip": "55123",
"relative_distance": "0.00"

},
{
"index": "",
"title": "Java Title3",
"productType": "java",
"zip": "55103",
"relative_distance": "10.98"

},
{
"index": "",
"title": "Lava Title",
"productType": "Lava",
"zip": "55109",
"relative_distance": "15.51"

},
{
"index": "",
"title": "Java Title2",
"productType": "java",
"zip": "92307",
"relative_distance": "1448.84"

}
]
}

Proximity Search with telephone area code

The proximity search can also be enabled through 3 digit telephone area code.

Sample Feed data

<feed xmlns:xs="http://www.w3.org/2001/XMLSchema" version="2.0">
<channel>
<title>Product Feed</title>
<Item>
<link>https://qa.example.com/product-title/p/prod1</link>
<title>
<![CDATA[Java Title1]]>
</title>
<description>
<![CDATA[<p>Prod1 description</p>]]>
</description>
<productType>Java</productType>
<ProductId>prod1</ProductId>
<zipcode>55123</zipcode>
<areacode>218</areacode>
<imageUrl>/content/dam/Images/product/prod1.jpg</imageUrl>
</Item>
<Item>
<link>https://qa.example.com/product-title/p/prod2</link>
<title>
<![CDATA[Java Title2]]>
</title>
<description>
<![CDATA[<p>Prod2 description</p>]]>
</description>
<productType>java</productType>
<ProductId>prod2</ProductId>
<zipcode>92307</zipcode>
<areacode>320</areacode>
<imageUrl>/content/dam/Images/product/prod2.jpg</imageUrl>
</Item>
<Item>
<link>https://qa.example.com/product-title/p/prod3</link>
<title>
<![CDATA[Java Title3]]>
</title>
<description>
<![CDATA[<p>Prod3 description</p>]]>
</description>
<productType>java</productType>
<ProductId>prod3</ProductId>
<zipcode>55103</zipcode>
<areacode>507</areacode>
<imageUrl>/content/dam/Images/product/prod3.jpg</imageUrl>
</Item>
<Item>
<link>https://qa.example.com/product-title/p/prod5</link>
<title>
<![CDATA[Lava Title]]>
</title>
<description>
<![CDATA[<p>Lava description</p>]]>
</description>
<productType>Lava</productType>
<zipcode>55109</zipcode>
<areacode>612 </areacode>
<ProductId>prod5</ProductId>
<imageUrl>/content/dam/Images/product/prod5.jpg</imageUrl>
</Item>
</channel>
</feed>

Enable the metadata configuration for new areacode field — Settings →Metadata →Definitions

proximity-search-in-adobe-search-and-promote

Configure the new field in IndexConnector — Settings →Crawling →Index Connector

proximity-search-in-adobe-search-and-promote

Modify the presentation and Transports templates to include the “proximity output field” and if required metadata field area code to the response, sample custom presentation & transports templates below (modify the corresponding templates used)

custom_backend_json.tpl (Transport)

<search-content-type-header charset="UTF-8">
{
"general": {
"query" : "<search-query />",
"total" : "<search-total />",
"lower" : "<search-lower />",
"upper" : "<search-upper />"
},

"facets" : [
{
"name" : "n1",
"values" : [<search-field-value-list name="n1" quotes="yes" data="values" sortby="values" encoding="json" />],
"counts" : [<search-field-value-list name="n1" quotes="no" data="results" sortby="values" />]
}
],
"results" : [
<search-results>
{
"fields" :
[
{
"name" : "mdi",
"value" : "<search-display-field name="mdi" length="500" encoding="json" />"
},
{
"name" : "title",
"value" : "<search-display-field name="title" encoding="json" />"
},
{
"name" : "productType",
"value" : "<search-display-field name="productType" encoding="json" />"
},
{
"name" : "zip",
"value" : "<search-display-field name="zip" encoding="json" />"
},
{
"name" : "relative_distance",
"value" : "<search-display-field name="zip_proximity" encoding="json" />"
},
{
"name" : "areacode",
"value" : "<search-display-field name="areacode" encoding="json" />"
},
{
"name" : "relative_distance_areacode",
"value" : "<search-display-field name="areacode_proximity" encoding="json" />"
}

]
}
<search-if-not-last>,</search-if-not-last>
</search-results>
]
}

custom_presentation_json.tmpl (Presentation)

<guided-content-type-header content="application/json" />
<guided-if-query-param-defined gsname="callback" /><guided-query-param gsname="callback" />(</guided-if-query-param-defined>
{
"general" :
{
"query" : "<guided-query-param gsname='q' />",
"total" : "<guided-results-total />",
"page_lower" : "<guided-results-lower>",
"page_upper" : "<guided-results-upper>",
"page_total": "<guided-page-total/>"
},
"facets" :
[

],
"results" :
[
<guided-results gsname="default">
{
"index" : "<guided-result-index />",
"title" : "<guided-result-field gsname="title" escape="ijson" />",
"productType" : "<guided-result-field gsname="productType" escape="ijson" />",
"zip" : "<guided-result-field gsname="zip" escape="ijson" />",
"relative_distance" : "<guided-result-field gsname="relative_distance" escape="ijson" />",
"areacode" : "<guided-result-field gsname="areacode" escape="ijson" />",
"relative_distance_areacode" : "<guided-result-field gsname="relative_distance_areacode" escape="ijson" />"
}<guided-if-not-last>,</guided-if-not-last>
</guided-results>
]
}
<guided-if-query-param-defined gsname="callback">)</guided-if-query-param-defined>

Run the Full Index now and execute the query by specifying the distance

http://xxxxxx.guided.ss-omtrdc.net/?do=json&sp_q_location_1=218&sp_x_1=areacode&sp_q_max_1=200&sp_s=areacode_proximity

The result contains any documents located within 200 miles of area code 218, sorted in ascending order of distance from this area code.

{
"general": {
"query": "",
"total": "3",
"page_lower": "1",
"page_upper": "3",
"page_total": "1"
},
"facets": [],
"results": [
{
"index": "",
"title": "Java Title1",
"productType": "Java",
"zip": "55123",
"relative_distance": "",
"areacode": "218",
"relative_distance_areacode": "0.00"

},
{
"index": "",
"title": "Java Title2",
"productType": "java",
"zip": "92307",
"relative_distance": "",
"areacode": "320",
"relative_distance_areacode": "125.02"

},
{
"index": "",
"title": "Lava Title",
"productType": "Lava",
"zip": "55109",
"relative_distance": "",
"areacode": "612",
"relative_distance_areacode": "171.86"

}
]
}

The search can also be enabled based on the latitude/longitude by enabling metadata with latitude/longitude value in “±DD.DDDD±DDD.DDDD” format and searching with the same value(e.g. +44.8041, -93.1668).

The proximity search will associate the location data to the search records and allows us to search the records based on the relative location of the records.

References




Tuesday, July 28, 2020

Trunk Based Development and Feature Flags for Continuous Delivery

Trunk Based Development is a branching model in which developers create short-lived feature branches and merge back into the “trunk” branch, often called as the master branch.

The guiding principals of Trunk Based Development

  • There is one “trunk” branch where developers merge their changes.
  • Developers should merge small changes as often as they can.
  • Merges must be reviewed, tested, and must not destroy the “trunk”.
  • All code in “trunk” must be release ready at all times.
  • Feature branches must be short-lived.
  • Keep your commit messages as concise as possible

Comparing Trunk Based Development to GitFlow

trunk-based-development

The Trunk Based Branching Model

The below model can be used for scaled teams, the development is done with short-lived feature branches, the changes are often merged to the “trunk”. For small teams, the developers can directly merge the changes to the “trunk” in small chunks.

trunk-based-development

Changes made in the release branches — snapshots of the code when it’s ready to be released — are usually merged back to trunk as soon as possible. One key benefit of the trunk-based approach is that it reduces the complexity of merging events and keeps code current by having fewer development lines and by doing small and frequent merges.

The developers should experienced enough to make this model successful, this model often creates conflicts if the changes are not reviewed and tested rigorously. Use this model if you are looking to push out a new product fast and want to iterate quickly.

Feature Development with Feature Flags

Trunk Based Development uses Feature Flags as a mechanism to manage new feature releases. A feature flag is simply a boolean condition that modifies the behavior of a component, module, or function in your application.

Following a Feature Flag pattern trades the simplicity of isolated branch workflows, such as GitFlow, in favor of flexible feature rollouts, continuous delivery, and application personalization.

Setting Up Feature Flags

A simple way to begin using feature flags is to maintain a single file containing your feature flags. Let us see how to manage the flags in Typescript with React application through a simple approach. The feature flags can also be managed through external tools like optimizel or launchdarkly

featureFlags.tsconst featureFlags = {
hellowordnewfeature: false
}
export function getFeatureFlag(key){
return featureFlags[key] || false;
}
helloword.ts
//return feature based on the feature flag
import { getFeatureFlag } from "./featureFlags";
const createHelloWord = () => {
if(getFeatureFlag("hellowordnewfeature")){
return createNewHelloWord()
}
return createOldHelloWorld()
}

Here the new feature is returned based on the flag “hellowordnewfeature”, if the flag is “true” then the new feature(createNewHelloWord) is returned else the old feature(createOldHelloWorld).

This TypeScript module(featureFlags.ts) can be extended to fetch the features from external or internal services.

Existing Feature Development with Feature Flags

Existing feature development with feature flags is slightly more complex but offers more flexibility for continuous delivery and personalization.

Small Incremental Change

If the proposed feature is a small incremental change, we can modify an existing code path to augment behavior. Take for example adding a new calculation for the total.

featureFlags.tsconst featureFlags = {
hellowordnewfeature: false,
useNewcalculateTotal:true
}
export function getFeatureFlag(key){
return featureFlags[key] || false;
}
// before
const calculateTotal = (qty, val) => {
return qty * val
}
// after
const calculateTotal= (qty, val, tax) => {
if(getFeatureFlag("useNewcalculateTotal")){
return qty * val * tax
}
return qty *val
}

Large Modification

If the proposed feature is large, for example, we want to display a completely new TaxCalculator component, we would need to define a new code path and entry-point for that component.

featureFlags.tsconst featureFlags = {
hellowordnewfeature: false,
useNewcalculateTotal:true,
useNewTaxCalculation:true
}
export function getFeatureFlag(key){
return featureFlags[key] || false;
}
TaxCalculator.tsximport { getFeatureFlag } from "./featureFlags";
import { TaxCalculatorOld, TaxCalculatorNew } from "./components";
const TaxCalculator = props => {
if(getFeatureFlag("useNewTaxCalculation"){
return <TaxCalculatorNew />
}
return <TaxCalculatorOld />
}

New Feature Development with Feature Flags

New feature development with feature flags is simpler than existing features. Since there are no existing code paths for your code to execute, this code path will be disabled by default while this feature is WIP.

The new feature development process with flags should look like this;

  • create a feature flag for your new feature
  • begin working on your code
  • ensure your flag is false before merging into master
  • merge your code frequently
  • when the feature is ready for release, remove the flag

Conclusion

Trunk based Development and Feature Flags together can be used for continuous delivery, delivering the features faster to market. Planning them carefully will allow you to quickly deliver the new business features to the system. The feature flags can also be managed through external tools like optimizel or launchdarkly, tools provide SDK to manage the features external to the applications.

References

https://cloud.google.com/solutions/devops/devops-tech-trunk-based-development

https://featureflags.io/