Mongodb, Replace if exist, otherwise create new docs, - mongodb

Is there any built in function in mongodb which accepts an array as input, add each as item as a document, if the document already exist, based on unique indexes (see below), then the document is replaced
In my schema I got these indexes which defines the what calculation is unique
calculationSchema.index(
{ insId: 1, year: 1, month: 1, day: 1 },
{ unique: true }
);
Create function
export const createCalculations = async (calculations: ICalculation[]) => {
try {
const createdCalculations = await mongoose
.model("calculation")
.create(calculations); // Replace if it exist based on indexs above, otherwise create it
return Promise.resolve(createdCalculations);
} catch (err) {
console.log(err);
return Promise.reject(err);
}
};

Related

How to use azure cognitive search ? Can we use for global filteron table with pagination and sorting?

I am trying to return all the records that match my searchtext.So far I have only seen examples where we need to specify field name but I want return records if any of the field contains the searchtext, without specifying any field name. And I got to see $text , but unfortunatly it's not supported in cosmosdb API mongodb.
Can someone please help me to resolve this issue ?
Here is what I tried but failed
let querySpec = {
entity: "project",
$text: { $search: "\"test\"" } ,
$or: [{
accessType: "Private",
userName: userName
}, {
accessType: "Public"
}]
}
dbHandler.findandOrder(querySpec, sortfilter, "project").then(function (response) {
res.status(200).json({
status: 'success',
data: utils.unescapeList(response),
totalRecords:response.length
});
exports.findandOrder = function (filterObject, sortfilter, collectionname) {
return new Promise((resolve, reject) => {
return getConnection().then((db) => {
if (db == null) {
console.log("db in findandOrder() is undefined");
} else {
db.db(config.mongodb.dbname).collection(collectionname).find(filterObject).sort(sortfilter).toArray((err, res) => {
if (db) {
//db.close();
}
if (err) {
reject(err);
} else {
resolve(res);
}
});
}
});
});
};
Error:
{"message":{"ok":0,"code":115,"codeName":"CommandNotSupported","name":"MongoError"}}
I am using $regex as temperory solution as $text is not supported.
Please suggest ...
From the MongoDB manual, Create a Wildcard Index on All Fields:
db.collection.createIndex( { "$**" : 1 } )
but this is far from an ideal way to index your data. On the same page is this admonition:
Wildcard indexes are not designed to replace workload-based index planning.
In other words, know your data/schema and tailor indices accordingly. There are many caveats to wildcard indices so read the manual page linked above.
Alternately you can concatenate all the text you want to search into a single field (while also maintaining the individual fields) and create a text index on that field.

How to replace or insert if a document is not found in Mongodb?

address.update(
{creator:req.userData.unique_SHOP},
{$setOnInsert:PassAddress},
{
upsert:true,
}
)
this is my query
address = schema
creator is the key that i'm trying to match
passAddress has the all elements of the documents
const PassAddress = new address({
firstname:req.body.firstname,
lastname:req.body.lastname,
phone:req.body.phone,
zip:req.body.zip,
address1:req.body.address1,
address2:req.body.address2,
city:req.body.city,
state:req.body.state,
country:req.body.country,
creator:req.userData.unique_SHOP
});
any help?i am new to mongodb
This will work!
const PassAddress = new address({
firstname:req.body.firstname,
lastname:req.body.lastname,
phone:req.body.phone,
zip:req.body.zip,
address1:req.body.address1,
address2:req.body.address2,
city:req.body.city,
state:req.body.state,
country:req.body.country,
creator:req.userData.unique_SHOP
});
address.findOneAndUpdate(
{creator:req.userData.unique_SHOP}, // find a document with that filter
PassAddress, // document to insert when nothing was found
{upsert: true, new: true}, // options
function (err, doc) { // callback
if (err) {
// handle error
} else {
// handle document
}
}
);
Reference : https://silvantroxler.ch/2016/insert-or-update-with-mongodb-and-mongoose/
If passAddress has all the documents you want to insert of update just use simple update with upsert field
db.collection.update(<query>, <update>,{upsert: true)
//in your case
address.update(
{creator:req.userData.unique_SHOP},
passAddress,
{
upsert:true,
}
)

Can i decrement text in mongodb?

I have permalinks with numbers on the end like example_3 and I want to decrement each in a mongoDb update ( i.e. example_2) but it's text. Is there a way to do this?
posts.update(
{ 'title':doc.title, 'student':username, 'copy':false, 'class_number':{ '$gt': doc.class_number } },
{ '$inc': { 'class_number':-1, 'permalink':-1 } },
{ multi: true },
function(err, dox){
if (err) return callback(err, null);
console.log('decclassnumber');
console.log(dox + ' posts were decremented.');
callback(err, dox);
});
It doesn't make sense to increment/decrement an alphanumeric string; you need to separate the original string value into meaningful parts before asking MongoDB (or your application code) to adjust the numeric portion.
Normally with permalinks you would also be incrementing values rather than decrementing -- the whole intent of permalinks is to ensure that a given link is always pointing to the same resource.
It sounds like you actually want to implement a sequence pattern, where you find the next available sequence value to use.
For example, see: Create an Auto-Incrementing Sequence in the MongoDB manual.
Here's a slightly modified version of the getNextSequence() function in the documentation that uses upsert to either find an existing slug counter document or insert a new one. The return value is a new unique slug:
function getNextSequence(name) {
var ret = db.counters.findAndModify(
{
query: { _id: name },
update: { $inc: { seq: 1 } },
upsert: true,
new: true,
}
);
// Return the new slug (eg: "example_1")
return name + '_' + ret.seq;
}
> getNextSequence("example")
example_1
> getNextSequence("example")
example_2
> getNextSequence("example")
example_3
If you do want to decrement from some starting value, you could insert a starting value for your sequence and reduce that instead with $inc: { seq: -1 }.

Remove multiple documents from mongo in a single query

I have a list of mongo '_id' which I want to delete. Currently I am doing this
# inactive_users --> list of inactive users
for item in inactive_users:
db.users.remove({'_id' : item})
but my problem is the list is too huge... (it might go 100,000 +). So querying for every item in list will only increase the load on server. Is their a way to pass the entire list in mongo query so that I dont have to fire query again and again.
Thank you
db.users.deleteMany({'_id':{'$in':inactive_users}})
List them all and use $in operator:
db.users.remove({_id:{$in:[id1, id2, id3, ... ]}})
You need to pass the ids in a specific format using ObjectId():
db.users.remove({_id: {$in: [ObjectId('Item1'), ObjectId('Item2'), ObjectId('Item2')]}});
Remove doesn't accept integer - you have to use ObjectId instance with _id format as a string.
var collection = db.users;
var usersDelete = [];
var ObjectID = req.mongo.ObjectID; //req is request from express
req.body.forEach(function(item){ //req.body => [{'_id' : ".." , "name" : "john"}]
usersDelete.push(new ObjectID(item._id));
});
collection.remove({'_id':{'$in': usersDelete}},function(){
//res.json(contatos);
});
I had the same question and ran across these answers but it seems the MongoDB manual is recommending deleteMany instead of remove. deleteMany returns the delete count as well as an acknowledgement of the write concern (if the operation succeeded).
const ids = [id1, id2, id3...];
const query = { _id: { $in: ids} };
dbo.collection("users").deleteMany(query, function (err, obj) {
if (err) throw err;
});
Or with an arrow function:
const ids = [id1, id2, id3...];
const query = { _id: { $in: ids} };
dbo.collection("users").deleteMany(query, (err, obj) => {
if (err) throw err;
});
Or better yet, with a promise:
const ids = [id1, id2, id3...];
const query = { _id: { $in: ids} };
dbo.collection("users").deleteMany(query)
.then(result => {
console.log("Records Deleted");
console.log(JSON.stringify(result));
//for number removed...
console.log("Removed: " + result["n"]);
})
.catch(err => {
console.log("Error");
console.log(err);
});

Auto increment document number in Mongo / Mongoose

My app has several users, each user has documents. Each documents needs to have a sequence number, that may look something like this: 2013-1, 2013-2 (year and sequence number), or perhaps just a simple number: 1, 2, 3...
Currently, I am assigning the sequence number from user's settings when the Mongoose docuemnt is created. Based on that sequence number and the number format from user's settings, I am generating the final document number.
What I realized is that when 2 documents are created at the same time, they will get exactly the same number, because I am incrementing the sequence number in settings just after I have saved a document. But I am assigning the sequence number when I am creating (not saving yet) the document so the sequence number will be exactly the same for both documents.
I obviously need a way to handle this sequence number auto-incrementing at the moment of saving...
How can I assure that this number is unique and automatically incremented/generated?
#emre and #WiredPraire pointed me to the right direction, but I wanted to provide a full Mongoose-compatible answer to my question. I ended up with the following solution:
var Settings = new Schema({
nextSeqNumber: { type: Number, default: 1 }
});
var Document = new Schema({
_userId: { type: Schema.Types.ObjectId, ref: "User" },
number: { type: String }
});
// Create a compound unique index over _userId and document number
Document.index({ "_userId": 1, "number": 1 }, { unique: true });
// I make sure this is the last pre-save middleware (just in case)
Document.pre('save', function(next) {
var doc = this;
// You have to know the settings_id, for me, I store it in memory: app.current.settings.id
Settings.findByIdAndUpdate( settings_id, { $inc: { nextSeqNumber: 1 } }, function (err, settings) {
if (err) next(err);
doc.number = settings.nextSeqNumber - 1; // substract 1 because I need the 'current' sequence number, not the next
next();
});
});
Please note that with this method there is no way to require the number path in the schema, and there is no point as well, because it is automatically added.
You can achieve that through:
create sequence generator, which is just another document that keeps a counter of the last number.
Use a mongoose middleware to update the auto increment the desired field.
Here is a working and tested example with the todo app.
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/todoApp');
// Create a sequence
function sequenceGenerator(name){
var SequenceSchema, Sequence;
SequenceSchema = new mongoose.Schema({
nextSeqNumber: { type: Number, default: 1 }
});
Sequence = mongoose.model(name + 'Seq', SequenceSchema);
return {
next: function(callback){
Sequence.find(function(err, data){
if(err){ throw(err); }
if(data.length < 1){
// create if doesn't exist create and return first
Sequence.create({}, function(err, seq){
if(err) { throw(err); }
callback(seq.nextSeqNumber);
});
} else {
// update sequence and return next
Sequence.findByIdAndUpdate(data[0]._id, { $inc: { nextSeqNumber: 1 } }, function(err, seq){
if(err) { throw(err); }
callback(seq.nextSeqNumber);
});
}
});
}
};
}
// sequence instance
var sequence = sequenceGenerator('todo');
var TodoSchema = new mongoose.Schema({
name: String,
completed: Boolean,
priority: Number,
note: { type: String, default: '' },
updated_at: { type: Date, default: Date.now }
});
TodoSchema.pre('save', function(next){
var doc = this;
// get the next sequence
sequence.next(function(nextSeq){
doc.priority = nextSeq;
next();
});
});
var Todo = mongoose.model('Todo', TodoSchema);
You can test it out in the node console as follows
function cb(err, data){ console.log(err, data); }
Todo.create({name: 'hola'}, cb);
Todo.find(cb);
With every newly created object the you will see the priority increasing. Cheers!
This code is taken from MongoDB manual and it actually describes making the _id field auto increment. However, it can be applied to any field. What you want is to check whether the inserted value exists in database just after you inserted your document. If it is allready inserted, re increment the value then try to insert again. This way you can detect dublicate values and re-increment them.
while (1) {
var cursor = targetCollection.find( {}, { f: 1 } ).sort( { f: -1 } ).limit(1);
var seq = cursor.hasNext() ? cursor.next().f + 1 : 1;
doc.f = seq;
targetCollection.insert(doc);
var err = db.getLastErrorObj();
if( err && err.code ) {
if( err.code == 11000 /* dup key */ )
continue;
else
print( "unexpected error inserting data: " + tojson( err ) );
}
break;
}
In this example f is the field in your document that you want to auto increment. To make this work you need to make your field UNIQUE which can be done with indexes.
db.myCollection.ensureIndex( { "f": 1 }, { unique: true } )
You can use mongoose-auto-increment package as follows:
var mongoose = require('mongoose');
var autoIncrement = require('mongoose-auto-increment');
/* connect to your database here */
/* define your DocumentSchema here */
autoIncrement.initialize(mongoose.connection);
DocumentSchema.plugin(autoIncrement.plugin, 'Document');
var Document = mongoose.model('Document', DocumentSchema);
You only need to initialize the autoIncrement once.