Mongoose model custom event - javascript

I'm looking for the right way to attach events to my mongoose models/schema. My example model:
currency.js
var mongoose = require('mongoose');
var currencySchema = new mongoose.Schema({
_id: {
type: String,
required: true,
unique: true
},
name: {
type: String,
trim: true
}
});
var Currency = mongoose.model('Currency', currencySchema);
module.exports = Currency;
How to properly attach EventEmitter so I can emit event and catch it properly?
Currency.findOne({'name': name}, function (err, currency) {
currency.emit("up","Event sent");
callback(err, wallet);
});

You can't.
EventEmitter needs to contains functions and another type of data than can't be stored in a MongoDB BSON. Allowed types are the following:
MinKey (internal type)
Null
Numbers (ints, longs, doubles)
Symbol, String
Object
Array
BinData
ObjectID
Boolean
Date, Timestamp
Regular Expression
MaxKey (internal type)
More information in the MongoDB documents: BSON types

Would extending the Model work?
import events from 'node:events';
class CurrencyModel extends mongoose.Model {
static emitter = new events.EventEmitter();
static on(...args) {
return this.emitter.on(...args);
}
static emit(...args) {
return this.emitter.emit(...args);
}
}
currencySchema.loadClass(CurrencyModel);
Then in your code you can call Currency.emit('something') and Currency.on('something', callback).
You can also build in pre and post hooks like this:
currencySchema.pre('save', function (next) {
this.constructor.emit('save', this);
next();
});
EDIT: if you want to use currency.on instead of the model "Currency.on" you can make on and emit instance methods, but I'd keep the emitter static - your memory usage would skyrocket.

Related

Accessing the name of a class as a string in JavaScript when class was created using another constructor [duplicate]

