Sequelize allows for data relationships, but I can't seem to see if there is a way to have it return a query result composed of the relation? I could do this as two queries, but I am curious as to whether Sequelize provides for this? For example Playlist and PlaylistEntry:
Playlist: sequelize.define('playlist', {
name: Sequelize.STRING,
description: Sequelize.STRING
}),
PlaylistEntry: sequelize.define('playlist_entry', {
playlist: Sequelize.INTEGER
//track: Sequelize.INTEGER
})
PlaylistEntry.belongsTo(
Playlist,
{ as: 'Playlist', foreignKey: { name: 'fk_playlist' }});
What I would be hoping for (pseudocode):
Query:
Playlist.find(where: {id: playlistId}, nestedResult: true);
Result:
[{
id: 123,
name: 'abc'
playlistEntries: [{
id: 321,
track: 431
}]
}]
Playlist.find({
where: {},
include: [
{model: PlaylistEntry} //this should be PlaylistEntry model
]
})
http://docs.sequelizejs.com/en/latest/docs/models-usage/#eager-loading
Related
Hi I have a following code. From this I need a total rows count from the Comment Model. Can you please help? Thanks in Advance!
Post.hasMany(models.Comment, {
as: 'postComment',
foreignKey: 'postId',
});
Comment.belongsTo(models.Post, {
foreignKey: 'postId',
as: 'post',
});
return Post.findAndCountAll({
attributes: [['postId', 'id'], 'target_population', 'title', ['type', 'category'], 'stage', ['text_content', 'comment'], ['time_posted', 'engagement_timeframe'], ['closing_date', 'engagement_completion'], ['engagement_end', 'engagement_close'], ['non_constituent_view', 'non_constituents'], ['discussion_board_enabled', 'discussion_board'], 'attached_documents', 'description'],
where: where,
offset: offset,
limit: limit,
distinct: true,
include: [
{
model: Survey,
as: 'survey',
attributes: ['isOpen'],
where: {isOpen: "TRUE"},
required: false,
include: [
{
model: Question,
as: 'question',
include: [
{
model: ProposedAnswer,
as: 'choice',
attributes: [['proposedAnswerId', 'id'], ['answer', 'value']],
required: false,
}
],
attributes: [['questionId', 'id'], ['question', 'title'], ['questionType', 'type']]
}
]
},
{
model: User,
as: 'user',
attributes: ['first_name'],
where: {userId: {[Op.not]: null}},
},
{
model: Topic,
as: 'topic',
attributes: [['topicId', 'id'], ['name', 'value']],
through: {attributes: []},
},
{
model: Comment,
as: 'postComment',
},
],
order: [
['postId', 'DESC'],
]
})
I am trying to insert my nested object to Realm with To-One Relationships method, but I got an unexpected result where all value of my nested object is the same thing as the value from the first of my nested object that has been Relationship
This is my schema looks like
const PhotoSchema = {
name: 'CUSTOMER_PHOTOS',
properties: {
base64: 'string'
}
};
const TimeSchema = {
name: 'CUSTOMER_TIMES',
properties: {
warranty: 'float',
finish: 'float'
}
};
const MainSchema = {
name: 'CUSTOMERS',
primaryKey: 'id',
properties: {
id: 'int',
name: 'string',
photo: {type: 'CUSTOMER_PHOTOS'},
time: {type: 'CUSTOMER_TIMES'},
}
};
And try to insert some data like this
import Realm from 'realm';
Realm.open({
path: 'mydb.realm',
schema: [PhotoSchema, TimeSchema, MainSchema]
})
.then((realm) => {
realm.write(() => {
realm.create('CUSTOMERS', {
id: Date.now(),
name: 'John',
photo: {
base64: 'ImageBase64'
},
time: {
warranty: 31,
finish: 7
}
})
})
})
.catch((error) => {
console.error(error)
});
The process of inserting data is successfully BUT I got unexpected result when successfully get that data from Realm
Unexpected Result in console.log()
{
id: 1601335000882,
name: "John",
photo: {
base64: "ImageBase64"
},
// This value is the same as PhotoSchema
time: {
base64: "ImageBase64"
}
}
I want to the actual result like this
{
id: 1601335000882,
name: "John",
photo: {
base64: "ImageBase64"
},
time: {
warranty: 21
finish: 7
}
}
I there anything wrong with my code? The Documentation is not too detail about the method, the explanation and the example is just like one word
UPDATE:
I got an unexpected result only in the console.log() and if I try to access the property directly like MY_DATA.time.warranty the result is what I expected
The Answer is: No
To-One Relationships method is not only for one Schema, and Thanks to Angular San for showing an example of Inverse Relationships method.
Try Inverse Relationships
I got an expected result with Inverse Relationships method. In this method, you have to add one property that connected to Main Schema and I want to call this a combiner property
const PhotoSchema = {
name: 'CUSTOMER_PHOTOS',
properties: {
base64: 'string',
combiner: {type: 'linkingObjects', objectType: 'CUSTOMERS', property: 'photo'}
}
};
const TimeSchema = {
name: 'CUSTOMER_TIMES',
properties: {
warranty: 'float',
finish: 'float',
combiner: {type: 'linkingObjects', objectType: 'CUSTOMERS', property: 'time'}
}
};
const MainSchema = {
name: 'CUSTOMERS',
primaryKey: 'id',
properties: {
id: 'int',
name: 'string',
photo: 'CUSTOMER_PHOTOS',
time: 'CUSTOMER_TIMES',
}
};
I have the following schema:
const MenuSchema = new mongoose.Schema({
name: String,
type: String,
children: [{ type: ObjectId, ref: 'Menu' }],
});
And the following query:
const res = await Menu.aggregate([
{ $graphLookup: { from: "menus", startWith: "$children", connectToField: "children", connectFromField: "_id", as: "menus" }}
]);
As you can see, the menu schema is a self-referential data structure, children stores references to other instances of the same entity, with a 'type' field to differentiate the levels. I'm attempting to find and populate documents for each array of children (which are just BSON IDs) and return the results.
The above example seems to get most of the way there, however when I access one of the populated menus, it has a list of all the populated children in a flattened array, it doesn't retain the relationship structure.
For example, if I print out res, I get:
[ {
_id: 5f1212d053e5494bb45f18f3,
children: [ 5f1212d053e5494bb45f18f1 ],
name: 'Vodka',
type: 'Item',
__v: 0,
menus: [ [Object], [Object], [Object], [Object] ]
},
{
_id: 5f1212d053e5494bb45f18f4,
children: [ 5f1212d053e5494bb45f18f3, 5f1212d053e5494bb45f18f2 ],
name: 'Drinks',
type: 'Category',
__v: 0,
menus: [ [Object], [Object] ]
},
{
_id: 5f1212d053e5494bb45f18f5,
children: [ 5f1212d053e5494bb45f18f4 ],
name: 'Main Menu',
type: 'Menu',
__v: 0,
menus: [ [Object] ]
}
]
But when I print out res[1].menus, I get:
[
{
_id: 5f1212d053e5494bb45f18f3,
children: [ 5f1212d053e5494bb45f18f1 ],
name: 'Vodka',
type: 'Item',
__v: 0
},
{
_id: 5f1212d053e5494bb45f18f1,
children: [ 5f1212d053e5494bb45f18f0 ],
name: 'Double',
type: 'Variant',
__v: 0
},
{
_id: 5f1212d053e5494bb45f18f4,
children: [ 5f1212d053e5494bb45f18f3, 5f1212d053e5494bb45f18f2 ],
name: 'Drinks',
type: 'Category',
__v: 0
}
]
Which is all of the children in a flat array.
Is $graphLookup the correct approach, or am I just using it wrong?
I don't know if you are still looking for the answer for this, but if you use mongoose you can take advantage of the populate feature and use it as a middleware
Here's an example:
Let's say I want a list of people and their friends, and their friends-friends, etc. The result should look like this:
[
{
_id: "abc123",
name: "John Doe",
friends: [
{
_id: "efg456",
name: "Foo bar",
friends: [
{
_id: "hij789",
name: "Jane Doe",
friends: [more friends...]
}
]
}
]
]
In the db they are stored like this
{_id: "abc123", name: "John Doe", friends: ["efg456"]}
{_id: "efg456", name: "Foo bar", friends: ["hij789"]}
{_id: "hij789", name: "Jane Doe", friends: [more friends...]}
Your schema and middleware would be:
const Person = new Schema<Folder>({
name: {type: String, required: true},
friends: [{type: Schema.Types.ObjectId, ref: "person"}],
}, {timestamps: true})
Person.pre("find", function(next) {
this.populate("friends")
next()
})
Adding the function as a middleware to find will make it run for every person found. That includes the children in the friends array.
I have a simple question.
In Mongoose I have two models. One is for Leagues and the other one is for Fixtures. Each league has a unique id, and each fixture is related to a league with it's key of league_id.
In the Leagues schema I have also defined a virtual called fixtures.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LeagueSchema = new Schema(
{
_id: { type: mongoose.Schema.Types.ObjectId, select: false, auto: true },
id: { type: Number, required: true, unique: true, index: true },
name: { type: String, required: true },
}
);
LeagueSchema.virtual('fixtures', {
ref: 'fixtures',
localField: 'id',
foreignField: 'league_id',
justOne: false,
});
const Leagues = mongoose.model('leagues', LeagueSchema);
const FixtureSchema = new Schema(
{
_id: { type: mongoose.Schema.Types.ObjectId, select: false },
match_id: { type: Number, required: true, index: true, unique: true },
league_id: { type: Number, required: true },
league_name: { type: String, required: true },
timestamp: { type: Number, required: true, index: true },
}
);
const Fixtures = mongoose.model('fixtures', FixtureSchema);
Here is the problem. The list of all leagues is very big and not all of them will always have fixtures that match the .populate() so I want to exclude them from my query.
For example, if I don't want any fixtures older than a given timestamp, I will do the following:
(async () => {
const target_time = 1568851200000;
const leagues = await Leagues
.find({})
.populate({ path: 'fixtures', match: { timestamp: { $gte: target_time } } })
.lean();
console.log(leagues);
})();
What this does is it filters out the fixtures by timestamp correctly, but it does not exclude Leagues without fixtures from the query.
Here is what this query returns:
Current result:
[
{
id: 2,
name: 'Champions League',
fixtures: []
},
{
id: 5,
name: 'Europa League',
fixtures: [
[Object],
[Object],
[Object],
[Object],
]
},
{
id: 8,
name: 'Premier League',
fixtures: []
}
];
And here is what I want to achieve:
Desired result:
[
{
id: 5,
name: 'Europa League',
fixtures: [
[Object],
[Object],
[Object],
[Object],
]
},
];
I am aware that I can do something like leagues.filter(item => item.fixtures.length > 0), but this query is going to be called tens of times each second, I'm afraid that running another filter after the query can lead to performance issues.
Any help or possible alternatives are appreciated.
i did find a post by id and i did console log output and it was that:
{ comments: [ 5c263bf01b764a11c479f69c, 5c263c41133ace1655edee76 ],
_id: 5c263bc61b764a11c479f69b,
title: 'good laptop',
category: 'Laptop',
brand: 'dell',
Condition: 'Good Condition',
image: 'https://avisassets.abgemea.com/.imaging/flexibleIntroLarge/dms/DMS/local/ZA/fleet/fleet-page/luxury-cars-feature.jpg',
price: 2000,
priceState: 'Negociable',
city: 'Algiers',
phone: '71717171555',
email: 'test#test.com',
description: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
date: '28/11/2018',
userId: 5c25344c321f0d5a777ace00,
__v: 2 }
i did read mongoose documentation about populate but i didn't figure out how to populate the userIdand also populate comments and the nested userId under the comments
Assuming userId is a field in your Posts schema, you can do it this way:
Posts.findOne({ id: 'your-post-id' })
.populate('userId')
.populate({ path: 'comments',
populate: { path: 'userId',
model:'User' }
})
.exec(callback);
Your schemas should be similar to this:
var postsSchema = new Schema({
comments: { type: [Schema.ObjectId], ref: 'Comment' },
userId: { type: Schema.ObjectId, ref: 'User' },
...
});
var commentsSchema = new Schema({
userId: { type: Schema.ObjectId, ref: 'User' },
...
});