Mongo shell .save() triggers success callback but document not in database - mongodb

I am calling the .save() method on a Mongo document. The success callback is triggered and I am passed the saved document, but the table in my database still has zero documents in it. db.estimates.count() when I am using the uberEstimator database returns 0.
I've closely modeled this code off other working code I have, and I just can't figure out where I've gone wrong. Am I not using .save() correctly?
const mongoose = require('mongoose');
if (process.env.MONGODB_URI) {
mongoose.connect(process.env.MONGODB_URI);
} else {
mongoose.connect('mongodb://localhost/uberEstimator')
}
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => console.log('mongoose connection open!'));
var estimateSchema = new mongoose.Schema({
rideTier: String,
...
});
var Estimate = mongoose.model('Estimate', estimateSchema);
const save = ({display_name, distance, high_estimate, low_estimate, duration, estimate, currency_code,
start_latitude, start_longitude, end_latitude, end_longitude, start_address, end_address}) => {
let props = {
rideTier: display_name,
...
}
var estimate = new Estimate(props);
console.log(estimate);
estimate.save((err, newEstimate) => {
if (err) console.log(err);
console.log('saved new estimate to db');
console.log(newEstimate);
})
};
var sample = JSON.parse('{"localized_display_name": "uberX","distance": 6.17,"display_name": "uberX","product_id": "a1111c8c-c720-46c3-8534-2fcdd730040d","high_estimate": 17,"low_estimate": 13,"duration": 1080,"estimate": "$13-17","currency_code": "USD"}')
sample.start_address = '300 Albany St';
save(sample);
I get these logs:
mongoose connection open!
saved new estimate to db
{ _id: 5bca37267a478b31718445f3,
rideTier: 'uberX',
distance: 6.17,
highEstimate: 17,
lowEstimate: 13,
duration: 1080,
estimateString: '$13-17',
currency: 'USD',
start_address: '300 Albany St',
__v: 0 }
But still get the following when examining the table via Mongo shell:
> use uberEstimator
switched to db uberEstimator
> show tables
estimates
> use estimates
switched to db estimates
> db.estimates.count()
0

Command use estimates switches current database to estimates and I guess that's not what you want to achieve here. Based on your code it seems like you want to browse uberEstimator database and estimates collection so you can simply try:
use uberEstimator
db.estimates.count()

Related

How to Connect/Query Mongoose to an existing Database

I am working on trying to connect a Node/Express server to an existing MongoDB Database/Collection. I have already successfully connected to the database. However, I am having a tremendously difficult time setting up my models/schema to query.
The MongoDB is MongoDB Atlas and has one collection with over 800,000 documents. The name of the single collection is "delitosCollection".
I have tried the following to with no success:
var CrimeData = mongoose.model('DelitosCollection', new Schema({}),'delitosCollection');
mongoose.connection.on('open', function(ref){
console.log("connected to the mongo server");
CrimeData.find({}, (err,results) => {
if(err){
console.log("ERROR")
throw err
}
console.log("results: ", results.length)
} )
});
I know the connection is working as I am receiving the console.log with no errors. However, results.length is returning 0 when it should be over 800,000. Have spent way too many hours on this.
Create an empty schema for each collection you want to use
and then create a model to be used in your project
the model take 3 parameter
1)name of the model
2)schema name
3)collection name ( from mongodb atlas)
like that
const mongoose = require('mongoose');
mongoose.connect('mongodb uri')
const userSchema = new mongoose.Schema({});
const User = mongoose.model('User', userSchema, 'user');
then you can use the model normally
User.find({})
connection to mongo db
// Connect to mongoDB
mongoose.connect('mongodb://localhost/[yourDbName]',{useNewUrlParser:true})
.then(function(){
console.log('mongoDB connected');
})
.catch(function(){
console.log('Error :');
})
after that you will have to create your schema and then only you can query the database
create your schema like this
// Crimes Schema
const CrimeDetailsSchema= new Schema({
first_name: {
type: String,
required: true
},
last_name: {
type: String,
required: true
},
email: {
type: String,
required: true
}
});
const Profile = module.exports = mongoose.model('delitosCollection', CrimeDetailsSchema, 'delitosCollection');
after that create your queries
you can get an idea about that in mongoose documentation here
You can refer to the answer given below, just pass an empty object in schema
like db.model('users', new Schema({}))

MongoDB: is it possible to capture TTL events with Change Stream to emulate a scheduler (cronjob)?

