Commit 0727d13b authored by Robert Knight's avatar Robert Knight

Use "page" rather than "chunk" or "batch" in SearchClient

Consistently use the term "page" to refer to a subset of results fetched
from the search API instead of "batch" or "chunk". This is more
consistent within the rest of the client code and also consistent with
the API documentation.

 - Rename `chunkSize` constructor argument to `pageSize`
 - Change internal references to "batch" or "chunk" in SearchClient to
   "page"
parent d53e4c34
...@@ -22,14 +22,13 @@ export default class SearchClient extends TinyEmitter { ...@@ -22,14 +22,13 @@ export default class SearchClient extends TinyEmitter {
/** /**
* @param {(query: SearchQuery) => Promise<SearchResult>} searchFn - Function for querying the search API * @param {(query: SearchQuery) => Promise<SearchResult>} searchFn - Function for querying the search API
* @param {Object} options * @param {Object} options
* @param {number} [options.chunkSize] - page size/number of annotations * @param {number} [options.pageSize] - page size to use when fetching results
* per batch
* @param {boolean} [options.separateReplies] - When `true`, request that * @param {boolean} [options.separateReplies] - When `true`, request that
* top-level annotations and replies be returned separately. * top-level annotations and replies be returned separately.
* NOTE: This has issues with annotations that have large numbers of * NOTE: This has issues with annotations that have large numbers of
* replies. * replies.
* @param {boolean} [options.incremental] - Emit `results` events incrementally * @param {boolean} [options.incremental] - Emit `results` events incrementally
* as batches of annotations are available * as pages of annotations are fetched
* @param {number|null} [options.maxResults] - Safety valve for protection when * @param {number|null} [options.maxResults] - Safety valve for protection when
* loading all annotations in a group in the NotebookView. If the Notebook * loading all annotations in a group in the NotebookView. If the Notebook
* is opened while focused on a group that contains many thousands of * is opened while focused on a group that contains many thousands of
...@@ -43,7 +42,7 @@ export default class SearchClient extends TinyEmitter { ...@@ -43,7 +42,7 @@ export default class SearchClient extends TinyEmitter {
constructor( constructor(
searchFn, searchFn,
{ {
chunkSize = 200, pageSize = 200,
separateReplies = true, separateReplies = true,
incremental = true, incremental = true,
maxResults = null, maxResults = null,
...@@ -53,7 +52,7 @@ export default class SearchClient extends TinyEmitter { ...@@ -53,7 +52,7 @@ export default class SearchClient extends TinyEmitter {
) { ) {
super(); super();
this._searchFn = searchFn; this._searchFn = searchFn;
this._chunkSize = chunkSize; this._pageSize = pageSize;
this._separateReplies = separateReplies; this._separateReplies = separateReplies;
this._incremental = incremental; this._incremental = incremental;
this._maxResults = maxResults; this._maxResults = maxResults;
...@@ -67,17 +66,17 @@ export default class SearchClient extends TinyEmitter { ...@@ -67,17 +66,17 @@ export default class SearchClient extends TinyEmitter {
} }
/** /**
* Fetch a batch of annotations. * Fetch a page of annotations.
* *
* @param {SearchQuery} query - Query params for /api/search call * @param {SearchQuery} query - Query params for /api/search call
* @param {string} [searchAfter] - Cursor value to use when paginating * @param {string} [searchAfter] - Cursor value to use when paginating
* through results. Omitted for the first page. See docs for `search_after` * through results. Omitted for the first page. See docs for `search_after`
* query param for /api/search API. * query param for /api/search API.
*/ */
async _getBatch(query, searchAfter) { async _getPage(query, searchAfter) {
/** @type {SearchQuery} */ /** @type {SearchQuery} */
const searchQuery = { const searchQuery = {
limit: this._chunkSize, limit: this._pageSize,
sort: this._sortBy, sort: this._sortBy,
order: this._sortOrder, order: this._sortOrder,
_separate_replies: this._separateReplies, _separate_replies: this._separateReplies,
...@@ -116,28 +115,28 @@ export default class SearchClient extends TinyEmitter { ...@@ -116,28 +115,28 @@ export default class SearchClient extends TinyEmitter {
return; return;
} }
const chunk = results.rows.concat(results.replies || []); const page = results.rows.concat(results.replies || []);
if (this._resultCount === null) { if (this._resultCount === null) {
// Emit the result count (total) on first encountering it // Emit the result count (total) on first encountering it
this._resultCount = results.total; this._resultCount = results.total;
this.emit('resultCount', this._resultCount); this.emit('resultCount', this._resultCount);
} }
if (this._incremental) { if (this._incremental) {
this.emit('results', chunk); this.emit('results', page);
} else { } else {
this._results = this._results.concat(chunk); this._results = this._results.concat(page);
} }
// If the current batch was full, there might be additional batches available. // If the current page was full, there might be additional pages available.
const nextBatchAvailable = chunk.length === this._chunkSize; const nextPageAvailable = page.length === this._pageSize;
// Get the cursor for the start of the next batch. This is the last // Get the cursor for the start of the next page. This is the last
// value for whatever field results are sorted by from the current batch. // value for whatever field results are sorted by from the current page.
const nextSearchAfter = const nextSearchAfter =
chunk.length > 0 ? chunk[chunk.length - 1][this._sortBy] : null; page.length > 0 ? page[page.length - 1][this._sortBy] : null;
if (nextBatchAvailable && nextSearchAfter) { if (nextPageAvailable && nextSearchAfter) {
this._getBatch(query, nextSearchAfter); this._getPage(query, nextSearchAfter);
} else { } else {
if (!this._incremental) { if (!this._incremental) {
this.emit('results', this._results); this.emit('results', this._results);
...@@ -168,7 +167,7 @@ export default class SearchClient extends TinyEmitter { ...@@ -168,7 +167,7 @@ export default class SearchClient extends TinyEmitter {
get(query) { get(query) {
this._results = []; this._results = [];
this._resultCount = null; this._resultCount = null;
this._getBatch(query); this._getPage(query);
} }
/** /**
......
...@@ -68,7 +68,7 @@ describe('SearchClient', () => { ...@@ -68,7 +68,7 @@ describe('SearchClient', () => {
}); });
it('fetches pages of results for a single URI', async () => { it('fetches pages of results for a single URI', async () => {
const client = new SearchClient(fakeSearchFn, { chunkSize: 3 }); const client = new SearchClient(fakeSearchFn, { pageSize: 3 });
client.get({ uri: 'http://example.com' }); client.get({ uri: 'http://example.com' });
await awaitEvent(client, 'end'); await awaitEvent(client, 'end');
...@@ -115,7 +115,7 @@ describe('SearchClient', () => { ...@@ -115,7 +115,7 @@ describe('SearchClient', () => {
}); });
it('emits "end" only once', done => { it('emits "end" only once', done => {
const client = new SearchClient(fakeSearchFn, { chunkSize: 2 }); const client = new SearchClient(fakeSearchFn, { pageSize: 2 });
client.on('results', sinon.stub()); client.on('results', sinon.stub());
let emitEndCounter = 0; let emitEndCounter = 0;
client.on('end', () => { client.on('end', () => {
...@@ -126,8 +126,8 @@ describe('SearchClient', () => { ...@@ -126,8 +126,8 @@ describe('SearchClient', () => {
client.get({ uri: 'http://example.com' }); client.get({ uri: 'http://example.com' });
}); });
it('emits "results" with chunks in incremental mode', async () => { it('emits "results" with pages in incremental mode', async () => {
const client = new SearchClient(fakeSearchFn, { chunkSize: 2 }); const client = new SearchClient(fakeSearchFn, { pageSize: 2 });
const onResults = sinon.stub(); const onResults = sinon.stub();
client.on('results', onResults); client.on('results', onResults);
...@@ -139,7 +139,7 @@ describe('SearchClient', () => { ...@@ -139,7 +139,7 @@ describe('SearchClient', () => {
}); });
it('emits "resultCount" only once in incremental mode', async () => { it('emits "resultCount" only once in incremental mode', async () => {
const client = new SearchClient(fakeSearchFn, { chunkSize: 2 }); const client = new SearchClient(fakeSearchFn, { pageSize: 2 });
const onResultCount = sinon.stub(); const onResultCount = sinon.stub();
client.on('resultCount', onResultCount); client.on('resultCount', onResultCount);
...@@ -152,7 +152,7 @@ describe('SearchClient', () => { ...@@ -152,7 +152,7 @@ describe('SearchClient', () => {
it('emits "results" once in non-incremental mode', async () => { it('emits "results" once in non-incremental mode', async () => {
const client = new SearchClient(fakeSearchFn, { const client = new SearchClient(fakeSearchFn, {
chunkSize: 2, pageSize: 2,
incremental: false, incremental: false,
}); });
const onResults = sinon.stub(); const onResults = sinon.stub();
...@@ -280,7 +280,7 @@ describe('SearchClient', () => { ...@@ -280,7 +280,7 @@ describe('SearchClient', () => {
].forEach(({ sortBy, sortOrder, expectedSearchAfter }) => { ].forEach(({ sortBy, sortOrder, expectedSearchAfter }) => {
it('sets correct "search_after" query parameter depending on `sortBy` and `sortOrder`', async () => { it('sets correct "search_after" query parameter depending on `sortBy` and `sortOrder`', async () => {
const client = new SearchClient(fakeSearchFn, { const client = new SearchClient(fakeSearchFn, {
chunkSize: 2, pageSize: 2,
sortBy, sortBy,
sortOrder, sortOrder,
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment