Why isn't ensureIndex() working as I would expect? - mongodb

Given the code
express = require('express')
bodyParser = require('body-parser')
url = require('url')
app = express()
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({ extended: true }))
app.set 'port', process.env.PORT || 5000
app.use express.static(__dirname + '/public')
mongodb = require('mongodb')
mongojs = require('mongojs')
dbLocation = process.env.MONGOLAB_URI || 'mongodb://localhost/wesave-companion';
db = mongojs(dbLocation)
transactions = db.collection 'transactions'
transactions.ensureIndex({item_id: 1}, unique: true)
app.post '/transactions', (req, resp) ->
transaction = buildTransaction req.query
createTransaction transaction
resp.status = 200
resp.send 'ok'
createTransaction = (transaction, user) ->
transactions.insert transaction, (err, doc) ->
return err if err
return doc if doc
I would generally expect that based on mongodb documentation, http://docs.mongodb.org/manual/reference/method/db.collection.ensureIndex/ my db would not be allowing multiple records with an identical 'item_id' key to exist, but that is not the case.
I've tried writing the ensureIndex method as both:
transactions.ensureIndex({item_id: 1}, {unique: true})
and
transactions.ensureIndex({"item_id": 1}, {unique: true})
and
transactions.ensureIndex({'item_id': 1}, {unique: true})
But clearly, using the mongoshell, records entries are definitely duplicating. What am I missing here?
Interestingly, if I attempt to write the index directly to the db, I get the below response, which I'm not sure how to interpret.
What direction should I be digging? Thx.

You don't have any duplicates. You're calling db.transactions.find(), which returns the entire contents of your collection.
It returns one document, which means your collection contains exactly one document.
You're calling it three times, which, you will note, returns the same thing every time.

Related

How to create in mongoose if not exists but not update

I've only found ways to create OR update a document. I need to create if not exists but do nothing if it already exists. How to do it in mongoose?
Please note that findOneAndUpdate won't work, because if it fins the document, it updates it! I don't want that.
UPDATE:
I just want to create a variable called order_number that can be incremented. However, to increment, I must make sure the document exists. However, I cannot update its value in case it exists.
I tried:
let r = await OrderNumber.findOneAndUpdate({ unique: 0 }, { unique: 0, order_number: 0 }, { upsert: true });
It successfully creates when it does not exist, but always updates order_number to 0. I don't want this to happen.
As only way I've found it via two DB calls as in general inserts doesn't have any filters unless findOneAnd* or upsert on updates are used - which we're not looking at. Then only option is to make two calls, here is basic code, please wrap those in proper function & try/catch to add error handling :
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const orderNumberSchema = new Schema({
any: {}
}, {
strict: false
});
const OrderNumber = mongoose.model('order_number', orderNumberSchema, 'order_number');
let resp = await OrderNumber.findOne({ unique: 0 });
if (!resp) {
let orderNumberObj = new OrderNumber({ unique: 0, order_number: 0 })
let r = await orderNumberObj.save();
}

mongodb update failes on express session

I am using passportjs for authentication. I am using mongodb to store express sessions. The document that gets saved contains 3 fields _id, session, expires.
When I try to update the document using mongoose it does not. It does however find the document but does not update.
Would there be any lock on the document? When I try to update from mongo console it does...
Please help.
here is my code:
var conditions = { '_id' : req.sessionID};
var update = { $set: { 'ip': '111.11.111.1112' }};
Sessions.update(conditions, update, function (err, res) {
if (err) return callback("FAILED");
return callback("SUCCESS");
});
This is my schema.....
var mongoose = require('mongoose');
// define the schema for our user model
var sessionsSchema = mongoose.Schema({
_id : String,
session : String,
ip : String,
expires : String
});
// create the model for users and expose it to our app
module.exports = mongoose.model('sessionsIP', sessionsSchema, 'sessionsIP');

How can I upsert using Mongoose based on _id?

When I do this:
client_id = req.param("client_id") ? null
client =
name: req.param "clientName"
status: 'active'
Client.update {_id: client_id}, client, {upsert: true}, (err, updRes) ->
if err
res.json
error: "Couldn't create client"
else
res.json client
It will create a new client record, except with a null _id field. I assume that's because the insert part of the upsert looks to the query to create the document. How can I do it so that if no doc is found, then insert a new ObjectId?
Not sure if you have figured this one out yet, but in case you don't want to run into trouble with unique key constraints like Mustafa mentioned above, the way I've done it is by using mongoose's findByIdAndUpdate:
// require mongoose
client_id = req.param("client_id") ? new mongoose.Types.ObjectId
client =
name: req.param "clientName"
status: 'active'
Client.findByIdAndUpdate client_id, client, {upsert: true}, (err, updRes) ->
if err
res.json
error: "Couldn't create client"
else
res.json client
I've never written CoffeeScript, so forgive me if some of that syntax is wrong, but I think it should be fairly obvious what I'm trying to do.
One thing to notice is that you need to make sure client_id is neither an empty string (as that would cause an 'Invalid ObjectID' error) nor null (because mongoose seems infer the new document's id from the one you're querying for). In case it is, I create a new ObjectId, which will cause the query to miss and therefore create a new document with that ID since I pass {upsert: true}.
This seems to have solved the problem for me.
All you have to do with Mongoose is to call save on the client, Mongoose will figure out on its own if it's an update or an insert:
client_id = req.param("client_id") ? null
client_data =
name: req.param "clientName"
status: 'active'
_id: client_id
client = new Client(client_data)
client.save (err) ->
if err
res.json
error: "Couldn't save client"
else
res.json client
Note: Client is a Mongoose model.

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);