I'm new to MongoDB and I'm looking for a way to do the following:
I have a collection of a number of available "things" to be used.
The user can "save" a "thing" and decrement the number of available things.
But he has a time to use it before it expires.
If it expires, the thing has to go back to the collection, incrementing it again.
It would be ideal if there was a way to monitor "expiring dates" in Mongo. But in my searches I've only found a TTL (time to live) for automatically deleting entire documents.
However, what I need is the "event" of the expiration... Than I was wondering if it would be possible to capture this event with Change Streams. Then I could use the event to increment "things" again.
Is it possible or not? Or would there be a better way of doing what I want?
I was able to use Change Streams and TTL to emulate a cronjob. I've published a post explaining what I did in details and gave credits at:
https://www.patreon.com/posts/17697287
But, basically, anytime I need to schedule an "event" for a document, when I'm creating the document I also create an event document in parallel. This event document will have as its _id the same id of the first document.
Also, for this event document I will set a TTL.
When the TTL expires I will capture its "delete" change with Change Streams. And then I'll use the documentKey of the change (since it's the same id as the document I want to trigger) to find the target document in the first collection, and do anything I want with the document.
I'm using Node.js with Express and Mongoose to access MongoDB.
Here is the relevant part to be added in the App.js:
const { ReplSet } = require('mongodb-topology-manager');
run().catch(error => console.error(error));
async function run() {
console.log(new Date(), 'start');
const bind_ip = 'localhost';
// Starts a 3-node replica set on ports 31000, 31001, 31002, replica set
// name is "rs0".
const replSet = new ReplSet('mongod', [
{ options: { port: 31000, dbpath: `${__dirname}/data/db/31000`, bind_ip } },
{ options: { port: 31001, dbpath: `${__dirname}/data/db/31001`, bind_ip } },
{ options: { port: 31002, dbpath: `${__dirname}/data/db/31002`, bind_ip } }
], { replSet: 'rs0' });
// Initialize the replica set
await replSet.purge();
await replSet.start();
console.log(new Date(), 'Replica set started...');
// Connect to the replica set
const uri = 'mongodb://localhost:31000,localhost:31001,localhost:31002/' + 'test?replicaSet=rs0';
await mongoose.connect(uri);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function () {
console.log("Connected correctly to server");
});
// To work around "MongoError: cannot open $changeStream for non-existent database: test" for this example
await mongoose.connection.createCollection('test');
// *** we will add our scheduler here *** //
var Item = require('./models/item');
var ItemExpiredEvent = require('./models/scheduledWithin');
let deleteOps = {
$match: {
operationType: "delete"
}
};
ItemExpiredEvent.watch([deleteOps]).
on('change', data => {
// *** treat the event here *** //
console.log(new Date(), data.documentKey);
Item.findById(data.documentKey, function(err, item) {
console.log(item);
});
});
// The TTL set in ItemExpiredEvent will trigger the change stream handler above
console.log(new Date(), 'Inserting item');
Item.create({foo:"foo", bar: "bar"}, function(err, cupom) {
ItemExpiredEvent.create({_id : item._id}, function(err, event) {
if (err) console.log("error: " + err);
console.log('event inserted');
});
});
}
And here is the code for model/ScheduledWithin:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ScheduledWithin = new Schema({
_id: mongoose.Schema.Types.ObjectId,
}, {timestamps: true});
// timestamps: true will automatically create a "createdAt" Date field
ScheduledWithin.index({createdAt: 1}, {expireAfterSeconds: 90});
module.exports = mongoose.model('ScheduledWithin', ScheduledWithin);
Thanks for the detailed code.
I have two partial alternatives, just to give some ideas.
1.
Given we at least get the _id back, if you only need a specific key from your deleted document, you can manually specify _id when you create it and you'll at least have this information.
(mongodb 4.0)
A bit more involved, this method is to take advantage of the oplog history and open a watch stream at the moment of creation (if you can calculate it), via the startAtOperationTime option.
You'll need to check how far back your oplog history goes, to see if you can use this method:
https://docs.mongodb.com/manual/reference/method/rs.printReplicationInfo/#rs.printReplicationInfo
Note: I'm using the mongodb library, not mongoose
// https://mongodb.github.io/node-mongodb-native/api-bson-generated/timestamp.html
const { Timestamp } = require('mongodb');
const MAX_TIME_SPENT_SINCE_CREATION = 1000 * 60 * 10; // 10mn, depends on your situation
const cursor = db.collection('items')
.watch([{
$match: {
operationType: 'delete'
}
}]);
cursor.on('change', function(change) {
// create another cursor, back in time
const subCursor = db.collection('items')
.watch([{
$match: {
operationType: 'insert'
}
}], {
fullDocument : 'updateLookup',
startAtOperationTime: Timestamp.fromString(change.clusterTime - MAX_TIME_SPENT_SINCE_CREATION)
});
subCursor.on('change', function(creationChange) {
// filter the insert event, until we find the creation event for our document
if (creationChange.documentKey._id === change.documentKey._id) {
console.log('item', JSON.stringify(creationChange.fullDocument, false, 2));
subCursor.close();
}
});
});

