Let's say we're building a blog using Parse.com's javscript sdk where users can follow eachother and have a feed.
The 'Follow' class includes a pointer to the followee & follower.
While the 'Post' class includes a pointer the creator (followee^).
if I now want to fetch all rows inside 'Post' where the creator equals followee and the user equals follower inside the 'Follow' class; then sort them using descending("createdAt"), how would one do that (efficiently)?
I have checked the api reference and there doesn't appear to be a straightforward answer to this issue: https://www.parse.com/docs/js/api/classes/Parse.Query.html
Any help is gladly welcome. Thanks.
This can be accomplished using a compound query, specifically matchesKeyInQuery. Compound queries only count as 1 API request.
First, create the inner query to find Follow objects where the requesting user is a follower.
var innerQuery = new Parse.Query("Follow");
innerQuery.equalTo("follower", request.user);
Next, create the outer query to find Post objects where their followee key matches the followee key in the results of our inner query.
var outerQuery = new Parse.Query("Post");
outerQuery.descending("createdAt");
outerQuery.matchesKeyInQuery("followee", "followee", innerQuery);
This will give you all of the Posts, in descending order by creation date, for all of the "followees" being followed the requesting user.
Cheers,
Russell
Related
I have a collection of employees that has data sent to it. Right now there is 4 employees but eventually there will be many more.
I want to add a grouping feature so that the user can sort the employees by their group. I am trying to find the best way to assign these employees groups and I found the reference field type in cloud firestore and thought I could use it to solve my problem. But I am stuck and not sure the most efficeient way to use it to link employees to a group.
This is my database. Right now I have the employees doc (ex. 2569) and inside that is a sub-collection with 2 documents in itself.
So end goal is to assign employees groups and then be able to sort and display them separately. Right now I have the group name assigned in articles/group -> groupName: "example".
(display them hopefully with ".Where( "groupName" "==" "example" ) somehow in code without hard-coding the group name. The group name will be created by the user so it could be anything)
Is what I am doing a good start? I know this question is a little odd but I am stuck and could really use some pointers on where to head next.
A collection group query would allow you to query all articles regardless of which employee contained them:
db.collectionGroup('articles')
.where('groupName', '==', 'X')
.get()
This would match documents in any collection (i.e. employees) where the last part of the collection path is articles. If you would like to find the employees who belong to a certain groupName, you may want to find the parent by retrieving the collection this DocumentReference belongs to.
Once you have the parent of the CollectionReference, you will get a reference to the containing DocumentReference of your subcollection.
I need to do a query where I can show only specific data using an 'AND' statement or equivalent to it. I have taken the example which is displayed in the Firebase Documentation.
// Find all dinosaurs whose height is exactly 25 meters.
var ref = firebase.database().ref("dinosaurs");
ref.orderByChild("height").equalTo(25).on("child_added", function(snapshot) {
console.log(snapshot.key);
});
I understand this line is going to retrieve all the dinosaurs whose height is exactly 25, BUT, I need to show all dinosaurs whose height is '25' AND name is 'Dino'. Is there any way to retrieve this information?
Thanks in advance.
Actually firebase only supports filtering/ordering with one propery, but if you want to filter with more than one property like you said I want to filter with age and name, you have to use composite keys.
There is a third party library called querybase which gives you some capabilities of multy property filtering. See https://github.com/davideast/Querybase
You cannot query by multiple keys.
If you need to sort by two properties your options are:
Create a hybrid key. In reference to your example, if you wanted to get all 'Dino' and height '25' then you would create a hybrid name_age key which could look something like Dino_25. This will allow you to query and search for items with exactly the same value but you lose the ability for ordering (i.e. age less than x).
Perform one query on Firebase and the other client side. You can query by name on Firebase and then iterate through the results and keep the results that match age 25.
Without knowing much about your schema I would advise you to make sure you're flattening your data sufficiently. Often I have found that many multi-level queries can be solved by looking at how I'm storing the data. This is not always the case and sometimes you may just have to take one of the routes I have mentioned above.
I have two classes - _User and Car. A _User will have a low/limited number of Cars that they own. Each Car has only ONE owner and thus an "owner" column that is a to the _User. When I got to the user's page, I want to see their _User info and all of their Cars. I would like to make one call, in Cloud Code if necessary.
Here is where I get confused. There are 3 ways I could do this -
In _User have a relationship column called "cars" that points to each individual Car. If so, how come I can't use the "include(cars)" function on a relation to include the Cars' data in my query?!!
_User.cars = relationship, Car.owner = _User(pointer)
Query the _User, and then query all Cars with (owner == _User.objectId) separately. This is two queries though.
_User.cars = null, Car.owner = _User(pointer)
In _User have a array of pointers column called "cars". Manually inject pointers to cars upon car creation. When querying the user I would use "include(cars)".
_User.cars = [Car(pointer)], Car.owner = _User(pointer)
What is your recommended way to do this and why? Which one is the fastest? The documentation just leaves me further confused.
I recommend you the 3rd option, and yes, you can ask to include an array. You even don't need to "manually inject" the pointers, you just need to add the objects into the array and they'll automatically be converted into pointers.
You've got the right ideas. Just to clarify them a bit:
A relation. User can have a relation column called cars. To get from user to car, there's a user query and then second query like user.relation("cars").query, on which you would .find().
What you might call a belongs_to pointer in Car. To get from user to car you'd have a query to get your user and you create a carQuery like carQuery.equalTo("user", user)
An array of pointers. For small-sized collections, this is superior to the relation, because you can aggressively load cars when querying user by saying include("cars") on a user query. Not sure if there's a second query under the covers - probably not if parse (mongo) is storing these as embedded.
But I wouldn't get too tied up over one or two queries. Using the promise forms of find() will keep your code nice and tidy. There probably is a small speed advantage to the array technique, which is good while the collection size is small (<100 is my rule of thumb).
It's easy to google (or I'll add here if you have a specific question) code examples for maintaining the relations and for getting from user->car or from car->user for each approach.
I'm trying to figure out with Breeze how to expand a specific navigation property for all items in an array of entities with a single request.
On this page of the Breeze documentation it shows the following way of achieving this:
var orderEntityType = selectedOrders[0].entityType;
var navProp = orderEntityType.getNavigationProperty("OrderDetails");
var navQuery = EntityQuery
.fromEntityNavigation(selectedOrders, navProp)
.expand("Product");
manager.executeQuery(navQuery).fail(handleFail);
However, when I tried this I get the error
The 'entity' parameter must be an entity
So I looked up in the documentation specifically for the EntityQuery.fromEntityNavigation method and it shows:
// 'employee' is a previously queried employee
var ordersNavProp = employee.entityType.getProperty("Orders");
var query = EntityQuery.fromEntityNavigation(employee, ordersNavProp);
The documentation indicates that it is for a specific entity, not multiple. Which is consistent with the error I'm getting.
Is it possible to get all the navigation properties in a single request, or is the preferred way to iterate over an array making a request for each entity?
Basically, I'm working on filtering a list of items. My goal is that when a user selects a filter it then expands the needed navigation property at that time instead of loading all the data up front.
Thanks for the help.
I think this might be a typo or some out of date information on the navigation properties documentation page. According to the API documentation for EntityQuery.fromEntityNavigation, the first parameter should be a single entity, not an array. Took a look at the breeze code, didn't see any evidence that an array of entities could be passed.
As a workaround, you could construct the query a bit differently. Continuing with the Order/OrderDetails scenario, you could do something like this:
var subsetOfOrders = ..., // array containing the subset of orders whose OrderDetails we need to load
predicates = subsetOfOrders.map(function(order) { return new breeze.Predicate('OrderId', '==', order.OrderId()); }),
predicate = breeze.Predicate.or(predicates),
query = new breeze.EntityQuery('Orders').expand('OrderDetails').where(predicate);
manager.executeQuery(query)...
If you're able to query the order details directly you don't even need expand. Breeze will wire up the freshly loaded OrderDetails to the respective orders entities that are already cached in the entity manager:
var subsetOfOrders = ..., // array containing the subset of orders whose OrderDetails we need to load
predicates = subsetOfOrders.map(function(order) { return new breeze.Predicate('OrderId', '==', order.OrderId()); }),
predicate = breeze.Predicate.or(predicates),
query = new breeze.EntityQuery('OrderDetails').where(predicate);
manager.executeQuery(query)...
This predicate based workaround may or may not be feasible depending on the number of orders you're dealing with. Could end up with a long query string. You could then consider using a dedicated controller action (ie "OrderDetailsByOrderId(int[] orderIds)" and use the withParameters EntityQuery method to load the order details using the new action.
The documentation was in error. I just corrected it.
#Jeremy Danyow offered a superb explanation and a solution. I probably would use his approach to solve a specific use case.
The documentation now discusses the problem and describes yet another approach that might be more appropriate if you were trying to write a general utility.
// create an array of filter criteria (`wherePredicate`) for each order
var predicates = orders.map(function (order) {
return EntityQuery.fromEntityNavigation(order,'OrderDetails')
.wherePredicate;
});
// OR the predicates together
var filter = breeze.Predicate.or(predicates);
EntityQuery.from('OrderDetails')
.where(filter)
.expand('Product')
.using(em).execute().catch(handleFail);
Thanks to you both for identifying the problem and working through it.
I'm using Parse.com to manage my models, and I came to a problem that I couldn't find a good solution.
Let's say that I have to models:
Team: name, number, country
Member: name, Team (Pointer to Team)
I want to fetch ALL Teams, and include all it's Members in one single query. If this is not possible, I will have to run a query for every Team that I fetches.
Is it possible with Parse? I read their docs. but couldn't find a way to doit...
If the point is to get all of both members and teams, why not get all members and use
includeKey("Team")
to include all team objects in the members query?
On another note, when designing for parse (or any other NoSQL database), you should start with defining what queries you will make and then design your "schema".
Since you have a pointer to Team from Member, it seems that this is a one-to-many relationship. A team can have many members, but a member can only belong to one team.
So, what queries will you mostly perform?
Never "list all Teams a Member belongs to", because it can only be one.
You will query for members, and it would probably be nice to see the Team as well.
You will (apparently) query for Team(s) and need to get all members for that team.
Other queries related to Team or Member?
If you need a list of members in a Team, you could make "Members" a PFRelation from Team to Member. I know this seems odd if you're used to SQL databases, but that is not unusual in NoSQL databases.
Looking through the link in your post, my best guess is this:
var Member = Parse.Object.extend("Member");
var query = new Parse.Query(Member);
// Include the Team data with each Member
query.include("post");
query.find({
success: function(members) {
for (var i = 0; i < members.length; i++) {
// This does not require a network access.
var team = comments[i].get("team");
}
}
});
The above (untested) sample is modified from the section on include.
You may not be able to do what you want here, depending on the size of your members list and team list... I ran across this in the docs:
If you want to retrieve objects where a field contains a Parse.Object
that matches a different query, you can use matchesQuery. Note that
the default limit of 100 and maximum limit of 1000 apply to the inner
query as well, so with large data sets you may need to construct
queries carefully to get the desired behavior. In order to find
comments for posts containing images, you can do: