Tips for mapping data structure String[ ] [ ] into model class - dozer

i need your help and tips for mapping data with dozer.
Following situation is existent:
getting String [ ] [ ] from a service layer.
first [ ] represents rows
second [ ] represents columns
fyi : This data depends on request parameter "table".
// exemplary service call
String[] [] result = service.getData (String table);
String[] [] result = service.getData (Enum table);
Depending on parameter table, another destination entity has to be used.
example :
String[][] for parameter "table_a" should use destination object "com.foo.TableA"
String[][] for parameter "table_b" should use destination object "com.bar.TableB"
The Service layer can not be changed. The layer always returns this data struture as result.
My problem is that i have no good starting point for this situation.
I have to iterate over the first [] and then depends on parameter table , a mapping has to be done.
// useful way?
List<com.foo.TableA> mappedResult = new ArrayList<com.foo.TableA>();
for (int i = 0; i < result.size ; i++) {
String[] rowData = result[i];
mappedResult.add(mapper.map(rowData; com.foo.TableA.class));
}
// mapping.xml
<mappings>
<mapping>
<class-a>how to configure String[] ? </class-a>
<class-b> com.foo.TableA </class-b>
<field>
<a>src[0] </a> // 1st column
<b>column_id</b>
</field>
<mapping>
</mappings>
Any hints are welcome!
Thank you in advance

For accessing data from the String-array, i have used a wrapper
public class RowDataWrapper {
private final row[];
public RowDataWrapper (String[] row) {
this.row=row;
}
}
and its usage within mapping.xml
<mappings>
<mapping>
<class-a>foo.bar.RowDataWrapper</class-a>
<class-b>com.foo.TableA</class-b>
<field>
<a>row[0] </a> // 1st column
<b>column_id</b>
</field>
<mapping>
</mappings>

Related

How can I use the entitymanager metamodel to find fields with certain properties

I wonder if it is possible to use the entitymanager metamodel to scan over loaded entities to identify fields with certain properties during a test. For example I want to see if any fields are defined as strings with a "max length of 1" to report them as possible "Char" type columns.
val model = em.metamodel
model.entities.forEach { entity ->
entity.attributes.filter { !it.isCollection }.filter { it.javaType.isAssignableFrom(String::class.java) }
.forEach { attribute ->
attribute // <- TODO here be dragons
}
}"

How to get data sorted with relation using TypeORM?

I want to get user data with relation sorted but with this code it just sort the user but I want to sort data that have relation with user I'm using eager, could any one help me ?
getUerWithId(pramas: string): Observable<userEntity[]> {
return from(this.userRepository.find({
where: [
{ id: pramas }
],
order:{
id:'DESC'
}
}))
}
With the repository, I don't know if it is possible, but you can try with query builder to get ordered relations. Follow example bellow:
this.userRepository.createQueryBuilder()
.innerJoinAndSelect("User.notification", "Notification")
.orderBy({'Notification.createdAt': 'DESC'})
.where("User.id = :id", {
id: Number(id),
})
.getOne();
Remember to put the right relationship for your entity, and right names for your properties.

DynamoDB - How to upsert nested objects with updateItem

