Not in query with Parse.com API - parse-platform

Given the Objects:
_User: (id, name, etc)
Trip: (id, _user, startDate, endDate, notes)
Is it possible to use the Parse Javascript API to find users who don't have any trips? In SQL it would be:
SELECT * FROM _User WHERE id NOT IN (SELECT _User FROM Trips)
Is that possible in Parse without selecting all users and then checking whether each one has trips?
Perhaps the question is better expressed in code:
var Trip = Parse.Object.extend("Trip");
var innerQuery = new Parse.Query(Trip);
var query = new Parse.Query(Parse.User);
query.doesNotMatchQuery("???", innerQuery);
query.find({
success: function(users) {
users.forEach(function(u){
console.log(u.get('name'));
});
}
});
What should go in the ??? User, _User and id return all Users, not those where there are no Trips.

In the end I used promises for this and can find no way to do it 'out of the box'. It's almost certainly best to wrap this up into cloud code.
Here's some sample code:
// Setup outer query
var query = new Parse.Query("Trip");
query.include('nomad');
query.find().then(function(tripsStartingNextWeek) {
var promises = [];
tripsStartingNextWeek.forEach(function(t){
promises.push((function(t){
var promise = new Parse.Promise();
var toNomad = t.get('nomad');
// inner query
var myFutureTrips = new Parse.Query("Trip");
myFutureTrips.notEqualTo('deleted', true);
myFutureTrips.find().then(function(futureTrips) {
// these records match both queries
if (futureTrips.length > 0) {
// do something
} else {
// do something else
}
promise.resolve();
});
return promise;
})(t));
});
return Parse.Promise.when(promises);
})
.then(function() {
status.success("Last trip job finished");
});

Related

Update Parse user via Cloud Code

I'm trying to update some attributes on my user via cloud code, but even when there is no error, nothing is being updated??? Any thoughts?
Parse.Cloud.define("updateDetailsUser", function (request, response) {
var query = new Parse.Query("User");
query.equalTo("objectId", request.user.id);
var mobileNumber = request.params.mobileNumber;
var fullName = request.params.fullName;
var email = request.params.email;
query.first({
success: function (user) {
user.set("mobileNumber", mobileNumber);
user.set("fullName", fullName);
user.set("email", email);
user.save(null, {
useMasterKey: true,
success: function (updated) {
}
});
}
});
});
For all the classes that are built-in, it suggests to use the Parse class name for them. In case of User, you need to use Parse.User.
So, to fix this, just replace
var query = new Parse.Query("User"); with var query = new Parse.Query(Parse.User); and it should work.
To read further : http://parseplatform.org/Parse-SDK-JS/api/

Parse Cloud - Get user informations by objectId

I'm trying to get user lang from User class in Parse Cloud. lang is one of the columns in User class. I wanna get lang of the user. My entire Cloud Code is as following (it didn't work):
Parse.Cloud.beforeSave('Order', function(request, response) {
var orderState = request.object.get('orderState');
var subtitleMessage = '';
var userLang = '';
var currentUser = request.object.get('user');
var userQuery = new Parse.Query(Parse.User);
userQuery.equalTo('objectId', currentUser.id);
.find()
.then((result)=>{
userLang = result.get('lang');
})
if (orderState === undefined || ['nonApproved', 'approved', 'delivered', 'canceled'].indexOf(orderState) < 0) {
response.error("OrderState is null or not one the ['nonApproved', 'approved', 'delivered', 'canceled']!");
} else {
var query = new Parse.Query(Parse.Installation);
query.include('user');
query.equalTo('user', request.object.get('user'));
Parse.Push.send(
{
where: query,
data: {
title: "MyTitle",
alert: subtitleMessage
}
},
{
success: function() {
},
error: function(error) {
response.error(error)
},
useMasterKey: true
}
);
response.success();
}
});
The answer from Jake T. has some good points. Based on his answer and your comment on the question, you could try something like this:
Parse.Cloud.beforeSave('Order', function(request, response) {
var currentUser = request.object.get('user');
currentUser.fetch({useMasterKey: true}).then(function(user) {
var userLang = user.get('lang');
// send push notifications based on userLang here
response.success();
}).catch(function(error) {
// handle any errors here
console.error(error);
});
});
Verify you actually have a User object shell from request.object.get("user"); And, if you do, you can just call currentUser.fetch() instead of performing a query, unless there are other objects you may need to include.
Since you used a query, the result is an array, even if there is only a single object returned (or none, it would be simply []). So, you're doing Array.get("lang"), which shouldn't do anything. Try if( results && results.length > 0 ) user = results[0];, then you should be able to access user.get("lang");
You should have ACL / CLP's set up for your User class. Ideally, these should not be accessible by people who are not the user or master. So, if that is set up properly, your solution may be passing {useMasterKey:true} as an option to the query / fetch.

