-
Notifications
You must be signed in to change notification settings - Fork 153
add bulkDelete_underscoreAPI.js and count_underscoreAPI.js to stored procedures samples #5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dwhieb
wants to merge
18
commits into
Azure:master
Choose a base branch
from
dwhieb:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
30255cb
init clear.js, length.js
dwhieb 4a5695a
remove ESLint comments
dwhieb f55af41
remove ESLint comments
dwhieb 780ca9c
rename count.js > bulkDelete_underscoreAPI.js
dwhieb be725a2
rename length.js > count_underScoreAPI.js
dwhieb f0f61d2
inline predicate function
dwhieb afbbec7
fix typo: 'deletion' > 'counting'
dwhieb b0f3304
inline predicate function in count_underscoreAPI.js
dwhieb 5b696e2
change commit message
dwhieb 1763c99
remove reference to module
dwhieb 3c15749
change 'sessions' to more generic 'documents'
dwhieb ea5876a
replace filterOn and filterValue params with filterObj
dwhieb d42afe4
change `handler` function to `filterHandler`
dwhieb a449eb0
use filterKey & filterValue as params rather than filterObj
dwhieb 780d08e
remove .eslintrc
dwhieb 584a827
fix logic in filter function
dwhieb 321329b
temporarily rename files
dwhieb 8643276
camelCase filenames for consistency
dwhieb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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,121 @@ | ||
/** | ||
* A stored procedure for Azure DocumentDB which deletes documents with a specified filter key and value | ||
* @function | ||
* @param {String} [filterKey] A key to filter documents on. Only documents with the specified key are deleted. If no key is provided, all documents are deleted. If you would like to also specify a value for this key, pass the filterValue parameter as well. The filterKey parameter must be pesent if the filterValue is present. | ||
* @param {Any} [filterValue] If provided, the value that the filterKey must have in order for the document to be deleted. If no filterValue is provided, all documents with the specified filterKey are deleted. | ||
* @return {responseBody} | ||
*/ | ||
function bulkDelete(filterKey, filterValue) { | ||
|
||
if (filterValue && !filterKey) { | ||
throw new Error('If the "filterValue" parameter is provided, the "filterKey" parameter must be provided as well.'); | ||
} | ||
|
||
const response = __.response; // get the response object | ||
|
||
/** | ||
* The response body returned by the stored procedure | ||
* @const | ||
* @typedef {Object} responseBody | ||
* @type {Object} responseBody | ||
* @prop {Number} deleted The number of documents which were deleted. | ||
* @prop {Boolean} continuation Whether there are still more documents to delete. | ||
*/ | ||
const responseBody = { | ||
deleted: 0, | ||
continuation: true, | ||
}; | ||
|
||
/** | ||
* Recursively deletes each document in an array, and then attempts to get more to delete | ||
* @function | ||
* @param {Array} docs The array of documents to delete | ||
*/ | ||
function deleteDocuments(docs) { | ||
if (docs.length > 0) { | ||
|
||
// attempt to delete the first document in the array | ||
const accepted = __.deleteDocument(docs[0]._self, function handler(err) { | ||
if (err) throw err; | ||
|
||
responseBody.deleted++; // increment deleted counter | ||
docs.shift(); // remove document from array | ||
deleteDocuments(docs); // delete the next doc | ||
|
||
}); | ||
|
||
// if the delete request was not accepted due to timeout, return the {@link responseBody} with a continuation | ||
if (!accepted) response.setBody(responseBody); | ||
|
||
} else { | ||
|
||
// if there are no more documents to delete, try getting more | ||
getDocuments(); | ||
|
||
} | ||
} | ||
|
||
/** | ||
* Filters for documents based on the provided filter key and value ({@link filterOn}, {@link filterValue}), and immediately begins deleting them as results are returned | ||
* @function | ||
* @param {String} [continuationToken] A continuation token, if one was received from a previous request | ||
*/ | ||
function getDocuments(continuationToken) { | ||
|
||
/** | ||
* Handler for the filter request | ||
* @function | ||
* @param {Object} err The error object, if any was thrown | ||
* @param {Number} err.number The error code | ||
* @param {String} err.body The body of the error message | ||
* @param {Array} docs The retrieved documents | ||
* @param {Object} info Info about the request, including a continuation token | ||
* @param {String} info.continuation The continuation token, if any was passed | ||
* @return {responseBody} | ||
*/ | ||
const filterHandler = function filterHandler(err, docs, info) { | ||
if (err) throw err; | ||
|
||
if (docs.length > 0) { | ||
|
||
// if documents were found, begin deleting them immediately (prioritizes deletion over searching) | ||
deleteDocuments(docs); | ||
|
||
} else if (info.continuation) { | ||
|
||
// if the filter came back empty but with a continuation token, get the next set of results | ||
getDocuments(info.continuation); | ||
|
||
} else { | ||
|
||
// if there are no more documents and no continuation token, return the {@link responseBody} without a continuation | ||
responseBody.continuation = false; | ||
response.setBody(responseBody); | ||
|
||
} | ||
|
||
}; | ||
|
||
// filter the collection for documents using a filter function | ||
// NB: The filter function must be inlined in order to take advantage of index | ||
// (otherwise it will be a full scan). | ||
const accepted = __.filter(function filter(doc) { | ||
|
||
if (filterValue) { | ||
return doc[filterKey] === filterValue; | ||
} else if (filterKey) { | ||
return doc.hasOwnProperty(filterKey); | ||
} | ||
|
||
return true; | ||
|
||
}, { continuation: continuationToken }, filterHandler); | ||
|
||
// if the filter request is not accepted due to timeout, return the response with a continuation | ||
if (!accepted) response.setBody(responseBody); | ||
|
||
} | ||
|
||
getDocuments(); // start the stored procedure | ||
|
||
} |
118 changes: 59 additions & 59 deletions
118
samples/stored-procedures/BulkImport.js → samples/stored-procedures/bulkImport.js
This file contains hidden or 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 |
---|---|---|
@@ -1,59 +1,59 @@ | ||
/** | ||
* This script called as stored procedure to import lots of documents in one batch. | ||
* The script sets response body to the number of docs imported and is called multiple times | ||
* by the client until total number of docs desired by the client is imported. | ||
* @param {Object[]} docs - Array of documents to import. | ||
*/ | ||
function bulkImport(docs) { | ||
var collection = getContext().getCollection(); | ||
var collectionLink = collection.getSelfLink(); | ||
// The count of imported docs, also used as current doc index. | ||
var count = 0; | ||
// Validate input. | ||
if (!docs) throw new Error("The array is undefined or null."); | ||
var docsLength = docs.length; | ||
if (docsLength == 0) { | ||
getContext().getResponse().setBody(0); | ||
return; | ||
} | ||
// Call the CRUD API to create a document. | ||
tryCreate(docs[count], callback); | ||
// Note that there are 2 exit conditions: | ||
// 1) The createDocument request was not accepted. | ||
// In this case the callback will not be called, we just call setBody and we are done. | ||
// 2) The callback was called docs.length times. | ||
// In this case all documents were created and we don't need to call tryCreate anymore. Just call setBody and we are done. | ||
function tryCreate(doc, callback) { | ||
var isAccepted = collection.createDocument(collectionLink, doc, callback); | ||
// If the request was accepted, callback will be called. | ||
// Otherwise report current count back to the client, | ||
// which will call the script again with remaining set of docs. | ||
// This condition will happen when this stored procedure has been running too long | ||
// and is about to get cancelled by the server. This will allow the calling client | ||
// to resume this batch from the point we got to before isAccepted was set to false | ||
if (!isAccepted) getContext().getResponse().setBody(count); | ||
} | ||
// This is called when collection.createDocument is done and the document has been persisted. | ||
function callback(err, doc, options) { | ||
if (err) throw err; | ||
// One more document has been inserted, increment the count. | ||
count++; | ||
if (count >= docsLength) { | ||
// If we have created all documents, we are done. Just set the response. | ||
getContext().getResponse().setBody(count); | ||
} else { | ||
// Create next document. | ||
tryCreate(docs[count], callback); | ||
} | ||
} | ||
} | ||
/** | ||
* This script called as stored procedure to import lots of documents in one batch. | ||
* The script sets response body to the number of docs imported and is called multiple times | ||
* by the client until total number of docs desired by the client is imported. | ||
* @param {Object[]} docs - Array of documents to import. | ||
*/ | ||
function bulkImport(docs) { | ||
var collection = getContext().getCollection(); | ||
var collectionLink = collection.getSelfLink(); | ||
|
||
// The count of imported docs, also used as current doc index. | ||
var count = 0; | ||
|
||
// Validate input. | ||
if (!docs) throw new Error("The array is undefined or null."); | ||
|
||
var docsLength = docs.length; | ||
if (docsLength == 0) { | ||
getContext().getResponse().setBody(0); | ||
return; | ||
} | ||
|
||
// Call the CRUD API to create a document. | ||
tryCreate(docs[count], callback); | ||
|
||
// Note that there are 2 exit conditions: | ||
// 1) The createDocument request was not accepted. | ||
// In this case the callback will not be called, we just call setBody and we are done. | ||
// 2) The callback was called docs.length times. | ||
// In this case all documents were created and we don't need to call tryCreate anymore. Just call setBody and we are done. | ||
function tryCreate(doc, callback) { | ||
var isAccepted = collection.createDocument(collectionLink, doc, callback); | ||
|
||
// If the request was accepted, callback will be called. | ||
// Otherwise report current count back to the client, | ||
// which will call the script again with remaining set of docs. | ||
// This condition will happen when this stored procedure has been running too long | ||
// and is about to get cancelled by the server. This will allow the calling client | ||
// to resume this batch from the point we got to before isAccepted was set to false | ||
if (!isAccepted) getContext().getResponse().setBody(count); | ||
} | ||
|
||
// This is called when collection.createDocument is done and the document has been persisted. | ||
function callback(err, doc, options) { | ||
if (err) throw err; | ||
|
||
// One more document has been inserted, increment the count. | ||
count++; | ||
|
||
if (count >= docsLength) { | ||
// If we have created all documents, we are done. Just set the response. | ||
getContext().getResponse().setBody(count); | ||
} else { | ||
// Create next document. | ||
tryCreate(docs[count], callback); | ||
} | ||
} | ||
} | ||
|
132 changes: 66 additions & 66 deletions
132
samples/stored-procedures/Count.js → samples/stored-procedures/count.js
This file contains hidden or 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 |
---|---|---|
@@ -1,66 +1,66 @@ | ||
/** | ||
* This is executed as stored procedure to count the number of docs in the collection. | ||
* To avoid script timeout on the server when there are lots of documents (100K+), the script executed in batches, | ||
* each batch counts docs to some number and returns continuation token. | ||
* The script is run multiple times, starting from empty continuation, | ||
* then using continuation returned by last invocation script until continuation returned by the script is null/empty string. | ||
* | ||
* @param {String} filterQuery - Optional filter for query (e.g. "SELECT * FROM docs WHERE docs.category = 'food'"). | ||
* @param {String} continuationToken - The continuation token passed by request, continue counting from this token. | ||
*/ | ||
function count(filterQuery, continuationToken) { | ||
var collection = getContext().getCollection(); | ||
var maxResult = 25; // MAX number of docs to process in one batch, when reached, return to client/request continuation. | ||
// intentionally set low to demonstrate the concept. This can be much higher. Try experimenting. | ||
// We've had it in to the high thousands before seeing the stored proceudre timing out. | ||
// The number of documents counted. | ||
var result = 0; | ||
tryQuery(continuationToken); | ||
// Helper method to check for max result and call query. | ||
function tryQuery(nextContinuationToken) { | ||
var responseOptions = { continuation: nextContinuationToken, pageSize : maxResult }; | ||
// In case the server is running this script for long time/near timeout, it would return false, | ||
// in this case we set the response to current continuation token, | ||
// and the client will run this script again starting from this continuation. | ||
// When the client calls this script 1st time, is passes empty continuation token. | ||
if (result >= maxResult || !query(responseOptions)) { | ||
setBody(nextContinuationToken); | ||
} | ||
} | ||
function query(responseOptions) { | ||
// For empty query string, use readDocuments rather than queryDocuments -- it's faster as doesn't need to process the query. | ||
return (filterQuery && filterQuery.length) ? | ||
collection.queryDocuments(collection.getSelfLink(), filterQuery, responseOptions, onReadDocuments) : | ||
collection.readDocuments(collection.getSelfLink(), responseOptions, onReadDocuments); | ||
} | ||
// This is callback is called from collection.queryDocuments/readDocuments. | ||
function onReadDocuments(err, docFeed, responseOptions) { | ||
if (err) { | ||
throw 'Error while reading document: ' + err; | ||
} | ||
// Increament the number of documents counted so far. | ||
result += docFeed.length; | ||
// If there is continuation, call query again with it, | ||
// otherwise we are done, in which case set continuation to null. | ||
if (responseOptions.continuation) { | ||
tryQuery(responseOptions.continuation); | ||
} else { | ||
setBody(null); | ||
} | ||
} | ||
// Set response body: use an object the client is expecting (2 properties: result and continuationToken). | ||
function setBody(continuationToken) { | ||
var body = { count: result, continuationToken: continuationToken }; | ||
getContext().getResponse().setBody(body); | ||
} | ||
} | ||
/** | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this file getting modified? Looks like it's not. Can we remove if it from the change? |
||
* This is executed as stored procedure to count the number of docs in the collection. | ||
* To avoid script timeout on the server when there are lots of documents (100K+), the script executed in batches, | ||
* each batch counts docs to some number and returns continuation token. | ||
* The script is run multiple times, starting from empty continuation, | ||
* then using continuation returned by last invocation script until continuation returned by the script is null/empty string. | ||
* | ||
* @param {String} filterQuery - Optional filter for query (e.g. "SELECT * FROM docs WHERE docs.category = 'food'"). | ||
* @param {String} continuationToken - The continuation token passed by request, continue counting from this token. | ||
*/ | ||
function count(filterQuery, continuationToken) { | ||
var collection = getContext().getCollection(); | ||
var maxResult = 25; // MAX number of docs to process in one batch, when reached, return to client/request continuation. | ||
// intentionally set low to demonstrate the concept. This can be much higher. Try experimenting. | ||
// We've had it in to the high thousands before seeing the stored proceudre timing out. | ||
|
||
// The number of documents counted. | ||
var result = 0; | ||
|
||
tryQuery(continuationToken); | ||
|
||
// Helper method to check for max result and call query. | ||
function tryQuery(nextContinuationToken) { | ||
var responseOptions = { continuation: nextContinuationToken, pageSize : maxResult }; | ||
|
||
// In case the server is running this script for long time/near timeout, it would return false, | ||
// in this case we set the response to current continuation token, | ||
// and the client will run this script again starting from this continuation. | ||
// When the client calls this script 1st time, is passes empty continuation token. | ||
if (result >= maxResult || !query(responseOptions)) { | ||
setBody(nextContinuationToken); | ||
} | ||
} | ||
|
||
function query(responseOptions) { | ||
// For empty query string, use readDocuments rather than queryDocuments -- it's faster as doesn't need to process the query. | ||
return (filterQuery && filterQuery.length) ? | ||
collection.queryDocuments(collection.getSelfLink(), filterQuery, responseOptions, onReadDocuments) : | ||
collection.readDocuments(collection.getSelfLink(), responseOptions, onReadDocuments); | ||
} | ||
|
||
// This is callback is called from collection.queryDocuments/readDocuments. | ||
function onReadDocuments(err, docFeed, responseOptions) { | ||
if (err) { | ||
throw 'Error while reading document: ' + err; | ||
} | ||
|
||
// Increament the number of documents counted so far. | ||
result += docFeed.length; | ||
|
||
// If there is continuation, call query again with it, | ||
// otherwise we are done, in which case set continuation to null. | ||
if (responseOptions.continuation) { | ||
tryQuery(responseOptions.continuation); | ||
} else { | ||
setBody(null); | ||
} | ||
} | ||
|
||
// Set response body: use an object the client is expecting (2 properties: result and continuationToken). | ||
function setBody(continuationToken) { | ||
var body = { count: result, continuationToken: continuationToken }; | ||
getContext().getResponse().setBody(body); | ||
} | ||
} | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this file getting modified? Looks like it's not. Can we remove if it from the change?