Kmongo: how to add unique field - mongodb

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))

Related

problem with Neo4j query, calling foreach for the previous match results

I have this class (node):
case class User(name: String, lastName: String, age: Int, city: String)
I'm trying to add relationships for an existing node (name=Alice) with this query:
MATCH (user:User {name: $name}),(friend:User) WHERE friend.name IN [$fNames] WITH user,friend CREATE (user)-[f:FRIEND]->(friend) RETURN f
I'm passing in fNames = List(Bob, David), and all nodes exist in db (for Alice, Bob and David).
This query returns no results, and that one neither:
MATCH (user:User {name: $name})-[f:FRIEND]->(friend:User) RETURN friend
I tried using FOREACH in the first query but was getting errors (something about Nodes vs Paths).
My goal is to add FRIEND relationships (edges) between Alice and all User nodes (vertices) with "name" field in fNames list.
How can I do that using a single cipher query?
Thanks,
Joe
Since $fNames is already a list, [$fNames] would enclose that list within another list -- which is not what you intended. You should just use $fNames as-is:
MATCH (user:User {name: $name}), (friend:User)
WHERE friend.name IN $fNames
CREATE (user)-[f:FRIEND]->(friend)
RETURN f
Also, to speed up this query, you should consider creating an index on :User(name).

Mongodb changing the unique key

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.

f# Insert on MongoDB using Records

I've been trying for a while to insert on MongoDB using only records with no success.
My problem is that I want to create a simple insert function which I send a generic type and it is inserted into the database.
Like so.
let insert (value: 'a) =
let collection = MongoClient().GetDatabase("db").GetCollection<'a> "col"
collection.InsertOne value
From this function, I tried inserting the following records.
// Error that it can't set the Id
type t1 = {
Id: ObjectId
Text: string
}
// Creates the record perfectly but doesn't generate a new Id
type t2 = {
Id: string
Text: string
}
// Creates the record and autogenerates the Id but doesn't insert the Text, and there are two Ids (_id, Id#)
type t3 = {
mutable Id: ObjectId
Text: string
}
// Creates the record and autogenerates the Id but for every property it generates two on MongoDB (_id, Id#, Text, Text#)
type t4 = {
mutable Id: ObjectId
mutable Text: string
}
So does anyone can think of a solution for this or am I stuck having to use a class.
// Works!!!
type t5() =
member val Id = ObjectId.Empty with get, set
member val Name = "" with get, set
Also, does anyone has any Idea of why when the C# MongoDB library translates the mutable he gets the property with the # at the end?
I would be fine with having all my properties set as mutable, although this wouldn't be my first choice, having he create multiple properties on the DB is quite bad.
You could try annotating your records with CLIMutable (and no mutable fields).
The #s end up in the DB because MongoDB using reflection and F# implementing mutable with backing fields fieldName#

Play, Scala, Ebean: Checking if relationship exists

Consider a hypothetical situation where I have two models: Company and User, defined like so
case class Company(name: String) extends Model {
#Id
var id: Long = _
#OneToMany(fetch = FetchType.LAZY)
var admins: util.List[User] = new util.ArrayList[User]()
}
case class User(email: String) {
#Id
var id: Long = _
}
Next, I have a request coming in and I want to check if user_id 200 is an admin of a company_id 100. The obvious solution is to fetch the company with that id, and then check iteratively in the admins list if a user_id but that is quite inefficient. What's the best way to go about this?
I think that the easiest way to solve this problem is to simply add relation from user to company. If you need calling such query then such relation has sense:
Here is relation added to User class:
#ManyToOne
var company:Company = _
And here is example how to check if user(id=200) is admin of company(id=100):
val user = Ebean.find(classOf[User], 200L)
println(user.company.id==100L)
Second option here is using RawSql. We can do it in a way similar to this:
val sql="select u.company_id as id from user u where u.id=200"
val rawSql = RawSqlBuilder.parse(sql).create()
val query = Ebean.find(classOf[Company])
query.setRawSql(rawSql)
val list = query.findList()
println(list(0).id==100L)

Overwrite duplicate values in Scala List

I have the following Comet server:
object UserServer extends LiftActor with ListenerManager {
private var users: List[UserItem] = Nil
def createUpdate = users
override def lowPriority = {
case UserItem(user, room, active, stamp) => {
users :+= UserItem(user, room, active, stamp);
updateListeners()
}
}
}
Currently each time the form is submitted a new UserItem is added to the users list. What I'm trying to do is each time the server receives a new user, instead of concatenating to the list it should overwrite an existing item with the same user and room handle.
So if the list contains the following:
UserItem("jam_2323", "demo-room", "James", "1320073365")
UserItem("jim_4533", "demo-room", "Jim", "1320073365")
The next time these users submit the form the above two items in the list will be replaced with the new stamp value:
UserItem("jam_2323", "demo-room", "James", "1320073435")
UserItem("jim_4533", "demo-room", "Jim", "1320073435")
Thanks in advance for any help, much appreciated :)
This sounds like a classic case where you need a map, rather than a list. I don't know about the details of Lift / Comet, but I guess you want something like
case class User(id: String)
case class Activity(room: String, active: String, stamp: String)
var lastUserActivity = Map[User, Activity]()
...
case UserItem(id, room, active, stamp) => {
lastUserActivity += User(id) -> Activity(room, active, stamp)
}
If you adjust UserItem from being a straight case class (I assume) to being one where you have overriden equals to ignore the stamp field, then you could make users into a Set.
Alternatively, you could filter the List to remove the old matching values before appending.