Foreign key conditions in sailsjs Waterline ORM - javascript

I have defined two models like below, what i need to do is to retrieve the unique neighborhoods that belong to active partners (partner.status=1), i can retrieve the grouped neighborhoods like this
Locations.find({groupBy: [ 'neighborhood' ],sum['id']}, function(err,locations){});
and then match against a retrieved list of active Partners or backwards (ie first getting the active partners with the locations for each partner and pushing them to an array after verifying they are not already in there) or just by a custom sql query (which i am trying to stay away from)
but... i want to know if there is some kind of way to do it with the ORM as i have seen model.find().populate('model') doesn't accept parameters beyond the desired model not the where in the find method accept foreign keys conditions.
Partner:{
attributes:{
businessName:{
type:'string'
},
status:{
type:'INT'
}
locations:{
model:'Locations'
via:'partner_id'
}
}
}
Locations:{
attributes:{
partner_id:{
model:'Partners',
type:'INT'
},
neighborhood:{
type:'STRING'
}
}
}
Thanks in advance!

Is this what you looking for?
Locations.find({where: {value1: key1}})
.populate('partners', {where: {value2: key2}})
.exec(function(err, locations){
// Now you've got what you need
});
P/S: Please checkout SailsJS v0.10.x documentation on GitHub. Personally I use this: https://github.com/balderdashy/sails-docs/tree/0.10
The documents are not up-to-date though :-)

Related

Query firebase realtime database where child has property

I'm trying to query my Firebase Realtime Database to find all games a user belongs to.
I have a list of games, each game has a property of players. Each child of players has a key of $uid, and within that {key: $uid, name: "joe"}
Is it possible to get all games this way? Or do I need to start keeping another index of players_games/$uid/$game?
I've tried firebase.database().ref('games').orderByChild('players').equalTo(token.uid), but this yields null
It looks like database.ref('games').orderByChild('players/${token.uid}') works, but then I'd need to give .read access to all of games, or do this server-side.
Your current data structure makes it easy to find all the users for a specific game. It does not however make it easy to find all the games for a specific user. To allow that, you'll want to add an addition data structure that inverts the information.
So that'd look something like this:
player_games: {
"XDYNyN8il6TDsM4LuttwDzNuytj1": {
"-M5vf...U5zK": true
},
"NxH14...mxY2": {
"-M5vf...U5zK": true
}
}
Also see:
Firebase query if child of child contains a value
Firebase Query Double Nested
I recommend you also study the Firebase documentation on structuring your database, specifically the section on avoiding nested data. By mixing entity types as you currently do, you'll likely run into problems with security, and scalability.
The most idiomatic way to model your many-to-many relationship in the Firebase database is with four top-level lists:
players: {
$playerId: { ... }
}
games: {
$gameId: { ... }
}
player_games: {
$playerId: {
$gameId: true
}
}
game_players: {
$gameId: {
$playerId: true
}
}
Also see:
Many to Many relationship in Firebase

What is the best practice of firestore data structure?

