Using a selfdefined ObjectId and avoiding double entries in Mongoose - mongodb

I am getting JSON objects through an external API in node.js and want to store them in MongoDB. I defined a model like this:
var Product = new Schema({
id: ObjectId,
name: String});
And now I'm trying to store an object:
JSONProduct = { id: 1234, name: 'The Foo Bar' };
product = new Product(JSONProduct);
product.save();
The object is stored fine in the "products" collection, but the id from the JSONProduct is replaced by a MongoDB created value:
{ "id" : ObjectId("119894980274616772006500"), "name" : "The Foo Bar" }
The main reason why I want to use my Product id over the MongoDB created one is, that I want to prevent duplicate entries for products. I get the JSON Product objects through a cronjob triggered call on an external API, including already existing ones. Maybe there is another, better way to do this?

You are defining an field as an ObjectID, but you are assigning a Number to it. To create an ObjectID you need to do something like:
new ObjectId('something');
However, in your case this is probably not the best idea. Define your model like this:
var Product = new Schema({
external_id: {type: Number, unique: true},
name: {type: String},
});
You can specify unique on a field to create a unique index for that field.

In the question you've mentioned,
The object is stored fine in the "products" collection, but the id from the JSONProduct is replaced by a MongoDB created value:
{ "id" : ObjectId("119894980274616772006500"), "name" : "The Foo Bar" }
But I think the it is created as:
{ "_id" : ObjectId("119894980274616772006500"), "name" : "The Foo Bar" }
Also, you can pass in your product id to field by name "_id", then mongo will not create any separate IDs and it'll not accept duplicate values and it'll have indexing automatically for that field.
But make sure you push unique values of product id to _id.

Related

mongodb - understanding references ids and ObjectId - which format to use?

var publisherSchema = Schema({
_id : Number,
name : String,
founded : Number,
books : [{ type: Schema.Types.ObjectId, ref: 'Book' }]
});
var bookSchema = Schema({
_id : Number,
title : String,
author : [{type: String}],
pages: Number,
language: String
});
var Book = mongoose.model('Book', bookSchema);
var Publisher = mongoose.model('Publisher', publisherSchema);
Trying to understand ObjectId: the type for books as listed as Schema.Types.ObjectId. Objectids are generated by Mongodb automatically.
If I add ids to the book array manually, type will be just strings. Do I need to somehow also generate Objectids saved in the books array so when I make a query (findById) I will find it or is it enough if I just add those id's as strings?
I read in another post its better to generate ObjectIds and not just strings to reduce space. Im confused by this as I read that they normally generated by mongodb. If I can (and should) generate them for ids -how to?
When I make a reference
books : [{ type: Schema.Types.ObjectId, ref: 'Book' }]
that is just for the developer to know that the id's are suppose to belong to the books collection, there is no internal linking functionality to the books collection from the publisher collection?!
Thanks!!

MongoDB - how to only update field if field does not exist

How can I update a mongo document with the following requirements:
Find a document by email property:
If the document exists:
If both retrieved and new document have property A, keep property A (the retrieved one).
If retrieved document property A is null or undefined or doesn't exist, update using property A of the new object.
If the document doesn't exist
Insert the new document.
The findOneAndUpdate seems not to convey the both 3 of the requirements. Thanks.
My recommendation is to go the following path:
db.getCollection('<some-collection>').update(
{ email: 'someguy#email.com' },
{
$set: {
name: "some guy",
username: someguy,
tel: '1234'
}
},
{ upsert: true }
);
Check upsert documentation:
https://docs.mongodb.com/manual/reference/method/db.collection.update/#upsert-option
Lets go through your requirements now:
3. If the document doesn't exist, insert the new document.
Yes, it will insert new document to collection if it doesnt find the document by email. Resulting document will be combination of find condition + $set + autogenerated _id, so it will look something like this:
{
_id: ObjectId(...)
email: 'someguy#email.com'
name: "some guy",
username: someguy,
tel: '1234'
}
2. If retrieved document property A is null or undefined or doesn't exist, update using property A of the new object.
All properties provided in $set will unconditionally be persisted in the database, which also covers your requirement of updating null/undefined values
3. If both retrieved and new document have property A, keep property A (the retrieved one).
If both newly provided A and database A are the same, we dont have a problem.
If As are different, dont you want to store the new A value?
If you are afraid of nulls/undefined values, you can omit them before providing object to $set.
What is the use-case for you not wanting to update database property with newly provided value?
One use-case i can see is that you want to pass createdAt in case you are creating new record, but dont want to update that value for existing records.
If thats the case, and you know those properties in advance, you can use $setOnInsert update operator. https://docs.mongodb.com/manual/reference/operator/update/#id1
So your update query can look like this:
db.getCollection('<some-collection>').update(
{ email: 'someguy#email.com' },
{
$set: {
name: "some guy",
username: someguy,
tel: '1234'
},
$setOnInsert: {
createdAt: new Date(),
updatedAt: new Date()
}
},
{ upsert: true }
);
I hope this helps!
You need not retrieve the document for updating the property A. You can use the update API of mongo to do so. Please find the psuedo code below:
db.<collection>.update({
"$or": [
{ "PropertyA": { "$exists": false } },
{ "PropertyA": null }
]
}, {$set: {"PropertyA": "NewValue"}});
The above code is for one property, but I think you can figure out how to scale it up.
Hope this helps !!

