id attribute in nested Java class mapped as MongoDb ObjectId() - spring-data-mongodb

Following structure:
#Document
public class Palace {
#Id
private String id;
private Location location;
// additional attributes, getter, setter
}
.
public class Location {
private String id;
// additional attributes, getter, setter
}
As far as I understand the concept of ObjectId, there can be only one ObjectId (_id) in a MongoDB document and it must be at the root level of the document. But when inserting data via Spring Boot 1.5.14 I get the followng structure:
{
"_id" : ObjectId("5b05387a8db58e0001d38851"),
"location" : {
"_id" : ObjectId("5ae712d1b2b197000132cd9b"),
}
}
My question is: Is this the expected behaviour in spring-data-mongo? I would have expected the following structure:
{
"_id" : ObjectId("5b05387a8db58e0001d38851"),
"location" : {
"id" : "5ae712d1b2b197000132cd9b",
}
}
If I annotate the Location id with #Field
public class Location {
#Field("id")
private String id;
// additional attributes, getter, setter
}
then the Document is saved as expected, but querying with repository method
getPalaceByLocationId()
won't give any results.

Some insights into id field. id field is treated a bit differently in spring-data-mongodb.
Check out the documentation.
Extending the documentation for your case, when you declare an id like this
public class Location {
private String id;
// additional attributes, getter, setter
}
It is stored as _id in database. You can cross check by looking into the database. The same is true when you annotate it with #Id
{
"_id" : ObjectId("5b31fad36a19cc45db205056"),
"location" : {
"_id" : ObjectId("5ae712d1b2b197000132cd9b")
}
}
5ae712d1b2b197000132cd9b is the id you had set using location.setId(). Internally it is converted to ObjectId and stored as _id.
But when you add #Field annotation, things change. Let's assume you add an annotation like this
#Field("id")
private String id;
Then the document looks like this in database.
{
"_id" : ObjectId("5b31fad36a19cc45db205056"),
"location" : {
"id" : "5ae712d1b2b197000132cd9b"
}
}
With this the problem is you cannot retrieve by id. Because when you write
findPalaceByLocationId or findByLocation_Id it(spring-data) tries to look for _id field which doesn't exist.
Only way around this is just use it like this
#Field
private String id;
or just
private String id;
This will create _id in database and you can do findByLocationId
I know it is a bit sketchy. But that is how it works.
And regarding
As far as I understand the concept of ObjectId, there can be only one
ObjectId (_id) in a MongoDB document and it must be at the root level
of the document.
that is incorrect.
A small info table regarding mapping
Field definition Resulting Id-Fieldname in MongoDB
String id _id
#Field String id _id
#Field('x') String id x
#Field('id') String id id (InAccessible)
#Id String x _id
#Field('x') #Id String x _id

Related

Writing parquet output with selected attributes from Bean

I have a bean class
#Getter
#Setter
public class Employee {
String id;
String name;
String depart;
String address;
final String pipe= "|";
#Override
public String toString() {
return id +pipe+ name +pipe+depart;
}
}
And I have a JavaRDD<Employee> emprdd;
and when I do the emprdd.saveAsText(path);. I get the output as based on the toString method.
Now I wanted to write into the parquet format after converting it to the dataframe but I need only (id,name,depart). I tried sqlContext.createDataframe(rdd,Employee.class); (syntax ignored), but I dont need all the properties.
Can anyone guide me through this. (This is a sample , I have bean class with 350+ attributes)

SQLKata return complex object with Include/IncludeMany

I need to return from a query a list of students where each students has assignments.
class Student {
public string Name{ get; set; }
public IEnumerable<Assignment> Assignments{ get; set; }
}
class Assignment {
public string Title { get; set; }
}
so tried to create two queries and then use include/includemany but i get other errors (The given key 'Id' was not present in the dictionary)
var students = db.Query('Student')
.Select(...)
.Where()
var assignments= db.Query('Assignment')
.Select(...)
.Where()
var result = students.IncludeMany('Assignments', assignments).Get()
but this does not work. How can you properly create complex/nested objects with SQLKata?
I asume your example is complete, but you should add the primairy and foreign key properties on the queries. (They are not in your classes so I added them based on conventions sql kata uses)
var students = db.Query("Student")
.Select("Id", "Name");
var assignments = db.Query("Assignment")
.Select("StudentId", "Title");
var result = students
.IncludeMany("Assignments", assignments)
.Get();
If they are not specified as an argument to the IncludeMany method, SqlKata will use the "Id" field as primary key and the "StudentId" field as the foreign key in the relationship. You can add these as extra arguments to the IncludeMany method.
var result = students
.IncludeMany("Assignments", assignments, "StudentId", "Id")
.Get();
Please note that at this moment SqlKata doesn't seem to support this on the generic method Get<> GetAsync<>.

Objectify index is not created

I try to use objectify on google app engine standart environment and get exception. My classes look like this:
#Entity
public class Company {
#Id Long id;
#Index String companyName;
public Company() {
}
public Company(Long id, String companyName) {
this.id=id;
this.companyName = companyName;
}
}
#Entity
public class CompanyProject {
#Id Long id;
#Index String projectName;
#Parent Key<Company> owner;
public String cost;
public CompanyProject() {
}
public CompanyProject(long userId, String projectName) {
this();
this.projectName = projectName;
owner = Key.create(Company.class, userId); // Creating the Ancestor key
}
}
When I query data like this:
Key<Company> theUser = Key.create(Company.class, 1);
Iterable<CompanyProject> projects = ObjectifyService.ofy().load().type(CompanyProject.class).ancestor(theUser).order("projectName").list();
I get exception
com.google.cloud.datastore.DatastoreException: no matching index found. recommended index is:
- kind: CompanyProject
ancestor: yes
properties:
- name: projectName
Without order("projectName") query works just fine. Removed all entities of this kind from datastore, than added new, still get this exception. I use Gradle, not Maven if this matters. Maybe should be extra build step to create indexes or smth.
You are filtering on multiple properties (well, ancestor counts as 'another property') so you need a multi-property index defined in datastore-indexes.xml.
See the official documentation: https://cloud.google.com/appengine/docs/standard/java/config/indexconfig

