I use jhipster to generate 2 entity Employee and Department
and relationship:
Employee.java
#ManyToOne
#JsonIgnoreProperties(value = "employees", allowSetters = true)
private Department department;
Department.java
#OneToMany(mappedBy = "department")
#Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private Set<Employee> employees = new HashSet<>();
and when I called api/employees , I had:
{
"id": 1,
"code": "FU_EMP_DTP726",
"name": "Concrete system",
"birthDate": "2020-11-30",
"address": "Granite frame",
"phone": "0152104977",
"salary": 65309.0,
"department": {
"id": 1,
"code": "FU_DE_787778",
"name": "Tools"
}
}
but with api/departments , I had:
{
"id": 1,
"code": "FU_DE_787778",
"name": "Tools",
"employees": null
}
I didn't know why department's employees is null
In bidirectional one-to-many relationship case jhipster do not get all ‘child’ form “One” side,because “N+1” problem .
For your purpose you need to be modify the generated code。use EntityGraph
modify repository、add a “employees” Set into ” DepartmentDTO“ and Mapper
How to see both sides in one-to-many relationship generated by JHipsterHow to see both sides in one-to-many relationship generated by JHipster
Related
am generating enties some thing like below, Person
entity Employee {
firstName String
lastName String
}
entity Role {
Name String
}
relationship OneToMany {
Employee{role} to Role{employee required}
}
The generated Employee does not include the RoleDTO. I would like it to return the following:
{
"id": 1,
"firstName": "John",
"lastName": "Doe",
"roles":
[
{
"id": 1
"name": "Admin"
},
{
"id": 2
"name": "Collector"
}
]
}
But the DTO generated are working other ways round.
Role is having List of EmployeeDTO's.
Manual Code change is one option,but in my project i have alsmot 40 entities and this mdoification has to be done for almost all entities. In that case Jhispter is generated code is not usefull any more.
everybody, could you please tell me
I use the objection.js (ORM) library for knex.js
joined Users and Roles tables, everything is great.
I use it:
const users = await User.query().eager().roles')
I'm getting it:
{"id":1, "email": "ann#mail.com", "password": "qwe", "role":1, "roles":{"id":1, "name": "admin"}}
But how can I get a flat structure? :
{"id":1, "email": "ann#mail.com", "password": "qwe", "role "admin"}
I would normally expect Objection to do this with a HasOneRelation relationship, so I'm going to assume that's what you have here:
{
"id": 1,
"email": "ann#mail.com",
"password": "qwe",
"role": 1,
"roles": {
"id":1,
"name": "admin"
}
}
If your users can only have one role at a time, that should be fine. To get a flat structure, you can either post-process it in JavaScript:
return {
...user,
role: roles.name
}
or create a virtual attribute:
export default class User extends Model {
static get virtualAttributes() {
return ['roleName'];
}
roleName() {
return this.roles.name;
}
// ...
}
This will not stop the roles object from being added, but it'll provide a first-level alias for the name in the JSON.
Say I just wanted to get a list of all transactions that involved a specific asset (assuming I would need a query for this?). Should I use the TransactionRegistry or the Historian? What's the difference?
we have a current issue open for Historian to show history of changes/ deltas for a particular asset - https://github.com/hyperledger/composer/issues/991 As a workaround you can do the following - so for sample network trade-network with an asset Commodity (and a transaction class 'Trade') you could create a query eg:
query selectTransaction {description: "choose specific commodity asset"
statement: SELECT org.acme.biznet.Trade
WHERE (commodity == _$commodity ) }
On the difference:
Historian records all transaction activities (eg. create Asset, create participant, create identity etc etc - and also business network specific custom transactions like 'TransferAsset' or 'PlaceOrder') including (where assets / participants are concerned) what changed.
For the TransactionRegistry itself (ie a particular class - say 'TransferAsset' or 'PlaceOrder') this is stored in the Transaction registry for that class - you may have many Transaction classes in your business network. But nothing in here would be related to other activities, such as system activities, that also recorded in Historian records.
to query - you would do something like this (in a query file for example):
query myTransactions{
description: "return all transactions made (ie system transactions)"
statement: SELECT org.acme.sample.PlaceOrder
}
ie SELECT org.acme.sample.NAME_OF_TRANSACTION_CLASS
For Historian queries - eg
SELECT org.hyperledger.composer.system.HistorianRecord WHERE (transactionType == 'myTranType'
see more examples here -> https://www.ibm.com/developerworks/cloud/library/cl-create-powerful-blockchain-queries-with-hyperledger-composer/index.html
furthermore, to see the transaction data (deltas) for the asset id you're zoning in on - ie available through the transactionInvoked field of the transaction class (eg org.acme.trading.Trade transaction class). you could use REST APIs with loopback filters -eg either (both return a promise below):
return this.httpClient.get('http://localhost:3000/api/Trade?filter=%7B%22include%22%3A%22resolve%22%7D', {withCredentials: true}).toPromise();`
or
return this.httpClient.get('http://localhost:3000/api/Trade?filter=%7B%22include%22%3A%22resolve%22%7D').toPromise();
which has the {"include":"resolve"} filter to resolve relationships in the transaction class - each resolved transaction has the transaction deltas. Then you could look for the asset id in question.
Sample unresolved transaction class (below, followed by resolved txn class):
Not resolved:
[
{
"$class": "org.acme.mynetwork.Trade",
"commodity": "resource:org.acme.mynetwork.Commodity#1",
"newOwner": "resource:org.acme.mynetwork.Trader#2",
"transactionId": "354dca97fc6ac00aabbd923883e3ec2a3d09b8c75a54a8f536a88b6df31e8a0f",
"timestamp": "2018-03-23T12:02:11.228Z"
},
{
"$class": "org.acme.mynetwork.Trade",
"commodity": "resource:org.acme.mynetwork.Commodity#2",
"newOwner": "resource:org.acme.mynetwork.Trader#1",
"transactionId": "9da43acca718633ac8870e6ea34c3c9f481194e48bcdba42673570177091809f",
"timestamp": "2018-03-23T12:02:31.294Z"
}
]
Resolved with {"include":"resolve"} as a filter:
[
{
"$class": "org.acme.mynetwork.Trade",
"commodity": {
"$class": "org.acme.mynetwork.Commodity",
"tradingSymbol": "1",
"description": "werwer",
"mainExchange": "wrrewer",
"quantity": 10,
"owner": {
"$class": "org.acme.mynetwork.Trader",
"tradeId": "2",
"firstName": "tes2t",
"lastName": "test"
}
},
"newOwner": {
"$class": "org.acme.mynetwork.Trader",
"tradeId": "2",
"firstName": "tes2t",
"lastName": "test"
},
"transactionId": "354dca97fc6ac00aabbd923883e3ec2a3d09b8c75a54a8f536a88b6df31e8a0f",
"timestamp": "2018-03-23T12:02:11.228Z"
},
{
"$class": "org.acme.mynetwork.Trade",
"commodity": {
"$class": "org.acme.mynetwork.Commodity",
"tradingSymbol": "2",
"description": "Ut fugiat.",
"mainExchange": "ACE2",
"quantity": 10,
"owner": {
"$class": "org.acme.mynetwork.Trader",
"tradeId": "1",
"firstName": "test",
"lastName": "test"
}
},
"newOwner": {
"$class": "org.acme.mynetwork.Trader",
"tradeId": "1",
"firstName": "test",
"lastName": "test"
},
"transactionId": "9da43acca718633ac8870e6ea34c3c9f481194e48bcdba42673570177091809f",
"timestamp": "2018-03-23T12:02:31.294Z"
}
]
I'm building a rails app off a tutorial I found and am trying to use a JSONAPI Active Model Serializer to generate a response of that format.
In an initializer, I've put:
ActiveModelSerializers.config.adapter = :json_api
In my gemfile:
gem 'active_model_serializers', '~> 0.10.0.rc3'
I'm expecting two resource level keys, data and relationships as per the json-api specs. However, is not separating out the relationship to its own object. This is my request for /contacts.
{
"data": [
{
"id": "1",
"type": "contacts",
"attributes": {
"family-name": "La",
"given-names": "ch",
"company": {
"id": 1,
"name": "Lorem Inc",
"phone": "+1 (415) 555-1234",
"email": "office#lorem.inc",
"website": "www.lorem.inc",
"address": "213 Main St. 94063 San Francisco, CA",
"customer_id": "10001",
"additional_info": "",
"created_at": "2017-01-31T05:47:02.024Z",
"updated_at": "2017-01-31T05:47:02.024Z"
},
"title": null,
"phone": null,
"email": null,
"website": null,
"address": null,
"customer-id": null,
"additional-info": null
}
}
]
}
Company is a belong_to for contacts. Here are my serializers.
class CompanySerializer < ActiveModel::Serializer
attributes :id, :name, :phone, :email, :website, :address, :customer_id, :additional_info
end
class ContactSerializer < ActiveModel::Serializer
attributes :id, :family_name, :given_names, :company, :title, :phone, :email, :website, :address, :customer_id, :additional_info
end
These are my models:
class Contact < ApplicationRecord
belongs_to :company
validates :family_name, presence: true
validates :given_names, presence: true
end
class Company < ApplicationRecord
validates :name, presence: true
end
Everything else is just default generated code from rails cli. I'm not sure what else I need to add here because it is my understanding the default rails behavior is to generate a response that will show everything in the serializer. I'm assuming that jsonapi adapter should separate that out for me.
What else do I need to do to get the jsonapi adapter working properly?
I was missing relationships in my serializer!
Also answered in x post
https://github.com/rails-api/active_model_serializers/issues/2044
I'm trying to model document a hierarchy in CouchDB to use in my system, which is conceptually similar to a blog. Each blog post belongs to at least one category and each category can have many posts. Categories are hierarchical, meaning that if a post belongs to CatB in the hierarchy "CatA->CatB" ("CatB is in CatA)", it belongs also to CatA.
Users must be able to quickly find all post in a category (and all its children).
Solution 1
Each document of the post type contains a "category" array representing its position in the hierarchy (see 2).
{
"_id": "8e7a440862347a22f4a1b2ca7f000e83",
"type": "post",
"author": "dexter",
"title": "Hello",
"category":["OO","Programming","C++"]
}
Solution 2
Each document of the post type contains the "category" string representing its path in the hierarchy (see 4).
{
"_id": "8e7a440862347a22f4a1b2ca7f000e83",
"type": "post",
"author": "dexter",
"title": "Hello",
"category": "OO/Programming/C++"
}
Solution 3
Each document of the post type contains its parent "category" id representing its path in the hierarchy (see 3). A hierarchical category structure is built through linked "category" document types.
{
"_id": "8e7a440862347a22f4a1b2ca7f000e83",
"type": "post",
"author": "dexter",
"title": "Hello",
"category_id": "3"
}
{
"_id": "1",
"type": "category",
"name": "OO"
}
{
"_id": "2",
"type": "category",
"name": "Programming",
"parent": "1"
}
{
"_id": "3",
"type": "category",
"name": "C++",
"parent": "2"
}
Question
What's the best way to store this kind of relationship in CouchDB? What's the most efficient solution in terms of disk space, scalability and retrieval speed?
Can such a relation be modelled to take into account localised category names?
Disclaimer
I know this question has been asked a few times already here on SO, but it seems there's no definitive answer to it nor an answer which deals with the pros and cons of each solution. Sorry for the length of the question :)
Read so far
CouchDB - The Definitive Guide
Storing Hierarchical Data in CouchDB
Retrieving Hierarchical/Nested Data From CouchDB
Using CouchDB group_level for hierarchical data
There's no right answer to this question, hence the lack of a definitive answer. It mostly depends on what kind of usage you want to optimize for.
You state that retrieval speed of documents that belong to a certain category (and their children) is most important. The first two solutions allow you to create a view that emits a blog post multiple times, once for each category in the chain from the leaf to the root. Thus selecting all documents can be done using a single (and thus fast) query. The only difference of second solution to first solution is that you move the parsing of the category "path" into components from the code that inserts the document to the map function of the view. I would prefer the first solution as it's simpler to implement the map function and a bit more flexible (e.g. it allows a category's name to contain a slash character).
In your scenario you probably also want to create a reduced view which counts the number of blog posts for each category. This is very simple with either of these solutions. With a fitting reduction function, the number of post in every category can be retrieved using a single request.
A downside of the first two solutions is that renaming or moving a category from one parent to another requires every document to be updated. The third solution allows that without touching the documents. But from the description of your scenario I assume that retrieval by category is very frequent and category renaming/moving is very rare.
Solution 4 I propose a fourth solution where blog post documents hold references to category documents but still reference all the ancestors of the post's category. This allows categories to be renamed without touching the blog posts and allows you to store additional metadata with a category (e.g. translations of the category name or a description):
{
"_id": "8e7a440862347a22f4a1b2ca7f000e83",
"type": "post",
"author": "dexter",
"title": "Hello",
"category_ids": [3, 2, 1]
}
{
"_id": "1",
"type": "category",
"name": "OO"
}
{
"_id": "2",
"type": "category",
"name": "Programming",
"parent": "1"
}
{
"_id": "3",
"type": "category",
"name": "C++",
"parent": "2"
}
You will still have to store the parents of categories with the categories, which is duplicating data in the posts, to allow categories to be traversed (e.g. for displaying a tree of categories for navigation).
You can extend this solution or any of your solutions to allow a post to be categorized under multiple categories, or a category to have multiple parents. When a post is categorized in multiple categories, you will need to store the union of the ancestors of each category in the post's document while preserving the categories selected by the author to allow them to be displayed with the post or edited later.
Lets assume that there is an additional category named "Ajax" with anchestors "JavaScript", "Programming" and "OO". To simplify the following example, I've chosen the document IDs of the categories to equal the category's name.
{
"_id": "8e7a440862347a22f4a1b2ca7f000e83",
"type": "post",
"author": "dexter",
"title": "Hello",
"category_ids": ["C++", "Ajax"],
"category_anchestor_ids": ["C++", "Programming", "OO", "Ajax", "JavaScript"]
}
To allow a category to have multiple parents, just store multiple parent IDs with a category. You will need to eliminate duplicates while finding all the ancestors of a category.
View for Solution 4 Suppose you want to get all the blog posts for a specific category. We will use a database with the following sample data:
{ "_id": "100", "type": "category", "name": "OO" }
{ "_id": "101", "type": "category", "name": "Programming", "parent_id": "100" }
{ "_id": "102", "type": "category", "name": "C++", "parent_id": "101" }
{ "_id": "103", "type": "category", "name": "JavaScript", "parent_id": "101" }
{ "_id": "104", "type": "category", "name": "AJAX", "parent_id": "103" }
{ "_id": "200", "type": "post", "title": "OO Post", "category_id": "104", "category_anchestor_ids": ["100"] }
{ "_id": "201", "type": "post", "title": "Programming Post", "category_id": "101", "category_anchestor_ids": ["101", "100"] }
{ "_id": "202", "type": "post", "title": "C++ Post", "category_id": "102", "category_anchestor_ids": ["102", "101", "100"] }
{ "_id": "203", "type": "post", "title": "AJAX Post", "category_id": "104", "category_anchestor_ids": ["104", "103", "101", "100"] }
In addition to that, we use a view called posts_by_category in a design document called _design/blog with the the following map function:
function (doc) {
if (doc.type == 'post') {
for (i in doc.category_anchestor_ids) {
emit([doc.category_anchestor_ids[i]], doc)
}
}
}
Then we can get all the posts in the Programming category (which has ID "101") or one of it's subcategories using a GET requests to the following URL.
http://localhost:5984/so/_design/blog/_view/posts_by_category?reduce=false&key=["101"]
This will return a view result with the keys set to the category ID and the values set to the post documents. The same view can also be used to get a summary list of all categories and the number of post in that category and it's children. We add the following reduce function to the view:
function (keys, values, rereduce) {
if (rereduce) {
return sum(values)
} else {
return values.length
}
}
And then we use the following URL:
http://localhost:5984/so/_design/blog/_view/posts_by_category?group_level=1
This will return a reduced view result with the keys again set to the category ID and the values set to the number of posts in each category. In this example, the categories name's would have to be fetched separately but it is possible to create view where each row in the reduced view result already contains the category name.