How can you set a user collection attribute to an array of objects in meteor?

We're using collection2 (obviously with simple schema) and trying to save an array of objects to a single property on the Meteor.users collection. For example our data might be:
[
{name: "paul"},
{name: "darryn"},
{name: "tom"}
]
in reality our object is more complex
when trying to do this with $set in an update on the users collection we've either gotten 500's or managed to delete the user object entirely when turning off validation.
we've also gotten the following error a number of times:
Validation object must have at least one operator / meteor mongo
This StackOverflow Question mentions it but doesn't offer a solution that makes sense in our context.
My question is two fold. How should the schema be defined for this as we've been trying with type: [Object] which I'm not sure is right, and secondly how should the update statement be created in the method.
Any thoughts, or help would be amazing.
First define the schema for your complex object. Here I've just added age as a key:
Schema.Foo = new SimpleSchema({
name: { type: String },
age: { type: Number, min: 0 }
});
Then augment the user schema with a key whose type is an array of the type you just defined
Schema.User = new SimpleSchema({
foo: { type: [Schema.foo] },
etc...
});

How to get a saved object / newly created object

I usually manipulate object by creating them unique "codes". (So I create an object with the code "test" and fetch the object back using objects.find({ code: "test" }). But I find this weird and counter-productive.
I'd like to know if it's possible to simply create a new object, and get its saved object or simply its ObjectId back once saved?
That would be nice if we could do this:
objects.save({ name: "Test Object" });
And get { ObjectId: "47cc67093475061e3d95369d", name: "Test Object" } in return.
Is there any way to do so?
Thanks!
Is this what you're looking for?
> var record = {hello:"goodbye"};
> db.test.save(record);
> record;
{ "hello" : "goodbye", "_id" : ObjectId("4f90c9106aee6e21b4e55c65") }
I do not think there is an API to get the autogenerated id back from the database, and there is also no real need:
If your "code" is really unique, you can use that as the object id.
objects.save({ _id: "test" });
The _id field does not have to be of type ObjectId, it just has to be unique. If you already have some primary key type data, you do not have to use the autogenerated id. This way, you also save one index.
If you want to use a generated ObjectId, generate it yourself before you call save:
ObjectId id = new ObjectId();
objects.save({ _id: id, something: 12345 });

DbRef with Mongoose - mongoose-dbref or populate?

I have the following 2 schemas:
Company Event:
var companyEventSchema = new Schema({
name : String,
description
date : Date,
attendees : [ { type : Schema.ObjectId, ref : 'Member' } ],
]});
And Member
var memberSchema = new Schema({
name : String,
emailAddress: String,
password :String,
created: { type: Date, default: Date.now }
});
Is the way i've ref'd Member from companyEventSchema correct?
I'm trying to do something a long the lines of a dbref.
I saw theres a separate project for that though... mongoose-dbref
However, the mongoose docs say the above provides "dbref like functionality"
Which would be more efficient?
You only need to use an actual DBRef (and mongoose-dbref) for the case where a field can contain ObjectIds that reference documents in potentially more than one collection. A DBRef is a tuple of an ObjectId, a collection name, and an optional database name.
Mongoose ref: fields, however, contain just an ObjectId and it's the Mongoose schema that defines what one collection the ObjectIds reference.
So Mongoose ref: fields are more efficient and should always be used unless you need the multi-collection reference support that DBRef provides.