I am planning to update the document, if similar values already exists in other records of the document.
Say, if root document has few records in which few fields(track,time) are similar, then I want to update the isOriginal field for such record as false.
function updateArticlesDetailsX() {
var collection = getContext().getCollection();
var collectionLink = collection.getSelfLink();
var response = getContext().getResponse();
var docCount = 0;
var counter = 0;
tryQueryAndUpdate();
function tryQueryAndUpdate(continuation) {
var query = {
query: "select * from root r ORDER BY r.created.epoch DESC"
};
var requestOptions = {
continuation: continuation
};
var isAccepted =
collection
.queryDocuments(collectionLink,
query,
requestOptions,
function queryCallback(err, documents, responseOptions) {
//response.setBody(responseOptions);
if (err) throw err;
if (documents.length > 0) {
document.upd
// If at least one document is found, update it.
docCount = documents.length;
//response.setBody("Found " + docCount + " documents");
for (var i=0; i<docCount; i++){
tryUpdate(documents[i]);
}
//response.setBody("Updated " + docCount + " documents");
}
if (responseOptions.continuation) {
// Else if the query came back empty, but with a continuation token;
// repeat the query w/ the token.
tryQueryAndUpdate(responseOptions.continuation);
}
//else {
// throw new Error("Document not found.");
// }
});
if (!isAccepted) {
//throw new Error("The stored procedure timed out");
}
}
function tryUpdate(document) {
//Optimistic concurrency control via HTTP ETag.
var requestOptions = { etag: document._etag };
//Update statement goes here:
document.update({"track":{document.track},"Time":{document.Time} }, {"$set":{"isOriginal":"false!"}});
document.created = {
date: "2016-06-22 19:18:14",
epoch: 1466623094582
}
//response.setBody(document);
var isAccepted = collection
.replaceDocument(document._self,
document,
requestOptions,
function replaceCallback(err, updatedDocument, responseOptions) {
if (err) throw err;
counter++;
response.setBody("Updated " + counter + " documents");
// response.setBody(updatedDocument);
});
// If we hit execution bounds - throw an exception.
if (!isAccepted) {
//throw new Error("The stored procedure timed out");
}
}
}
Related
I have the need to assign an sequence number for each document inserted in a collection.
I found the following suggestion which hints that such feature is implemented by the function "DocumentId(collection)":
https://feedback.azure.com/forums/263030-azure-cosmos-db/suggestions/6408183-i-want-atomic-counter
When inserting documents directly from .NET code as:
var _cosmosClient = new CosmosClient(new CosmosConfiguration(endpoint, accountKey));
var _container = _cosmosClient.Databases[DatabaseId].Containers[ContainerId];
for (int i = 0; i < 15; i++) {
await _container.Items.CreateItemAsync("event", new { aggregateId = "event", id = Guid.NewGuid().ToString() });
}
And when i query the collection as below, function "DocmentId()" seems to return an incremented integer / sequence just as I need:
SELECT f.id, DocumentId(f) FROM Events f
[
{
"id": "2215dc32-89c1-464c-a559-0269be3068f4",
"$1": 9
},
{
"id": "ca6fac1c-59e1-42e2-858c-9646d6800a7e",
"$1": 10
},
{
"id": "54891f66-26de-461a-8ee7-2b23eb2d7c18",
"$1": 11
}
]
If I instead insert multiple document via a stored procedure, the DocumentId() function behaves differently.
All documents inserted by the stored procedure now has the same value and the value is just a big integer.
{
"id": "046f1a2b-7e6c-488b-af4c-bb06762dae9d",
"$1": 576460752303423500
},
{
"id": "d4f5a8d3-3e5a-456c-8ec9-3967a4250a08",
"$1": 576460752303423500
},
{
"id": "HEAD",
"$1": 576460752303423500
}
Do you know why the DocumentId function behaves differently when a document has been inserted from a stored procedure?
Do you know how I can achieve to have a auto incremented number assigned to each inserted document?
Regards Niclas
Stored Procedure:
function appendEvents(header, docs, etag) {
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);
}
var options = { disableAutomaticIdGeneration: true };
// Call the CRUD API to create a document.
tryCreateEvent(docs[count], callback);
// Insert or update the header:
upsertStreamHeader(
header,
null,
function (err, doc, options) {
if (err) throw err;
}
);
function tryCreateEvent(doc, callback) {
// Assign the offset value relative from the header.offset:
//doc.offset = header.offset + count + 1;
var isAccepted = collection.createDocument(
collectionLink,
doc,
options,
callback
);
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.
tryCreateEvent(docs[count], callback);
}
}
function upsertStreamHeader(doc, continuation, callback) {
var query = { query: "SELECT * FROM root r WHERE r.id = #id", parameters: [{ name: "#id", value: doc.id }] };
var requestOptions = { continuation: continuation };
var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, function (err, retrievedDocs, responseOptions) {
if (err)
{
throw err;
}
if (retrievedDocs.length > 0)
{
tryReplaceStreamHeader(retrievedDocs[0], doc, callback);
}
else
{
tryCreateStreamHeader(doc, callback);
}
});
if (!isAccepted) {
throw new Error("Unable to query documents");
}
}
function tryCreateStreamHeader(doc, callback) {
var isAccepted = collection.createDocument(collectionLink, doc, callback);
if (!isAccepted) {
throw new Error("Unable to schedule create document");
}
}
function tryReplaceStreamHeader(docToReplace, docContent, callback) {
var isAccepted = collection.replaceDocument(
docToReplace._self,
docContent,
{ etag: etag },
callback);
if (!isAccepted) {
throw new Error("Unable to schedule replace document");
}
}
}
The next SP suppose to run over the collection and keep query for the next batch of documents (10 docs every batch). but instead return the same 10 documents every time.
function sample(prefix) {
var continuations = [],
ids = [],
context = getContext(),
collection = context.getCollection(),
response = context.getResponse();
var queryOptions = { pageSize: 10, continuation: null };
for (i = 0; i < 10; i++) {
// get all user wish list actions
var query = "select * from w",
accept = collection.queryDocuments(collection.getSelfLink(), query, queryOptions, processMultiUsers);
if (!accept) throw "Unable to read user's sessions";
}
getContext().getResponse().setBody(ids);
function processMultiUsers(err, docs, options) {
if (err) throw new Error("Error: " + err.message);
if (docs == undefined || docs.length == 0) throw new Error("Warning: Users not exists");
for (j = 0; j < docs.length; j++) {
ids.push(docs[j].UserId);
}
queryOptions.continuation = options.continuation;
continuations.push(options.continuation);
}}
In the script that you wrote, the execution of the queries are done synchronously and they are queued up with the same initial continuation token, which is null. Instead, we need to take the token from the first query and then queue the next and continue.
The below sample should help achieve what you are looking for
function sample(continuationToken) {
var collection = getContext().getCollection();
var maxResult = 10;
var documentsProcessed = 0;
var ids = [];
var filterQuery = "select * from w";
tryQuery(continuationToken);
function tryQuery(nextContinuationToken) {
var responseOptions = { continuation: nextContinuationToken, pageSize: maxResult };
if (documentsProcessed >= maxResult || !query(responseOptions)) {
setBody(nextContinuationToken);
}
}
function query(responseOptions) {
return (filterQuery && filterQuery.length) ?
collection.queryDocuments(collection.getSelfLink(), filterQuery, responseOptions, onReadDocuments) :
collection.readDocuments(collection.getSelfLink(), responseOptions, onReadDocuments);
}
function onReadDocuments(err, docFeed, responseOptions) {
if (err) {
throw 'Error while reading document: ' + err;
}
documentsProcessed += docFeed.length;
for (var i = 0; i < documentsProcessed; i++) {
ids.push(docFeed[i].UserId);
}
if (responseOptions.continuation) {
tryQuery(responseOptions.continuation);
} else {
setBody(null);
}
}
function setBody(continuationToken) {
var body = { continuationToken: continuationToken, documentsProcessed: documentsProcessed, ids: ids };
getContext().getResponse().setBody(body);
}
}
I have a document where I want turn of is_approved = false, if multiple columns are similar(posted_by and location).
I am not able to write a query which can get multiple duplicate columns..
function Prop() {
var collection = getContext().getCollection();
var collectionLink = collection.getSelfLink();
var response = getContext().getResponse();
var counter = 0;
var responseBody = {
updated: 0,
continuation: true,
error: "",
log: ""
};
// Validate input.
getFullListOfPosts();
// Recursively queries for a document by id w/ support for continuation tokens.
// Calls findDuplicates(document) as soon as the query returns a document.
function getFullListOfPosts(continuation) {
var query = {
query: "select * from root r ORDER BY r.created.epoch DESC"
};
var requestOptions = {
continuation: continuation
};
var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, proccessFullListOfPosts);
// If we hit execution bounds - throw an exception.
if (!isAccepted) {
responseBody.log += "Query not accepted";
response.setBody(responseBody);
}
}
function proccessFullListOfPosts(err, documents, responseOptions) {
if (err) {
responseBody.error = err;
throw err;
}
if (documents.length > 0) {
// If documents are found, update them.
responseBody.log += "Found documents: " + documents.length;
findDuplicates(documents);
}
}
// Updates the supplied document according to the update object passed in to the sproc.
function findDuplicates(documents) {
if (documents.length > 0) {
responseBody.log += "Updating documents " + documents.length;
//for (var i = 0; i < documents.length; i++) {
var first = documents[0];
//The below query is not right. I want the above query to fetch all records with same posted_by and location and turn its is_approved to false
var query = 'select * from root r WHERE r.is_approved = true AND r.posted_by = "' + first.posted_by + '"';
var requestOptions = {
etag: first._etag
};
var isAccepted = collection.queryDocuments(collectionLink, query, requestOptions, identifyRecordsToDisable);
// Update the document. // If we hit execution bounds - throw an exception.
if (!isAccepted) {
responseBody.log += "Update not accepted";
response.setBody(responseBody);
}
} else {
getFullListOfPosts();
}
}
function identifyRecordsToDisable(err, documents, responseOptions) {
if (err) {
responseBody.error = err;
throw err;
}
if (documents.length > 0) {
// If documents are found, update them.
responseBody.log += "Found documents: " + documents.length;
for (var i = 0; i < documents.length; i++) {
var j = documents[i];
j.is_approved = false;
responseBody.log += "^Updated" + j.id + j.is_approved + ""
var requestOptions = {
etag: j._etag
};
var isAccepted = collection.replaceDocument(j._self, j, requestOptions, onReplaced);
responseBody.log += "*" + j.id + j.is_approved + "*"
}
findDuplicates(documents);
}
}
function onReplaced(err, updatedDocument, responseOptions) {
if (err) {
responseBody.error = err;
//throw err;
}
counter++;
response.setBody("Updated " + counter + " documents");
}
}
In the above code, in the function 'findDuplicates', I am unable to write a correct query through which I can get all the duplicate records.
I tried using distinct, group by. having. I think documented doesn't support them
The following query should help you find documents that match both property values.
SELECT *
FROM root r
WHERE r.is_approved = true
AND r.posted_by = "' + first.posted_by + '"
AND r.location = "' + first.location + '"
I have a socket function defined as
var funcs = require('./funcs');
socket.on(EVENT_ACCEPT_ORDER, function(data, callback)
{
data = JSON.parse(data);
var bookingId = data.bookingId;
var userId = data.userId;
console.log("Accepting booking...." + bookingId);
var query = "UPDATE bookings SET bookingStatus = " + BOOKING_STATUS_ACCEPTED + " WHERE id = " + bookingId + " AND bookingStatus = " + BOOKING_STATUS_IN_QUEUE;
con.query(query, function(err, rows,fields)
{
if(err)
{
console.log("mysql query error");
}
else
{
if(rows.changedRows > 0)
{
var indexOfUser = usersList.indexOf(userId);
if(indexOfUser > -1)
{
userSockets[indexOfUser].emit(EVENT_USER_ORDER_ACCEPTED);
}
callback({"message": "Success","error":false, "booking": funcs.getBooking(con, bookingId)});
}
else
callback({"message": "Success","error":true});
}
});
});
Funcs is defined as
module.exports = {
"getBooking": function (con, bookingId)
{
var query = "SELECT * FROM bookings WHERE id = " + bookingId + " LIMIT 1";
con.query(query, function(err, rows,fields)
{
if(err)
{
console.log("mysql query error");
}
else if (rows.length == 1)
{
var booking = rows[0];
var userId = rows[0]['userId'];
var query = "SELECT id, name, phone, imagePath FROM users WHERE id = " + userId + " LIMIT 1";
con.query(query, function(err, rows,fields)
{
if(err)
{
console.log("mysql query error");
}
else if (rows.length == 1)
{
booking['user'] = rows[0];
return booking;
}
});
}
});
}
}
Everything is running fine except
callback({"message": "Success","error":false, "booking": funcs.getBooking(con, bookingId)});
in this function instead of booking, i am only getting
{"error":false,"message":"Success"}
Why am i not getting the booking function result?
You are not getting the result, because the result of the callback function in con.query is not returned to the caller of getBooking. It is the asynchronous pattern, which you are not processing correctly.
The way it is supposed to work is that the getBooking gets an extra argument: a function to be called when data are available (in an internal asynchronous call to con.query). Such a function is then provided by the caller and in this function you do whatever you want with the data:
funcs.js
"getBooking": function (con, bookingId, callback) {
...
con.query(query, function(err, rows,fields) {
...
// instead of return booking do
callback(err, booking);
...
}
}
main module
// instead of
callback({"message": "Success","error":false, "booking": funcs.getBooking(con, bookingId)});
// do
funcs.getBooking(con, bookingId, function(err, booking) {
callback({"message": "Success","error":false, "booking": booking});
});
I am afraid this is not the only issue in your code, but this should be the first to fix. Read further about processing asynchronous calls in general and specifically in node.js and fix other places in your code correspondingly.
In node.js + sqlite3:
What is a good way to notify a caller from a callback about errors and get the caller to try again ? When we get a database locked error - would like to try and run the query again.
TestController.prototype.addDevices = function (number_of_devices, callback_after_all_devices_have_been_added) {
var controller = this;
var db = controller.connectToDb();
for(i = 0; i < number_of_devices; i++) {
db.each("SELECT api_key as ak FROM user_table ORDER BY RANDOM() LIMIT 1", function(err, row) {
//sometimes we get a SQLITE: database locked error
if (err !== null) {
console.log("Error found");
//i-- this will not work - but how do we do it then?
return;
}
console.log("Error: " + err);
//code to process if we get an entry from the user_table ..
});
}
}
This is how I managed to solve this - added a retry counter and moved the problem code to its own function, passing in the epilogue as a callback. It works - but is this a good way?
(other than using promises or the async module?)
TestController.prototype.addDevices = function (number_of_devices, callback_after_all_devices_have_been_added) {
var number_of_users = 0;
var controller = this;
controller.pending_db_writes_for_device = number_of_devices;
for(i = 0; i < number_of_devices; i++) {
var api_key = controller.getRandomAPIKeyFromDB(10,function(access_key) {
var mac_address = controller.generateRandomMAC();
var friendly_name = Faker.random.array_element(["Washing Machine","Television","Dishwasher","Set top box"]);
var description = Faker.Lorem.sentence(5);
controller.registerDeviceWithAPI(mac_address, friendly_name, description, access_key, callback_after_all_devices_have_been_added);
});
}
}
TestController.prototype.getRandomAPIKeyFromDB = function (retry_counter, callback) {
var controller = this;
var db = controller.connectToDb();
console.log("Retry counter: "+ retry_counter);
db.each("SELECT api_key as ak FROM user_table ORDER BY RANDOM() LIMIT 1", function(err, row) {
if (err !== null) {
console.log("Error: " + err + "retry count: "+ retry_counter);
console.log("Error found");
if(retry_counter === 0) {
console.log("Bailing out after retry limit reached");
return;
}
controller.getRandomAPIKeyFromDB(--retry_counter,callback);
}
else {
console.log("Successfully got api key: " + row.ak);
callback(row.ak);
}
}
);
}