Get Data from parse.com Promises

I am trying to use parse.com promises to retrieve job data as well as user relation data associated with the job. I have a function that returns promises but not job data. How do I get the job & employee information from the returned promises?
Logically I want to:
1) Query Parse to get array of jobs
2) For each job, query Parse again to get the employee relation information
3) Create local object that contains job & employee details
4) Add each job object to local array
5) Load table with array of objects once all information has been retrieved from Parse
I can do steps 1-4 but I can't figure out how to wait until all information has been retrieved from Parse to refresh the local table.
function getJobPromises (){
var promises = [];
var Job = Parse.Object.extend("Job");
var query = new Parse.Query(Job);
query.equalTo("company", company);
query.notEqualTo("isDeleted", true);
query.limit(1000); // raise limit to max amount
query.find().then(function(results) {
// Create a trivial resolved promise as a base case.
var promise = Parse.Promise.as();
_.each(results, function(result) {
// For each item, extend the promise with a function to add it to the job array
promise = promise.then(function() {
// Return a promise that will be resolved when the job details have been added to the array
var object = result;
promises.push(getEmployeeName(object));
allJobDataArray = promises;
});
});
return Parse.Promise.when(promises);
}).then(function() {
// Every job has been retrieved
console.log("All items have been returned. Refresh table...");
console.log(allJobDataArray);
});
}
The function that does the relational query to get the users associated with the job
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
console.log(employeeNameArray);
}
},
error: function(error){
response.error(error);
}
});
}
Update
It is now working thanks to #eduardo
I have a public array to hold the job objects.
var jobObjectsArray = [];
In the getEmployeeName function I am creating the job objects and adding them to that array
function getJobPromises (){
var promises = [];
var Job = Parse.Object.extend("Job");
var query = new Parse.Query(Job);
query.equalTo("company", company);
query.notEqualTo("isDeleted", true);
query.limit(1000); // raise limit to max amount
query.find().then(function(results) {
_.each(results, function(result) {
promises.push(getEmployeeName(result));
});
return Parse.Promise.when(promises);
}).then(function(allJobDataArray) {
// allJobDataArray should be actually an Array of Array
console.log(jobObjectsArray);
refreshTable();
});
}
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return new Promise(
function(resolve, reject) {
rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
var objAllJobs = new Object();
objAllJobs["jobId"] = jobObject.id;
objAllJobs["location"] = jobObject.get("location");
objAllJobs["startTime"] = jobObject.get("startTime");
objAllJobs["employee"] = employeeNameArray;
jobObjectsArray.push(objAllJobs);
}
console.log(employeeNameArray);
resolve(employeeNameArray);
},
error: function(error){
reject(error);
}
});
}
);
There was a few incorrect uses of the promise concept. I will go through them, but first here is the final code:
function getJobPromises (){
var promises = [];
var Job = Parse.Object.extend("Job");
var query = new Parse.Query(Job);
query.equalTo("company", company);
query.notEqualTo("isDeleted", true);
query.limit(1000); // raise limit to max amount
query.find().then(function(results) {
_.each(results, function(result) {
promises.push(getEmployeeName(result));
});
return Parse.Promise.when(promises);
}).then(function(allJobDataArray) {
// allJobDataArray should be actually an Array of Array
console.dir(allJobDataArray);
console.log(allJobDataArray[0]);
});
}
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
}
console.log(employeeNameArray);
return employeeNameArray;
},
error: function(error){
response.error(error);
}
});
}
"Parse.Promise.as()" should be used only if you have a value that you want to return as a promise. Something like:
Parse.Promise.as("my value").then(function(foo) {
console.log(foo) // "my value"
});
So if your "getEmployeeName" function is returning a promise, which means that this "rQuery.query().find" returns a promise, you don't have to create a new promise or use the "Parse.Promise.as()", it is already a promise and you can push it to the promises array.
Another problem was that you were not return anything in the "getEmployeeName" method callback. Take a look into my version, I'm returning "employeeNameArray".
My version will only work if this "rQuery.query().find" method returns a promise. If that is not the case, you can create a new promise using its callbacks like this:
function getEmployeeName(jobObject) {
var employeeNameArray = [];
//Query to get array of employees for the passed in job
var rQuery = jobObject.relation("employee");
return new Promise(
function(resolve, reject) {
rQuery.query().find({
success: function(employees){
//Get employees full name for each job
for (var i = 0; i < employees.length; i++) {
var objEmployee = employees[i];
var fullName = objEmployee.get("fullName");
employeeNameArray.push(fullName);
}
console.log(employeeNameArray);
resolve(employeeNameArray);
},
error: function(error){
reject(error);
}
});
}
);
}
Please notice this "new Promise()" depends on the browser support of Promise, I don't know if Parse has an equivalent. Anyways you can use it with a polyfill that implements the necessary code if the browser has no support.
More about standard promises: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Polyfill: https://github.com/jakearchibald/es6-promise/
Hope this helps.

