I'm developing an app with backend and I decided to try using Google App Engine for my backend. Since I'm really new on Google App Engine, I'm little bit confused with the logic.
Basically, I have a couple of model classes to represent my object types. Lets say one of them is User and another is Item. Users have items and an item can belong more than one user. So User X can have 25 items including Item A, and User Y can have totally different 20 items and also the Item A.
Right now my User class looks like this:
#Entity
public class User {
#Id private Long id;
private String name;
private String emailAddress;
private String photoURL;
//All getters and setters...
}
And my Item class is approximately same. One of my questions is, where should I add some kind of list, like a list of Items into User. And which annotation should I use? What will that annotation provide me as a result (a reference, an id or a complete object)?
Another question related to this is, in my endpoint class, how can I get a list of Items that a specific User has (or list of Users that owns a specific Item)?
One last totally unrelated question, should I do anything to make id auto increment or will it be automatic if I won't provide any id while inserting an item?
You can search in the datastore for 2 things: keys and indexed properties.
class Thing {
#Id Long id;
#Index String property;
}
At some point you save some entities
Thing thing1 = new Thing();
thing1.property = "yes";
Thing thing2 = new Thing();
thing2.property = "no";
ofy().save().entities(thing1, thing2).now();
Now you can search for all entities based on their indexed properties. E.g. for all things with property == "yes".
List<Thing> things = ofy().load().type(Thing.class).filter("property", "yes").list();
Would return exactly thing1.
The same works with Lists of properties. And it works with lists of references/keys to other properties.
class User {
#Id Long id;
#Index List<Key<Item>> items;
}
class Item {
#Id
Long id;
}
List<User> searchUsersWithItem(long itemId) {
Key<Item> itemKey = Key.create(Item.class, itemId);
return ofy().load().type(User.class).filter("items", itemKey).list();
}
List<User> searchUsersWithItem(Item item) {
return ofy().load().type(User.class).filter("items", item).list();
}
// just loads all the referenced items in the owner
List<Item> searchItemsWithOwner(User owner) {
return new ArrayList<Item>(ofy().load().<Item>values(owner.items).values());
}
filter works with refs, keys and entitiy instances.
To be found things must be indexed https://cloud.google.com/datastore/docs/concepts/indexes / https://github.com/objectify/objectify/wiki/Queries
What's left for you to decide is how you model your relation. There are multiple ways. A user that owns a set of items which can be owned by set of users is actually a many-to-many relation. You could represent it like
class User { List<Key<Item>> items; }
class Item { }
or
class User { }
class Item { List<Key<User>> owners; }
or
class User { List<Key<Item>> items; }
class Item { List<Key<User>> owners; }
or even
class User { }
class Item { }
class Ownership { Key<Item> item; Key<User> user; }
Each approach has it's ups and downs with respect to data consistency and searchability / performance. In the initial example it's trivial to search for all items of a user since all you have to to is to load that one user and you have the list of items. The other direction requires the query approach.
So with respect to search performance you benefit from having the list of owners in the items as well as the list of items in the user because that way you don't need queries at all. The big downside becomes data consistency. If you fail to update both user and item at the same time you can have items that believe to be owned by a user where the user thinks different.
The last approach, using an explicit "Ownership" entity is essentially the traditional pivot / junction table https://en.wikipedia.org/wiki/Many-to-many_%28data_model%29 that is the result of transforming a many-many relation into 2 one-many relations. Using that would result in easy consistency, but the worst query performance.
Parent relations can sometimes be useful but only if there is an actual 1 to many relation where the parent needs to exist.
Also note how keys are not foreign keys like in traditional SQL databases as they can exist without an entity. So you'll have to take care of consistency regardless of what you do.
Related
I have 2 classes:
public class ChatGroup{
final public String name;
private List<ChatContact> contacts;
/* ---getters/setters/constructors-- */
}
public class ChatContact implements Parcelable, Comparable {
final public String name;
final public String jid;
public Status status;
/* ---getters/setters/constructors-- */
}
Then I have a list of ChatGroup items:
List<ChatGroup> chatGroupList = .....;
As you can see every ChatGroup has a list of ChatContact., and what I need is to search inside chatGroupsList, for ChatContacts that matches a query (search by username).
A way I'm doing, is do an auxilar list, search for every group, and look "inside" for ever chatContact, if exist I add the group with the contact:
private List<ChatGroup> searchContacts(String query) {
List<ChatGroup> filteredContacts = new ArrayList<>();
for (ChatGroup chatGroup : chatGroupList) {
ChatGroup auxChatGroup = new ChatGroup(chatGroup.name);
for (ChatContact chatContact : chatGroup.getContacts()) {
if (chatContact.name.toLowerCase().contains(query)) {
auxChatGroup.addContact(chatContact);
}
}
if (auxChatGroup.getContacts().size() > 0)
filteredContacts.add(auxChatGroup);
}
for (ChatGroup chatGroup : filteredContacts) {
Collections.sort(chatGroup.getContacts());
}
return filteredContacts;
}
All of this works perfect. But right now, this list has few groups with few contacts each one, but in a future will be a high number of elements, and this could be a "slow" solution.
So my question is, there is another faster way to do this type of search?
Unfortunately, if you are seriously going to search for something like "a" and want everyone who has the letter A at any point in their name, that type of search does not index well.
But looking at your algorithm, I see a few possible improvements.
Initialize ChatGroup auxChatGroup = null and only create the object when you find a result that matches the filter. This will avoid creating a few unnecessary objects if you have lots of rooms.
Sorting the list of contacts every time you do a search seems like a lot of wasted effort. Using a sorted collection such as TreeSet could offer you a huge time savings on each search.
If the number of groups becomes huge, as in millions, then consider using a multi-threaded search.
Depending on your use case, it may be possible to return a filtered "view" instead of a snapshot. However that may add some complexity and possible gotchas.
I have this relationship in one table like this:
#ManyToOne
#JoinColumn(name="TEAM_HOME", nullable=false)
private Team team;
#Column(name="TEAM_AWAY", insertable=false, updatable=false)
private int teamAway;
And this relationship in other:
#XmlTransient
#OneToMany(mappedBy="team",fetch=FetchType.EAGER)
private Set<Result> result;
I want to reference one column from table Team multiple times in table Result, bet I don't know how. I have tried different ways but without success.
You will need two Relations from Team to Result:
#OneToMany(mappedBy="teamHome",...)
private Set<Result> resultsHome;
#OneToMany(mappedBy="teamAway",...)
private Set<Result> resultsAway;
but nothing stops you from adding methods like
public Set<Result> getResults() {
Set<Result> results = new HashSet<>();
results.addAll(resultsHome);
results.addAll(resultsAway);
return results;
}
public void addResult(Result result) {
if (result.teamHome == this) {
resultsHome.add(result);
} else {
resultsAway.add(result);
}
}
I guess this is not what you were hoping for. But putting all Results in the same collection would not be correct as it is something different. After all, you would probably want something like the inverse result for your "away"-matches in that collection.
You should really ask yourself if you really want those 2 Relations in your Team class. Wouldn't a DAO.getMatchesForTeam(...) serve the same purpose? When you really want to make a model of sports events, you might end up with plenty of relations in Team and it will clutter up your code. The #XMLTransient annotation already indicates that the results aren't that important to the team for them to be transported to the client.
Oh and btw: There is something between team and result: the match. The result should only hold the result. That way you can make a Result.inverse() method that will give you the result from the other team's perspective. But there is nothing like a Match.inverse() because the home team will always be the home team.
I have two classes. The OrderSlip class has a one-to-many relationship with orderedItemDescription.
class OrderSlip {
String employeeID
int serving
int tableNumber
static hasMany = [orderedItemDescription: OrderedItemDescription]
}
class OrderedItemDescription {
MenuItem menuItem
MenuItemProgressStatus progress//progress
String descriptionOfOrder
int quantity = 1
static belongsTo = OrderSlip
}
Now my problem is how do i iterate orderedItemDescription so that when i update my orderSlip i can add many orderedItemDescriptions along with its properties.
def updateOrderSlip(Long id) {
User currentUser = springSecurityService.currentUser
def orderSlipInstance = Table.get(id)
//other codes for orderedItemDescription here
orderSlipInstance.employeeID = currentUser.username
orderSlipInstance.serving= Integer.parseInt(params.serving)
orderSlipInstance.tableNumber= params.tableNumber
render(action:'server')
}
Im doing something like this in my gsp. im only adding data to the DOM with the add buttons. Then for the send order im hoping i can update it like the problem since im also adding many g:hiddenField for each orderedItemDescription in my summary
You should be persisting each new instance OrderedItemDescription somehow.
You can store it immediately in the DB upon click on add-button with the status flag set to incomplete. When you save the whole order, you must change the incomplete to complete.
Another option would be to keep the items in the http session. Upon send order you iterate through the in-session items and persist them all along with the order instance.
Both ways have advantages and drawbacks, but they both are useful.
I'm trying to merge these three objects into a single complex object:
public class Person {
private String name;
private List<Event> events;
// getters and setters
}
public class Event {
private String name;
private List<Gift> gifts;
// getters and setters
}
public class Gift {
private String name;
private String recipient;// the name of the person
private String eventName;
// getters and setters
}
My goal is to save the Person object in MongoDB using Morphia and this how I want my document laid out. I've created a document builder, of sorts, that combines lists of each object. Each Person gets a list of all Events, but can only receive specific Gifts. While my document builder does create a document that Morphia can persist, only the Gifts of that last recipient (sort order) are inserted into the Events for all Persons. Though for the correct Events.
public void merge() {
for (Person person : listOfPersons) {
for (Event event : listOfEvents) {
// somePersonsGifts: a sublist of gifts based on Event and Person.
List<Gift> somePersonsGifts = new ArrayList<Gift>();
for (Gift gift : listOfGifts) {
if (person.getName().equals(gift.getRecipient()) && gift.getEventName().equals(event.getName())) {
somePersonsGifts.add(gift);
}
}
event.setGifts(somePersonsGifts);
}
person.setEvents(listOfEvents)
}
}
If I modify the code slightly to process one person at a time by removing the outer loop and having the method take an argument for specific index of the Persons list:
public void merge(int p) {
Person person = listOfPersons.get(p);
//...and so on
I get one complete Person object with the correct gifts. If try to feed the this modified version into a loop, the problem comes back. I've tried using regular for-loops and synchronized collections. I've tried using Google Guava's ImmutableArrayList and still no luck. I know the problem is that I'm changing the lists while accessing them but I can't find anyway around it. I wrote a DAO that uses the MongoDB driver directly and it works properly, but it's a lot more code and quite ugly. I really want this approach to work, the answer is in front of me but I just can't see it. Any help would be greatly appreciated.
Here is your problem:
List<Gift> somePersonsGifts = new ArrayList<Gift>();
....
event.setGifts(somePersonsGifts);
You add the gifts only for one person; if you want to aggregate all the gifts into the event, re-use the existing list.
I don't know anything about MongoDB or Morphia but I suspect the problem is your use of the setters event.setGifts(somePersonsGifts) and person.setEvents(events). Your code does not seem to merge the existing gift and event lists with the ones you are calculating further in the loop, which is how you would want it to behave (if I understand the question correctly).
You should retrieve the allready existing gift list (and event list too) instead of overwriting them with empty new ones.
I don't know if the method merge() is inside the list but I assume that since you are using the list events here
person.setEvents(events);
Maybe you meant
person.setEvents(listOfEvents)
Notice that you are adding all the events to each person. If all the persons went to all the events, it is unnecessary to have the events inside the person.
Im using ORMLite in my Android app. I need to persist this class, which has a HashMap. What is a good way of persisting it? Its my first time trying to persist a HashMap, also first time with ORMLite so any advice would be greatly appreciated!
*Edit*
If that makes any difference, the Exercise class is simply a String (that also works as id in the database), and the Set class has an int id (which is also id in database), int weight and int reps.
#DatabaseTable
public class Workout {
#DatabaseField(generatedId = true)
int id;
#DatabaseField(canBeNull = false)
Date created;
/*
* The hashmap needs to be persisted somehow
*/
HashMap<Exercise, ArrayList<Set>> workoutMap;
public Workout() {
}
public Workout(HashMap<Exercise, ArrayList<Set>> workoutMap, Date created){
this.workoutMap = workoutMap;
this.created = created;
}
public void addExercise(Exercise e, ArrayList<Set> setList) {
workoutMap.put(e, setList);
}
...
}
Wow. Persisting a HashMap whose value is a List of Sets. Impressive.
So in ORMLite you can persist any Serializable field. Here's the documentation about the type and how you have to configure it:
http://ormlite.com/docs/serializable
So your field would look something like:
#DatabaseField(dataType = DataType.SERIALIZABLE)
Map<Exercise, List<Set>> workoutMap;
Please note that if the map is at all large then this will most likely not be very performant. Also, your Exercise class (and the List and Set classes) need to implement Serializable.
If you need to search this map, you might consider storing the values in the Set in another table in which case you might want to take a look at how ORMLite persists "foreign objects".