I´m using mongoose and I need to find a model name from a model instance.
In one part of the code I have:
const schema = new mongoose.Schema({
name: {
type: String,
required: true
},
phone: {
type: String,
required: true
}
}
const schema = new mongoose.Schema('MyData', schema);
let instance = new this({
name: 'Pete',
phone: '123'
});
Ths instance variable is passed around in my code. Later I need to find out instance name, but I´m no sure if there is a way to do it, something like:
let instanceName = getInstanceName(instance); <== Expects 'MyData' to be returned
Is that possible using mongoose ?
I realized I had a model not an instance of a model so I needed to use something else.
If you have a model, you can get the name as below:
const model = mongoose.model("TestModel", schema);
const collectionName = model.collection.collectionName;
If you have a specific item/instance of the model:
const instance = new model({...});
const collectionName = instance.constructor.modelName
as Hannah posted.
The name of the model can be accessed using this instance.constructor.modelName.
In my case I was looking for how to get a discriminator model name from a mongoose model, and the suggested solutions didn't work:
const PerformanceResult = DropDown.discriminator('PerformanceResult', new db.Schema({
name: { type: String, required: true }
}))
export default PerformanceResult
console.log(PerformanceResult.constructor.modelName) // undefined
console.log(PerformanceResult.collection.collectionName) // DropDown (parent name)
you can use this:
console.log(PerformanceResult.modelName) // PerformanceResult
mongoose version: "^5.11.8"

OverwriteModelError: Cannot overwrite `Movies` model once compiled [duplicate]

Not Sure what I'm doing wrong, here is my check.js
var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
var user = mongoose.model('users',{
name:String,
email:String,
password:String,
phone:Number,
_enabled:Boolean
});
user.find({},{},function (err, users) {
mongoose.connection.close();
console.log("Username supplied"+username);
//doSomethingHere })
});
and here is my insert.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/event-db')
var user = mongoose.model('users',{
name:String,
email:String,
password: String,
phone:Number,
_enabled:Boolean
});
var new_user = new user({
name:req.body.name,
email: req.body.email,
password: req.body.password,
phone: req.body.phone,
_enabled:false
});
new_user.save(function(err){
if(err) console.log(err);
});
Whenever I'm trying to run check.js, I'm getting this error
Cannot overwrite 'users' model once compiled.
I understand that this error comes due to mismatching of Schema, but I cannot see where this is happening ? I'm pretty new to mongoose and nodeJS.
Here is what I'm getting from the client interface of my MongoDB:
MongoDB shell version: 2.4.6 connecting to: test
> use event-db
switched to db event-db
> db.users.find()
{ "_id" : ObjectId("52457d8718f83293205aaa95"),
"name" : "MyName",
"email" : "myemail#me.com",
"password" : "myPassword",
"phone" : 900001123,
"_enable" : true
}
>
Another reason you might get this error is if you use the same model in different files but your require path has a different case.
For example, in my situation I had require('./models/User') in one file, and then in another file where I needed access to the User model, I had require('./models/user').
I guess the lookup for modules & mongoose is treating it as a different file. Once I made sure the case matched in both it was no longer an issue.
The error is occurring because you already have a schema defined, and then you are defining the schema again. Generally what you should do is instantiate the schema once, and then have a global object call it when it needs it.
For example:
user_model.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var userSchema = new Schema({
name:String,
email:String,
password:String,
phone:Number,
_enabled:Boolean
});
module.exports = mongoose.model('users', userSchema);
check.js
var mongoose = require('mongoose');
var User = require('./user_model.js');
var db = mongoose.createConnection('localhost', 'event-db');
db.on('error', console.error.bind(console, 'connection error:'));
var a1= db.once('open',function(){
User.find({},{},function (err, users) {
mongoose.connection.close();
console.log("Username supplied"+username);
//doSomethingHere
})
});
insert.js
var mongoose = require('mongoose');
var User = require('./user_model.js');
mongoose.connect('mongodb://localhost/event-db');
var new_user = new User({
name:req.body.name
, email: req.body.email
, password: req.body.password
, phone: req.body.phone
, _enabled:false
});
new_user.save(function(err){
if(err) console.log(err);
});
I had this issue while 'watching' tests.
When the tests were edited, the watch re-ran the tests, but they failed due to this very reason.
I fixed it by checking if the model exists then use it, else create it.
import mongoose from 'mongoose';
import user from './schemas/user';
export const User = mongoose.models.User || mongoose.model('User', user);
I had this issue while unit testing.
The first time you call the model creation function, mongoose stores the model under the key you provide (e.g. 'users'). If you call the model creation function with the same key more than once, mongoose won't let you overwrite the existing model.
You can check if the model already exists in mongoose with:
let users = mongoose.model('users')
This will throw an error if the model does not exist, so you can wrap it in a try/catch in order to either get the model, or create it:
let users
try {
users = mongoose.model('users')
} catch (error) {
users = mongoose.model('users', <UsersSchema...>)
}
If you are using Serverless offline and don't want to use --skipCacheInvalidation, you can very well use:
module.exports = mongoose.models.Users || mongoose.model('Users', UsersSchema);
I have been experiencing this issue & it was not because of the schema definitions but rather of serverless offline mode - I just managed to resolve it with this:
serverless offline --skipCacheInvalidation
Which is mentioned here https://github.com/dherault/serverless-offline/issues/258
Hopefully that helps someone else who is building their project on serverless and running offline mode.
If you made it here it is possible that you had the same problem i did.
My issue was that i was defining another model with the same name.
I called my gallery and my file model "File". Darn you copy and paste!
I solved this by adding
mongoose.models = {}
before the line :
mongoose.model(<MODEL_NAME>, <MODEL_SCHEMA>)
Hope it solves your problem
This happened to me when I write like this:
import User from '../myuser/User.js';
However, the true path is '../myUser/User.js'
Click here! Official example.
Most important! thing is to export like this
export default mongoose.models.Item || mongoose.model('Item', itemsSchema)
To Solve this check if the model exists before to do the creation:
if (!mongoose.models[entityDBName]) {
return mongoose.model(entityDBName, entitySchema);
}
else {
return mongoose.models[entityDBName];
}
I know there is an accepted solution but I feel that the current solution results in a lot of boilerplate just so that you can test Models. My solution is essentially to take you model and place it inside of a function resulting in returning the new Model if the Model has not been registered but returning the existing Model if it has.
function getDemo () {
// Create your Schema
const DemoSchema = new mongoose.Schema({
name: String,
email: String
}, {
collection: 'demo'
})
// Check to see if the model has been registered with mongoose
// if it exists return that model
if (mongoose.models && mongoose.models.Demo) return mongoose.models.Demo
// if no current model exists register and return new model
return mongoose.model('Demo', DemoSchema)
}
export const Demo = getDemo()
Opening and closing connections all over the place is frustrating and does not compress well.
This way if I were to require the model two different places or more specifically in my tests I would not get errors and all the correct information is being returned.
This may give a hit for some, but I got the error as well and realized that I just misspelled the user model on importing.
wrong: const User = require('./UserModel');
correct: const User = require('./userModel');
Unbelievable but consider it.
Here is one more reason why this can happen. Perhaps this can help someone else. Notice the difference, Members vs Member. They must be the same...
export default mongoose.models.Members || mongoose.model('Member', FamilySchema)
Change to:
export default mongoose.models.Member || mongoose.model('Member', FamilySchema)
What you can also do is at your export, make sure to export an existing instance if one exists.
Typescript solution:
import { Schema, Document, model, models } from 'mongoose';
const UserSchema: Schema = new Schema({
name: {
type: String
}
});
export interface IUser extends Document {
name: string
}
export default models.Users || model<IUser>('Users', UserSchema);
This problem might occur if you define 2 different schema's with same Collection name
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const userSchema = new Schema({
name: String,
});
// Trying to get the existing model to avoid OverwriteModelError
module.exports = mongoose.model("user") || mongoose.model('user', userSchema);
You can easily solve this by doing
delete mongoose.connection.models['users'];
const usersSchema = mongoose.Schema({...});
export default mongoose.model('users', usersSchema);
There is another way to throw this error.
Keep in mind that the path to the model is case sensitive.
In this similar example involving the "Category" model, the error was thrown under these conditions:
1) The require statement was mentioned in two files: ..category.js and ..index.js
2) I the first, the case was correct, in the second file it was not as follows:
category.js
index.js
I solved this issue by doing this
// Created Schema - Users
// models/Users.js
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
export const userSchema = new Schema({
// ...
});
Then in other files
// Another file
// index.js
import { userSchema } from "../models/Users";
const conn = mongoose.createConnection(process.env.CONNECTION_STRING, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
conn.models = {};
const Users = conn.model("Users", userSchema);
const results = await Users.find({});
Better Solution
let User;
try {
User = mongoose.model("User");
} catch {
User = mongoose.model("User", userSchema);
}
I hope this helps...
I faced this issue using Next.js and TypeScript. The top answers made it such that typings would not work.
This is what works for me:
const { Schema } = mongoose
export interface IUser {
name: string
email: string
}
const UserSchema = new Schema<IUser>({
name: { type: String, required: true },
email: { type: String, required: true },
})
const UserModel = () => mongoose.model<IUser>('User', UserSchema)
export default (mongoose.models.User || UserModel()) as ReturnType<
typeof UserModel
>
I faced the same Issue with NextJS and MongoDB atlas. I had a models folder
with the model of session stored, but the problem was not that I defined the Schema twice.
Make sure the Collection is empty and does not have a previous Document
If it does, then Simply declare a Model without Schema, like this:
const Session = mongoose.model("user_session_collection")
You can delete the previous records or backup them, create the schema and then apply query on the database.
Hope it helped
Below is the full solution to similar problem when using Mongoose with Pagination in combination with Nuxt and Typescript:
import {model, models, Schema, PaginateModel, Document } from 'mongoose';
import { default as mongoosePaginate } from 'mongoose-paginate-v2';
export interface IUser extends Document {
name: string;
}
const UserSchema: Schema = new Schema({
name: String
});
UserSchema.plugin(mongoosePaginate)
interface User<T extends Document> extends PaginateModel<T> {}
const User: User<IUser> = models['User'] as User<IUser> || model<IUser>('User', UserSchema) as User<IUser>;
export default User
tsconfig.json:
{
"compilerOptions": {
"target": "ES2018",
"module": "ESNext",
"moduleResolution": "Node",
"lib": ["ESNext", "ESNext.AsyncIterable", "DOM"],
"esModuleInterop": true,
"allowJs": true,
"sourceMap": true,
"strict": true,
"noEmit": true,
"baseUrl": ".",
"paths": {
"~/*": ["./*"],
"#/*": ["./*"]
},
"types": ["#types/node", "#nuxt/types"]
},
"exclude": ["node_modules"]
}
To make pagination working you will also need to install
#types/mongoose-paginate-v2
The above solution should also deal with problems related to hot reloading with Nuxt (ServerMiddleware errors) and pagination plugin registration.
A solution that worked for me was just to check if an instance of the model exists before creating and exporting the model.
import mongoose from "mongoose";
const { Schema } = mongoose;
const mongoosePaginate = require("mongoose-paginate");
const articleSchema = new Schema({
title: String, // String is shorthand for {type: String}
summary: String,
data: String,
comments: [{ body: String, date: Date }],
date: { type: Date, default: Date.now },
published: { type: Boolean, default: true },
tags: [{ name: String }],
category: String,
_id: String,
});
const Post = mongoose.models.Post ? mongoose.models.Post : mongoose.model("Post",articleSchema);
export default Post;
The schema definition should be unique for a collection, it should not be more then one schema for a collection.
If you want to overwrite the existing class for different collection using typescript
then you have to inherit the existing class from different class.
export class User extends Typegoose{
#prop
username?:string
password?:string
}
export class newUser extends User{
constructor() {
super();
}
}
export const UserModel = new User ().getModelForClass(User , { schemaOptions: { collection: "collection1" } });
export const newUserModel = new newUser ().getModelForClass(newUser , { schemaOptions: { collection: "collection2" } });
I had the same problem,
reason was I defined schema an model in a JS function, they should be defined globally in a node module, not in a function.
just export like this
exports.User = mongoose.models.User || mongoose.model('User', userSchema);
ther are so many good answer but for checking we can do easier job.
i mean in most popular answer there is check.js ,our guy made it so much complicated ,i suggest:
function connectToDB() {
if (mongoose.connection.readyState === 1) {
console.log("already connected");
return;
}
mongoose.connect(
process.env.MONGODB_URL,
{
useCreateIndex: true,
useFindAndModify: false,
useNewUrlParser: true,
useUnifiedTopology: true,
},
(err) => {
if (err) throw err;
console.log("DB connected");
},
);
}
readyState== 1 means connected
so does not try to connect again
so you won't get the error
i think it because of connecting while it is connected
it is another way of connecting to db
Make sure you are not using the same model name for two different schemas.
Example:
// course model
const mongoose = require("mongoose");
const courseSchema = new mongoose.Schema({
course: {
type: String,
required: true,
},
course_category: {
type: String,
required: true,
}
});
module.exports = mongoose.model("course", courseSchema);
// student model
const mongoose = require("mongoose");
const studentSchema = new mongoose.Schema({
first_name: {
type: String,
required: true,
},
last_name: {
type: String,
required: true,
}
});
module.exports = mongoose.model("course", studentSchema);

TypeError: elements.get is not a function error in firebase functions

I am getting the below error log when running a firebase function, i am trying to get documents and values inside recentPosts array field.
Error: Unknown error status: Error: Unknown error status: TypeError: elements.get is not a function
at new HttpsError (/srv/node_modules/firebase-functions/lib/providers/https.js:90:19)
at admin.firestore.collectionGroup.where.get.then.catch.error (/srv/lib/index.js:71:15)
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
Class:
class PostTable {
public commentCount : number;
public dateTime : number;
public docId : string;
public post : string;
public userId : string;
public userName : string;
constructor(commentCount: number, dateTime: admin.firestore.Timestamp, docId: string, post : string, userId : string, userName : string) {
this.commentCount = commentCount
this.dateTime= dateTime.toDate().getTime()
this.docId=docId
this.post=post
this.userId=userId
this.userName=userName
}
}
function:
export const getPosts = functions.https.onCall((data, context) => {
if (!context.auth) {
// Throwing an HttpsError so that the client gets the error details.
throw new functions.https.HttpsError('failed-precondition', 'The function must be called ' +
'while authenticated.');
}
let responseCollection : PostTable[] = []
admin.firestore().collectionGroup('recentPostColl').where('users', "array-contains",context.auth.token.name)
.get()
.then(collectionOfPosts => {
if(!collectionOfPosts.empty)
{
collectionOfPosts.forEach(element => {
let collection : Map<String, Object>[] = element.get('recentPosts')
collection.forEach(elements => {
try {
const p : PostTable = new PostTable(elements.get('commentCount')as number, elements.get('dateTime') as admin.firestore.Timestamp
,elements.get('docId') as string,elements.get('post') as string, elements.get('userId') as string, elements.get('userName') as string);
const stamp : admin.firestore.Timestamp = elements.get('dateTime') as admin.firestore.Timestamp
const date : Date = stamp.toDate()
if(date.getTime() > new Date(data.date).getTime())
{
responseCollection.push(p)
}
} catch (error) {
throw new functions.https.HttpsError(error, 'Constructor error');
}
});
});
}
})
.catch(error =>{
throw new functions.https.HttpsError(error, 'code error');
})
return responseCollection
})
Document:
In your code, collectionOfPosts is a QuerySnapshot object. You're using its forEach method to iterate the QueryDocumentSnapshot objects contained within it. Then, you're using get() to grab a specific field out of that snapshot. That field is going to be represented as plain JavaScript array type, since the field is an array. It's not a strongly typed array - you should assume that all the field types are plain JavaScript types and not TypeScript collection types. You can't simply cast a plain JavaScript types to a TypeScript collection types.
You'll need to provide a specific, safe assertion you can make about that field (assuming it contains exactly what you think) in JavaScript terms:
let collection : object[] = element.get('recentPosts')
You can then index into collection as if it was a normal array:
let item = collection[0]
or iterate it as you would any other JavaScript array.
The individual items will be object type, and you also deal with them as if they were plain old JavaScript objects.
If you want to deal with TypeScript collection types instead, you will have to write some code to convert all those JavaScript arrays and objects to their TypeScript collection type equivalents.

Sequelize not returning instance of model

The sequelize documentation (http://docs.sequelizejs.com/en/v3/docs/raw-queries/) states:
If you pass a model the returned data will be instances of that model.
// Callee is the model definition. This allows you to easily map a query to a predefined model
sequelize.query('SELECT * FROM projects', { model: Projects }).then(function(projects){
// Each record will now be a instance of Project
})
I have defined a model for a resource called agent.
module.exports = function(sequelize, DataTypes) {
let Agent = sequelize.define('Agent', {
responseStatus: DataTypes.STRING,
agentnum: {
type: DataTypes.STRING,
primaryKey: true,
allowNull: false,
field : 'agentno'
},
fname : {
type: DataTypes.STRING,
allowNull : false,
field: 'fname'
},
lname : {
type: DataTypes.STRING,
allowNull: false,
field : 'lname'
},
fullname : {
type: DataTypes.STRING,
allowNull : false,
field: 'full_name'
},
status : {
type: DataTypes.STRING,
allowNull: false,
field: 'business_status'
},
loginDate: DataTypes.DATE
}, {
freezeTableName: false,
timestamps: false
});
return Agent;
};
And when calling sequelize.query with my query and specifying model:Agent, I get an error thrown from sequelize:
TypeError: this.model.bulkBuild is not a function
the stack points to sequelize\lib\dialects\abstract\query.js:675.
This error persists until I apply a QueryType of sequelize.QueryTypes.RAW. At this point the query completes and I get a JSON response but it is not an instance of my Agent model. The JSON response from the sequelize query contains field names that should be mapped to.
I have imported my model (its just one) according to the directions found in their express sample (https://github.com/sequelize/express-example/blob/master/models/index.js). The models collection shows that my Agent model is included.
import Sequelize from 'sequelize';
import config from './config';
export default callback => {
const sequelize = new Sequelize(config.database, config.username, config.password, config.params);
sequelize.sync().then(function() {
let db = { }
let agentModel = sequelize.import('model/agent.js');
db[agentModel.name] = agentModel;
db.sequelize = sequelize;
db.Sequelize = Sequelize;
db.sequelize.authenticate().then(function() {
console.log('CONNECTION OK');
});
callback(db);
}).catch(function(err) {
console.log('FAILED TO CONNECT: ', err.message);
});
}
I want the query to return an instance of Agent when that query is run (invoked from a POST to my api). I am using MS SQL Server 2008 R2.
Any input is appreciated. Thanks.
EDIT 1/30 Here is the code generating the sequelize object, and passing in the model. The model collection shows my item is added, but it has no properties.
connectDb: (function () {
var sequelize;
function createInstance() {
var sequelizeInstance, connectedAndAuthenticated;
sequelizeInstance = new Sequelize(config.database, config.username, config.password, config.params);
connectedAndAuthenticated = sequelizeInstance.authenticate();
connectedAndAuthenticated.sequelize = sequelizeInstance;
connectedAndAuthenticated.Sequelize = Sequelize;
var model = sequelizeInstance.import('../src/model/agent.js');
return connectedAndAuthenticated;
}
return {
getInstance : function () {
if (!sequelize) {
sequelize = createInstance();
}
return sequelize;
}
};
}())
EDIT 1/26 After manipulating the QueryTypes, I discovered two things - that I inadvertently created a table in the database with the name of the model (Agent), and that the object returned has a tablename property value of empty. The schema and tablename are specified by me, but the query, being a stored procedure that joins a number of queries and tables, does not directly map to an object in my database named Agent. That being said, the documentation to me seems to suggest that this does not and should not matter, as I am creating my own model that is bound to the query result.
sequelize doc is confusing .i'm explaining you clean way to use sequelize
put
var models = require('../models');
in your code file be sure models directory contain index.js as you told me in your question and also Project model. be careful, other then correctly configured model there must not anything.
Now put
models.sequelize.query("select 1 as val").then(function(result){
console.log(result)
})
in your code
to check connection also you should use find query like
models.Projects.findAll().then(function(result){
console.log(result)
})
It seems like a simple typo. I don't think Agent is actually defined in your scope. I think you should be passing agentModel or whatever you bound the import to.
let agentModel = sequelize.import('model/agent.js');
db.sequelize.query( "DECLARE #response VARCHAR(256); EXEC API_Login #agentnum = N'" + agentNum + "', #hashedPassword = '" + password + "', #response = #response OUTPUT; SELECT #response AS N'response'",{ model: agentModel, type: sequelize.QueryTypes.RAW}) .spread(function(Agent) { res.status(200).json(Agent); }) .catch(function(err) { handleError(err, res); });
Note I'm using {model: agentModel, ...} not {model: Agent, ...} because Agent is undefined outside of the callback.
Your error TypeError: this.model.bulkBuild is not a function makes perfect sense if Agent is not actually a model but something else (or undefined).
UPDATE
You mention in comments on the post below that: "I have synced the model - the query attempts to create a table, instead of binding to the passed in agent model" and "It should not be creating a table".
Why do you think that is that is the case? Creating the table during sync() is normal behaviour for Sequelize.
I think you misunderstand how Sequelize works. It creates a table for every registered model during sync. If it can't create that table it might be returning an invalid model and that is why you are getting errors.
Models are explicity tied to individual database tables, that's the fundamental behavior of Sequelize. Each model instance represents a row of that table. If you are working with stored proceedures then you are probably better off using using the native database library and defining your own abstraction layer.
I'm sure you can disable and/or override all the default synchronization between the model and the underlying DB table but at a certain point or complexity you'd have basically just written your own abstraction library and it would be cleaner to just do it from scratch.
Maybe you can use Sequelize the way you want but at the very least you should not be calling sync(). What side-effects that leads to I can't say but I can say that unless you define your own beforeSync hook sync() will always create a table in your schema:
Model.prototype.sync = function(options) {
options = options || {};
options.hooks = options.hooks === undefined ? true : !!options.hooks;
options = Utils._.extend({}, this.options, options);
var self = this
, attributes = this.tableAttributes;
return Promise.try(function () {
if (options.hooks) {
return self.runHooks('beforeSync', options);
}
}).then(function () {
if (options.force) {
return self.drop(options);
}
}).then(function () {
return self.QueryInterface.createTable(self.getTableName(options), attributes, options, self);
})
...
}).return(this);
};

Mongo Cursor update

I am using TypeScript/Javascript with Meteor/Mongo.
I am trying to update a cursor with a timestamp on its lastMessageCreatedAt field.
updateChat(senderId: string, chatId: string): void {
const chatExists = !!Chats.find(chatId).count();
if (!chatExists) throw new Meteor.Error('chat-not-exists',
'Chat doesn\'t exist');
const chat1 = Chats.find(chatId);
const receiverId = chat1.memberIds.find(memberId => memberId != senderId); // <=== error TS2339: Property 'memberIds' does not exist on type 'Cursor<Chat>'.
const chat = {
memberIds: [senderId, receiverId],
lastMessageCreatedAt: new Date()
};
Chats.update(chat); // <=== error TS2346: Supplied parameters do not match any signature of call target.
},
model
interface Chat {
_id?: string;
memberIds?: string[];
title?: string;
picture?: string;
lastMessage?: Message;
lastMessageCreatedAt?: Date;
receiverComp?: Tracker.Computation;
lastMessageComp?: Tracker.Computation;
}
Question
However, I get the above errors. How do I update the cursor to have the timestamp? I am new to Meteor/Mongo, so I may be approaching this entirely wrong.
The update code is incorrect. It should be something like this
var date=new Date();
Chats.update({membersIds:{$all:[senderId,receiverId]}},{$set:{lastMessageCreatedAt: date}});
Refer docs for more information.
First if there is a single chat, its preferable to use the mongo findOne(), rather than find(), especially if you are using record _id. Remember the find(), or findOne() takes a query json object, and an optional projection (Mongo speak for the fields to return) json object. If the projection is omitted it returns all fields.
const chatFound = Chats.findOne(
{ '_id':
{ $eq: chatId},
});
Similarly you can use the update() or updateOne() mongo methods. Just read the docs as they are slightly different, but essentially take a query, update, & options json object.
Chats.update(
{ '_id':
{ $eq: chatId},
},
{ 'memberIds': memberIds,
'lastMessageCreatedAt': new Date()
}
);
One Mongo feature that is very useful, but doesn't appear to be needed for your situation, is the upsert, which either inserts or updates a record....
Chats.update(
{ '_id':
{ $eq: chatId},
},
{ 'memberIds': memberIds,
'lastMessageCreatedAt': new Date()
},
{ upsert: true }
);
Finally remember you can use the Mongo command line to test your query before coding. Run Meteor Mongo from your terminal window.

Categories

Resources