8

I’m building a personal shop app where users can sell items to each other, but I’m having a difficult time figuring out how to manage the products. For instance if you want to sell a t-shirt you should be able to pick a size and color etc. but if you sell a computer you should specify the year, cpu power etc. All products have a title, price, images and so on, but how would you get by with the varying attributes? I am using mongodb for the objects.

I was thinking about having a field attributes which should be an object with the different details, and then a have field type that would define which properties that exists. If type = 'Computer then I would know that attributes would look something like this.

attributes: { 
    capacity: 1000 // gb
    ram: 4096 // MB  
}

etc.

In a normal object oriented design I would have done this through inheritance / interfaces. If you have any idea on the best approach for this in mongoose / node.js I would be happy to hear about it.

If I'm not making myself clear in the question please tell me what is vague and what should be clarified

Edit:

The following article describes one solution to the problem http://learnmongodbthehardway.com/schema/chapter8/

It however doesn't state where to put the attributes. One solution might be to just store it in the category itself, but I'm not sure about best practices here though.

chrs
  • 5,906
  • 10
  • 43
  • 74
  • are you asking how to define the schema or how to approach the thing in general? – James LeClair Nov 13 '15 at 14:45
  • Possible through `discriminator mapping` https://github.com/Automattic/mongoose/pull/1647 and this has been discussed here in SO. – jpaljasma Nov 13 '15 at 14:47
  • To start with a good kick in the right direction would be useful. Regarding defining the schema is also useful to know, but that I can read up on – chrs Nov 13 '15 at 14:47
  • there's arguments both ways for this - but part of the nosql movement is not having to adhere to schemas. have you tried defining attributes as `attributes: { type: mongoose.Schema.Types.Mixed, default: {} }` - should get you rolling. Otherwise i would look at what @jpaljasma said or even just make some sub-schemas and validate against those based on product type – James LeClair Nov 13 '15 at 14:51

3 Answers3

11

An easy way to add inheritance to your Mongoose Schemas is to use Discriminators. This will allow you to create a parent Schema that can store attributes that are across all your products such as title, price, and images. You can then create child Schemas that will contain attributes specific to a product type such as Electronics and Clothing. For example in the Electronics Schema you can add attributes for cpu and ram that will not be present in the Clothing Schema.

Here is a basic example of how I set this up using Node and Mongoose.

Node/Javascript

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

// When you create a Electronic Product, it will set the type to Eletronic. 
var options = { discriminatorKey: 'type' };

// parent Product schema.
var productSchema = new mongoose.Schema({ name: String, price: Number }, options);
var Product = mongoose.model('Product', productSchema);

// child Electronic schema.
var ElectronicProduct = Product.discriminator('Electronic', new mongoose.Schema({ cpu: Number }, options));
var computer = new ElectronicProduct({ name: 'computer', price: 100, cpu: 5 });
computer.save();

// child Clothing schema. 
var ClothingProduct = Product.discriminator('Clothing', new mongoose.Schema({ size: String }, options));
var shirt = new ClothingProduct({ name: 'shirt', price: 50, size: 'Small' });
shirt.save();

If you log the objects that are saved they should look like

{ _id: 564b55983e5eec1ce2a44038,
  type: 'Electronic',
  cpu: 5,
  price: 100,
  name: 'computer' }
{ _id: 564b55983e5eec1ce2a44039,
  type: 'Clothing',
  size: 'Small',
  price: 50,
  name: 'shirt' }

When you attempt to access attributes that are not in the Product schema, it would be good practice to check if the attribute exists before trying to access it.

jjbskir
  • 8,474
  • 9
  • 40
  • 53
3

If what you are asking is how to approach the schema I would say make your model attributes an array of objects or of type mixed

http://mongoosejs.com/docs/schematypes.html

Because you can have many possible options for attributes it's impossible to know all of them in advance. Maybe you want the user to define the name of the attribute(the key) and the value as well. Then once you get an object you can use a for inloop to use the key as the label and the value.

example:

var attributes = [];

for ( attr in item.attributes ) {
    //do something with the key=>value pair
    attributes.push({
        label: attr,
        value: item.attributes[attr]
    }); 
}

//iterate through the new array to display your item attributes
Jose Carrillo
  • 1,830
  • 3
  • 16
  • 19
1

For this dynamic data scenario i would prefer keeping attributes as empty object in the Mongoose Model, so that you can use varying attributes for each documents.

Model

var mongoose = require('mongoose');
Schema = mongoose.Schema;

var productSchema = new mongoose.Schema({
    title:{type:String,required}, // Common Attributes
    price:{type:Number,required}, // Common Attributes
    images:[buffer], // Common Attributes
    //Other Common Attributes goes here
    attributes:{} // Empty attributes object which can vary for each product.
});

var Product = mongoose.model('Product', productSchema);

You can use the attributes object to specify the varying attributes for the specific product.

Controller

var computer = new Product({
 title: 'Computer',
 price: 1000,
 attributes:{
   year:2000,
   cpu:'test',
   power:'75 RMS'
  // Other Attributes goes here.
 }
});


var shirt = new Product({
 title: 'T-Shirt',
 price: 999,
 attributes:{
   size:30,
   color:'black'
  // Other Attributes goes here.
 }
});

Reference:

http://mongoosejs.com/docs/schematypes.html ( Look for the Mixed Data Type)

Note:

There are other ways to create this schema, one way is to have attributes as embedded document with all the combinations of attributes and can keep the value as null if it is not applicable.

Hope this helps.

SUNDARRAJAN K
  • 2,237
  • 2
  • 22
  • 38