Import Parse class to algolia

I am trying to import my class (2500 records) from Parse.com into an Algolia index. There is a limit of 100 records by default which obviously is not working for me. Even if I use query.limit = 1000;
How can the below code be used to import my whole class?
Parse.Cloud.define("createIndex", function(request, response) {
var algoliasearch = require('cloud/algoliasearch.parse.js');
var client = algoliasearch('9PsdfsdWVU7', '3b24e897bfb4esdfsdfsdf209e25c28');
var index = client.initIndex('exercises');
console.log("running");
var objectsToIndex = [];
//Create a new query for Contacts
var query = new Parse.Query('Exercises');
query.limit = 1000;
// Find all items
query.find({
success: function(exercises) {
// prepare objects to index from contacts
objectsToIndex = exercises.map(function(exercise) {
// convert to regular key/value JavaScript object
exercise = exercise.toJSON();
// Specify Algolia's objectID with the Parse.Object unique ID
exercise.objectID = exercise.objectId;
return exercise;
});
// Add or update new objects
index.saveObjects(objectsToIndex, function(err, content) {
if (err) {
throw err;
}
console.log('Parse<>Algolia import done');
});
response.success("worked");
},
error: function(err) {
response.error("failed");
throw err;
}
});
});
Parse.com has a find limit, and all other limitations when you want to "get all the objects", you can find more information here: https://parse.com/docs/js/guide#performance-limits-and-other-considerations
For your current issue you could do this:
Parse.Cloud.define("createIndex", function(request, response) {
var algoliasearch = require('cloud/algoliasearch.parse.js');
var client = algoliasearch('9PsdfsdWVU7', '3b24e897bfb4esdfsdfsdf209e25c28');
var index = client.initIndex('exercises');
console.log("running");
//Create a new query for Contacts
var Exercises = Parse.Object.extend('Exercises');
var query = new Parse.Query(Exercises);
var skip = -1000;
var limit = 1000;
saveItems();
function saveItems() {
skip += limit;
query.skip(skip + limit);
query.limit(limit);
query.find({success: sucess, error: error});
}
function sucess(exercices) {
if (exercices.length === 0) {
response.success("finished");
return;
}
exercises = exercises.map(function(exercise) {
// convert to regular key/value JavaScript object
exercise = exercise.toJSON();
// Specify Algolia's objectID with the Parse.Object unique ID
exercise.objectID = exercise.objectId;
return exercise;
});
index.saveObjects(exercises, function(err) {
if (err) {
throw err;
}
console.log('Parse<>Algolia import done');
response.success("worked");
saveItems();
});
}
function error() {
response.error("failed");
throw err;
}
});

Parse.com - Promises - Return the parent object with other one-to-one object as a property

