Mongoose, get position of element in array - node.js

I am developping the messaging part of an app.
I have a schema that represents a conversation. It contains an array of ObjectId, and each ObjectId refers to a message.
I would like the user to sent to my API a message _id, so I can retrieve all the messages _ids on the array, after the one he sent.
So he can update with all the messages he doesn't have yet.
I can I get the position of the _id he sends me, within the messages: [ObjectId] array, into the query ?

Mongo doesn't support this type of query on the server, so if you take this approach you will have to do it within your application.
You might want to consider other possible architectures. First, after you have the position of the last ObjectId, how will you make your subsequent query? You could instead query the messages collection directly, using the timestamp that is encoded in the _id to return only newer messages:
db.messages.find({_id: {$gt: lastViewedMessage._id}})

Related

How to expose MongoDB documents primary keys in a REST API?

I am building a REST API with MongoDB + nodeJS. All the documents are stored and are using _id as the primary key. I've read here that we should not expose the _id and we should use another ID which is not incremental.
In the DB, a document is represented as:
{
_id: ObjectId("5d2399b83e9148db977859ea")
bookName: "My book"
}
For the following the endpoints, how should the documents be exposed?
GET /books
GET /books/{bookId}
Currently my API returns:
{
_id: "5d2399b83e9148db977859ea"
bookName: "My book"
}
but should it instead return something like:
{
id: "some-unique-id-generated-on-creation"
bookName: "My book"
}
Questions
Should I expose the _id so that one can make queries such as:
GET /books/5d2399b83e9148db977859ea
Should I use a UUID for my ID instead of ObjectId?
Should I keep the internal _id (but never expose it) and create another attribute id which would use UUID or another custom generated ID ?
Is it a good practice to work with _id in my backend or should I only make queries using my own custom ID? Example: find({ id: }) instead of find({ _id: })
To answer your questions.
You can expose _id so that authenticated users can make queries like GET, PUT and PATCH on that _id.
MongoDB has support that allows you to generate your own BSON ID and use it, instead of mongodb created it's own _id during the insert.
There is no need of duplicating logic, the main purpose of _id is to identify each document separately and having two id columns means you are storing redundant data, follow DRY (DO NOT REPEAT YOURSELF) principle wherever possible.
It's not a bad practice to work with _id in your backend.
Hope this helps!
Given you're using Mongoose, you can use 'virtuals', which are essentially fake fields that Mongoose creates. They're not stored in the DB, they just get populated at run time:
// Duplicate the ID field.
Schema.virtual('id').get(function(){
return this._id.toHexString();
});
// Ensure virtual fields are serialised.
Schema.set('toJSON', {
virtuals: true
});
Any time toJSON is called on the Model you create from this Schema, it will include an 'id' field that matches the _id field Mongo generates. Likewise you can set the behaviour for toObject in the same way.
You can refer the following docs:
1) https://mongoosejs.com/docs/api.html
2) toObject method
In my case, whether it's a security risk or not, but my _id is a concatenation of any of the fields in my Document that are semantically considered as keys, i.e. if i have First Name, Last Name, and Email as my identifier, and a fourth field such as Age as attribute, then _id would be concatenation of all these 3 fields. It would not be difficult to get and update such record as long as I have First Name, Last Name and email information available

How to figure out the type of _id after it sent to the client?

I have app running node.js with mongodb. When I query data in the Chrome debugger I can see if _id field is ObjectId, or if it's just a string.
But when data is sent to the client, the result json is always string. Therefore, if I send the document back to update on the server side it will be string unless I transform it into ObjectId. So the question is how I can figure out if I should transform this string into ObjectId, or leave it as a string?
Or, maybe there is a way to keep _id as kind of ObjectId when the object is sent to the client?
NB I don't have any mongoose models, I work with raw data using Node.js MongoDB driver.
If you need to know the type of the field _id in the client side, a simple workaround should be to include a idType type field like this:
{
"_id": "1234",
"idType": "string",
...
}
This way you can easily check this field on your client and guess the type.

Adding extra value at Model.create with Mongoose

I have a large array that comes in from an API that I'd like to store straight into MongoDB.
Model.create(largeArray) ... // many documents created
The problem is, I have one additional key:value pair that I need to set for all documents in that array. It's a user id, and many documents are created for a given user once per API call. So for a given Model.create call, the user id is the same for every doc in the array.
Without mapping over the array, is there an efficient way of adding a field with a consistent value? Something like Model.create(myLargeArray, {userId: someUserId}) would be ideal, but I know this isn't the case with the Mongoose API.
function addDocsForUser(largeArray, someUserId) {
// each element of largeArray needs to have `userId: someUserId` added to it
return Model.create(largeArray)
}

Generate a unique ObjectId for a collection

I would like to generate a unique ObjectId for a resource to give back clients with the nodejs mongodb driver.
IE:
var ObjectID = require('mongodb').ObjectID;
var objectId = new ObjectID();
Reading about an ObjectId it seems that there are some cases in which this id may not be unique. Even though this is extremely rare I still cannot take a chance on having a duplicate id.
Question #1, Using this driver is it possible (even though unlikely) to get a duplicate id doing this:
var objectId = new ObjectID();
Question #2 (if above is not 100% guarantee to give a unique id):
Does this driver guarantee that for a certain collection the ObjectId that is automatically created when a new document is inserted is unique? If yes, how? If yes, can I somehow duplicate that behavior when calling new ObjectID() myself without a collection?
If the driver or the mongo server ensures (100% of the time) that for a collection every new doc gets a unique id, I could always have a collection of just ids, then when generating a new, empty doc for that collection I would ensure I get a unique ObjectId. However seems like overkill to have another collection just to store ids.
That being said some might ask why not just generate the ObjectId in a collection and update that doc later with data. The answer is that in my case data may not ever come later and I don't want to implement logic to check for empty docs that only contain an id.
It's very unlikely that the same ObjectID will generate as mongo guarantees unique ID. objectID is created with a combination of two same values and two different values (unix epoch time, and a random value). However, in any case of a duplicate, you won't be allowed to insert a document as objectID acts as a primary key and insert function will return duplicate key error to your callback. Read more here. The same error is returned if mongo node native library creates a duplicate ObjectID.
UPDATE: again after reading the code base, if "hypothetically" the objectID that was generated by the library isn't unique, the answer is no. We are not ensured by the library that the id is unique, but we are ensured of a duplicate error doesn't matter who or what sent the id.
Here's the process:
1. generates ID
2. Sends straight to server.
3. Returns results.
Mongo isn't looping in nodeJS with existing ids because the library isn't storing it in cache. Read the code base for the library.

Mongoose.js: is it possible to change name of ObjectId?

Some question about mongo ObjectId in mongoose
1) Can be ObjectId field by named not as _id? And How to do that? When I do in my code:
MySchema = new mongoose.Schema({
id : mongoose.Schema.ObjectId
});
it changes nothing.
2) If I have objectId field called _id is it possible to return from request another name for this field (for example just "id" - to send it on the in web response);
3) And question just for understanding: why is the ObjectId _id field accessible through "id" property not "_id"?
Thanks, Alex
The "_id" element is part of the mongodb architecture which guarantee that every document in a collection can be uniquely identified. This is especially important if you use sharding to allow unique identifier across disparate machine. Therefore this is a design choice so there is no way to get ride of it :)
The default value for _id are generated as follows:
timestamp
hash of the machine hostname
pid of the generating process
increment
but you can use whatever value you want as long is unique.
If it's easier for you think about the _id of something which has to be there, but you really don't care about :) Just leave the system to auto generate it and use your own identifier.
So if you still wanna create your own "id" execute something like that:
db.mySchema.ensureIndex({"id": 1}, {"unique" : true})
but make sure that is really unique and it doesn't conflict with the API you use.
2) Rename it on the application side, just before sending it as the web response.
3) I think this is because of the API you use. Maybe the author found it more logical to return the id instead of _id ? Honestly never tried mongoose :)

Resources