Receiving Data from Mongodb using Mongoose [duplicate]

I just started learning MongoDB and mongoose. Currently I have the following structure:
database -> skeletonDatabase
collection -> adminLogin
When I run db.adminLogin.find() from the command line I get:
{ "_id" : ObjectId("52lhafkjasfadsfea"), "username" : "xxxx", "password" : "xxxx" }
My connection (this works, just adding it FYI)
module.exports = function(mongoose)
{
mongoose.connect('mongodb://localhost/skeletonDatabase');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
console.log('Conntected To Mongo Database');
});
}
My -js-
module.exports = function(mongoose)
{
var Schema = mongoose.Schema;
// login schema
var adminLogin = new Schema({
username: String,
password: String
});
var adminLoginModel = mongoose.model('adminLogin', adminLogin);
var adminLogin = mongoose.model("adminLogin");
adminLogin.find({}, function(err, data){
console.log(">>>> " + data );
});
}
My console.log() returns as >>>>
So what am I doing wrong here? Why do I not get any data in my console log? Thanks in advance for any help.
mongoose by default takes singular model names and pairs them with a collection named with the plural of that, so mongoose is looking in the db for a collection called "adminLogins" which doesn't exist. You can specify your collection name as the 2nd argument when defining your schema:
var adminLogin = new Schema({
username: String,
password: String
}, {collection: 'adminLogin'});
Had a problem with injecting it within an express route for my api so I changed it thanks to #elkhrz by first defining the schema and then compiling that one model I want to then pull like so:
app.get('/lists/stored-api', (req, res) => {
Apis.find(function(err, apis) {
if (err) return console.error(err);
res.send(apis);
});
});
I wouldn't send it to the body, I would actually do something else with it especially if you plan on making your API a production based application.
Run through this problem and read up on possible proper ways of rendering your data:
How to Pass Data Between Routes in Express
Always a good idea to practice safe procedures when handling data.
first compile just one model with the schema as an argument
var adminLogin = mongoose.model('adminLogin', adminLogin);
in your code adminLogin does not exist, adminLoginModel does;
after that ,instead to
adminLogin.find({}, function(err, data){
console.log(">>>> " + data );
});
try this
adminLogin.find(function (err, adminLogins) {
if (err) return console.error(err);
console.log(adminLogins);
is important the "s" because mongo use the plural of the model to name the collection, sorry for my english...

mongoose model save does not trigger callback and pass to chai-http when collection does not exist in mongo

I create an post api and use mongoose model to save the request data to database. Then I use postman to test this api. Everything works. Everything works I mean collection created, data saved to the collection, and get data from response. see the following code
const eSchema = new mongoose.Schema({
model: String,
year: Number,
price: Number
});
const eModel = mongoose.model('bmw', eSchema);
app.post('/api/bmw', (req, res) => {
const newRecord = new eModel(req.body);
newRecord.save((error, record) => {
res.status(201).send(record);
});
});
Then I try to add test to this api. I use mocha with chai-http. see the following code
let chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
it('POST /api/bmw', () => {
chai
.request(app)
.post('/api/bmw')
.send({ model: '440i', year: 2018, price: 85000 })
.end((error, res) => {
console.log('POST', res.body);
});
});
Step1: Drop database (no db, no collection)
Step2: I run the test using mocha path-to-test-file --timeout 20000
Step3: repeat the step2
After step 2, I do not see any logged data(console.log('POST', res.body)). however, the collection has been created and data has been saved to mongo database.
After step3, I do see the logged data. I assume because the collection already exists in mongo base on the step2.
Then I do one step further. I repeat from step1 to step3. but this time I put a log in the model save callback, then I realized the callback is not called either after step2. But if I use postman instead of chai-http, it works everytime.
Does anyone has the same problem? Is this a capability issue between mongoose model and chai-http? Did I miss settings or anything?
Personally, I think it should work regardless the collection exists in mongo.
that api send is an asynchronous request. you need to pass the done function into your it() block to flag completion...like the below:
let chai = require('chai');
const chaiHttp = require('chai-http');
chai.use(chaiHttp);
it('POST /api/bmw', (done) => {
chai
.request(app)
.post('/api/bmw')
.send({ model: '440i', year: 2018, price: 85000 })
.end((error, res) => {
if(error) done(error)
else{
console.log('POST', res.body);
done();
}
});
});

How to access a preexisting collection with Mongoose?

I have a large collection of 300 question objects in a database test. I can interact with this collection easily through MongoDB's interactive shell; however, when I try to get the collection through Mongoose in an express.js application I get an empty array.
My question is, how can I access this already existing dataset instead of recreating it in express? Here's some code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
mongoose.model('question', new Schema({ url: String, text: String, id: Number }));
var questions = mongoose.model('question');
questions.find({}, function(err, data) { console.log(err, data, data.length); });
This outputs:
null [] 0
Mongoose added the ability to specify the collection name under the schema, or as the third argument when declaring the model. Otherwise it will use the pluralized version given by the name you map to the model.
Try something like the following, either schema-mapped:
new Schema({ url: String, text: String, id: Number},
{ collection : 'question' }); // collection name
or model mapped:
mongoose.model('Question',
new Schema({ url: String, text: String, id: Number}),
'question'); // collection name
Here's an abstraction of Will Nathan's answer if anyone just wants an easy copy-paste add-in function:
function find (name, query, cb) {
mongoose.connection.db.collection(name, function (err, collection) {
collection.find(query).toArray(cb);
});
}
simply do find(collection_name, query, callback); to be given the result.
for example, if I have a document { a : 1 } in a collection 'foo' and I want to list its properties, I do this:
find('foo', {a : 1}, function (err, docs) {
console.dir(docs);
});
//output: [ { _id: 4e22118fb83406f66a159da5, a: 1 } ]
You can do something like this, than you you'll access the native mongodb functions inside mongoose:
var mongoose = require("mongoose");
mongoose.connect('mongodb://localhost/local');
var connection = mongoose.connection;
connection.on('error', console.error.bind(console, 'connection error:'));
connection.once('open', function () {
connection.db.collection("YourCollectionName", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data); // it will print your collection data
})
});
});
Update 2022
If you get an MongoInvalidArgumentError: The callback form of this helper has been removed. error message, here's the new syntax using async/await:
const mongoose = require("mongoose");
mongoose.connect('mongodb://localhost/productsDB');
const connection = mongoose.connection;
connection.on('error', console.error.bind(console, 'connection error:'));
connection.once('open', async function () {
const collection = connection.db.collection("Products");
collection.find({}).toArray(function(err, data){
console.log(data); // it will print your collection data
});
});
I had the same problem and was able to run a schema-less query using an existing Mongoose connection with the code below. I've added a simple constraint 'a=b' to show where you would add such a constraint:
var action = function (err, collection) {
// Locate all the entries using find
collection.find({'a':'b'}).toArray(function(err, results) {
/* whatever you want to do with the results in node such as the following
res.render('home', {
'title': 'MyTitle',
'data': results
});
*/
});
};
mongoose.connection.db.collection('question', action);
Are you sure you've connected to the db? (I ask because I don't see a port specified)
try:
mongoose.connection.on("open", function(){
console.log("mongodb is connected!!");
});
Also, you can do a "show collections" in mongo shell to see the collections within your db - maybe try adding a record via mongoose and see where it ends up?
From the look of your connection string, you should see the record in the "test" db.
Hope it helps!
Something else that was not obvious, to me at least, was that the when using Mongoose's third parameter to avoid replacing the actual collection with a new one with the same name, the new Schema(...) is actually only a placeholder, and doesn't interfere with the exisitng schema so
var User = mongoose.model('User', new Schema({ url: String, text: String, id: Number}, { collection : 'users' })); // collection name;
User.find({}, function(err, data) { console.log(err, data, data.length);});
works fine and returns all fields - even if the actual (remote) Schema contains none of these fields. Mongoose will still want it as new Schema(...), and a variable almost certainly won't hack it.
Go to MongoDB website, Login > Connect > Connect Application > Copy > Paste in 'database_url' > Collections > Copy/Paste in 'collection' .
var mongoose = require("mongoose");
mongoose.connect(' database_url ');
var conn = mongoose.connection;
conn.on('error', console.error.bind(console, 'connection error:'));
conn.once('open', function () {
conn.db.collection(" collection ", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data); // data printed in console
})
});
});
I tried all the answers but nothing worked out, finally got the answer hoe to do it.
var mongoose = require('mongoose');
mongoose.connect('mongodb://0.0.0.0:27017/local');
// let model = require('./test1');
setTimeout(async () => {
let coll = mongoose.connection.db.collection(<Your collection name in plural form>);
// let data = await coll.find({}, {limit:2}).toArray();
// let data = await coll.find({name:"Vishal"}, {limit:2}).toArray();
// let data = await coll.find({name:"Vishal"}, {projection:{player:1, _id:0}}).toArray();
let data = await coll.find({}, {limit:3, sort:{name:-1}}).toArray();
console.log(data);
}, 2000);
I have also mentioned some of the criteria to filter out. Delete and update can also be done by this.
Thanks.
Make sure you're connecting to the right database as well as the right collection within the database.
You can include the name of the database in the connection string.
notice databasename in the following connection string:
var mongoose = require('mongoose');
const connectionString = 'mongodb+srv://username:password#hosturl.net/databasename';
mongoose.connect(connectionString);