Mongodb changing the unique key - mongodb

I have made the users email the unique key for my entire users database:
var usersSchema = new Schema({
_id: String, // Users Unique Email address
name: String, // Users name
phone: String, // Users phone number
country: String, // Country
type: String, // tenant/agent/manager/admin
username: String, // Username for login
password: String, // Password string
trello: Object, // Trello auth settings
settings: Object, // Settings for manager and other things
createDate: Number, // Date user was created
contactDate: Number, // Date user was last contacted
activityDate: Number // Date of last activity on this user (update/log/etc)
});
So what if the user changes email address?
Is my only way to delete the record and create it again?
Or is there a smarter way?
And the users._id (email) have relations in 16 other tables.
Example the booking table
var bookingSchema = new Schema({
_id: String, // Unique booking ID
user: String, // User ID --> users._id
property: String, // Property ID --> property._id
checkin: Number, // Check in Date
checkout: Number // Check out Date
});
One user can have a LOT of bookings
What I would do is find all records that matches the email and then do a for (i=1 ; i<booking.length ; i++) and then update the email of each record
Is there a smarter way to update all emails that matches using only one mongo call?
(the reason is there are so many relations, so my loop seems a bit like a very primitive way of doing it)

I would say it's much cleaner to create a field for email and create an Unique Index on that.
Unfortunately still the relationship as the ones inside the Relational databases isn't supported! There are plans according to the latest talks to create this feature natively.
The best solution for you would be to think how to use the sub-documents to make things more consistent.

Related

Kmongo: how to add unique field

I have a simple user data class that looks like:
#Serializable
data class User(
#SerialName("_id")
val _id: Id<User> = newId(),
val email: String,
var password: String,
var tokens: Array<String> = arrayOf()
)
And I'd like the email value to be unique, i've tried a unique annotation which seemed most appropiate, but with no success.
I've also tried google and the KMongo website but I could not find an answer.
You need to create an index on the field (or combination of fields) that you want to ensure uniqueness on.
db.getCollection<User>().createIndex(User::email,
indexOptions = IndexOptions().unique(true))

How to add users manually to Meteor.users collection?

I am having super user which I added manually and this user can other users manually through a form I give him.
lets say if I save the input entered by the user like the code shown below:
Session.set('name', t.find('#name').value);
Session.set('password', t.find('#pass').value);
Session.set('email', t.find('#email').value);
how do I store those values in those sessions in the Meteor.users, after checking that there is no match in the email and username?
and how do I encrypt the password before storing it in my database?
This code when called from the client side as:
Meteor.call('createUser','someemail#gmail.com','password123',function(err,res){
.....
})
creates a user in Meteor.users collection with the id given below in the method
Meteor.methods({
createUser(email,password){
check(email,String);
check(password,String);
let id = Accounts.createUser({
email: email,
password: password,
profile: {} //anything you like to add to profile.
})
}
})

How to keep track of users making Stripe Payments

