Parse.com multiple relational queries - javascript

Here is a question for parse.com gurus.
I am using Parse Javascript API and trying to execute a query over 2 Pointers and cannot get it to work.
So i have following classes: Posts, Users, Groups. Posts has Pointer to Users. Users has a Pointer to Groups.
I need to get all POSTS, where a USER belongs to GROUP, which name starts with "Admin". Here is my code that doesn't work:
var Posts = Parse.Object.extend("Posts");
var Users = Parse.Object.extend("Users");
var Groups = Parse.Object.extend("Groups");
var query = new Parse.Query(Posts);
var innerQueryUsers = new Parse.Query(Users);
var innerQueryGroups = new Parse.Query(Groups);
innerQueryGroups.startsWith("name", "Admin");
innerQueryUsers.matchesQuery("group", innerQueryGroups);
query.matchesQuery("user", innerQueryUsers);
query.find({
success: function(data) {
},
error: function(error){
// here i get error: {code: 102, message: "bad type for $inQuery"}
}
});
Anybody have an idea how to do it right?

Edit - This can be done in one (untested) query by combining a group query and a post query with the same user query...
function postsInGroup(name) {
var groupQuery = new Parse.Query("Group");
groupQuery.equalTo("name", name);
var userQuery = new Parse.Query(Parse.User);
userQuery.matchesQuery("group", groupQuery);
var postQuery = new Parse.Query("Post");
postQuery.matchesQuery("user", userQuery);
return postQuery.find();
}
Call it like this...
postsInGroup("Admin").then(function(posts) {
console.log(JSON.stringify(posts));
}, function(error) {
console.log(JSON.stringify(error));
});
Its not clear what savings there is between this approach and first querying the group. It's likely that parse.com runs the inner queries much as you would. The difference in readability is a matter of taste.

Related

Resuing IDBTransaction in callbacks throws TransactionInactiveError

