I have a query to a NoSQL database. Since in this type of database there are not many options for complex queries, I need to make two nested queries to other tables within a loop
created by the first query. Using promises, I decided to read the first nested query. However, the whole block is not "waiting" for the most internal query, and the results it brings
(from users) are not being counted. Here is the simplified code:
The block does the following: For each room (outermost loop), it reads all messages (first promise, from messageRef) and, for each message, takes the user data (second promise, from
usersRef). The first nested query is ok (brings up all messages). However, the user data for each message is not being loaded (in this case, I just put the name, for simplicity).
The messageRef and usersRef objects return promises that are stored in the promises array. At the end, when all are finalized (Promise.all), it returns the array with all the data.
Each "obj" object has the data of each room. In obj.messages, it has the array of message objects and, at each obj.messages, the user data.
roomRef.on('value', function(data){
var promises = [],
threads = [],
counter = 0;
$.each(data.val(), function(id, obj){
obj.id = id;
obj.messages = [];
var promise = messageRef.child(id).on('child_added', function(data){
var msgobj = data.val();
obj.messages.push(msgobj);
return usersRef.orderByChild('login').equalTo(msgobj.createdBy.login).once('child_added', function(data){
msgobj.createdBy.name = data.val().name;
});
});
promises.push(promise);
threads[counter] = obj;
counter += 1;
});
Promise.all(promises).then(function(){
console.log(threads) // array with all data
});
});
Thanks for your help.
If you need to query then use query not a event callback child_added.
child_added callback isn't return promise. It triggered when data added. change them to query that return promise.
roomRef.on('value', function(data) {
var promises = [],
$.each(data.val(), function(id, obj) {
obj.id = id
obj.messages = []
var promise = new Promise(resolve => {
messageRef.child(id).on('child_added', function(data) {
var msgobj = data.val()
obj.messages.push(msgobj)
usersRef
.orderByChild('login')
.equalTo(msgobj.createdBy.login)
.once('child_added', function(data) {
msgobj.createdBy.name = data.val().name
resolve(msgobj)
})
})
})
promises.push(promise)
})
Promise.all(promises).then(function(threads) {
console.log(threads) // array with all data
})
})
Related
I have an awsAppSync client that is set up as follows:
that._getClient = function() {
return client = new AWSAppSyncClient({
url: appSyncUrl,
region: AWS.config.region,
auth: {
type: AUTH_TYPE.AWS_IAM,
credentials: AWS.config.credentials
}
});
}
And a mutate function that is called like this:
that.mutate = function(mutation, variables) {
let client = that._getClient();
return client.mutate({ mutation: mutation, fetchPolicy: 'no-cache', variables: variables })
.then(function (result){
return result;
});
}
I need to make subsequent queries to create different records that depend on one another, so I'm returning the Id of the newly created record to use in the callback of the previous query.
The problem is, that in every callback where the mutate function is called, the mutation that caused this callback gets executed again. For example, I do:
appSync.mutate(mutation, requestParams)
.then(function (response) {
let id = response.id
requestParams = {//new stuff}
return appSync.mutate(mutation, requestParams)
})
.then(//and so forth)
Now, I've seen some posts on here that say that it might be something to do with the optimistic response, and so on, but I actually get two new records in my database. I also considered the cache doing something trippy, but As you can see from the code, I (think) disabled it for the mutation.
Please help.
I have a function that returns a BehaviorSubject but when I try to use the data I get back from the function I need to use it once all the data is back, is there a way to know when the BehaviorSubject is done pulling all the data?
I tried using .finally but it never gets called. Here is the code I'm using.
getData() {
let guideList = '';
this.getChildren(event.node)
.subscribe(
function(data) {
console.log('here');
guideList = data.join(',');
},
function(err) {
console.log('error');
},
function() {
console.log('done');
console.log(guideList);
}
);
}
getChildren(node: TreeNode) {
const nodeIds$ = new BehaviorSubject([]);
//doForAll is a promise
node.doForAll((data) => {
nodeIds$.next(nodeIds$.getValue().concat(data.id));
});
return nodeIds$;
}
Attached is a screen shot of the console.log
Easiest way is to just collect all the data in the array and only call next once the data is all collected. Even better: don't use a subject at all. It is very rare that one ever needs to create a subject. Often people use Subjects when instead they should be using a more streamlined observable factory method or operator:
getChildren(node: TreeNode) {
return Observable.defer(() => {
const result = [];
return node.doForAll(d => result.push(d.id)).then(() => result);
});
}
I have a table called friends with fields
userid and friendid.
I want to query the database to find a user's friends. This is working fine by using the following code:
Parse.Cloud.define("searchfriend", function(request, response) {
var query = new Parse.Query("friends");
query.equalTo("player", request.params.myid);
query.find({
success: function(results) {
var listfreundids = [];
for (var i = 0; i < results.length; ++i) {
listfreundids[i] = results[i].get("friend");;
}
response.success(listfreundids);
},
error: function() {
response.error("error");
}
});
});
Now I have the problem to find the username matching the friendid because i cannot use a 2nd query within the for loop to query the user database...
Using promises you can split this up into several separate parts. Promises are really awesome to use and really easy to create your own promises too.
What I would do is split this up into a query that finds the friend ids and then a query that finds the friends...
Parse.Cloud.define("searchfriend", function(request, response) {
getFriendIDs(request.params.myid).then(function(friendIDs) {
return getFriendUserNames(friendIDs);
}).then(function(friends) {
response.success(friends);
}), function(error) {
response.error(error);
});
});
// function to get the IDs of friends
function getFriendIDs(myID) {
var promise = new Parse.Promise();
var query = new Parse.Query("friends");
query.equalTo("player", myID);
query.find().then(function(friendIDs) {
promise.resolve(friendIDs);
}, function(error) {
promise.reject(error);
});
return promise;
}
// function to get the friends from a list of IDs
function getFriendUserNames(friendIDs) {
var promise = new Parse.Promise();
var query = new Parse.Query("_User");
query.containedIn("id", friendIDs);
query.find().then(function(friends) {
// here I am just returning the array of friends
// but you can pull the names out if you want.
promise.resolve(friends);
}, function(error) {
promise.reject(error);
});
return promise;
}
You could always user a matches query too...
// function to get friends
function getFriends(myID) {
var promise = new Parse.Promise();
var friendQuery = new Parse.Query("friends");
friendQuery.equalTo("player", myID);
var userQuery = new Parse.Query("User");
userQuery.matchesKeyInQuery("ID", "friendID", friendQuery);
userQuery.find().then(function(friends) {
promise.resolve(friends);
}, function(error) {
promise.reject(error);
});
return promise;
}
This will perform a joined query where it gets the friend IDs first and then uses the friend ID to get the user and returns the user object.
Also, use promises. They are much easier to work with and can be pulled apart into separate working units.
Of course, I have no idea if the syntax here is correct and what the correct names should be or even what your object model looks like but hopefully this can act as a guide.
You do not have to query for each friend id that you have found (inefficient). Instead, after getting the list of friend ids, you can query the user database via sending the list of friend ids with the query.containedIn constraints. This strategy will actually decrease the number of query count.Based on retrieved result you can get friend (previously found) information. One more thing to remember, call response success after the operations are executed.Hope this helps.
Regards.
How would I use Backbones fetch to deal with callback results that contain a cursor? I'm going to use this simple example of a book that is fetching pages.
var Book = Backbone.Collection.extend({
model: Page,
recursiveFetch: function(cursor) {
this.fetch({
url: 'book/pages',
data: {
cursor: {cursor here};
},
success: function(response) {
if (response.cursor) {
this.recursiveFetch(response.cursor);
}
}
});
}
})
I need to be able to use fetch to keep fetching until the response doesn't contain a cursor. It should keep adding page models, but not replacing and overwriting them. It needs to do something like the example above, though I'm not sure of the best way to implement it.
I think that all you need to do is add in a {remove: false} into your fetch options. Its also worth mentioning that the this context of your success function may not be the collection, so you might want to pass it into the success function as a parameter. The end result would be:
recursiveFetch: function(cursor) {
this.fetch({
remove:false, // prevents removal of existing models
url: 'book/pages',
success: function(collection, response) {
if (response.cursor) {
collection.recursiveFetch(response.cursor);
}
}
});
}
The fix is very simple: add cursor to the parameters only when it's present. In other cases (i.e. the first time) make a normal request with the rest of the parameters.
var CursorCollection = Backbone.Collection.extend({
fetchAll: function(cursor) {
var params = {
// if you have some other request parameters...
};
// if we have a cursor from the previous call, add it to the parameters
if (cursor) { params.cursor = cursor; }
this.fetch({
remove: false,
data: params,
success: function(collection, response) {
if (response.cursor) {
return collection.fetchAll(response.cursor);
}
}
});
}
});
Then the first time you call it collection.fetchAll() and it recurses until it gets a response without a cursor.
Note, that the remove: false parameter is very important to accumulate the results as pointed out by #dcarson.
I created a custom validator that check if a username is used on a DB.
The whole process of validation works. What is not working is result.
function createExistingUsernameValidator() {
var name = 'existingUsernameValidator';
var ctx = { messageTemplate: 'Questa partita I.V.A. o codice fiscale sono giĆ stati inseriti.', displayName: "Partita IVA o Codice Fiscale" };
var val = new Validator(name, valFunction, ctx);
return val;
function valFunction(value, context) {
var result = ko.observable(true);
require('services/datacontext').getIsUserByUsername(value, result)
.then(function () {
debugger;
return !result();
});
}
}
The promise works: I know because it hits the debbugger line and the retunrnig value is correct.
But the validator always evaluate as false because I'm not returning anything when the validator is called. In other words: it won't wait for the promise.
Is it my bad javascript or something else?
Any help is welcome.
Thank you!
Edited after answer
I've come to a solution that involves Knockout Validation (very useful script).
function createIsExistingUserKoValidation() {
ko.validation.rules['existingUsername'] = {
async: true,
validator: function (val, params, callback) {
if (val) {
var result = ko.observable();
require('services/datacontext').getIsUserByUsername(val, result)
.then(function () {
callback(!result());
});
}
},
message: ' Existing username.'
};
ko.validation.registerExtenders();
}
In the entity creation:
var createDitta = function () {
var ditta = manager.createEntity(entityNames.ditta,
{
id: newGuid(),
legaleRappresentante: createPersona(),
isAttiva: true
});
ditta.pivaCodFiscale.extend({ existingUsername: { message: ' Existing username.', params: true } });
ditta.pivaCodFiscale.isValidating(false);
return ditta;
};
ditta.pivaCodFiscale.isValidating(false); this is needed because isValidating is initialized with true.
The problem is that your valFunction as written will ALWAYS return 'undefined'. ( which is 'falsy'.
The 'return !result()' expression is NOT the return value of 'valFunction', it is simply the result of an anonymous function that executes AFTER valFunction has already returned. This is the async nature of promises.
What you are trying is to write an 'asynchronous' validation which is NOT supported out of the box with Breeze, but the idea IS a good one.
I think that you might be able to accomplish what you want by having your async callback actually 'set' a value on the entity and have that set operation itself trigger a seperate 'synchronous' validation.
This IS a good idea for Breeze to support more naturally so please feel free to add a feature request to the Breeze User Voice for something like "asynchonous validation". We use this to gauge the communities interest in the various proposed features/extensions to Breeze.