Mongoose/MongoDB: Targeting specific sub-schemas? - node.js

I have the following schema in mongoose:
const textSchema = new Schema({
title: { type:String, unique: true },
sentences: [SentenceSchema],
})
Let's suppose that a typical text has around 100 sentences. Now I've changed one sentence up a little bit in the front-end and need to send that change to my server/mongodb. How would I best do this with mongoose+rest api? Would I update the whole article because of a change in a single sentence? Or is there a way for me to target only that very sentence that I want to change?
PS: I am sorry, this question is probably quite basic, but it would help me out a lot! Thanks in advance for your answers!

No, you don't need to change whole article. What you can do is, you can find sentence that actually change by using sentence id and update particular sentence. You need to use findOneAndUpdate() method.
Please refer below query
Text.findOneAndUpdate(
{ "_id": textId, "sentences._id": updatedSentenceId },
{
"$set": {
"sentences.$": updatedSentence
}
},
function(err,doc) {
}
);

Related

Date of Birth in Mongoose Schema

I feel as though this is a simple question, but after doing google searches and looking at the mongoose documentation, I have not found a good answer (or at least one that I understand).
I want to include in the Profile Schema a Date of Birth Block. Now, this is my idea but I am unsure if this will work well within MongoDB.
I want to be able to have the User enter this information into their profile, and save it into the database.
birthday: {
day: {
type: Number
},
month: {
type: Number
},
year: {
type: Number
}
Is this the best method? does it cause problems in the long run? what is your guys opinion on the proper way to using Node.Js/Mongoose Schemas and using birthdates?
I appreciate the help guys.
Why not just use something like this?
birthday: { type: Date }
Whenever user inputs all the three fields, combine it to form the birthday and save it as date object. This seems much cleaner and it's easy to query too, considering mongo supports aggregation on fields like $month.
Birth-date format for mongoose Schema
dateOfBirth: {
type: Date,
required: true,
trim: true,
}

Mongoose partial field search without RegEx

Let's say I have this schema:
var mongoose = require("mongoose")
var userSchema = new mongoose.Schema({
name: {type: String},
// other fields
}, { collation: { locale: "en_US", strength: 1 } });
I use collation so that the search is case-insensitive
Then let's say I have a document with name "Dave"
{
name: "Dave",
// other fields
}
then, I search for it but without writing the whole word
var userList = {
.find({name: "da"})
.exec();
}
How can I make this work without using a regex expression? Which are quite slow. I have tried doing an index and then searching with the $text method but I don't know how to make it so that it searches only a specific field within the document.
I believe using REGEX is your best solution. What you are trying to do is literally what regex is designed for. Yeah it's slow, but any other option you try to implement will probably be slower.
Creating a text index, and using the $text is only designed to match full words so you cannot use this method.
If you are truly desperate, and really don't want to use regex you can try something else... Try creating and storing an object, with each possible substring in the document. Object lookup is O(1) time, which means it will be faster, but the tradeoff is you are storing an absurd amount of data in the database. If this is ok with you, then give 'er a try.
Let's use Dave for example. The object you store could look something like this:
{
"d": 1,
"da": 1,
"dav": 1,
"dave"" 1
}
We can store this object in a field called substrings. Then when we do the database lookup, it's as simple as:
User.find({ 'substrings.da': { $exists: true }})
But please consider using regex... It's so much simpler, so much cleaner and it's designed for exactly what you want.

Change something in database based on

I am currently building a Mongo, Node, Express web app where deals are rendered on the screen in a list if they are "active" . However, I would like to programtically change the "active" status of a deal to (see below for dealSchema) at the "dealEndingDate". Think of it like a deal expiring. Is there some sort of listener I can add to constantly troll the database and update the documents... I welcome all suggestions. Thank you!
{
"_id": {
"$oid": "5a63c974aa17eb49c5260815"
},
"dealHeadline": "Test",
"active": true,
"dealBeginningTime": {
"$date": "2018-01-20T06:00:00.000Z"},
"dealEndingTime": {
"$date": "2018-01-22T23:45:00.000Z"}
}
A cronjob can be a solution for you .
An alternative way of doing this is to use expires, i.e. dealEndingTime: { type: Date, expires: '60s' }. Please note that this will remove the entire document 60 seconds after the set date. If you still want to keep the document, you can set up a separate Deal collection, where all your deals are stored. Then you can reference the deals for each document by using ObjectId: deals: [{_id: { type: Schema.Types.ObjectId, ref: 'deal' } Then simply checking the length of the array will reveal if there are any active deals for that document.
I hope this is useful to you!

How to update(remove) a document in the nested array

I have a test schema of mongodb implemented in mongoose.
var TestSchema = new mongoose.Schema({ exam:[ Exam ] });
var ExamSchema = mongoose.Schema({type:String, questions: [ { question:{ type: ObjectId, ref: 'Question' }, answer:String } ] });
var QuestionSchema = mongoose.Schema({ desciption:String, solution:String });
The idea of the test, is a student might participate a test of several exams, each exam has a type name (could be Math or Physics ) and a list of question ObjectID as well as the corresponding answer filled by the student.
This code could help to add new question and answer to certain exam in the test
TestModel.update({'_id':pid,'exam.type':type},{'$push':{'exam.$.questions':{'question':questionsId,'answer':answer}}},options,function(err,ref){
if(err) {
console.log('add question to Exam'.red,err);
callback(err, null);
}else{
console.log('add question to Exam'.green+ref);
callback(null,ref);
}
})
It works well by adding but it comes to removing a question and answer, the update doesn't work.
Model.update({'_id':pid,'exam.type':type},{'$pull':{'exam.$.questions':questionId}},options,function(err,ref)
Model.update({'_id':pid,'exam.type':type},{'$pull':{'exam.$.questions.question':questionId}},options,function(err,ref)
Model.update({'_id':pid,'exam.type':type,'exam.questions.question':questionId},{'$pull':{'exam.$.questions.$.question':questionId}},options,function(err,ref)
Model.update({'_id':pid,'exam.type':type,'exam.questions.question':questionId},{'$pull':{'exam.questions.$.question':questionId}},options,function(err,ref)
I tried these methods, but none of these work
To use $ operator in the next modifier:
{'$pull': {'exam.$.questions': questionId}
You must at first use $elemMatch: operator in your query:
{'_id': pid, exam: { $elemMatch: {type: type} } }
There is a mongo syntax answer someone else may provide.
One aspect of meteor I love is that you get to use javascript/coffeescript everywhere. I humbly suggest you extend that strategy to your use of mongo updates. I find myself just using my json/object manipulation abilities directly and $set the whole thing, rather than learning another syntax. Some would say it is premature optimization to limit the fields you retrieve until proven it would have an effect, so you probably retrieve the data anyway.

create mongodb document with subdocuments atomically?

I hope I'm having a big brainfart moment. But here's my situation in a scraping szenario;
I'm wanting to be able to scrape over multiple machines and cores. Per site, I have different Front pages, I scrape (exmpl. for the site stackoverflow I'd have fronts stackoverflow.com/questions/tagged/javascript and stackoverflow.com/questions/tagged/nodejs).
An article could be on every Front and when I discover an article I want to create an Article if the url is unknown, if its known I want to make an Front entry in article.discover if Front is unknown and otherwise insert my FrontDiscovery to the apropriate Front.
Here are my Schemas;
FrontDiscovery = new Schema({
_id :{ type:ObjectId, auto:true },
date :{ type: Date, default:Date.now},
dims :{ type: Object, default:null},
pos :{ type: Object, default:null}
});
Front = new Schema({
_id :{ type:ObjectId, auto:true },
url :{type:String}, //front
found :[ FrontDiscovery ]
});
Article = new Schema({
_id :{ type:ObjectId, auto:true },
url :{ type: String , index: { unique: true } },
site :{ type: String },
discover:[ Front]
});
The Problem I am thinking I will eventually be running into is a race condition. When two job-runners (in parallel) find the same (before unknown) article and create a new one. Yes, I have a unique index on it and could handle it that way - quite inelegantly imho.
But lets go further; When - for what ever reason - my 2 job-runners are scraping the same front at the same time and both notice that for Front there is no entry yet and create a new one adding the FrontDiscovery, I'd end with two entry's for the same Front.
What are your strategies to circumvent such a situation? findByIdAndUpdate with the upsert:true for each document seperately? If so, how can I only push something to the embedded document collection and not overwrite everything else at the same time but still create the defaults if it hasnt been created?
Thank you for any help in directing me in the right direction! I really hope I'm having a massive brainfart..
Update with upsert=true can be used to perform an atomic "insert or update" (http://docs.mongodb.org/manual/core/update/#update-operations-with-the-upsert-flag).
For instance if we want to make sure a document in Front collection with specific url is inserted exactly once, we could run something like:
db.Front.update(
{url: 'http://example.com'},
{$set: {
url: 'http://example.com'},
found: true
}
)
Operations on a single document in MongoDB are always atomic. If you make updates that span over multiple documents, then no atomicity is guaranteed. In such cases, you can ask yourself: do I really need the operations to be atomic? If the answer is no, then you probably will find your way around working with potentially unconsistent data. If the answer is yes and you want to stick with MongoDB, check out the design pattern on Two Phase Commits.

Resources