Sequelize/Postgres - Finding a chat with ONLY these two users - node.js

I have these two models: User and Conversation
User = sequelize.define('user', {
name: Sequelize.STRING
});
Conversation = sequelize.define('conversation', {
name: Sequelize.STRING
});
User.belongsToMany(Conversation, { through: 'user_conversation' });
Conversation.belongsToMany(User, { through: 'user_conversation' });
User and Conversation have a many to many relationship.
What I am trying to do is get all conversations that have userA and userB, and NO one else.
I have tried these two methods:
Conversation.findAll({
include: [{
model: User,
where: { id: { Op.and: [ userA.id, userB.id ] }
}]
});
This doesn't return anything, which makes sense. Id can't be A and B at the same time.
Conversation.findAll({
include: [{
model: User,
where: { id: { Op.or: [ userA.id, userB.id ] }
}]
});
This returns any conversation that users A and B are participating in.
How can I find any conversation where ONLY users A and B are involved? Can I somehow count the includes?

Related

sequelize join up the tables

Working on an already setup db and also new to sequelize. I have like four tables
customer
library
books
excerpt
excerpt has redundant book_id from books. books has redundant library_id from library, and library has redundant customer_id from customer.
They were not declared foreign in db but its kinda acting the foreign key type and I make the association when I hit the orm functions.
My question:
I have a customer_id and book_id, and I have to fetch excerpt records based on book_id but have to go top the db to match the customer_id as well. (for multi tenancy)
the flow is: excerpt> book_id - books > library_id - library > customer_id - customer
I have written this code but its not working
async read_book_id(customer_id, book_id) {
const excerpts = await this.model.findAll({ // this.model being the excerpt model
where: { book_id: book_id},
include: [
{
model: this.db.Books,
association:
this.model.belongsTo(this.db.Books, {
foreignKey: 'book_id',
}),
where: { book_id: book_id},
include: [
{
model: this.db.Library,
association: this.model.belongsTo(this.db.Library, {
foreignKey: 'library_id',
}),
where: { customer_id: customer_id },
},
],
},
],
});
Basically this is something extended from this another code i wrote which is working for me. If I have to check only one level above thats working fine for me e.g
// reading books based on library_id
async read(customer_id, library_id) {
const books= await this.model.findAll({
where: {
library_id: library_id,
},
include: [
{
model: this.db.Library,
association: this.model.belongsTo(this.db.Library, {
foreignKey: 'library_id',
}),
where: { customer_id: customer_id },
},
],
});
}
This works fine for me.
Can you please tell how to run the first code block?
Assuming you already registered all associations just like I suggested in the comments above you need to indicate the correct models in include options along with correct conditions:
const excerpts = await this.model.findAll({ // this.model being the excerpt model
where: { book_id: book_id},
include: [
{
model: this.db.Books,
include: [
{
model: this.db.Library,
where: { customer_id: customer_id },
required: true
},
],
},
],
});```

how to attach or detach record on many to many sequelize association?

I have many to many association like this following model:
const Movie = sequelize.define('Movie', { name: DataTypes.STRING });
const Actor = sequelize.define('Actor', { name: DataTypes.STRING });
const ActorMovies = sequelize.define('ActorMovies', {
MovieId: {
type: DataTypes.INTEGER,
references: {
model: Movie,
key: 'id'
}
},
ActorId: {
type: DataTypes.INTEGER,
references: {
model: Actor,
key: 'id'
}
}
});
Movie.belongsToMany(Actor, { through: ActorMovies });
Actor.belongsToMany(Movie, { through: ActorMovies });
And I succsessfully create Movie when create an Actor record with this following code:
Actor.create({
name: 'Jhony',
movies: [
{ name: 'Movie 1'}, // it will generate Movie with ID 1
{ name: 'Movie 2'} // it will generate Movie with ID 2
]
}, {
include: [ Movie ]
})
but my question how can I attach multiple existing Movie record when creating an Actor?
I already try:
Actor.create({
name: 'Edward',
movieIds: [1, 2]
}, {
include: [ Movie ]
})
and:
Actor.create({
name: 'Edward',
movies: [{id: 1}, {id: 2}]
}, {
include: [ Movie ]
})
But stil didn't work. Anyone can help me, please. Thanks in advance
You can't link existing movies to a new actor while creating it. You need to call setMovies of the new actor model instance:
const actor = await Actor.create({
name: 'Edward',
})
await actor.setMovies([1, 2])
Also, please pay attention that if you execute more than one query that changes something in DB it would be much more reliable to use transactions to turn all this queries into one atomic operation.

Sequelize: Ordering by Nested Associations

I have 4 tables:
ChatRooms
Participants
Messages
Users
ChatRoom has many Participants
ChatRoom has many Messages
User has many Messages
User has Many Participants
Participant belongs to User
Message belongs to User
I'm trying to use Sequelize to query Users and under the Participants relation, I want it to return a list of 10 Participants(which is essentially the user) ordered by the ChatRoom with the most recent message. Essentially Participants would be the most recently active ChatRoms.
// My current code is:
User.findAll({
include: [
{
model: Participant,
include: [
{
model: ChatRoom,
include: [{ model: Message}]
}
],
order: [[ChatRoom, Message, 'id', 'DESC']]
}
],
})
// This is what my server is returning right now,
// but the ordering is not working:
// participantId: 2 should be on top as it has a more recent message
{
userId: 1,
name: 'Kevin',
participants: [
{
participantId: 1,
userId: 1,
chatRoomId: 1,
chatRoom:
{
chatRoomId: 1,
Messages: [{
MessageId: 1,
message: 'message1',
userId: 1
},
{
MessageId: 2,
message: 'message2',
userId: 2
}]
}
},
{
participantId: 2,
userId: 1,
chatRoomId: 2,
chatRoom:
{
chatRoomId: 2,
Messages: [{
MessageId: 3,
message: 'message3',
userId: 1
},
{
MessageId: 4,
message: 'message4',
userId: 3
}]
}
}
]
}
I don't know if what I want is possible. The important part is that the query returns the ChatRooms with the most recent messages. I don't have to do it inside the User object. I can do it in a separate query if need be and I am also open to designing the schema a different way.
I discovered that my posted code does work. The issue is that when I used limit (which I had left out for simplicities sake) it does a subquery and causes it to have a "column not found" error.
I tried a subQuery: false but it still didn't allow me to combine a limit with order.
Here you should try this.
Add > separate:true in your join with include and after that your code should be like below
User.findAll({
subQuery:false
include: [
{
model: Participant,
include: [
{
model: ChatRoom,
include: [{ model: Message}],
order: [['id', 'DESC']]
}
],
}
],
})

Sequelize and querying on complex relations

I'm trying to understand how best to perform a query over multiple entities using Sequelize and Node.js.
I have defined a model "User" which has a belongsToMany relation with a model "Location". I then have a model "Asset" which also has a belongsToMany relation with "Location". When I have an instance of a User I would like to fetch all Assets that are associated with Locations that the User is associated with.
I tried the following which doesn't seem to work...
user.getLocations().then(function(userLocations) { return Asset.findAll({ where: { "Locations" : { $any : userLocations } }) })
Could anyone offer any suggestions?
Try this query:
User.findById(user_id, {
include: [{
model: Location,
required: true
}]
}).then(user => Asset.findAll({
where: {
user_id: user.id,
location_id: {
$in: user.locations.map(location => location.id)
}
}
})).then(assets => {
// The rest of your logic here...
});
This was the final result...
User.findById(user_id, {
include: [{
model: Location,
as: 'Locations', // Was needed since the original relation was defined with 'as'
required: true
}]
}).then(user => Asset.findAll({
include: [{
model: Location,
as: 'Locations',
where: {
id: {
$in: user.Locations.map(location => location.id)
}
}
}]
})).then(assets => {
// The rest of your logic here...
});

Build query to get chat conversation messages in sails js

Lets say i have the following model schema described as below to add some chat functionality between 2 users in my sails app. For instance if i have user A sending a message to user B, then user B will reply back to user A which will always create 2 conversations between both users. So my question is how can i query both conversations to get messages from A and B. I tried something like this but maybe theres a simple logic.
// User Model
attributes: {
username: {
type: 'string',
required: true,
unique: true,
},
conversations_sender: {
collection: conversation,
via: 'sender'
},
conversations_recipient: {
collection: conversation,
via: 'recipient'
}
}
// Conversation model
attributes: {
sender: {
model: user
},
recipient: {
model: user
},
messages: {
collection: 'message',
via: 'conversation'
}
}
// Message model
attributes: {
text: {
type: 'string'
},
conversation: {
model: 'conversation'
},
}
// Conversation Controller
get: function(req, res) {
var params = {
or : [
{
sender: req.param('sender'),
recipient: req.param('recipient')
},
{
sender: req.param('recipient'),
recipient: req.param('sender')
}
]
}
Conversation.find(params) ...
}
You should rethink your schema a bit, see this link that has a good database design for your needs:
http://www.9lessons.info/2013/05/message-conversation-database-design.html
You should be able then to fetch all the messages with the 2 user ids like this:
Conversation.findAll({sender: ..., receiver: ...})
Also you will need a timestamp for the messages, in the future you'll want to sort them somehow and also make the nice 'Read yesterday' feature

Resources