I'm currently trying to figure out a way for my MEAN stack application to keep track of which users have paid to grant them access to a certain portion of my webpage. I've considered several options: Stripe customer ID, MongoDB record, And HTML attribute I can update.
My mean stack keeps track of users by JWT, and it appears stripe assigns them their own customer ID which isn't ideal. Can it done with JWT as opposed to their forced cutomer ID?
MongoDB record. Which is what I'm thinking might be the best option. When a new user has been created, i'll give it an attribute of hasPaid = no. Then update the record of that customer when a payment is submitted. Then I guess run a script to set everyone back to unpaid each day?
HTML element/attribute. I don't know if this is even possible; but it would be cool to create a key that is carried during the HTML session after payment is received. If the person closers the browser then the session would be closed?
I'm looking for guidance on my 3 options to determine if they're the best solution. Also, if anyone has any suggestions as to alternatives, I'm all ears!
Thanks in advance.
Speaking generally, the most common approach would be the second one: use an attribute in your data model that indicates whether the user has paid/should be granted access. When a charge is created [0] successfully, update the model to indicate so, then filter access based on this attribute.
[0] https://stripe.com/docs/api/node#create_charge
Use a Boolean value in your user model.
var UserSchema = new Schema({
name: String,
hasPaid: {type: Boolean, default: false} //set this false
});
then in your REST API routes, the user buys the product; now set hasPaid to true
// req.user._id is passport config
User.findOneAndUpdate({_id: req.user._id}, {$set: {"hasPaid":istrue}}).exec(function(err) {
if(err) {
throw err;
}else {
req.flash('success', 'Thank you for submitting the form.');
res.render('charge', {
layout: 'dash',
user: req.user,
success: req.flash('success')
});
}
});
Now you can keep track of the users that purchased your products to grant them access to other parts of your site.
Stripe.js comes with Checkout.js which makes it even easier to use Stripe's service.
Copy and paste this into your html/jade/handlebars or view file. This will display a popup form to let the user type in his or her cc information.
<form action="/charge" method="POST">
<script
src="https://checkout.stripe.com/checkout.js"
class="stripe-button"
data-key="pk_test_bla3hf&kc033"
data-image="/square-image.png"
data-name="Demo Site"
data-description="2 widgets ($20.00)"
data-amount="2000">
</script>
</form>
You will receive a token once the user presses submit that you grab on your server. From inside your REST API route, you can charge the customer:
var token = req.body.stripeToken; // Using Express
// Create a charge: this will charge the user's card
var charge = stripe.charges.create({
amount: 1999, // Amount in cents
currency: "usd",
source: token,
metadata: {
user: req.user._id,
email: req.user.local.email
}
description: "Example charge" //keep track by writing a description or you can use the metadata property Stripe has to offer in the charges object
},function(err, charge) {
if (err && err.type === 'StripeCardError') {
// The card has been declined
}else {
res.redirect('/thanks-for-your-order');
console.log('charge here ' + charge.id); //store the id
console.log('charge here ' + charge.invoice); //store the invoice
console.log('charge here ' + charge.customer); //store the customer id
}
});
You can now track each order by storing the properties of the charge object in any model you wish.

How to model mongodb collections for Cassandra database (migration)?

