mongodb (mongoose) throws E11000 duplicate key error collection - node.js

I have a state model which is a lookup with just a name that i have set to unique because i don't want two states with the same name. now I have vacation model with a state property that i have set to the state schema. error E11000 duplicate key error collection is thrown after the first vacation is inserted when I insert a second vacation with the same state.I know mongodb throws the exception because I already have a vacation with the state.name as the first one.
const stateSchema = new mongoose.Schema({
name: {
type: String,
required: true,
minlength: 1,
maxlength: 30,
unique: true
}
});
const State = mongoose.model("State", stateSchema);
------------------------------------------------------
const vacationSchema = new mongoose.Schema({
-- some properties
state: {
type: stateSchema,
required: true
}
-- some properties
});
const Vacation = mongoose.model("Vacation", vacationSchema);
so how can i force unique state names in the states collection, but allow multiple vacations to have the same state in the vacations state? do I have to explicitly change state: {type: stateSchema} to {type: new Schema(...etc ?

You've two options :
If there is only one field name in State collection, also if it doesn't change(kind of nothing like rename/alter), Plus every vacation document contains only one state name then you would include state name value in state field directly in your Vacation document, this helps in if you don't want to hit other collection every time you query Vacation plus no need to maintain two collections. There is no point of directly storing names in Vacation collection if you're maintaining State collection, as there won't be any validation whether a given name exists in State collection or not, unless you do a DB call to check it before inserts to Vacation, because storing data into collections is independent - So you might need to remove unique : true from stateSchema, your code should work, as the above code is about storing data into Vacation or simply in Vacation schema make State as string & use State schema for storing data into State Collection.
Or if you really need to maintain another collection as what you've now, then you need to have a mapping between documents in State collection to documents in Vacation. This is helpful if most documents in Vacation will have multiple State names in array field. Where State names are unique in State collection. Optimal way of having relation is thru _id of State documents. If need to do that then make changes in Vacation Schema as,
state: {
type: [{ type: Schema.Types.ObjectId, ref: 'State' }],// you can make this single value instead of array.
required: true
}
After this you need to use .populate() on reads - Ref : mongoose populate
If you want to store state name as value in Vacation collection, then make state field as string, then you can use .populate() on reads - Ref : mongoose populate virtuals or similar to mongoDB's native $lookup - Ref : mongoDB $lookup
Note :
Uniqueness on State name helps when you've transactions related to State and also you need separate collection when you need to make frequent changes only on State related data + on reads as well you need only data related to State or Vacation - you make those independent as State is unique you can refer that in vacation.
Not only in mongoose schema, you also create unique index on a field, that way it will be on DB level which helps either way when running queries directly on DB or thru code, whereas mongoose schema triggers thru code, Also when you create an unique index on existing collection & runs into issue means there is already duplicate data for that field.

Related

Firebase Cloudfirestore update elements in a collections

i'm using firebase functions with nodejs and cloudfirestore to build an API, now i have a problem, i have a collection an inside the collection an array of objects.
My goal is to update only a specific element without read the whole object and then push it again.
This is how my "order's collection" looks like:
"someRestaurantId":[{id:1,desc:"test",val:3000},{id:2,desc:"test",val:4000},{id:4,desc:"test",val:5000}]
My goal es only update id 2.
This is what i been trying:
Get the full list based on
const document = db.collection('orders').doc(req.params.restId);
Iterate and find the correct element, modify it and update it again.
await document.update(fullobject)
The problem is that i need something more easy to handle since there could be thousand of element inside that array.
Looking at described issue, I think you should use subcollection (sometimes called nested collection) for that. However from the description it is hard to tell what do you have now.
Firestore collection structure is always like this:
collection -> document -> fields and nestedCollections
than nestedCollection has the same structure, so you can nest whole structure how many times you want, but always collection contain only documents and each document contain only fields and collection.
In presented collection description array is directly in the collection or directly in document which both are impossible.
Anyway my idea is to structure it following way:
Collection orders containing docs of various restIDs
every docs (ex. "someRestaurantId") contain subcollection with name ex. "RestOrders"
in the subcollection will add documents with ids like (1,2,3 etc.)
every doc in subcollection will have fields desc, val
Collection Orders:
Document RestID_1:
Subcolection RestOrders:
Document 1: {val: 3000, desc: test}
Document 2: {val: 3000, desc: test}
...
Document RestID_2:
Subcolection RestOrders:
Document 1: {val: 3000, desc: test}
Document 2: {val: 3000, desc: test}
...
And now to update value of "RestID_1", order "id 2" you can use:
db.collection('Orders')
.doc('RestID_1')
.collection('RestOrders')
.doc('2')
.update( {val: 4000} )
In above solution you avoid reading and updating big array. You can update only one filed of one document in nested sub-collection.

Why am I unable to change values of a Mongoose object to a different type ‘directly’?

I've just spent a good hour figuring out something mind-boggling (at least to me, as a JS noob) and I'd like to understand the underlying logic (or just why it works this way, because I think it's illogical and quite unexpected).
Suppose I'm using Mongoose to retrieve documents from a database, some or all of which include a date property (created with new Date()), a numeric property, and a string property.
[{
string: 'foo',
date: '2018-10-13T21:11:39.244Z',
number: 10
},
...
{
string: 'bar',
date: '2018-10-13T21:12:39.244Z',
number: 20
}]
I thus obtain an array of objects and now want to take the date property for each object and change the value to a string, so I do something like:
doc.find({}, (err, list) => {
list.forEach((item, index) => {
list[index].date = 'new value'
})
})
But I can't do that!
I can do list[index].string = 'new value' as well as list[index].date = new Date() but I can't change values that are of a different type, in this example date and number.
However, when I do list[index]._doc.date = 'new value', which took so long to figure out because I didn't know Mongoose objects weren't just plain old objects and focused on solving problems I didn't have instead, I can modify the value just fine.
It appears that the mongoose object somehow translates obj.key to obj._doc.key only if the type of the value matches but I'd appreciate a more detailed explanation than my uneducated guesses.
I suppose you want to use multi type on a document field, Mongoose support this by "Mixed" type when you define the Schema.
You can get more detail from https://mongoosejs.com/docs/schematypes.html#mixed.

Mongo index [String] property

I was just reviewing some code, and saw such property in the mongoose scheme:
names: {
type: [String],
index: true
}
As far as I understand how indexes work, they are binary trees, and how is this going to be organized as a node of a tree? Is there at all any sense of indexing such property?
'If you index a field that holds an array value, MongoDB creates separate index entries for every element of the array.' Per MongoDB documentation on multikey index.

What is the meaning `required` in mongoose Schema?

I am writing a mongoose schema, and I would like to understand the properties of the same.
Here is my schema:
var UserSchema = new Schema({
name: String,
username: { type: String, required: true, index: { unique: true }},
password: { type: String, required: true, select: false }
});
Why required is not declared for `name' - ?
Why required declared?
What is select - true/false -means?
When the index - should declared any why?
Why required is not declared for `name' - ?
Answer: When a field is mandatory to fill then in that case we mention it as required. So here "name" is not required or mandatory field.
Why `required' declared?
Answer: As mentioned above, When a field is mandatory to be filled then in that case we mention it as required.
What is select - true/false -means?
Answer: This means that it will not be returned by default in the data when you fetch the document. you can specify if this path should be included or excluded from query results by default.
Schema options
When the index - should declared any why?
Answer: Index should be declared when you are searching data on that field frequently so when you create indexing on that field in that case it do not search that field in all the collections it will search value for that field using index and will return result very quickly.
How indexes work in mongodb
Here, these act as model for your project. So, required is used as validation and index is working as index over that field
Now you have two ways :
either put validation over here in schemas/models
or just manually create validation for form at frontend using JS/Jquery and then long route
Now your answers:
Name is not compulsory to be filled in. That's why no required is put over there.
when there is mandatory to fill any value for that field. Then required is used in schemas.
True/False enables or disables the usage of validation over that field. If you are using false means filling in for that field isn't compulsion at all. But using false is considered a good practice.
Index is special data structure which are used for increasing performance during read/search operations. It increases the speed of operations and are stored in memory.
whenever we have to validate the particular field, so we used required.
required: true means you must fill that field.
required: false means you may or may not fill that field, but its a good practice.

Sort by a array element (document) field - MongoDB/Mongoose

This is the concerned part from the schema
`
var CandidateSchema = new Schema({
calculateScore:[{
jobname:{type:Schema.ObjectId,ref: 'Job'}
,Score:{type:Number,default:0}
}]
})
`
A candidate can apply to multiple jobs and get scored differently for different jobs. I want to sort the candidates depending on the specific job's Score. Any Idea?
Assuming the variable objectId holds the ObjectId of the referred Job, you can aggregate the records to get the records sorted by the score of that particular Job.
Since the stage operator $project does not support the $elemeMatch operation, we cannot use it to directly get the Job sub document that we want and sort based on it.
$project a separate field named temp_score to have a copy of the original calculateScore array.
$redact other sub documents from calculateScore other than whose jobname contains the
id we are looking for. Now calculateScore will contain only one
element in the array, i.e the element whose jobname is the id
that we have specified.
Based on this sub document's score sort the records in descending
order.
Once the sorting is done, project our original calculatescore
field, which is in temp_score.
The code:
var objectId = ObjectId("ObjectId of the referred Job"); // Needs to be fetched
// from the Job collection.
model.aggregate(
{$project:{"temp_score":{"level":{$literal:1},
"calculateScore":"$calculateScore"},
"calculateScore":1}},
{$redact:{$cond:[
{$and:[
{$eq:[{$ifNull:["$jobname",objectId]},objectId]},
{$ne:["$level",1]}
]
},
"$$DESCEND",
{$cond:[{$eq:["$level",1]},
"$$KEEP","$$PRUNE"]}]}},
{$sort:{"calculateScore.Score":-1}},
{$project:{"_id":1,
"calculateScore":"$temp_score.calculateScore"}},
function(err,res)
{
console.log(res);
}
);

Resources