Should I normalize the users profile_image_url links and the fields e.g. profile_background_color that users can customize their profiles?
Have one single User class with everything.
class User {
String profile_big_url
String profile_thumb_url
String profile_background_color
String profile_text_color
String profile_link_color
String profile_sidebar_fill_color
String profile_sidebar_border_color
String profile_background_image_url
String profile_background_tile
}
Or three classes
class User{
Photo photo
Profile profile
}
class Photo{
User user
String profile_big_url
String profile_thumb_url
}
class Profile{
User user
String profile_background_color
String profile_text_color
String profile_link_color
String profile_sidebar_fill_color
String profile_sidebar_border_color
String profile_background_image_url
String profile_background_tile
}
Which is better regarding scale, performance, modification?
I wouldn't worry about it. Splitting the tables results in smaller selects but these are just short strings, so the cost of pulling in more data than you need is minor. Joins can be expensive, but again, these would be simple ones.
An alternative would be to leave everything in one table, but split the class logically into three just in the code, using components. See http://grails.org/doc/latest/guide/5.%20Object%20Relational%20Mapping%20%28GORM%29.html#5.2.2%20Composition%20in%20GORM
Related
I have recently started to use Spring Data MongoDB and I wonder if there is any way to avoid writing entities' attributes explicitly as they are stored in the database. For example, given the following class representing a MongoDB collection:
public class Employee {
#Id
public String id;
private double salary;
...
}
If I want to make a query using MongoTemplate like:
public List findEmployeeBySalaryRange(double salary) {
Query query = new Query();
query.addCriteria(Criteria.where("salary").lt(salary));
...
}
I would like to avoid writing "salary", since that will make the code harder to maintain in the future in case the field name changes. I am thinking of something like getting the field name from the class attribute, but I am not quite sure how. Is there a way to do it? I have looked into the documentation but did not find anything related unless I missed it.
Thanks in advance.
You may create a Utility Class to store all database field names, use #Field annotation on field with constant from that class and use that constant in query to avoid error prone hardcoded Strings.
In Employee Model
#Field(DbFields.SALARY)
private double salary;
In Query,
query.addCriteria(Criteria.where(DbFields.SALARY).lt(salary));
In DbFields Utility class
public static final String SALARY = "salary";
If we have an object in groovy, for example Customer[name, email, phone] and a String in the form
String infoLine = "Stanislav,stanislav#stackoverflow.com,004612345678"
What is the easiest way to parse that string and populate the fields of that object?
(The example string we can split, that is why the question is from array of String)
Assuming you have a constructor
Customer(String name, String email, String phone)
You can do:
new Customer(*infoLine.split(','))
If you don't want to write a constructor, you can get Groovy to create one for you:
import groovy.transform.*
#TupleConstructor
class Customer {
String name
String email
String phone
}
String infoLine = "Stanislav,stanislav#stackoverflow.com,004612345678"
new Customer(*infoLine.split(','))
Or even better, #Immutable as this makes the properties final
#Immutable
class Customer {
String name
String email
String phone
}
Another option (assuming your fields are defined in the order they appear in the string, and there are no other fields), would be to generate a map of [name: 'aaa', emai... etc, and tell groovy to convert the map to a Customer like:
class Customer {
String name
String email
String phone
}
String infoLine = "Stanislav,stanislav#stackoverflow.com,004612345678"
def customer = [
Customer.declaredFields.findAll { !it.synthetic }*.name,
infoLine.split(',')
].transpose().collectEntries() as Customer
But this feels kinda brittle, and it's probably quicker to add the annotation or constructor.
I'm coding an app/game where user inputs his own rules on textfields stored in a SQFLite database.
Rules are related to a friend, so it's in a class Friend, with an id, name, rule0, rule1, rule2...rule9. Which is REALLY annoying for a lot of reasons, but I think I need it to be in Friend class so it get stored with the right friend.id.
I got the obvious error 'only static members can be accessed on initializers' when i try
List<Strings> rules = [rule0, rule1];
or
List<dynamic> rules = [...];
Does anyone knows how I could fix this ?
Create a new class Rules and each rule has ID related to the right friend ?
Or if I can just create a dynamic list whithin Friend ?
Really lost on this. Any help is welcome.
class Friend {
int id;
String name;
String pic;
bool play;
String rule0;
String rule1;
String rule2;
String rule3;
String rule4;
String rule5;
String rule6;
String rule7;
String rule8;
String rule9;
Friend();
// i have then a fromMap() and a toMap() for the database
}
I'm not completely sure, but it seems like you're having trouble with both the SQL aspect as well as the class representation of your data.
If you know for sure there will only be a set number of rules, you could approach it in the way you currently have. The addition I'd do is to make a 'getter' function for the list of rules i.e.
List<String> get rules => [rule1, rule2, rule2, ...].where((item) => item != null).toList()
Note that if you were using a constructor, you could instead build the list in the constructor as follows:
class Friend {
int id;
String name;
String pic;
bool play;
String rule0;
String rule1;
String rule2;
String rule3;
String rule4;
String rule5;
String rule6;
String rule7;
String rule8;
String rule9;
List<String> rules;
Friend(
{this.id,
this.name,
this.pic,
this.play,
this.rule0,
this.rule1,
this.rule2,
this.rule3,
this.rule4,
this.rule5,
this.rule6,
this.rule7,
this.rule8,
this.rule9})
: rules = [
rule0,
rule1,
rule2,
rule3,
rule4,
rule5,
rule6,
rule7,
rule8,
rule9,
].where((item) => item != null).toList(growable: false);
}
I wouldn't recommend that though (or if you do, you should probably be making either rule* or rules private or maintaining state somehow). The list of strings doesn't store references to the rule0, rule1, ... strings but rather stores the strings themselves. You could get around this by making a Rule class and making rule0-9 final, but that's getting a bit complicated.
It most likely makes more sense for this to be a dynamic list of items. SQL doesn't handle that, so you're going to have to make a separate table for rules.
You'll have two tables something like this, where id are both PRIMARY KEYs and friendId is a FOREIGN KEY:
Friend(id, name, pic, play) and Rule(id, friendId, text)
Saving and updating will become more complicated now. You'll have to be more careful about maintaining the list of rules in the database, but it isn't all that difficult. If you know that you'll only be writing to the list from one location (i.e. one class), you could keep the difference between what the class's current state and the initial state were, but realistically unless you're dealing with 10's of thousands of rules, it probably won't matter appreciably.
How to dynamize cucumber value and pass that into the login page, for example:
If I use Adam in the cucumber scenario then it should automatically use adam login details and other information and if i use another name it should use that person's information. So i do not have to manually enter it in every step defination. How Can i achieve it?
Scenario: Add an item to shopping bag to place the order using mastercard
Given that "Adam" is logged in to his account
When he searches and adds an item from "men" section to his shopping bag
Then he can place the order
export class LoginUser implements Task {
static called(name: string): LoginUser {
return new LoginUser(name);
}
performAs(actor: PerformsTasks): PromiseLike<void> {
return actor.attemptsTo(
Click.on(homePageElementsMap.lnk_login),
Enter.theValue('test#test.com').into(loginPageElementsMap.txt_login_email),
Enter.theValue('password111').into(loginPageElementsMap.txt_login_pwd),
Click.on(loginPageElementsMap.btn_login)
);
}
constructor(private name: string) {
}
}
disclaimer: this is a java answer, I misread the tag. I am sure you can something similar for js.
For our tests we have a variety of users as well and we use a java class with userdata for this, set up as maps.
We pass the value to a static method public Map<String, String> getUserData(String username) which contains a switch for the variety of users we have, each user is given a map of data we want to use in the tests. The users each have a private Map in the class containing login information.
Of course, there are more scalable ways, this one just gives us the flexibility of a single public getUserData(userName) and behind this we can do with the storage whatever we want.
Short answer: static java code containing user information for a variety of usernames.
Is this the only way to do it? - http://docs.behat.org/en/v2.5/guides/1.gherkin.html
This is my first question on any of these websites so pardon my unprofessionalism.
I use playframework with SIENA module (with GAE) and I came accross the following problem:
Given 3 entities:
public class Meeting extends Model{
#Id
public Long id;
public String place;
#Owned
Many<MeetingUser> users;
.
.
.
}
public class User extends Model{
#Id
public Long id;
public String firstName;
public String lastName;
#Owned
Many<MeetingUser> meetings;
.
.
.
}
public class MeetingUser extends Model{
#Id
public Long id;
public Meeting meeting;
public User user;
.
.
.
public User getUser(){
return Model.all(User.class).filter("id", user).get();
}
public Meeting getMeeting(){
return Model.all(Meeting.class).filter("id", meeting).get();
}
}
For instance I am listing a meeting and all their users:
public static void meetingInfo(Long meetingId){
Meeting meeting = Models.all(Meeting.class).filter("id",meetingId);
List<MeetingUser> meetingusers = meeting.asList();
List<User> users = new ArrayList<User>();
for(MeetingUser mu: meetingusers){
users.add(mu.getUser());
}
render(users);
}
This is done(is there any better way here?) however when it comes to filtering (especially dynamic filtering for many many fields) I can not use the Query's filter method on the MeetingUser as I need to filter on a MeetingUser's field's field (firstName). The same problem arise for ordering. I need the solution for both problems.
I hope my problem is clear and I appreciate any kind of help here.
Remember that you are in GAE which is a NoSQL DB.
So you can't do Join request as in RDBMS.
Yet, this is not really the pb you have so this was just to be sure you are aware of it ;)
So if you want to find the person having given firstname in a given meeting, can you try the following:
List<MeetingUser> meetingusers = meeting.users.asQuery().filter("firstname", "XXX");
(you can also order)
Nevertheless, knowing that you can't join, remember that you can't write a query searching for a meeting in which there are users whose firstname is XXX as it would require some joins and it doesn't exist in GAE. In this case, you need to change your model following NoSQL philosophy but this is another subject
regards
Let's try to give a way to do what you want...
Your relation is a Many-to-Many which is always the worst case :)
You want to filter Meeting by User's firstname.
It requires a join request which is not possible in GAE. In this case, you must change your model by denormalizing it (sometimes use redundancy also) and manage the join by yourself. Actually, you must do the job of the RDBMS by yourself. It seems overkill but in fact, it's quite easy. The only drawback is that you must perform several requests to the DB. NoSQL means No Schema (& No Join) so there are a few drawbacks but it allows to scale and to manage huge data load... it depends on your needs :)
The choice you did to create the MeetingUser which is a "joined" table and a kind of denormalization is good in GAE because it allows to manage the join yourself.
Solution:
// fetch users by firstname
List<User> users = users.all().filter("firstName", "John").fetch();
// fetch meetingusers associated to these users (verify the "IN" operator works because I didn't use that for a long time and don't remember if it works with this syntax)
List<MeetingUser> meetingusers = MeetingUser.all().filter("user IN", users);
// now you must fetch the whole meeting because in MeetingUser, only the Meeting ID is stored (other fields are Null or O)
List<Meeting> meetings = new ArrayList<Meeting>()
for(MeetingUsers mu:meetingusers) {
meetings.add(meetingusers.meeting);
}
// use the batch feature to fetch all objects
Meeting.batch(Meeting.class).get(meetings);
// you have your meetings
Hope this helps!