I have 2 tables in parse
Memories and Locations with a relation One-To-One, but the pointer is in Location table ( Pointer )
What i need is to read all memories ten by ten with skip and limit and for each memory to atach a property with location object
By now i have this:
var _ = require('underscore.js');
Parse.Cloud.define("feed", function(request, response) {
var memories = Parse.Object.extend("Memories");
var memoriesQuery = new Parse.Query(memories);
memoriesQuery.skip(0);//request.params.skip);
memoriesQuery.limit(10);//request.params.limit);
memoriesQuery.descending("createdAt");
memoriesQuery.include("group");
var parsedResults = [];
memoriesQuery.find().then(function(memories) {
var promise = Parse.Promise.as();
_.each(memories, function(memory) {
promise = promise.then(function() {
var locations = Parse.Object.extend("Locations");
var locationsQuery = new Parse.Query(locations);
locationsQuery.equalTo("memory", memory);
var subPromise = Parse.Promise();
locationsQuery.first().then(function(location) {
memory["location"] = location;
console.log(JSON.stringify(memory) + " ........ " + memory["location"]);
console.log("=============");
parsedResults.push(memory);
subPromise.resolve(memory);
});
return subPromise ;
});
console.log("-----------");
console.log("Promise:" +promise.toString());
});
return promise;
}).then(function(){
response.success(parsedResults);
});
});
I have no clue what to do.. More than 10 hours of tries.
I appreciate any help!
Finally solved!
Maybe it will help somebody.
Solution:
var _ = require('underscore.js');
var memoriesResult = [];
Parse.Cloud.define("feed", function(request, response) {
var promises = [];
var promise = findMemories();
promise.then(function(memories){
console.log("success promise!!");
_.each(memories, function (memory) {
console.log("each");
promises.push(findLocation(memory));
});
return Parse.Promise.when(promises);
}).then(function(){
console.log("Finish");
response.success(memoriesResult);
}, function(error){
console.error("Promise Error: " + error.message);
response.error(error);
});
});
function findMemories(){
console.log("Enter findMemories");
var memories = Parse.Object.extend("Memories");
var memoriesQuery = new Parse.Query(memories);
memoriesQuery.skip(0);//request.params.skip);
memoriesQuery.limit(10);//request.params.limit);
memoriesQuery.descending("createdAt");
memoriesQuery.include("group");
var promise = new Parse.Promise();
memoriesQuery.find().then(function(memories) {
console.log("Memories found!");
promise.resolve(memories);
}, function(error) {
promise.reject(error);
});
return promise;
}
function findLocation(memory) {
console.log("Enter findLocation");
var locations = Parse.Object.extend("Locations");
var locationsQuery = new Parse.Query(locations);
locationsQuery.equalTo("memory", memory);
var promise = new Parse.Promise();
locationsQuery.first().then(function(location) {
console.log("Location found");
memoriesResult.push({"memory": memory, "location" : location});
promise.resolve(memory);
}, function(error) {
promise.reject(error);
});
return promise;
}
After doing some experiments, I've come up with the following requirements and solution:
You want to page through Memories items a page at a time, sorted by their createdAt
You want the Locations related to each memory
You are limited by the fact that the relationship is Locations->Memories
Your first step is to define the query for memories:
var memoryQuery = new Parse.Query('Memories');
memoryQuery.skip(request.params.skip);
memoryQuery.skip(request.params.limit);
memoryQuery.descending('createdAt');
You can now use this query to limit the Locations objects returned. If your links are all setup correctly you will get back 10 records:
var locationQuery = new Parse.Query('Locations');
// limit to only the page of Memory items requested
locationQuery.matchesQuery('memory', memoryQuery);
// include the Memory pointer's data
locationQuery.include('memory');
var memoriesResult = [];
locationQuery.find().then(function(locations) {
var result = _.map(locations, function(location) {
memory: location.get('memory'),
location: location
});
response.success(result);
}, function(error) {
response.error(error);
});
The only issue with the above is that I'm not sure on the order of the returned records, so you might want to re-sort them before returning them. That is extremely simple though with the underscore library.
This will result in 2 queries no matter the page size.

Resources