Looking for help with reading from MongoDB in Node.JS

I have a number of records stored in a MongoDB I'm trying to output them to the browser window by way of a Node.JS http server. I think I'm a good portion of the way along but I'm missing a few little things that are keeping it from actually working.
The code below uses node-mongo-native to connect to the database.
If there is anyone around who can help me make those last few connections with working in node I'd really appreciate it. To be fair, I'm sure this is just the start.
var sys = require("sys");
var test = require("assert");
var http = require('http');
var Db = require('../lib/mongodb').Db,
Connection = require('../lib/mongodb').Connection,
Server = require('../lib/mongodb').Server,
//BSON = require('../lib/mongodb').BSONPure;
BSON = require('../lib/mongodb').BSONNative;
var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost';
var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : Connection.DEFAULT_PORT;
sys.puts("Connecting to " + host + ":" + port);
function PutItem(err, item){
var result = "";
if(item != null) {
for (key in item) {
result += key + '=' + item[key];
}
}
// sys.puts(sys.inspect(item)) // debug output
return result;
}
function ReadTest(){
var db = new Db('mydb', new Server(host, port, {}), {native_parser:true});
var result = "";
db.open(function (err, db) {
db.collection('test', function(err, collection) {
collection.find(function (err, cursor){
cursor.each( function (err, item) {
result += PutItem(err, item);
});
});
});
});
return result;
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end("foo"+ReadTest());
}).listen(8124);
console.log('Server running on 8124');
Sources:
- mongo connectivity code:
https://github.com/christkv/node-mongodb-native/blob/master/examples/simple.js
- node. http code: nodejs.org
EDIT CORRECTED CODE
Thanks to Mic below who got me rolling in the right direction. For anyone interested, the corrected solution is here:
function ReadTest(res){
var db = new Db('mydb', new Server(host, port, {}), {native_parser:true});
var result = "";
res.write("in readtest\n");
db.open(function (err, db) {
res.write("now open\n");
db.collection('test', function(err, collection) {
res.write("in collection\n");
collection.find(function (err, cursor){
res.write("found\n");
cursor.each( function (err, item) {
res.write("now open\n");
var x = PutItem(err, item);
sys.puts(x);
res.write(x);
if (item == null) {
res.end('foo');
}
});
});
});
});
}
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.write("start\n");
ReadTest(res);
}).listen(8124);
console.log('Server running on 8124');
My guess is that you are returning result, writing the response, and closing the connection before anything is fetched from the db.
One solution would be to pass the response object to where you actually need it, something like:
function readTest(res) {
db.open(function (err, db) {
db.collection('test', function(err, collection) {
collection.find(function (err, cursor) {
res.writeHead(200, {'Content-type' : 'text/plain'});
cursor.each( function (err, item) { res.write(item); });
res.end();
...
Of course, you should also handle errors and try to avoid nesting too many levels, but that's a different discussion.
Instead of writing all the low-level Mongodb access code, you might want to try a simple library like mongous so that you can focus on your data, not on MongoDB quirks.
You might want to try mongoskin too.
Reading documents
To apply specific value filters, we can pass specific values to the find() command. Here is a SQL query:
SELECT * FROM Table1 WHERE name = 'ABC'
which is equivalent to the following in MongoDB (notice Collection1 for Table1):
db.Collection1.find({name: 'ABC'})
We can chain count() to get the number of results, pretty() to get a readable result. The results can be further narrowed by adding additional parameters:
db.Collection1.find({name: 'ABC', rollNo: 5})
It's important to notice that these filters are ANDed together, by default. To apply an OR filter, we need to use $or. These filters will be specified depending upon the structure of the document. Ex: for object attribute name for an object school, we need to specify filter like "school.name" = 'AUHS'
We're using here the DOT notation, by trying to access a nested field name of a field school. Also notice that the filters are quoted, without which we'll get syntax errors.
Equality matches on arrays can be performed:
on the entire arrays
based on any element
based on a specific element
more complex matches using operators
In the below query:
db.Collection1.find({name: ['ABC','XYZ']})
MongoDB is going to identify documents by an exact match to an array of one or more values. Now for these types of queries, the order of elements matters, meaning that we will only match documents that have ABC followed by XYZ and those are the only 2 elements of the array name
{name:["ABC","GHI","XYZ"]},
{name:["DEF","ABC","XYZ"]}
In the above document, let's say that we need to get all the documnts where ABC is the first element. So, we'll use the below filter:
db.Schools.find({'name.0': 'ABC' })