I am new to Cassandra and trying migrate my App from MongoDB to Cassandra
I have the following collections in MongoDB
PhotoAlbums
[
{id: oid1, title:t1, auth: author1, tags: ['bob', 'fun'], photos: [pid1, pid2], views:200 }
{id: oid2, title:t2, auth: author2, tags: ['job', 'fun'], photos: [pid3, pid4], views: 300 }
{id: oid3, title:t3, auth: author3, tags: ['rob', 'fun'], photos: [pid2, pid4], views: 400 }
....
]
Photos
[
{id: pid1, cap:t1, auth: author1, path:p1, tags: ['bob','fun'], comments:40, views:2000, likes:0 }
{id: pid2, cap:t2, auth: author2, path:p2, tags: ['job','fun'], comments:50, views:50, likes:1, liker:[bob] }
{id: pid3, cap:t3, auth: author3, path:p3, tags: ['rob','fun'], comments:60, views: 6000, likes: 0 }
...
]
Comments
[
{id: oid1, photo_id: pid1, commenter: bob, text: photo is cool, likes: 1, likers: [john], replies: [{rep1}, {rep2}]}
{id: oid2, photo_id: pid1, commenter: bob, text: photo is nice, likes: 1, likers: [john], replies: [{rep1}, {rep2}]}
{id: oid3, photo_id: pid2, commenter: bob, text: photo is ok, likes: 2, likers: [john, bob], replies: [{rep1}]}
]
Queries:
Query 1: Show a list of popular albums (based on number of likes)
Query 2: Show a list of most discussed albums (based on number of
comments)
Query 3: Show a list of all albums of a given author on
user's page
Query 4: Show the album with all photos and all comments
(pull album details, show photo thumbnails of all photos in the
album, show all comments of selected photo
Query 5: Show a list of
related albums based on the tags of current album
Given the above schema and requirements, how should I model this in Cassandra?
As I have experience with both Cassandra and Mongo, I'll take a shot at this. The tricky part here, is that MongoDB allows for very loose restrictions around indexing and querying. Cassandra has a trickier model in that respect, but one that should perform fast, at scale, if created correctly. Also, the aspect of counting likes/views/comments on a photo or album can also get tricky, as you'll want to use Cassandra's counter type for that (which has its own challenges).
Disclaimer: Others may solve these problems differently. And I may choose to solve them differently if my first attempt didn't perform. But this is what I would start with.
To satisfy Query 3 I would create a query table called PhotoAlbumsByAuthor and query it like this:
CREATE TABLE PhotoAlbumsByAuthor (
photoalbumid uuid,
title text,
author text,
tags set<text>,
photos set<uuid>,
PRIMARY KEY(author,title,photoalbumid)
);
> SELECT * FROM photoalbumsbyauthor WHERE author='Malcolm Reynolds';
That will return all albums that the user Malcolm Reynolds has created, sorted by title (as title is the first clustering key).
For Query 4 I would create comments as a user defined type (UDT):
CREATE TYPE yourkeyspacename.comment (
commenter text,
commenttext text
);
Then I would create a query table called PhotosByAlbum and query it like this:
CREATE TABLE PhotosByAlbum (
photoalbumid uuid,
photoid uuid,
cap text,
auth text,
path text,
tags set<text>,
comments map<uuid,frozen <comment>>,
PRIMARY KEY(photoalbumid,photoid)
);
> SELECT * FROM PhotosByAlbum WHERE photoalbumid=a50aa80a-8714-44b4-9b97-43ec4b13daa6;
When you add a comment to this table, the uuid key of the map is the commentid. This way you can quickly grab all of the keys and/or values on your application side. In any case, this will return all photos for a given photoalbumid, along with any comments.
I would solve Query 5 in a similar way, by creating a query table (you should be noticing a pattern by now) called PhotoAlbumsByTag and query it like this:
CREATE TABLE PhotoAlbumsByTag (
tag text,
photoalbumid uuid,
title text,
author text,
photos set<uuid>,
PRIMARY KEY(tag,title,photoalbumid)
)
SELECT * FROM PhotoAlbumsByTag WHERE tag='family';
This will return all photo albums with the "family" tag. Note, that this is a denormalized structure of the tags set<text> used above, which means that a photo album will have one entry in this table for each tag it contains. I thought about possibly reusing one of the prior query tables with a secondary index on tags set<text> (as Cassandra now allows indexes on collections) but secondary indexes don't typically perform well. And you would still have to execute a query for each tag in the current album anyway (using a SELECT with the IN keyword is known to not perform well, either).
As for the first two queries, I would create specific tables to store the likes/views/comments counts like this:
CREATE TABLE PhotoCounters (
photoid uuid,
views counter,
comments counter,
likes counter,
PRIMARY KEY (photoid)
);
When using the counter type, Cassandra requires that the primary key and counters be the only columns in that table (can't mix counters with non-counter columns). And I would also process queries/reports on those offline, in an OLAP fashion, using Hadoop or Spark. Hope this helps.

Cassandra/NoSQL newbie: the right way to model?

as the title says I am fairly (read: completely) new to NoSQL DBS such as Cassandra. As many others, I learned RMDBS before. So I did a little reading on 'WTF is a super column' and other obvious google hits, but I am still not sure how to model this:
Say I want to save Users, as in username/password/name/etc... what if that user has like, a mobile phone and a landline telephone? is this the 'right' way to do it? (using the same abbreviated JSON style as seen on other sites)
Users: { // <-- this is the Users SuperColumnFamily, keyed by username
myuser: { // <-- this is a User SuperColumn
username = "myuser", // <-- this is the username Column
email = "myuser#googlemail.com",
...
},
...
}
Phone: { // <-- this is where the users phone numbers are stored
myuser: {
mobile = "0129386835235",
landline = "123876912384",
},
...
}
opinions/corrections please
First things first, don't use super columns. See:
http://www.quora.com/Cassandra-database/Why-is-it-bad-to-use-supercolumns-in-Cassandra
Now to answer your question. The example you described is easily modeled with just a regular column family:
Users: { <- This is the name of the column family
username1: { <- this is a row key within the column family, it is one of your usernames
email: user#email.com <- these are all of the columns within this row, they correspond to attributes for this user
mobile: ...
landline: ...
}
username2: { <- another row for a different user
email: diff#email.com
}
}
You can see the flexible schema above in that each row has a different set of columns for describing that user.
For more info on the cassandra data model I would recommend reading over http://www.datastax.com/docs/1.0/ddl/index