My mobile device gets data from an API server and the result sets can get very complex. Here is an example of the structure being returned:
{
"Home" : {
"Person" : [
{
"name" : "Joe",
"age" : 16,
"gender" : "Male",
"occupation" : null
}, {
"name" : "Jane",
"age" : 23,
"gender" : "Female",
"occupation" : {
"position" : "Barista",
"years" : 4,
"type" : "Temporary"
}
}, {
"name" : "Samantha",
"age" : 7,
"gender" : "Female",
"occupation" : null
}
]
}
}
This is just ONE object. Where in this case I have created 2 models:
Home & Person...BUT there is another object within Person called Occupation but I refuse to create a model for this.
The current way I persist this data is that I persist the Person Object first since I will define it as a Foreign entity of Home and then when I save the Home object, I will use the reference of the Person to persist my Home object.
Is there a way to avoid doing all of this? Can I just have one model called Person instead while being able to persist all the inner objects?
Is it a bad idea to create another Model for small object that probably has only one or two properties inside?
Home & Person...BUT there is another object within Person called Occupation but I refuse to create a model for this.
Why do you refuse? There is little to no penalty for doing so and a lot of benefits of encapsulation. Your JSON is treating occupation as a class given the { ... } and so should you in Java.
For example, if you instead tried to embed the fields from occupation in Person, how would a null occupation field be represented? Would it be setting all of the fields to be null and 0? Then how would the following be represented?
"occupation" : {
"position" : null,
"years" : 0,
"type" : null
}
This is a hack a best.
Is it a bad idea to create another Model for small object that probably has only one or two properties inside?
Certainly not. It is the right thing to do. I would use some sort of json -> Java converter and just create your Person to match the JSON, field by field. The time you take to work on your data model will be repaid when you work with the object and your representation matches your model completely.
Related
I am starting with Spring and MongoDB. I have seen that there are several methods to insert and / or update. I have also read some posts here explaining some concepts. But I don't quite understand them.
Correct me if I'm wrong or if things are missing.
Update (): only updates an object and only works if it has an id.
Upsert (): Makes an Update if the object exists (it must have an id) or inserts it if it does not exist.
Insert (): You don't need an id and add a Document to the collection.
save (): I don't really know the difference with an insert.
If there are more methods that work similarly and that I forgot to mention, I would appreciate if you could explain it as well.
Save
The save method saves the document to collection for the entity type of the given object. When we pass collection name, then document is saved in the specified collection, even if entity is of different type.
`Student ram = new Student(101,"Ram",20);
mongoTemplate.save(ram);
Person newPerson = new Person(102, "Shyam");
mongoTemplate.save(newPerson, "student"); `
After Save
{ "_id" : 101, "name" : "Ram", "age" : 20, "_class" : "com.concretepage.entity.Student" }
{ "_id" : 102, "name" : "Shyam", "_class" : "com.concretepage.entity.Person" }
Insert
To insert a document into MongoDB collection, the MongoTemplate provides insert method. Find the code to insert one document.
Student ram = new Student(1,"Ram",20);
mongoTemplate.insert(ram);
After Insert
{ "_id" : 1, "name" : "Ram", "age" : 20, "_class" : "com.concretepage.entity.Student" }
for more info, Refer below link
https://www.concretepage.com/spring-5/spring-data-mongotemplate
This question already has answers here:
Are JSON schemas necessary for defining the structure of a JSON?
(2 answers)
Closed 4 years ago.
I have come across an API that returns different types of value for "fieldValue" as follow:
{
"id" : 123,
"fieldType" : "text",
"fieldValue" : "some test"
}
{
"id" : 456,
"fieldType" : "checkbox",
"fieldValue" :
[
{
"checkboxId" : 1,
"name" : "Homer"
},
{
"checkboxId" : 2,
"name" : "Marge"
}
]
}
{
"id" : 789,
"fieldType" : "Select",
"fieldValue" : {
"selectId" : 3,
"value" : "Lisa"
}
}
I am using GSON and it doesn't like that the fact that "fieldValue" can be either String or an object or an array. I have written custom deserializer to parse it.
My question is that does JSON specification allow JSON object to have loosely defined value type meaning fieldValue type can be either String, Array of objects or an object?
The JSON specification only mentions the syntax of the JSON object, not the semantics. So the parser will not check if a given value should be of type A or B. It will read whatever values are available and will report errors if the syntax is broken. It is up to your application to verify the contents and react accordingly.
No it does not even it sometimes might be a good policy. JSON specification itself does not tell what are the objects presented in JSON. That is in the responsibility of the API specification.
The API you have seems to rely on the discriminator field fieldType that I guess you are using to make your custom deserializing.
That is also the strategy that RuntimeTypeAdapterFactory uses which might be good solution also in your case.
Let us consider the below API.
/school/{schoolId or schoolName}
/school/{schoolId or schoolName}/students/{studentId or studentName}
I have to develop the above API's. What is the best way to achieve this in spring REST? I can simply get the URL path variable as a string and check whether it is an id or name and then process it.
But is there any other better solution for this?
There is an elephant in the room, School Name or Student Name is not unique. So it is not advisable to use it in place of id, else you will burn your hand.
/school/{schoolId or schoolName}
I would split this into 2 end points
1. /schools/{schoolId}
if found, return the school entity
HTTP-200
{
"id" : "id-123",
"name" : "worlds best school ever",
"address" : "123 somestreet, some city, some state"
}
if not found,
HTTP 404 and an empty body
2. /schools?name="worlds best school" (other optional things, like sort by, pagination etc)
if at least one entity found per search criteria
HTTP-200
[
{
"id" : "id-123",
"name" : "worlds best school ever",
"address" : "123 somestreet, some city, some state"
}
]
If NO entity found per search criteria
HTTP-200
[]
/school/{schoolId or schoolName}/students/{studentId or studentName}
This begs a question, does one student belong to more than one school, I don't think so. If that's the case, /school/{schoolId or schoolName} is an unnecessary/redundant information. If so, I would change it as
1. /students/{studentId}
if found
HTTP-200
{
"id" : "studentid-345",
"name" : "Albert Einstein",
"school_id" : "id-123"
}
if not found
HTTP 404 with empty body
2. /students?name="Albert Einstein" (other optional things, like sort by, pagination etc)
If at least one entity found per search criteria
HTTP-200
[
{
"id" : "studentid-345",
"name" : "Albert Einstein",
"school_id" : "id-123"
}
]
If NO entity found per search criteria
HTTP-200
[]
I'm trying determine if there is a way using spring-mongodb (or even using the mongo java API), to upsert a document containing a list such that the elements of the list are always a union of the values upserted.
Suppose I have the following Classes (made up to simplify things):
public class Patron {
private String name;
private String address;
private List<Book> booksRead;
// assume gets/sets
}
public class Book{
private String title;
private String author;
// assume gets/sets
}
Further, let's assume I get the updates on only the latest books read, but I want to keep in the DB the full list of all books read. So what I'd like to do is insert a Patron(with booksRead) if it doesn't exist or update their booksRead if the Patron already does exist.
So, the first upsert 'John Doe' is not in collection so the document is inserted and looks like this:
"_id": ObjectId("553450062ef7b63435ec1f57"),
"name" : "John Doe"
"address" : "123 Oak st, Anytown, NY, 13760"
"booksRead" : [
{
"title" : "Grapes of Wrath",
"author" : "John Steinbeck"
},
{
"title" : "Creatures Great and Small",
"author" : "James Herriot"
}
]
John re-reads 'Grapes of Wrath' and also reads 'Of Mice and Men'. An insert is attempted passing 2 books as books read, but I'd like to only insert 'Of Mice and Men' to the read list so the document looks like:
"_id": ObjectId("553450062ef7b63435ec1f57"),
"name" : "John Doe"
"address" : "123 Oak st, Anytown, NY, 13760"
"booksRead" : [
{
"title" : "Grapes of Wrath",
"author" : "John Steinbeck"
},
{
"title" : "Creatures Great and Small",
"author" : "James Herriot"
},
{
"title" : "Of Mice and Men",
"author" : "John Steinbeck"
}
]
Everything I've tried seems to point to needing to separate calls one for the insert and one for the update. Update.set works for the initial load (insert) but replaces full list on second update. $addToSet works on update, but complains about trying to insert into 'non-array' on initial insert.
UPDATE:
This appears to be an issue with Spring-mongodb. I can achieve the above with mongo java api calls, it just fails when using the spring equivalents. (at least up to spring-data-mongodb 1.6.2)
Easiest way to accomplish would be to remove the old entry and then re-add it with the added books. Ideally, this should be done transactionally.
I think you could do this using the addToSet operator.
You can find documentation on MongoDB's web site :
http://docs.mongodb.org/manual/reference/operator/update/addToSet/#up._S_addToSet
It is used with either update() or findAndModify() methods.
Issue appears to be at Spring layer. While I get errors upserting using its FindAndModify command, I don't have a issue with $addToSet with the mongo DBCollection.findAndModify and DBCollection.update methods.
I've been trying to wrap around my head around this...
I have the following referenced documents in a users and groups collection.
group documents
{
"_id" : ObjectId("52eabc9914cc8d6cc1e6f723"),
"className" : "org.xxxxxx.sms.core.domain.Group",
"name" : "CE Play group",
"description" : "CE Play group",
"creationdate" : ISODate("2014-01-30T20:56:57.848Z"),
"user" : DBRef("users", ObjectId("52ea69c714ccd207329b2476"))
}
{
"_id" : ObjectId("52ea69c714ccd207329b2477"),
"className" : "org.xxxxxx.sms.core.domain.Group",
"name" : "Default",
"description" : "Default sms group",
"creationdate" : ISODate("2014-01-30T15:03:35.916Z"),
"user" : DBRef("users", ObjectId("52ea69c714ccd207329b2476"))
}
users document
{
"_id" : ObjectId("52ea69c714ccd207329b2476"),
"className" : "org.xxxxxx.core.domain.User",
"username" : "jomski2009",
"firstname" : "Jome",
"lastname" : "Akpoduado",
"email" : "jomea#example.com",
"active" : true,
"usergroups" : [
DBRef("groups", ObjectId("52ea69c714ccd207329b2477")),
DBRef("groups", ObjectId("52eabc9914cc8d6cc1e6f723"))
]
}
I have a Morphia Datastore singleton object which has been set up in a class to retrieve a user group and perform some manipulations on. Say I wanted to fetch the group named "Default" with a supplied username "jomski2009" , how would I achieve this in Morphia without fetching the usergroups as a list and iterating over the list of groups just to find the group I want?
Thanks.
DBRef is used in Mongo as a client side concept. MongoDB does not do joins so the purpose of DBRef is to hand over to the client a location from which to fetch the required object. This is handled by various client libraries in different ways.
If it is feasible for your application to do so, you might want to take a look at using Embedded Annotation instead of the Reference type. Or at the very least to include a list of usernames in your Group objects in addition to the object references. This will allow you to filter these in queries.
Also it is worthwhile looking at moving any unique identifiers like "username" to the _id field of the document as long as it is always unique. Primary key lookups are always faster.