I'm making a blog app using firebase.
I want to know the best practice of data structure.
As far as I know, there are 2 case.
(I'm using react native)
case 1:
posts
-postID
-title,content,author(userID),createdDate,favoriteCount
favorites
-userID
-favoriteList
-postID(onlyID)
-postID(onlyID)
In this case, for example, when we need to get favorite posts.
firebase.firestore().collection(`favorites/${userID}/favoriteList`)
.get()
.then((snapshot) => {
snapshot.forEach((favorite) => {
firebase.firestore().collection(`favorites/`).doc(`${favorite.id}`)
.get()
.then((post) => {
myPostList.push(post.data())
});
});
in this case, we can't order the favorite posts by createdDate. So, need to sort client side. Even if so, we don't use limit() function.
case 2:
posts
-postID
-title,content,author(userID),createdDate,favoriteCount
favorites
-userID
-favoriteList
-postID
-title,content,author(userID),createdDate,favoriteCount
-postID
-title,content,author(userID),createdDate,favoriteCount
firebase.firestore().collection(`favorites/${userID}/favoriteList`).orderBy('createdDate','desc').limit(30)
.get()
.then((snapshot) => {
snapshot.forEach((post) => {
myPostList.push(post.data())
});
});
in this case, When the favorite post is modified by the author,
we have to update all of the favorite posts. (e.g. If 100 users save the post as a favorite, we have to update to 100 data.)
(And I'm not sure we can increment favoritecount by a transaction, exactly same.)
I think if we use firebase.batch(), we can manage it. But I think it seems Inefficient.
It seems that both ways are not perfect. Do you know the best practice of this case?
What about using arrays or Collection Groups?
solution 1: arrays
posts
-postID
-title,content,author(userID),createdDate,favoriteCount
-[favoriters(userID)]
Now you can query for a user's favorites by querying posts that "array-contains" the user's ID. You can also modify individual posts without iterating through a bunch data copies.
There's a limit to this approach though. Maximum size for a document is 1 MiB; assuming that a user ID is 4 bytes, a document can contain no more than 250K favoriters. Clients would also have to do some O(N) processing to add / remove favoriters.
solution 2: Collection Groups
posts
-postID
-title,content,author(userID),createdDate,favoriteCount
-favoriters {collection}
-userID
A collection group consists of all collections with the same ID. By default, queries retrieve results from a single collection in your database. Use a collection group query to retrieve documents from a collection group instead of from a single collection.
So we can fetch a user's favorite posts via
db.collectionGroup("favoriters").whereEqualTo("userID", <userID>).get();
To favorite a post, we just do
const postsRef = db.collection("posts");
postsRef.document(<postID>).collection("favoriters").add({ "userID", <userID> });
Maybe not a direct answer to your question, but the official documentation has an example for that:
Working with arrays, lists, and sets
Summary: Store and query data in array-like structures in documents.
Use case: If your app requires complex data objects like arrays,
lists, or sets, follow the model outlined in this solution. For
example, in a blogging app, you might want to create a set of related
posts.
https://firebase.google.com/docs/firestore/solutions/arrays

How to provide a collection of resources that contains links/ids to another resource with an Angular 2 service?

I have a collection of users. Each user has a cards array, which is composed of the ids of the cards that the user chose.
user.cards = [10, 12, 24, 31]
I want to have the 10 cards that are the most chosen by users.
I have user.service.js and card.service.js, which I guess are DAOs. card.service.js looks something like this (used plain Promises for simplicity).
(function(app) {
app.CardService = ng.core.Injectable({}).Class({
constructor: function() {},
getCards: function() {
return Promise.resolve(app.cards)
},
getCard: function(cardId) {
return Promise.resolve(app.cards.get(cardId));
},
getTop10Cards: function() {
// How would I go about doing this method?
}
})
})(window.app || (window.app = {}));
Logically, I would need the list of all the users, collect their cardIds and aggregate them to find the most used cards. But should CardService use UserService (as an injection)? Or is using a DAO in another DAO the wrong way to go about it? Should I have a separate collection that contains the links between each models (like a joint table)?
To help myself deal with this client-side problem, I investigated on what the back-end resources would look like. I came up with something similar than "How to handle many-to-many relationships in a RESTful API?".
These are what my API endpoints will potentially look like: /users, /cards, /cards/{cardId}/votes. /cards/{cardId}/votes contains resources that define the relationship between the users and the cards they have chosen.
In the code, I am indeed injecting vote.service.js inside card.service.js. When I GET the cards, I also compose them with their subset of votes. See "angular js model relationships" for more information.
I hope this will help anyone that will come across this question.
Cheers!

Query data in one collection based on data in another collection in MongoDB

I am trying to learn how to use MongoDB and am really confused how to do this. What I have are two collections, one which has a number of users and another collection which has a number of items. For example:
users:
{
"_id" : ObjectId("56dba03438e1a255b97e82b6"),
"name" : "john",
"age" : 25
}
items:
{
"_id" : ObjectId("56dba0db38e1a255b97e82b7"),
"name" : "pencil"
}
Now what I want to do in my app is to allow users to select an item but multiple users can select the same item. So I need to keep track of which users clicked which items. I thought about doing this using another collection which keeps track of the user id and item id (a user can only select an item once). Is this the correct approach? I created this collection:
useritems:
{
"_id" : ObjectId("56dba0db38e1a255b97e82b7"),
"userid" : "56db9fb038e1a255b97e82b5",
"itemid" : "56dba03438e1a255b97e82b6"
}
If this is the right approach, then I want to be able to click on an item in my app and for it to display a list of all the users who selected that item. How can I do this? I got as far as to display only the useritems collection documents where the itemid = itemid selected on the app...but now how would I display all of the users from the users collection based on the ones in the useritems collection?
router.get('/userlist/:id', function(req, res) {
var db = req.db;
var collection = db.get('useritems');
collection.find({'itemid' : '_id'},{},function(e,docs){
res.json(docs);
});
});
Thanks for the help, I'm really having a hard time understanding how this would work.
The idea of creating a third collection is a solution that mirrors how you would solve this problem in a relational database. With MongoDB, it often pays off to think about different patterns based on how you access your data.
In your case, I would not create another collection, but track which user has selected which item within the user document, within the item document, or within both documents. Which way you do this depends on your data access patterns.
Adding Selected Item to User Document
{
"_id": ObjectId("56dba03438e1a255b97e82b6"),
"name": "john",
"age": 25,
"selectedItemId": "56dba0db38e1a255b97e82b7"
}
If you will often want to see the item each user has selected, it makes sense to store the item inside the user document. When you retrieve a user, you would only have to do one extra call to the items collection to retrieve the item for that user. (If you decide to use Mongoose as an object-document mapper (ODM), then you can achieve this extra call by using Mongoose's populate method).
Adding User to the Item Document
{
"_id": ObjectId("56dba03438e1a255b97e82b7"),
"name": "pencil",
"selectedBy": [
"56dba0db38e1a255b97e82b4",
"56dba0db38e1a255b97e82b5",
"56dba0db38e1a255b97e82b6"
],
}
If you will often want to see which users have selected a given item, it makes sense to store an array of users inside the item document. When you retrieve an item, you would then have the IDs of the users that selected that item, which you could then retrieve from the database. (Again, if you decide to use Mongoose you can do this by using its populate method).
Adding Both Solutions
The reason why you would prefer one solution over another is that given your access pattern, you will be spared from iterating through the whole collection to get the data you need. For example, in the case were you add the array of users to an item, if you wanted to find the item a given user has selected, you would have to iterate though all the items and look in the array of user IDs until you found the user you wanted. Something similar would occur if you only stored the item ID inside a user document and suddenly needed to look at all the users for a given item. If both of these calls are made often, then it pays off having the data in both places. Indeed this "denormalises" your data and you will have to make sure that when you insert, update, and delete the data you do so in both places, but it's a far more scalable solution if you're making both types of queries often.
Embedding the Whole Item Document inside the User Document
{
"_id": ObjectId("56dba03438e1a255b97e82b6"),
"name": "john",
"age": 25,
"selectedItem": {
"name": "pencil"
}
}
Following the OP's comment, I'll address this scenario too. This is also a possible solution and can be very useful in simplifying the query needed to access the data. Just by querying the user document, you will be able to access what item he/she has selected without the extra query to a collection of items. The limitation of this approach is that if for whatever reason you want to update the name of the item from say "pencil" to "Pencil", you will have to ensure that you update it across all of the user documents, otherwise your data will be inconsistent. This gets more complicated when your embedded documents are more complex. Nonetheless, it is a widely used solution. If you're rarely updating your data, but reading it very often, especially if you are more interested in seeing the item picked by a given user, then it definitely speeds up your most frequent data access patterns.
You are right, only you need populate the userid to get all atributes of that collection. I suggest you use (if your are not) Mongoose
With mongoose:
UserItems
.find({'itemid' : '_id'})
.populate('userid')
.then( useritems => {
// here you have all users with their data for a specific item
return res.json(useritems);
});
You can add an array in the item document that keeps track of the IDs of the users who clicked that item.
This is assuming the ID is stored in a active session.
docs.user_who_clicked.push(req.user._id);
docs.save()
I wouldn't create a separate collection just for that unless you have a good reason. Just add a selectedBy to each Item document. Also, I find it simpler to just use my own unique names or IDs rather than looking things up with the internal Mongo IDs. Something like this:
var items = db.collection('items');
items.updateOne({itemname:'nuts'},{$push:{selectedBy:'johns'}});
//...
items.find({itemname:'nuts'}).toArray(function(err,items) {
console.log(items[0].selectedBy);
db.close();
});

sequelize - Where clause with associations

In sequelize I have boards and users setup, with a many to many association as follows
User.hasMany(Board, {through: BoardUsers});
Board.hasMany(User, {through:BoardUsers});
Is there anyway that, using a where clause I can find users that belong to one of a list of boards. For example, lets says I have 3 boards and I would like to find the first 20 users (using the default sort order) who belong to either board 1 or 3. Is there a way to do this without executing separate finds for each board, then manually combining the results.
I would love to be able to do something like:
User.findAll({where:{board: [1,3]}});
but I can't seem to find a way to do this.
As far as I can tell the equivalent SQL would be something like:
SELECT * FROM `users` WHERE id IN (SELECT userID FROM boardusers WHERE boardId IN (1,3))
But I would love to be able to do it through the ORM.
While I'm not sure if you can do this directly, you can always query for Boards and eagerly fetch users.
Something along these lines:
Board.findAll({where: {id: [1,3]}}, { include: [ User ] })
Quite a late response but just had this same question, and here's how I got it to work in Sequelize 3.24.0 (something like this);
User.findAll({ include: [{ model: Board, where: { $in: [1,3] }}] });

Categories