I'm developing a app which uses IndexedDB extensively. What I'm trying to do is save data of employees table and company table. Each employee belongs to a company and in employee object I've Company's ID and the objects of both of the entity will look like this.
Company's object:
{"id":1,"name":"ABC"}
Employee's object:
{"id":100,"name":"E1","company_id":1}
I'm saving company's details using auto-incremented key (called it appid), so my final object of company's look like this:
{"id":1,"name":"ABC","appid":1}
Where the appid will get auto-incremented as I insert records of company one by one. Now while inserting employee's object I want to find the localid(appid) of the company and save it in employee's object to make employee's object look like:
{"id":100,"name":"E1","company_id":1,"company_app_id":1}
I'm able to get the localid of the company by calling a method while saving employee's details, like:
var transaction = db.transaction(['employees'], 'readwrite');
var objStore = transaction.objectStore('employees');
var company_id=employeeobject.company_id;
companyDB.getCompanyById(company_id,function(companyObject){
transaction = db.transaction(['employees'], 'readwrite');
objStore = transaction.objectStore('employees');
// If I comment above two lines it throws me exception.
var request=objStore.put(employeeobject);
request.onsuccess = function (e) {
// using it for next insertion.
};
});
Problem with the above code is every time when I want to insert employee's data in table I need to reopen the trascation in callback function because if I don't open the transaction again it throws TransactionInactiveError.
I've searched for specific error on SO and found that Transaction get inactive as soon as it's no more used in current scope.
Above code work perfectly fine when I've couple of employee's objects.
But When I'm trying to execute the same code with ~1K of data it takes
(normal execution time x ~10).
By normal execution time I mean without fetching company's localid and saving employee's details directly.
So my question is, what is the best way I can insert the employee's data including company's localid with least execution time? Or Am I doing something wrong ?
It depends on how you're implementing companyDB.getCompanyById().
As presented, it's an asynchronous black box (maybe it's doing a network request?) And as you've discovered, Indexed DB transactions are only active (1) directly after creation and (2) in callbacks from requests made in that transaction, until "control returns to the event loop". Without further details, your best bet would be to batch the work - do the getCompanyById() lookups for N employees, then write those N records.
But from the description at the top "...data of employees table and company table..." maybe this is all within a single Indexed DB database, in which case just use a single transaction for everything:
var tx = db.transaction(['companies', 'employees'], 'readwrite');
employee_records.forEach(function(record) {
var company_id = record.company_id;
var req = tx.objectStore('companies').get(company_id);
req.onsuccess = function() {
var company = req.result;
record.company_app_id = company.app_id;
tx.objectStore('employees').put(record);
};
});
(I'm mangling your actual data/logic here, this is just to illustrate)
Hmm, maybe something like this helps?
function addCompany(db, company, callback) {
var tx = db.transaction('companies', 'readwrite');
var store = tx.objectStore('companies');
var request = store.add(company);
request.onsuccess = callback;
}
function addEmployee(db, employee, callback) {
var tx = db.transaction('employees', 'readwrite');
var store = tx.objectStore('employees');
var request = store.add(employee);
request.onsuccess = callback;
}
function addCompanyThenEmployee(db, company, employee, callback) {
addCompany(db, company, onAddCompany);
function onAddCompany(event) {
var newAppId = event.target.result;
employee.company_app_id = newAppId;
addEmployee(db, employee, callback);
}
}
var company = {'id': 1, 'name': 'xyz'};
var employee = {'id': 1, 'name': 'xyz'};
var request = indexedDB.open(...);
request.onsuccess = function(event) {
var db = event.target.result;
addCompanyThenEmployee(db, company, employee, onAddCompanyThenEmployee.bind(null, company, employee));
};
function onAddCompanyThenEmployee(company, employee, event) {
console.log('Added company', company, 'and employee', employee);
};

Does Parse.com permit the inclusion of pointers inside a relation query?

I have the following many-to-many relationship in my Parse.com application:
Bit ↔ Message
The following fetches related messages from a 'Bit' object:
const query = new Parse.Query('Bit');
query.include('participants');
query.include('user');
query.equalTo('objectId', req.query.bitId);
const promise = query.first().then(function(bit) {
return new Promise(function(resolve) {
const relation = bit.relation('messages');
relation.query().find().then(function(messages) {
resolve(messages);
});
});
});
*Note: messages is a relation column type not a pointer type.
The result inside messages is an array that contains all of the related messages for that bit. Each message contains a pointer to a User called sender. Is it possible to include this User object in the query result? It would be nice if I could use the include() method on a relational query like so:
relation.include('sender');
You can qualify a relation's query as you would any other query. (Also, you've got some superfluous promise code in the OP. I cleaned that up a little, but the point of the answer is on the commented line)...
var query = new Parse.Query('Bit');
query.include('participants');
query.include('user');
query.equalTo('objectId', req.query.bitId);
var promise = query.first().then(function(bit) {
var relationalQuery = bit.relation('messages').query();
relationalQuery.include("sender"); // <-- the point
return relationalQuery().find();
}).then(function(messages) {
return resolve(messages);
});

Cloud Code for Parse.com, Query about Parse.User

I need to write a background job in Cloud Code.
It should query the 'User' class for each user, get the array of skills in the "offer" column. Then it should query the 'User' class again for each user comparing the skills from the first user on a match with all the other users.
Here is what I currently have..
Parse.Cloud.job("backgroundJob", function(request, status) {
// Set up to modify user data
Parse.Cloud.useMasterKey();
var counter = 0;
// Query for all users
var users_query = new Parse.Query(Parse.User);
users_query.find(function(user) {
for(var i = 0; i < user.length; i++){
var searchSkills = user[i].get("search");
var query = new Parse.Query(Parse.User);
query.containedIn("offer", searchSkills);
var pushQuery = new Parse.Query(Parse.Installation);
pushQuery.matchesQuery('user', query);
}
Parse.Push.send({
where: pushQuery,
data: {
alert: "Found someone close to you!"
}
}, {
success: function() {
// Push was successful
},
error: function(error) {
// Handle error
}
});
status.message(counter + " users processed.");
counter++;
}).then(function() {
// Set the job's success status
status.success("Success");
}, function(error) {
// Set the job's error status
status.error("Error has been encountered");
});
});
I get following error in the Push Notification saying:
Cannot perform operation on non-existing column "offer"
Apparently it creates a new empty 'User' Class for my second user query.
Any help would be very much appreciated.
UPDATE! Thats how my user class looks like:
Here's the link for the containedIn method: https://parse.com/docs/js/api/classes/Parse.Query.html#methods_containedIn
So you need to have an "offer" column on your user, that contains a value that would be found in the array searchSkills. Sounds like you don't have an "offer" column. Maybe you have "Offer"? These are case sensitive.
For the second part, do you have a "user" field on your installations? That's something you'd have to set manually, I believe. So it sounds like you never set that, and it's creating a blank user field for the query.
Also, Query.find can only return 1000 results. If you want to go through all of your users, you'll have to use Query.each instead.