Dapper multi-mapping not returning null object when splitOn column is not in child object

I'm using dapper 1.50.2 with MySQL and running into a problem trying to map a left outer join child object to its parent. If I split on a column alias that doesn't actually exist in the child object, Dapper always creates a child object with default properties, even when there is nothing in the left join.
I created a simple example to demonstrate this:
public class ParentRecord
{
public string MemberID { get; set; }
public ChildRecord Child { get; set; }
}
public class ChildRecord
{
//public string Split { get; set; }
public string SomeField { get; set; }
}
using (MySqlConnection connection = new MySqlConnection(connectionString))
{
ParentRecord result = connection.Query<ParentRecord, ChildRecord, ParentRecord>(
#"SELECT 'FakeID' AS MemberID, NULL AS Split, NULL AS SomeField",
(mt, crt) =>
{
mt.Child = crt;
return mt;
},
splitOn: "Split").Single();
}
I would expect this to result a ParentRecord with the Child property set to null, but the Child property is set to a ChildRecord with all default fields.
If I uncomment the Split property in ChildRecord, or if I split on SomeField, this works as I'd expect.
Are there any good workarounds for this?
In the actual query I'm dealing with, there are multiple primary key and foreign key fields with the same names and I'd rather not change the property names in the POCOs to be unique. I'd prefer to be able to use column aliases that are just there to split on. I know this isn't normally how Dapper is set to up to work.
Any help would be appreciated, thanks.
This happen because the object Child initialize for default when you attribute the ctr param. Then the solution that I did implement was:
ParentRecord result = connection.Query<ParentRecord, ChildRecord, ParentRecord>(
#"SELECT 'FakeID' AS MemberID, NULL AS Split, NULL AS SomeField",
(mt, crt) =>
{
if (crt.SomeField != null){ mt.Child = crt; }
return mt;
},
splitOn: "Split").Single();

How to parse dynamic object in mvc?

I am working on ASP.NET MVC4.0.
My string is posting like this from view :-
[{"name":"AddressNumber","value":"1"},{"name":"OrganizationProd","value":""},{"name":"ClientId","value":""},{"name":"ProductId","value":""},{"name":"TaxId1","value":""},{"name":"TaxId2","value":""},{"name":"LaborID","value":"0"}]
And below is my controller's action method for that,which is receiving the input :-
[AllowAnonymous]
[HttpPost]
public ActionResult UpdateProducts(string ModelString){
}
And below is the string which i am getting in action(in ModelString variable):-
[{"name":"AddressNumber","value":"1"},{"name":"OrganizationProd","value":""},{"name":"ClientId","value":""},{"name":"ProductId","value":""},{"name":"TaxId1","value":""},{"name":"TaxId2","value":""},{"name":"LaborID","value":"0"}]
And after that i am deserializing the string like that :-
var sear = new JavaScriptSerializer();
var dictDynamic = sear.Deserialize<dynamic>(ModelString);
And i am getting the dynamic array in dictDynamic variable.And now i want to get the properties by its name not by indexing from dictDynamic object.
Currently i am getting the properties by indexing like this :-
dictDynamic[0]["value"]
dictDynamic[1]["value"]
But i want to parse it by properties name like this :-
dictDynamic["Name"]["value"]
dictDynamic["Description"]["value"]
Can anyone help me out on this ?
You could use ViewModel on server side, not sending model string.
You create ViewModel like this:
class ProductViewModel {
public int AddressNumber { get; set; }
public int ProductId { get; set; }
...
}
Then change your controller method:
[AllowAnonymous]
[HttpPost]
public ActionResult UpdateProducts(ProductViewModel vm){
...
}
And from your View you'll send json object like this:
{
"AddressNumber":"10",
"OrganizationProd":"1",
"ClientId":"1",
"ProductId":"1",
"TaxId1":"23",
"TaxId2":"23",
"LaborID":"10"
}
This will automaticaly bind your values from View to ViewModel on controller, and you can than use ViewModel object in your code, and you then have strongly typed entity.
Instead of this:
dictDynamic["AddressNumber"]
dictDynamic["OrganizationProd"]
now you can write this:
vm.AddressNumber
vm.OrganizationProd
You need to pass a JavaScript object to your function instead of an array. Array is not the correct data structure to use in this case. Objects have keys and values. The keys will be AddressNumber, OrganizationProd, ClientId, ProductId, TaxId1 etc. Their values will be 1, "", "0" etc.
For instance, for your example, this will be your object:
{
"AddressNumber":1,
"OrganizationProd":"",
"ClientId":"",
"ProductId":"",
"TaxId1":"",
"TaxId2":"",
"LaborID":0
}
You deserialize it like you do now:
var s = "{\"AddressNumber\":1, \"OrganizationProd\":\"\", \"ClientId\":\"\", \"ProductId\":\"\", \"TaxId1\":\"\", \"TaxId2\":\"\", \"LaborID\":0}";
var sear = new JavaScriptSerializer();
var dictDynamic = sear.Deserialize<dynamic>(s);
Once you deserialize, you will be able to reference the values like this:
dictDynamic["AddressNumber"]
dictDynamic["OrganizationProd"]

Resources