I am quite new to Parse.
I have a database set up using this code:
var Class = Parse.Object.extend("Class");
var Team = Parse.Object.extend("Team");
var Student = Parse.Object.extend("Student");
var newClass = new Class();
newClass.set("name", className);
newClass.set("code", classCode);
newClass.set("creator", currentUser);
var classACL = new Parse.ACL(currentUser);
classACL.setPublicReadAccess(true);
newClass.setACL(classACL);
newClass.save();
for (var i = 0; i < teamNames.length; i++) {
var team = new Team();
team.set("name", teamNames[i]);
var teamACL = new Parse.ACL(currentUser);
teamACL.setPublicReadAccess(true);
team.setACL(teamACL);
team.save();
for (var j = 0; j < studentNames[i].length; j++) {
if (studentNames[i][j]) {
var student = new Student();
student.set("name", studentNames[i][j]);
student.set("parent", team);
student.save();
}
}
team.set("parent", newClass);
team.save();
}
newClass.save(null, {
success: function(newClass) {
//success
},
error: function(newClass, error) {
//fail
}
});
Here Class, Team, and Student are modeled as one-to-many relationships.
Now when a student signs up for the class using his or her own user account, the corresponding Student's user column is set to the current user.
Then I want to list all the classes whose creator OR one of its student's user column (if exists) equals to currentUser.
How do I create such a query referencing multiple classes in Parse (or how can I optimize the database so that such a query can be made as efficient as possible -- without having to create two separate queries?)
Any help is appreciated.
Clarification:
I knew that I could do an or query as described in Parse docs (I should have stated this in the question), however, my question is about doing so on relational data (defined by a pointer type property to parent). Here I need user be a property of a Student instance, which belongs to Team, and then to Class, and I'd like to filter only Class objects that has either its creator property or one of its grandchildren's (an instance of Student) user property equal to the currentUser, effectively listing only the classes that you created or are registered as a student.
Since the current database schema is having nested Pointers, there is no easy way to achieve this without adjusting it.
Database Schema
In Class class, add a Relation or Array field to contain references to Student/User objects. If you use User as object pointer, we wouldn't need to look up for Student at first.
Query
I assume that you have students as new Array field in Class class. students contains User objects.
var user = Parse.User.current();
var studentQuery = new Parse.Query(Class);
var creatorQuery = new Parse.Query(Class);
studentQuery.equalTo("students", user);
creatorQuery.equalTo("creator", user);
var query = Parse.Query.or(studentQuery, creatorQuery);
query.find().then(function(objects){
// Proceed with the results
},function(error){
// Handle error
});
Ok, what you want to do in an OR query with an internal subquery. One call to parse and you can filter the student properties using the subquery.
var studentQuery = new Parse.Query(Student);
studentQuery.equalTo("user", Parse.User.current());
var firstQuery = new Parse.Query(Class);
firstQuery.matchesQuery("student", studentQuery);
var secondQuery = new Parse.Query(Class);
secondQuery.equalTo("creator", Parse.User.current());
var mainQuery = Parse.Query.or(firstQuery, secondQuery);
mainQuery.find({
success: function(results) {
// results contains a list of Classes where the user is either the creator or the user property of the student (if available)
},
error: function(error) {
// There was an error.
}
});
Related
As an example on basic setup one index is created.
db.onupgradeneeded = function(event) {
var db = event.target.result;
var store = db.createObjectStore('name', { keyPath: 'id' });
store.createIndex('by name', 'name', { unique: false });
};
Question:
Is it possible to create/append more indexes to the same objectStore on the future versionupdate? Since if I try:
db.onupgradeneeded = function(event) {
var db = event.target.result;
var store = db.createObjectStore('name', { keyPath: 'id' });
store.createIndex('by newName', 'newName', { unique: false });
};
It throws an error that current objectStore does already exist. An if I try to create store reference using transaction:
db.onupgradeneeded = function(event) {
var db = event.target.result;
var store = db.transaction('name', 'readwrite').objectStore('name');
store.createIndex('by newName', 'newName', { unique: false });
};
It throws that version change transaction is currently running
Yes it is possible. It can be a bit confusing at first. You want to get the existing object store via the implicit transaction created for you within onupgradeneeded. This is a transaction of type versionchange which is basically like a readwrite transaction but specific to the onupgradeneeded handler function.
Something like this:
var request = indexedDB.open(name, oldVersionPlusOne);
request.onupgradeneeded = myOnUpgradeNeeded;
function myOnUpgradeNeeded(event) {
// Get a reference to the request related to this event
// #type IDBOpenRequest (a specialized type of IDBRequest)
var request = event.target;
// Get a reference to the IDBDatabase object for this request
// #type IDBDatabase
var db = request.result;
// Get a reference to the implicit transaction for this request
// #type IDBTransaction
var txn = request.transaction;
// Now, get a reference to the existing object store
// #type IDBObjectStore
var store = txn.objectStore('myStore');
// Now, optionally inspect index names, or create a new index
console.log('existing index names in store', store.indexNames);
// Add a new index to the existing object store
store.createIndex(...);
}
You also will want to take care to increment the version so as to guarantee the onupgradeneeded handler function is called, and to represent that your schema (basically the set of tables and indices and properties of things) has changed in the new version.
You will also need to rewrite the function so that you only create or make changes based on the version. You can use event.oldVersion to help with this, or things like db.objectStoreNames.contains.
Something like this:
function myOnUpgradeNeeded(event) {
var is_new_db = isNaN(event.oldVersion) || event.oldVersion === 0;
if(is_new_db) {
var db = event.target.result;
var store = db.createObjectStore(...);
store.createIndex('my-initial-index');
// Now that you decided you want a second index, you also need
// to do this for brand new databases
store.createIndex('my-second-new-index');
}
// But if the database already exists, we are not creating things,
// instead we are modifying the existing things to get into the
// new state of things we want
var is_old_db_not_yet_current_version = !isNaN(event.oldVersion) && event.oldVersion < 2;
if(is_old_db_not_yet_current_version) {
var txn = event.target.transaction;
var store = txn.objectStore('store');
store.createIndex('my-second-new-index');
}
}
Pay close attention to the fact that I used event.target.transaction instead of db.transaction(...). These are not at all the same thing. One references an existing transaction, and one creates a new one.
Finally, and in addition, a personal rule of mine and not a formal coding requirement, you should never be using db.transaction() from within onupgradeneeded. Stick to modifying the schema when doing upgrades, and do all data changes outside of it.
I have the code below:
var FBref = new Firebase('https://employees.firebaseio.com/');
var peopleObject = $firebaseArray(FBref);
var person = {
"name":"John",
"age":"20",
"phone":"123-123-123"
};
peopleObject.$add(person);
I use the code above to add new records of employees successfully.
I need to update the John's phone number to 111-111-111. But the code below doesn't work.
var person = {
"name":"John",
"age":"20",
"phone":"111-111-111" //New phone number
};
peopleObject.$save(person);
Someone advice
Currently you are trying to save whole object, which wouldn't work.
Retrieve old record first by getting record from db first & then update that record. Thereafter save that object using $save method.
//if update action is happening is similar session you can get unique by below code
var someRecordKey;
peopleObject.$add(person).then(function(ref) {
//get record id of new person
someRecordKey = ref.key();
});
// get record by unique key
var person = peopleObject.$getRecord(someRecordKey);
// change a message and save it
item.phone = "111-111-111";
peopleObject.$save(person).then(function() {
// data has been saved to our database
});
I am looking for a simple strategy to store user data, as well as messages. I was thinking of using different key values like some random token (Ynjk_nkjSNKJN) for users and some real ids (1,2,3) for messages.
Has anyone ever had that problem?
The reason is that I would like to keep localStorage always up to date with new messages from the server, but users should not be deleted during an update.
Thanks
You can handle "tables" in localStorage this way:
//columns should be an array of column literals
function createTable(tableName, columns) {
db[tableName] = {rows: {}, columns: columns};
}
function insertInto(tableName, row, id) {
var newRow = {};
for (var columnName in row) {
if (db[tableName].columns.indexOf(columnName) === -1) {
//invalid column
return false;
}
newRow[columnName] = row[columnName];
}
db[tableName].rows[id] = newRow;
return true;
}
function getIDs(tableName, where) {
var IDs = [];
for (var id in db[tableName].rows) {
if (where(db[tableName].rows[id])) {
IDs[IDs.length]=id;
}
}
return IDs;
}
function update(tableName, where, what) {
what(tableName, getIDs(tableName, where));
}
function deleteRecord(tableName, where) {
var removeIDs = getIDs(tableName, where);
for (var id in removeIDs) {
//Could be done by regexes, but I am not fluent with them and I am lazy to check them out
delete db[tableName].rows[removeIDs[id]];
}
}
function select(tableName, where) {
var IDs = getIDs(tableName, where);
var result = {};
for (var id in db[tableName].rows) {
result[id] = db[tableName].rows[id];
}
return result;
}
function dropTable(tableName) {
delete db[tableName];
}
You probably see that this is only a minimalistic implementation, but with the same approach you can implement altering, joins, grouping and so on. My focus here was just to illustrate how you can create a database. Let's go to the next step, storing the database into localStorage:
localStorage.setItem("db", JSON.stringify(db));
You will need to be able to convert back the local storage item to object, especially because you want to reuse your database even after reload. Let's see how you should initialize db:
var db = !!localStorage.getItem("db") ? angular.fromJson(localStorage.getItem("db")) : {};
Localstorage is a key-value store, which stores everything in string format. So, the messages will be identified by one key (e.g. "messages") and the users another key (e.g. "users").
Then you need to create 2 (angular) services one for the messages and one for the users. Both will interface with localstorage (using the respective keys) and will perform the operations that you want.
If you provide us with more information then we could help you a bit more.
I have an object called Group, which stores an array of Users (default Parse user) as a column. I am trying to list all of these users' display names (column called "displayName") in a certain group, but for some reason, when I try to use the .get function on a user in the retrieved array, it only gives me the information for the current user. I checked my permissions and ACL and it says for each user Public Read, and the User class has public read and write permissions. Here is the code I am using:
var groupObject = Parse.Object.extend("Group");
var users = [];
var groupQuery = new Parse.Query(groupObject);
groupQuery.get(groupId,{
success: function(group)
{
users = group.get("Users");
for(var i=0;i<users.length;i++)
{
var user = users[i];
console.log("display name: " + user.get("displayName") + "username: " + user.get("username") + "id: " + user.id);
}
doneCallback(users);
},
error: function(object, error)
{
errorCallback(error);
}
});
I am able to console.log all of their ids, and the query is successful, but the only thing I can use get("column") on is the current user (which is part of the group users array).
I think you should use a Relation instead of an Array to store your users in the Group object. It will be way easier to add, remove, fetch and access your user objects via the relation. You also avoid some headache when the list of your users becomes large. Then you should be able to fetch all the users in any given group like this:
var groupObject = new Parse.Object("Group");
groupObject.set("objectId", yourGroupId);
var query = groupObject.relation("Users").query();
Parse.Cloud.useMasterKey();
query.find().then( function(users) {
_.each(users, function(user) {
console.log(user.get("displayName"));
});
});
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.