Hi I am newbie to dynamoDB. Below is the schema of the dynamo table
{
"user_id":1, // partition key
"dob":"1991-09-12", // sort key
"movies_watched":{
"1":{
"movie_name":"twilight",
"movie_released_year":"1990",
"movie_genre":"action"
},
"2":{
"movie_name":"harry potter",
"movie_released_year":"1996",
"movie_genre":"action"
},
"3":{
"movie_name":"lalaland",
"movie_released_year":"1998",
"movie_genre":"action"
},
"4":{
"movie_name":"serendipity",
"movie_released_year":"1999",
"movie_genre":"action"
}
}
..... 6 more attributes
}
I want to insert a new item if the item(that user id with dob) did not exist, otherwise add the movies to existing movies_watched map by checking if the movie is not already available the movies_watched map .
Currently, I am trying to use update(params) method.
Below is my approach:
function getInsertQuery (item) {
const exp = {
UpdateExpression: 'set',
ExpressionAttributeNames: {},
ExpressionAttributeValues: {}
}
Object.entries(item).forEach(([key, item]) => {
if (key !== 'user_id' && key !== 'dob' && key !== 'movies_watched') {
exp.UpdateExpression += ` #${key} = :${key},`
exp.ExpressionAttributeNames[`#${key}`] = key
exp.ExpressionAttributeValues[`:${key}`] = item
}
})
let i = 0
Object.entries(item. movies_watched).forEach(([key, item]) => {
exp.UpdateExpression += ` movies_watched.#uniqueID${i} = :uniqueID${i},`
exp.ExpressionAttributeNames[`#uniqueID${i}`] = key
exp.ExpressionAttributeValues[`:uniqueID${i}`] = item
i++
})
exp.UpdateExpression = exp.UpdateExpression.slice(0, -1)
return exp
}
The above method just creates update expression with expression names and values for all top level attributes as well as nested attributes (with document path).
It works well if the item is already available by updating movies_watched map. But throws exception if the item is not available and while inserting. Below is exception:
The document path provided in the update expression is invalid for update
However, I am still not sure how to check for duplicate movies in movies_watched map
Could someone guide me in right direction, any help is highly appreciated!
Thanks in advance
There is no way to do this, given your model, without reading an item from DDB before an update (at that point the process is trivial). If you don't want to impose this additional read capacity on your table for update, then you would need to re-design your data model:
You can change movies_watched to be a Set and hold references to movies. Caveat is that Set can contain only Numbers or Strings, thus you would have movie id or name or keep the data but as JSON Strings in your Set and then parse it back into JSON on read. With SET you can perform ADD operation on the movies_watched attribute. https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.ADD
You can go with single table design approach and have these movies watched as separate items with (PK:userId and SK:movie_id). To get a user you would perform a query and specify only PK=userId -> you will get a collection where one item is your user record and others are movies_watched. If you are new to DynamoDB and are learning the ropes, then I would suggest go with this approach. https://www.alexdebrie.com/posts/dynamodb-single-table/

Entity Core Insert Adding Duplicate Rows

I'm trying to inserts an object and it's coming from a JSON file. The object has relationships which I've configured in EF Core and show in the database properly.
Unfortunately some of the data has duplicate entries in it but the Save action only happens at the end - so instead of getting a single child I get two child object when only one should exist.
JArray obj2 = (JArray)result.SelectToken("resistances");
List<CardResistance> pCardResistances = new List<CardResistance>();
foreach (var result2 in obj2)
{
//Search for Resistance Value object and create if not exists
Resistance ResistanceObj = ctx.Resistances.SingleOrDefault(m => m.ResistanceValue.Equals((string)result2["value"]))
?? new Resistance
{
ResistanceValue = (string)result2["value"],
LastUpdateDate = DateTime.Now
};
pCardResistances.Add(
new PokemonCardResistance
{
PokemonCard = PokemonCardObj,
Resistance = ResistanceObj
}
);
}
CardObj.CardResistances = pCardResistances;
...
ctx.AddOrUpdate and ctx.SaveChanges occur later in the code.
If you have JSON with the:
"resistances": [
{
"type": "Value1",
"value": "-30"
},
{
"type": "Value1",
"value": "-30"
}
]
In it what's happening is both rows get inserted for the entity (caused as the Save happens right at the end of all of the object data being populated. How can I save just the child object after each loop so it won't insert this data twice?
Your problem is to do with how you are checking for existence:
List<CardResistance> pCardResistances = new List<CardResistance>();
foreach (var result2 in obj2)
{
//Search for Resistance Value object and create if not exists.
//This will do a DB hit each time - if it doesn't exist and you have two
//items of the same value to add, then you will get that it does not exist
//each time.
Resistance ResistanceObj = ctx.Resistances.SingleOrDefault(m =>
m.ResistanceValue.Equals((string)result2["value"]))
?? new Resistance
{
ResistanceValue = (string)result2["value"],
LastUpdateDate = DateTime.Now
};
//One way to fix this would be to check that there is not already an item in this
//list that has this value set. And only either add/overwrite if that is the case.
pCardResistances.Add(
new PokemonCardResistance
{
PokemonCard = PokemonCardObj,
Resistance = ResistanceObj
}
);
}
CardObj.CardResistances = pCardResistances;
The other thing to note is more of a question as to what this should do exactly. Getting two updates in the same JSON for the same entity like this is a bit of an odd approach - is this valid?
If it is valid, then which one should 'win' (first or last in the json) and you may then be better placed altering the collection that you loop around (obj2 in your code - less said about your naming conventions the better...).

