Simple e-commerce schema
Christian Kvalheim @christkv
Agenda
• The product
• The categories
• Product by categories
• Category navigation
• The cart
• Add product to cart
• Remove product from cart
• Expire cart
• The checkout
The product
 {
     sku: "111445GB3",
     title: "Simsong One mobile phone",
     description: "The greatest Onedroid phone on the market .....",

     manufacture_details: {
        model_number: "A123X",
        release_date: new ISODate("2012-05-17T08:14:15.656Z")
     },
     shipping_details: {
        weight: 350,
        width: 10,
        height: 10,
        depth: 1
     },
     quantity: 99,
     pricing: {
        price: 1000
     }
 }
The product, add categories
db.products.update(
  {sku: "111445GB3"},
  {$set: { categories: ['mobile/15G', 'mobile/fm'] }});


  {
    sku: "111445GB3",
    title: "Simsong One mobile phone",
    description: "The greatest Onedroid phone on the market .....",
    manufacture_details: {
       model_number: "A123X", release_date: new
  ISODate("2012-05-17T08:14:15.656Z")
    },
    shipping_details: {
       weight: 350, width: 10, height: 10, depth: 1
    },
    quantity: 99,

   categories: ['mobile/15G', 'mobile/
  fm'],
The product, add categories
• Add an index on the categories
db.products.ensureIndex({categories:1 })



• Locate all the products in a category
db.products.find({categories: /^mobile/fm/})



• If we use case sensitive pre-fix regular expressions
  we use the btree index and have very fast lookups
Categories
  {
      title: "Mobiles containing a FM radio",
      parent: "mobile",
      path: "mobile/fm"
  }


db.categories.insert({title: "Mobiles containing a FM radio", parent:
"mobile", path: "mobile/fm"})

db.categories.insert({title: "Mobiles with 15G support", parent:
"mobile", path: "mobile/15G"})

db.categories.ensureIndex({parent: 1, path: 1})

db.categories.ensureIndex({path: 1})
Categories, queries
• Locate all categories directly under /mobile
db.categories.find({parent: /^mobile/}, {_id: 0, path: 1})



• Locate all the categories under /mobile
db.categories.find({path: /^mobile/}, {_id: 0, path: 1})
Adding to Cart
Cart, example schema
{
    _id: "the_users_session_id",
    status:'active'

    quantity: 2,
    total: 2000,

    products: []
}


• Add item to cart
• Update inventory only if we have enough quantity
• Rollback if inventory update fails
Cart, add item to cart
    db.carts.update({
        _id: "the_users_session_id", status:'active'
      }, {
        $set: { modified_on: ISODate() },
        $push: {
           products: {
             sku: "111445GB3", quantity: 1, title: "Simsong One mobile
    phone", price:1000
           }
        }
      });

{
  _id: "the_users_session_id",
  status:'active'
  quantity: 2,
  total: 2000,
  products: [{sku: "111445GB3", quantity: 1, title: "Simsong One mobile
phone", price:1000}]
}
Cart, add to cart, inventory cover
  db.products.update({
      sku: "111445GB3", quantity: {$gte: 1}
    }, {
      $inc: {quantity: -1},
      $push: {
         in_carts: {
           quantity:1, id: "the_users_session_id", timestamp: new
  ISODate()
         }
      }

• If we did not update the stock, roll back cart add
if(!db.runCommand({getLastError:1}).updatedExisting) {
  db.carts.update({
       _id: "the_users_session_id"
    }, {
       $pull: {products: {sku:"111445GB3"}}
    })
}
Update Cart
Cart, update quantity
  db.carts.update({
       _id: "the_users_session_id", "products.sku": "111445GB3", status:
  "active"
    }, {
       $set: {
           modified_on: new ISODate(),
           "products.$.qty": new_quantity
         }
    })



• Update the quantity in the cart
 • new_quantity = 2
 • old_quantity = 1
 • delta_quantity = new_quantity - old_quantity
Cart, update inventory
  db.products.update({
       sku: "111445GB3",
       "in_carts.id": "the_users_session_id",
       quantity: {
         $gte: 1
       }
    }, {
       $inc: { quantity: (-1)*delta_quantity },
       $set: {
         "in_carts.$.quantity": new_quantity, timestamp: new ISODate()
       }
    })

• Attempt to update the inventory
 • new_quantity = 2
 • old_quantity = 1
 • delta_quantity = new_quantity - old_quantity
Cart, on failure rollback
  if(!db.runCommand({getLastError:1}).updatedExisting) {
    db.carts.update({
         _id: "the_users_session_id", "products.sku": "111445GB3"
      }, {
         $set : { "in_carts.$.quantity": old_quantity}
      })
  }




• If the update fails roll back the cart to the previous
  quantity
 • new_quantity = 2
 • old_quantity = 1
 • delta_quantity = new_quantity - old_quantity
Expire Carts
Cart, expire all the carts
 var carts = db.carts.find({status:"expiring"})
 for(var i = 0; i < carts.length; i++) {
   var cart = carts[i]

     for(var j = 0; j < cart.products.length; j++) {
       var product = cart.products[i]

         db.products.update({
              sku: product.sku,
              "in_carts.id": cart._id,
              "in_carts.quantity": product.quantity
           }, {
              $inc: {quantity: item.quantity},
              $pull: {in_carts: {id: cart._id}}
           })
     }

     db.carts.update({
          _id: cart._id,
          $set: {status: 'expired'}
       })
 }
Checkout
Insert an Order
 db.orders.insert({
    created_on: new ISODate("2012-05-17T08:14:15.656Z"),
    shipping: {
       customer: "Peter P Peterson",
       address: "Longroad 1343",
       city: "Peterburg",
       region: "",
       state: "PE",
       country: "Peteonia",
       delivery_notes: "Leave at the gate",
       tracking: {
          company: "ups", tracking_number: "22122X211SD",
          status: "ontruck",
          estimated_delivery: new ISODate("2012-05-17T08:14:15.656Z")
       },
    },
    payment: { method: "visa", transaction_id: "2312213312XXXTD" }
    products: {
       {quantity: 2, sku:"111445GB3", title: "Simsong mobile phone",
 unit_cost:1000, currency:"USD"}
    }
 })
Cart, finish up and clean up
db.carts.update({
    _id: "the_users_session_id"
  }, {
    $set: {status:"complete"}
  });

• Set the cart status as completed
db.products.update({
    "in_carts.id": "the_users_session_id"
  }, {
    $pull: {in_carts: {id: "the_users_session_id"}}
  }, false, true);


• Remove all carts from products
Thank you and happy
     schemaing
  twitter: @christkv

Mongo db ecommerce

  • 1.
  • 2.
    Agenda • The product •The categories • Product by categories • Category navigation • The cart • Add product to cart • Remove product from cart • Expire cart • The checkout
  • 3.
    The product { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, pricing: { price: 1000 } }
  • 4.
    The product, addcategories db.products.update( {sku: "111445GB3"}, {$set: { categories: ['mobile/15G', 'mobile/fm'] }}); { sku: "111445GB3", title: "Simsong One mobile phone", description: "The greatest Onedroid phone on the market .....", manufacture_details: { model_number: "A123X", release_date: new ISODate("2012-05-17T08:14:15.656Z") }, shipping_details: { weight: 350, width: 10, height: 10, depth: 1 }, quantity: 99, categories: ['mobile/15G', 'mobile/ fm'],
  • 5.
    The product, addcategories • Add an index on the categories db.products.ensureIndex({categories:1 }) • Locate all the products in a category db.products.find({categories: /^mobile/fm/}) • If we use case sensitive pre-fix regular expressions we use the btree index and have very fast lookups
  • 6.
    Categories { title: "Mobiles containing a FM radio", parent: "mobile", path: "mobile/fm" } db.categories.insert({title: "Mobiles containing a FM radio", parent: "mobile", path: "mobile/fm"}) db.categories.insert({title: "Mobiles with 15G support", parent: "mobile", path: "mobile/15G"}) db.categories.ensureIndex({parent: 1, path: 1}) db.categories.ensureIndex({path: 1})
  • 7.
    Categories, queries • Locateall categories directly under /mobile db.categories.find({parent: /^mobile/}, {_id: 0, path: 1}) • Locate all the categories under /mobile db.categories.find({path: /^mobile/}, {_id: 0, path: 1})
  • 8.
  • 9.
    Cart, example schema { _id: "the_users_session_id", status:'active' quantity: 2, total: 2000, products: [] } • Add item to cart • Update inventory only if we have enough quantity • Rollback if inventory update fails
  • 10.
    Cart, add itemto cart db.carts.update({ _id: "the_users_session_id", status:'active' }, { $set: { modified_on: ISODate() }, $push: { products: { sku: "111445GB3", quantity: 1, title: "Simsong One mobile phone", price:1000 } } }); { _id: "the_users_session_id", status:'active' quantity: 2, total: 2000, products: [{sku: "111445GB3", quantity: 1, title: "Simsong One mobile phone", price:1000}] }
  • 11.
    Cart, add tocart, inventory cover db.products.update({ sku: "111445GB3", quantity: {$gte: 1} }, { $inc: {quantity: -1}, $push: { in_carts: { quantity:1, id: "the_users_session_id", timestamp: new ISODate() } } • If we did not update the stock, roll back cart add if(!db.runCommand({getLastError:1}).updatedExisting) { db.carts.update({ _id: "the_users_session_id" }, { $pull: {products: {sku:"111445GB3"}} }) }
  • 12.
  • 13.
    Cart, update quantity db.carts.update({ _id: "the_users_session_id", "products.sku": "111445GB3", status: "active" }, { $set: { modified_on: new ISODate(), "products.$.qty": new_quantity } }) • Update the quantity in the cart • new_quantity = 2 • old_quantity = 1 • delta_quantity = new_quantity - old_quantity
  • 14.
    Cart, update inventory db.products.update({ sku: "111445GB3", "in_carts.id": "the_users_session_id", quantity: { $gte: 1 } }, { $inc: { quantity: (-1)*delta_quantity }, $set: { "in_carts.$.quantity": new_quantity, timestamp: new ISODate() } }) • Attempt to update the inventory • new_quantity = 2 • old_quantity = 1 • delta_quantity = new_quantity - old_quantity
  • 15.
    Cart, on failurerollback if(!db.runCommand({getLastError:1}).updatedExisting) { db.carts.update({ _id: "the_users_session_id", "products.sku": "111445GB3" }, { $set : { "in_carts.$.quantity": old_quantity} }) } • If the update fails roll back the cart to the previous quantity • new_quantity = 2 • old_quantity = 1 • delta_quantity = new_quantity - old_quantity
  • 16.
  • 17.
    Cart, expire allthe carts var carts = db.carts.find({status:"expiring"}) for(var i = 0; i < carts.length; i++) { var cart = carts[i] for(var j = 0; j < cart.products.length; j++) { var product = cart.products[i] db.products.update({ sku: product.sku, "in_carts.id": cart._id, "in_carts.quantity": product.quantity }, { $inc: {quantity: item.quantity}, $pull: {in_carts: {id: cart._id}} }) } db.carts.update({ _id: cart._id, $set: {status: 'expired'} }) }
  • 18.
  • 19.
    Insert an Order db.orders.insert({ created_on: new ISODate("2012-05-17T08:14:15.656Z"), shipping: { customer: "Peter P Peterson", address: "Longroad 1343", city: "Peterburg", region: "", state: "PE", country: "Peteonia", delivery_notes: "Leave at the gate", tracking: { company: "ups", tracking_number: "22122X211SD", status: "ontruck", estimated_delivery: new ISODate("2012-05-17T08:14:15.656Z") }, }, payment: { method: "visa", transaction_id: "2312213312XXXTD" } products: { {quantity: 2, sku:"111445GB3", title: "Simsong mobile phone", unit_cost:1000, currency:"USD"} } })
  • 20.
    Cart, finish upand clean up db.carts.update({ _id: "the_users_session_id" }, { $set: {status:"complete"} }); • Set the cart status as completed db.products.update({ "in_carts.id": "the_users_session_id" }, { $pull: {in_carts: {id: "the_users_session_id"}} }, false, true); • Remove all carts from products
  • 21.
    Thank you andhappy schemaing twitter: @christkv