Multiple MongoDB queries without using a loop?

I'm trying to figure out if I can make my MongoDB queries (fron Node.js) more quicker and more efficient.
Basically I have an array of "Players", each Player has a "player_id" property. I want to iterate over each Player and use the player_id to find data about the Player in a MongoDB database.
Because of the Asynchronous nature of Node.js I have to guarantee that when I send in a query to the database, the data that I get back corresponds to the player_id that was used to query.
So far I have the following code (that I simplified). What I'm most interested in is how I can achieve this without using a for loop. Thanks.
var playersArray = new Array({"player_id":1234567}, {"player_id":9847621}, {"player_id":0946783}, {"player_id":8712890});
queryDataForPlayers(playersArray, function(data){
//Done - Each Player now has a "new_data_property" property
})
function queryDataForPlayers(playersArray){
var newPlayersArray = new Array();
var counter = 0;
for(var i=0; i<playersArray.length; i++)
{
retrievePlayerData(playersArray[i].player_id, playersArray[i], function(err,data)
{
newPlayersArray.push(data);
if(++counter == playersArray.length)
{
callback(newPlayersArray);
}//end if
});
}
}
var Schema = mongoose.model('Schema');
var ObjectID = require('mongodb').ObjectID;
function retrievePlayerData(playerID, obj, callback){
Schema.find({_id:ObjectID(String(playerID))}, function(err,data){
obj["new_data_property"] = data;
callback(err,obj);
});
}
I can't really test this, but you can pass in an array of player ID's directly to mongo, and get a document set with the related data back in just one query, something like
var playersArray = new Array({"player_id":1234567}, {"player_id":9847621}, {"player_id":0946783}, {"player_id":8712890});
var Schema = mongoose.model('Schema');
var ObjectID = require('mongodb').ObjectID;
function queryDataForPlayers(playersArray, callback){
var player_ids = playersArray.map(function(player) {
return ObjectID(String(player.player_id));
});
Schema.find({
'_id': { $in: player_ids}
}, function(err, docs){
callback(err, docs);
});
}
Use $in operator... You can use it like
Schema.find({_id:{$in:[array_of_playerid]} }).exec(function(error,results)){
}

Making News Feed using Parse.com queries

I'm trying to create a news feed based on my friends and myself activities using Parse queries. I have a collection called "MeFollow" [has columns "me" "followUser"] which shows ppl I follow and another collection called "Activity" which stores toUser, fromUser, type, content.
this is how I find my followers:
var myFollowers = new Parse.Query("MeFollow");
myFollowers.equalTo("me", Parse.User.current());
but I can't find my friends activities using next query:
var friendsActivity = new Parse.Query("Activity");
friendsActivity.equalTo("fromUser", myFollowers.followUser);
my activities works fine:
var myActivity = new Parse.Query("Activity");
myActivity.equalTo("fromUser", Parse.User.current());
here is the main query:
var mainQuery = Parse.Query.or(myActivity, friendsActivity);
mainQuery.include("fromUser");
mainQuery.include("productId");
mainQuery.descending("createdAt");
mainQuery.find({
success: function(results) {
console.log(results);
response.success(results);
},
error: function(error) {
response.error(error);
// There was an error.
}
});
I think I cannot do "myFollowers.followUser"! Does anyone know how can I make this possible?
PS: I'm doing this in cloud code so it is javascript
You can't use myFollowers.followUser because myFollowers returns objects that show relations between me and followUser, not the followUser objects itself.
You should use matchesKeyInQuery like:
var friendsActivity = new Parse.Query("Activity");
friendsActivity.matchesKeyInQuery("fromUser", "followUser",myFollowers);
You can check this docs which is not easily accessed in the main documentation: https://www.parse.com/docs/js/api/symbols/Parse.Query.html#matchesKeyInQuery

Categories

Resources