CouchDB Map/Reduce view query from Ektorp

I'm trying to execute a query from java against a Map/Reduce view I have created on the CouchDB.
My map function looks like the following:
function(doc) {
if(doc.type == 'SPECIFIC_DOC_TYPE_NAME' && doc.userID){
for(var g in doc.groupList){
emit([doc.userID,doc.groupList[g].name],1);
}
}
}
and Reduce function:
function (key, values, rereduce) {
return sum(values);
}
The view seems to be working when executed from the Futon interface (without keys specified though).
What I'm trying to do is to count number of some doc types belonging to a single group. I want to query that view using 'userID' and name of the group as a keys.
I'm using Ektorp library for managing CouchDB data, if I execute this query without keys it returns the scalar value, otherwise it just prints an error saying that for reduce query group=true must be specified.
I have tried the following:
ViewQuery query = createQuery("some_doc_name");
List<String> keys = new ArrayList<String>();
keys.add(grupaName);
keys.add(uzytkownikID);
query.group(true);
query.groupLevel(2);
query.dbPath(db.path());
query.designDocId(stdDesignDocumentId);
query.keys(keys);
ViewResult r = db.queryView(query);
return r.getRows().get(0).getValueAsInt();
above example works without 'keys' specified.
I have other queries working with ComplexKey like eg:
ComplexKey key = ComplexKey.of(userID);
return queryView("list_by_userID",key);
but this returns only a list of type T (List) - using CouchDbRepositorySupport of course - and cannot be used with reduce type queries (from what I know).
Is there any way to execute the query with reduce function specified and a complex key with 2 or more values using Ektorp library? Any examples highly appreciated.
Ok, I've found the solution using trial and error approach:
public int getNumberOfDocsAssigned(String userID, String groupName) {
ViewQuery query = createQuery("list_by_userID")
.group(true)
.dbPath(db.path())
.designDocId(stdDesignDocumentId)
.key(new String[]{userID,groupName});
ViewResult r = db.queryView(query);
return r.getRows().get(0).getValueAsInt();
}
So, the point is to send the complex key (not keys) actually as a single (but complex) key containing the String array, for some reason method '.keys(...)' didn't work for me (it takes a Collection as an argument). (for explanation on difference between .key() and .keys() see Hendy's answer)
This method counts all documents assigned to the specific user (specified by 'userID') and specific group (specified by 'groupName').
Hope that helps anybody executing map/reduce queries for retrieving scalar values from CouchDB using Ektorp query.
Addition to Kris's answer:
Note that ViewQuery.keys() is used when you want to query for documents matching a set of keys, not for finding document(s) with a complex key.
Like Kris's answer, the following samples will get document(s) matching the specified key (not "keys")
viewQuery.key("hello"); // simple key
viewQuery.key(documentSlug); // simple key
viewQuery.key(new String[] { userID, groupName }); // complex key, using array
viewQuery.key(ComplexKey.of(userID, groupName)); // complex key, using ComplexKey
The following samples, on the other hand, will get document(s) matching the specified keys, where each key may be either a simple key or a complex key:
// simple key: in essence, same as using .key()
viewQuery.keys(ImmutableSet.of("hello"));
viewQuery.keys(ImmutableSet.of(documentSlug1));
// simple keys
viewQuery.keys(ImmutableSet.of("hello", "world"));
viewQuery.keys(ImmutableSet.of(documentSlug1, documentSlug2));
// complex key: in essence, same as using .key()
viewQuery.keys(ImmutableSet.of(
new String[] { "hello", "world" } ));
viewQuery.keys(ImmutableSet.of(
new String[] { userID1, groupName1 } ));
// complex keys
viewQuery.keys(ImmutableSet.of(
new String[] { "hello", "world" },
new String[] { "Mary", "Jane" } ));
viewQuery.keys(ImmutableSet.of(
new String[] { userID1, groupName1 },
new String[] { userID2, groupName2 } ));
// a simple key and a complex key. while technically possible,
// I don't think anybody actually does this
viewQuery.keys(ImmutableSet.of(
"hello",
new String[] { "Mary", "Jane" } ));
Note: ImmutableSet.of() is from guava library.
new Object[] { ... } seems to have same behavior as ComplexKey.of( ... )
Also, there are startKey() and endKey() for querying using partial key.
To send an empty object {}, use ComplexKey.emptyObject(). (only useful for partial key querying)