This repository has been archived by the owner on Dec 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 971
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4809 from brave/url-frequency
Change URL suggestions to consider last access timestamp and access count
- Loading branch information
Showing
6 changed files
with
169 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this file, | ||
* You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
const urlParser = require('url') | ||
const appConfig = require('../../../js/constants/appConfig') | ||
|
||
const sigmoid = (t) => { | ||
return 1 / (1 + Math.pow(Math.E, -t)) | ||
} | ||
|
||
const ONE_DAY = 1000 * 60 * 60 * 24 | ||
|
||
/* | ||
* Calculate the sorting priority for a history item based on number of | ||
* accesses and time since last access | ||
* | ||
* @param {number} count - The number of times this site has been accessed | ||
* @param {number} currentTime - Current epoch millisecnds | ||
* @param {boolean} lastAccessedTime - Epoch milliseconds of last access | ||
* | ||
*/ | ||
module.exports.sortingPriority = (count, currentTime, lastAccessedTime, ageDecayConstant) => { | ||
// number of days since last access (with fractional component) | ||
const ageInDays = (currentTime - (lastAccessedTime || currentTime)) / ONE_DAY | ||
// decay factor based on age | ||
const ageFactor = 1 - ((sigmoid(ageInDays / ageDecayConstant) - 0.5) * 2) | ||
// sorting priority | ||
// console.log(count, ageInDays, ageFactor, count * ageFactor) | ||
return count * ageFactor | ||
} | ||
|
||
/* | ||
* Sort two history items by priority | ||
* | ||
* @param {ImmutableObject} s1 - first history item | ||
* @param {ImmutableObject} s2 - second history item | ||
* | ||
* Return the relative order of two site entries taking into consideration | ||
* the number of times the site has been accessed and the length of time | ||
* since the last access. | ||
* | ||
* The base sort order is determined by the count attribute of the site | ||
* entry. A modifier is then computed based on the length of time since | ||
* the last access. A sigmoid function is used to weight more recent | ||
* entries higher than entries in the past. This is not a linear function, | ||
* entries in the far past with many counts will still be discounted | ||
* heavily as the sigmoid modifier will cancel most of the count | ||
* base parameter. | ||
* | ||
* Below is a sample comparison of two sites that have been accessed | ||
* recently (but not at the identical time). Each site is accessed | ||
* 9 times. The count is discounted by an aging factor calculated | ||
* using the sigmoid decay function. | ||
* | ||
* http://www.gm.ca/gm/ | ||
* | ||
* ageInDays 0.17171469907407408 | ||
* ageFactor 0.9982828546969802 | ||
* count 9 | ||
* priority 0.9982828546969802 | ||
* | ||
* http://www.gm.com/index.html | ||
* | ||
* ageInDays 0.17148791666666666 | ||
* ageFactor 0.9982851225143763 | ||
* count 9 | ||
* priority 0.9982851225143763 | ||
* | ||
*/ | ||
module.exports.sortByAccessCountWithAgeDecay = (s1, s2) => { | ||
const s1Priority = module.exports.sortingPriority( | ||
s1.get('count') || 0, | ||
(new Date()).getTime(), | ||
s1.get('lastAccessedTime') || (new Date()).getTime(), | ||
appConfig.urlSuggestions.ageDecayConstant | ||
) | ||
const s2Priority = module.exports.sortingPriority( | ||
s2.get('count') || 0, | ||
(new Date()).getTime(), | ||
s2.get('lastAccessedTime') || (new Date()).getTime(), | ||
appConfig.urlSuggestions.ageDecayConstant | ||
) | ||
return s2Priority - s1Priority | ||
} | ||
|
||
/* | ||
* Return a 1 if the url is 'simple' as in without query, search or | ||
* hash components. Return 0 otherwise. | ||
* | ||
* @param {ImmutableObject} site - object represent history entry | ||
* | ||
*/ | ||
module.exports.simpleDomainNameValue = (site) => { | ||
const parsed = urlParser.parse(site.get('location')) | ||
if (parsed.hash === null && parsed.search === null && parsed.query === null && parsed.pathname === '/') { | ||
return 1 | ||
} else { | ||
return 0 | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* global describe, it */ | ||
const suggestion = require('../../../app/renderer/lib/suggestion') | ||
const assert = require('assert') | ||
const Immutable = require('immutable') | ||
|
||
require('../braveUnit') | ||
|
||
const AGE_DECAY = 50 | ||
|
||
describe('suggestion', function () { | ||
it('sorts sites correctly', function () { | ||
assert.ok(suggestion.sortingPriority(10, 100, 50, AGE_DECAY) > suggestion.sortingPriority(10, 100, 40, AGE_DECAY), 'newer sites with equal access counts sort earlier') | ||
assert.ok(suggestion.sortingPriority(10, 100, 50, AGE_DECAY) < suggestion.sortingPriority(11, 100, 40, AGE_DECAY), 'Sites with higher access counts sort earlier (unless time delay overriden)') | ||
assert.ok(suggestion.sortingPriority(10, 10000000000, 10000000000, AGE_DECAY) > suggestion.sortingPriority(11, 10000000000, 1000000000, AGE_DECAY), 'much newer sites without lower counts sort with higher priority') | ||
}) | ||
|
||
it('sorts simple sites higher than complex sites', function () { | ||
const siteSimple = Immutable.Map({ location: 'http://www.site.com' }) | ||
const siteComplex = Immutable.Map({ location: 'http://www.site.com/?foo=bar#a' }) | ||
assert.ok(suggestion.simpleDomainNameValue(siteSimple) === 1, 'simple site returns 1') | ||
assert.ok(suggestion.simpleDomainNameValue(siteComplex) === 0, 'complex site returns 0') | ||
}) | ||
}) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters