Confirmed users
14,525
edits
No edit summary |
|||
| Line 1: | Line 1: | ||
= Places Query API | == Places Query API == | ||
Create a sensible, easy to use Query API for Places for Firefox.next & Jetpack. This API should make it possible to do targeted queries against history and bookmarks with a minimum of code. | |||
Create a sensible, easy to use Query API for Places for Firefox.next & Jetpack. This API should make it possible to do targeted queries against history and bookmarks with a minimum of code. | |||
= | == Goals/Use Cases == | ||
*An elegant and easy to use API. Fx devs, Jetpack and extension developers will all benefit. | |||
*allows us to prototype the new ui for Fx.next | |||
*<strike>'Pluggable' results handling</strike> Easy wrappable querying object and results<br> | |||
*Focus on JS usage | |||
*<strike>Paging Support - Sounds like a great use of Generators!</strike> dropped, there is no perf gain, can be a follow-up<br> | |||
*Fetch individual record by Id or other property | |||
<strike>(After talking to the Labs guys about Weave, we should try to provide a plugin-interface whereby you can allow a developer to keep track of and index random JSON objects.</strike> | |||
<strike>Dan Mills has example code that was created for the people store. Link coming soon.)</strike> | |||
== Non Goals == | |||
*Tailoring the API to tree views | |||
*Creating the perfect API (lets iterate) | |||
*Create an abstract datastore<br> | |||
*<strike>Intended as a snap-in replacement of the current API</strike> Future follow-up through a live-updating wrapper<br> | |||
*Wrap current sync and XPCOM API | |||
= | == Status == | ||
*Old prototype landed in Jetpack 0.8, currently being revised<br> | |||
*Team | |||
**'''API''': ddahl, mak | |||
**'''UX/UI''': not required so far, future users could though | |||
*Implementation bugs: {{bug|522572}} {{bug|545700}} {{bug|531940}} {{bug|543888}} | |||
*Next Steps | |||
**Iterate the feel of the API with the Places team and other interested developers. Ideas and feedback are encouraged. Build working examples to encourage feedback. | |||
| {{ | |||
== Timeline / Milestones == | |||
*2010/02 Landed in Jetpack 0.8 | |||
*2010/03 Work on "skeleton" API design with Marco | |||
*2010/05 New redesign, collecting feedback and proceeding.<br> | |||
== Delivery Requirements == | |||
*This is purely additive. | |||
*Coordination with Jetpack as this should be coded once, checked into places and imported into Jetpack's implementation. | |||
== Constraints == | |||
*Final implementation should use only Async Storage APIs | |||
== | == Dependencies == | ||
== Testing == | |||
*Comprehensive xpcshell tests | |||
*<strike>Example/Documentation style Browser Chrome tests</strike> Not for now, when treeviews will use this.<br> | |||
*TDD | |||
= | == Related Projects == | ||
*Jetpack<br> | |||
== | == Additional Details == | ||
[ | The initial API "sketches" are here: [https://wiki.mozilla.org/Firefox/Projects/PlacesQueryAPISketches] | ||
Faaborg has been mocking up designs and putting a lot pf thought into the UX: http://blog.mozilla.com/faaborg/2009/10/13/browsing-your-personal-web | |||
== Inspiration == | |||
Links to code we should study... | |||
==== Gloda ==== | |||
Gloda: [https://developer.mozilla.org/en/Thunderbird/gloda] | |||
facet.js: [http://mxr.mozilla.org/comm-central/source/mailnews/db/gloda/modules/facet.js] | |||
Gloda Fundamental Attribute provider: [http://mxr.mozilla.org/comm-central/source/mailnews/db/gloda/modules/fundattr.js] | |||
Explicit Attribute Provider: [http://mxr.mozilla.org/comm-central/source/mailnews/db/gloda/modules/explattr.js] | |||
FacetView: [http://mxr.mozilla.org/comm-central/source/mail/base/content/glodaFacetView.js] | |||
== Current Sketches == | |||
<pre class="brush:js;toolbar:false;">function callback(results) { | |||
// results is a simple array of ResultItem, it's pretty easy to wrap it | |||
// if more complex management is needed. | |||
if (results.length == 0) | |||
dump("En empty results array indicates last results push\n"); | |||
else | |||
dump("Collected " + results.length + " results this turn\n"); | |||
} | |||
// Results are pushed to the callback as soon as they are available. | |||
new PlacesQuery([object]QueryConf, [function]callback, [scope]thisObject); | |||
[ | // It's possible to pass multiple QueryConf objects, results will be merged | ||
// based on the .merge attribute of each config. | |||
// First query results are always unioned. | |||
// In case of multiple queries, sort, group and limit of the first query will | |||
// be used for the global result. | |||
new PlacesQuery([QueryConf1, QueryConf2], callback, thisObject); | |||
= | // It's possible to create the query, but run it later. | ||
let query = new PlacesQuery(QueryConf); | |||
query.execute(callback, thisObject); | |||
= | /* The query can be so configured: | ||
* | * | ||
* | * _QueryConf_ = { | ||
* phrase: string. | |||
* Containing this string in either title, uri or tags. Case | |||
* insensitive. Can use ^ and $ to match at beginning or end. | |||
* host: string. | |||
* Containing this string in the host. Case insensitive. | |||
* Can use ^ and $ to match at beginning or end. | |||
* uri: string. | |||
* Containing this string in the uri. Case insensitive. | |||
* Can use ^ and $ to match beginning or end. | |||
* annotated: array of strings. | |||
* With these annotations (Either page or item). | |||
* bookmarked: object | |||
* { | |||
* tags: array of strings. | |||
* Tagged with these tags. | |||
* at: object | |||
* { | |||
* folder: number. | |||
* Inside this folder. (non-recursive) | |||
* position: number. | |||
* At this position. (relative to folder). | |||
* If undefined or null matches all children. | |||
* } | |||
* id: number. | |||
* Bookmarked with this id. | |||
* when: object | |||
* { | |||
* begin: optional Date object | |||
* Bookmarks created after this time (included). | |||
* Defaults to epoch. | |||
* end: optional Date object | |||
* Bookmarks created before this time (included). | |||
* Defaults to now. | |||
* } | |||
* modified: object | |||
* { | |||
* begin: optional Date object | |||
* Bookmarks modified after this time (included). | |||
* Defaults to epoch. | |||
* end: optional Date object | |||
* Bookmarks modified before this time (included). | |||
* Defaults to now. | |||
* } | |||
* onlyContainers: boolean. | |||
* Removes any non-container from results. | |||
* Default is false. | |||
* excludeReadOnlyContainers: boolean. | |||
* Removes read only containers from results. | |||
* Default is false. | |||
* } | |||
* visited: object | |||
* { | |||
* count: object | |||
* This is lazily based on visit_count, thus is not going to work | |||
* for not counted transitions: embed, download, framed_link. | |||
* { | |||
* min: optional number. | |||
* With more than this many visits. | |||
* Defaults to 0. | |||
* max: optional number. | |||
* With less than this many visits. | |||
* Defaults to inf. | |||
* } | |||
* transitions: array of transition types. | |||
* With at least one visit for each of these transitions. | |||
* when: object | |||
* { | |||
* begin: optional Date object | |||
* With visits after this time (included). | |||
* Defaults to epoch. | |||
* end: optional Date object | |||
* With visits before this time (included). | |||
* Defaults to now. | |||
* } | |||
* excludeRedirectSources: boolean. | |||
* Removes redirects sources from results. | |||
* Default is false. | |||
* excludeRedirectTargets: boolean. | |||
* Removes redirects targets from results. | |||
* Default is false. | |||
* includeHidden: boolean. | |||
* Includes also pages marked as hidden. | |||
* Default is false. | |||
* allVisits: boolean. | |||
* Returns all visits ungrouped. | |||
* Default is false, that means visits are grouped by uri. | |||
* } | |||
* sort: object | |||
* { | |||
* by: string. | |||
* Either "none", "title", "time", "uri", "accessCount", "lastModified", | |||
* "frecency". Defaults to "none". | |||
* dir: string. | |||
* Either "asc" or "desc". Defaults to "asc". | |||
* } | |||
* group: string. | |||
* Either "tags", "containers", "days", "months", "years" or "domains". | |||
* Defaults to "none". | |||
* NOTE: Not yet implemented. | |||
* limit: number. | |||
* Maximum number of results to return. Defaults to all results. | |||
* merge: string. | |||
* How to merge this query's results with others in the same request. | |||
* Valid values: | |||
* - "union": merge results from the 2 queries. | |||
* - "except": exclude current results from the previous ones. | |||
* - "intersect": only current results that are also in previous ones. | |||
* } | |||
* | |||
* NOTE: In case of multiple queries, sort, group and limit of the first query | |||
* will be used for the global result. | |||
* | |||
* | |||
* The query returns an array of these simple ResultItem objects | |||
* ResultItem { | |||
* pageId: the place_id of the page, useful for external linking of resources | |||
* uri: | |||
* title: | |||
* host: | |||
* accessCount: visit count for pages, aggregated or null for containers | |||
* time: visit date, aggregated or null for containers | |||
* icon: url of the icon | |||
* sessionId: visit session or null if not available | |||
* itemId: bookmark id or null if not bookmarked (see isBookmarked) | |||
* isBookmarked: whether this is bookmarked or not | |||
* dateAdded: bookmark creation Date() or null if not bookmarked | |||
* lastModified bookmark modification Date() or null if not bookmarked | |||
* parentId: bookmark folder id or null if not bookmarked | |||
* tags: string of tags | |||
* tagsArray: array of tags | |||
* bookmarkIndex: position of the bookmark in his container or null | |||
* frecency: | |||
* visitId: id of the visit or null | |||
* referringVisitId: id of the originating visit or null | |||
* referringUri: uri of the originating visit or null | |||
* transitionType: transition of this visit or null | |||
* type: old container implementation type | |||
* readableType: "bookmark", "container", "separator", "visit", "page" | |||
* query: if this is a container will return a new PlacesQuery for contents | |||
* } | |||
* | |||
*/ | |||
// EXAMPLES (not all of them work, they are supposed to show the conf) | |||
[ | // Bookmarks toolbar. | ||
new PlacesQuery({ bookmarked: { | |||
folders: [PlacesUtils.bookmarksToolbarFolderId] | |||
}, | |||
group: "container" | |||
}); | |||
// History menu. | |||
new PlacesQuery({ visited: {}, | |||
sort: { by: "time", dir: "desc" }, | |||
limit: 10 | |||
}); | |||
// Start-of-awesomebar (still missing adaptive, keywords, ...). | |||
new PlacesQuery({ phrase: "someword", | |||
sort: { by: "frecency", dir: "desc" }, | |||
limit: 12 | |||
}); | |||
// Folder picker. | |||
new PlacesQuery({ bookmarked: { | |||
at: { folder: PlacesUIUtils.allBookmarksFolderId } | |||
onlyContainers: true, | |||
excludeReadOnlyContainers: true | |||
}, | |||
group: "container" | |||
}); | |||
// Most recent bookmarks. | |||
new PlacesQuery({ bookmarked: {}, | |||
sort: { by: "lastModified", dir: "desc"} | |||
limit: 5 | |||
}); | |||
// Most recent tags. | |||
new PlacesQuery({ bookmarked: {}, | |||
group: "tags", | |||
limit: 5 | |||
}); | |||
[[ | // Left pane query. | ||
new PlacesQuery({ bookmarked: { folders: [PlacesUIUtils.leftPaneFolderId] }, | |||
annotated: ["Places/OrganizerQuery"] | |||
group: "containers" | |||
}); | |||
// Downloads | |||
new PlacesQuery({ visited: { | |||
transitions: [PlacesUtils.history.TRANSITION_DOWNLOAD], | |||
includeHidden: true, | |||
allVisits: true | |||
}, | |||
sort: { by: "time", dir: "desc" } | |||
}); | |||
[ | // Visited bookmarks containing either "foo" or "bar". | ||
new PlacesQuery([ | |||
{ phrase: "foo", | |||
bookmarked: {}, | |||
visited: {} | |||
}, | |||
{ phrase: "bar", | |||
bookmarked: {}, | |||
visited: {} | |||
}, | |||
); | |||
// Visited bookmarks containing "foo", but not "bar". | |||
new PlacesQuery([ | |||
{ phrase: "foo", | |||
bookmarked: {}, | |||
visited: {} | |||
}, | |||
{ phrase: "bar", | |||
bookmarked: {}, | |||
visited: {}, | |||
merge: "except" | |||
}, | |||
); | |||
[ | // Visited bookmarks containing "foo" and "bar". | ||
new PlacesQuery([ | |||
{ phrase: "foo", | |||
bookmarked: {}, | |||
visited: {} | |||
}, | |||
{ phrase: "bar", | |||
bookmarked: {}, | |||
visited: {}, | |||
merge: "intersect" | |||
}, | |||
); | |||
</pre> | |||
== Testing == | |||
* Most of this will be automated and will not need QA involvement | |||
== | * QA will keep an eye out for regressions during regular release cycles | ||
* QA will run a testday once this has landed | |||
* | |||
* | |||
* | |||