Is it possible to implement with Django Restframework? - mysql

I'm making API Server with DRF(DB is MySQL).
Now I made some system similar with facebook's like.
First, below is my Database Structure.
[user table]
userkey(PK)
username
[article table]
articleNo(PK)
userkey(FK to user)
content
[like table]
articleNo
userkey(FK to user)
When user click the "Like" buttons, articleNo and User's key will be inserted into like table.
Currently, When I access to /article/, shows below result.
{
"articleNo": 1,
"userkey": "22222",
"content": "test1",
"date": "2018-02-14T22:34:36.673805+09:00"
},
{
"articleNo": 2,
"userkey": "11111",
"content": "test2",
"date": "2018-02-15T22:34:36.673805+09:00"
},
...
...
If like table has two row like this,
+-----------+---------+
| articleNo | userkey |
+-----------+---------+
| 1 | 11111 |
| 1 | 22222 |
+-----------+---------+
It means that 11111 and 22222 user likes articleNo==1.
So When user access to /article?userkey=11111, What I would like instead as output is something like:
{
"articleNo": 1,
"userkey": "22222",
"content": "test1",
"isLiked": "true", // add this line
"date": "2018-02-14T22:34:36.673805+09:00"
},
{
"articleNo": 2,
"userkey": "11111",
"content": "test2",
"isLiked": "false", // add this line
"date": "2018-02-15T22:34:36.673805+09:00"
},
...
...
Is it possible to implement with DRF?
Thanks.

Yes, this can be done entirely on the ORM level, by using Django 1.8 conditional expressions
Having the following model structure (some example values):
class User(models.Model):
userkey = models.AutoField(primary_key=True)
username = models.CharField(max_length=255)
class Article(models.Model):
articleNo = models.AutoField(primary_key=True)
user = models.ForeignKey(User)
content = models.TextField()
class Like(models.Model):
article = models.ForeignKey(Article)
user = models.ForeignKey(User)
To demonstrate how this works, I created some example data:
john = User.objects.create(userkey=1, username='John')
alice = User.objects.create(userkey=2, username='Alice')
john_article = Article.objects.create(articleNo=1, user=john, content='Hi, I am John!')
alice_article = Article.objects.create(articleNo=2, user=alice, content='Hi, I am John!')
alice_likes_john_article = Like.objects.create(user=alice, article=john_article)
alice_likes_her_article = Like.objects.create(user=alice, article=alice_article)
john_likes_his_article = Like.objects.create(user=john, article=john_article)
You could achieve what you want on the ORM level:
articles = Article.objects.all().annotate(
like_count=Sum(
Case(
When(like__user=john, then=1),
default=0,
output_field=IntegerField(),
)
),
).annotate(
likes=Case(
When(like_count__gt=0, then=True),
default=False,
output_field=BooleanField()
)
)
(If somebody knows a simpler way than the above, I would be happy to learn as well)
Now every Article instance in the articles queryset will have two additional attributes: likes_count with the number of likes an article has received from John, and likes, a boolean, indicating if John likes it or not. Obviously you're interested in the latter.
Just override the get_queryset() method of your Article ViewSet, and then add an additional field to the Serializer of your Article class.
Also, you probably need to somehow pass the user instance (or the id) for the filter, but that can be done in various ways, including (for example) reading the query params inside the get_queryset method.

Related

Gorm - query on one to many relation does not return required results

In relation store has many products.
I created the struct like this:
type Store1 struct {
StoreSeq uint `json:"storeSeq" gorm:"primaryKey; column:store_seq"`
NickName string `json:"nickName" gorm:"column:nick_name"`
RegDate *domain.CTime `json:"regDate" gorm:"column:reg_date"`
Product1 []Product1 `json:"products" gorm:"foreignKey:ProductSeq"`
}
func (*Store1) TableName() string {
return "store"
}
type Product1 struct {
ProductSeq uint `json:"productSeq"`
ProductTitle string `json:"productTitle"`
RegDate *domain.CTime `json:"regDate"`
StoreSeq *uint `json:"store_seq" `
}
func (*Product1) TableName() string {
return "product"
}
and I queried it like this:
pro := new(entity.Product1)
store := new(entity.Store1)
orm.GetData().
Model(pro).
Preload("Product1").
Joins("left join store on store.store_seq = product.store_seq").
Where("store.store_seq = ?", 1).
Find(&store)
In my database table has data like this
STORE
1 testStore 2022-03-01 23:19:18
PRODUCT
1 1 test 2022-03-01 23:19:18
2 1 testaaa 2022-03-01 23:19:18
I expect
"storeSeq": 1,
"nickName": "",
"regDate": "2022-03-01 23:19:18",
"products": [
{
"productSeq": 1,
"productTitle": "test",
"regDate": "2022-03-01 23:19:18",
"store_seq": 1
},
{
"productSeq": 2,
"productTitle": "testaaa",
"regDate": "2022-03-01 23:19:18",
"store_seq": 1
}
]
but it only returns one result:
"storeSeq": 1,
"nickName": "",
"regDate": "2022-03-01 23:19:18",
"products": [
{
"productSeq": 1,
"productTitle": "test",
"regDate": "2022-03-01 23:19:18",
"store_seq": 1
}
]
I checked the SQL query then I found that it executes two SQL queries
[1.725ms] [rows:2] SELECT `product`.`product_seq`,`product`.`product_title`,`product`.`reg_date`,`product`.`store_seq` FROM `product` WHERE `product`.`product_seq` = 1
AND
[6.370ms] [rows:1] SELECT `product`.`product_seq`,`product`.`product_title`,`product`.`reg_date`,`product`.`store_seq` FROM `product` left join store on store.store_seq = product.store_seq WHERE store.store_seq = 1
I don't know why it executes the first SQL query; I want it to execute the second query only.
I have no idea and this is my firstime to use Golang with gorm with serverless framework
I found out that I make wrong releation between product and store
store has many produts so I have to relation product1 []Product1 foriegnKey
with storeSeq but i set foriegnkey as product_seq
and i also find out that execute two queries it because of preload option.

Karate API framework how to match the response values with the table columns?

I have below API response sample
{
"items": [
{
"id":11,
"name": "SMITH",
"prefix": "SAM",
"code": "SSO"
},
{
"id":10,
"name": "James",
"prefix": "JAM",
"code": "BBC"
}
]
}
As per above response, my tests says that whenever I hit the API request the 11th ID would be of SMITH and 10th id would be JAMES
So what I thought to store this in a table and assert against the actual response
* table person
| id | name |
| 11 | SMITH |
| 10 | James |
| 9 | RIO |
Now how would I match one by one ? like first it parse the first ID and first name from the API response and match with the Tables first ID and tables first name
Please share any convenient way of doing it from KARATE
There are a few possible ways, here is one:
* def lookup = { 11: 'SMITH', 10: 'James' }
* def items =
"""
[
{
"id":11,
"name":"SMITH",
"prefix":"SAM",
"code":"SSO"
},
{
"id":10,
"name":"James",
"prefix":"JAM",
"code":"BBC"
}
]
"""
* match each items contains { name: "#(lookup[_$.id+''])" }
And you already know how to use table instead of JSON.
Please read the docs and other stack-overflow answers to get more ideas.

NodeJS, MySQL - JSON Stringify - Advanced query

I have an object in the table column saved using JSON.stringify and it looks like this:
[{
"id": 1,
"uID": 10
}, {
"id": 2,
"uID": 10
}, {
"id": 3,
"uID": 94
}]
I need a query that will check if a given column contains values e.g.
I want uID = 10 and id = 2 will return
I want uID = 10 and id = 5 will not return it
I want uID = 10 and id = 2, uID = 94 and id = 0 will not return it
(because uID = 94 and id = 0 is not here)
Unless you are querying programmatically where you can parse the JSON and then do the logic, I would recommend something like this:
SELECT * FROM Table WHERE Column LIKE '%"id": 1,"uID": 10%'
The LIKE keyword allows us to use wildcards (%) but still do an exact text match for what we define.
Add a wildcard in the middle, but note that order matters with this strategy.
SELECT * FROM Table WHERE Column LIKE '%"id": 1%"id": 2%'
I need it to work backwards too:] e.g. I have
[{
"id": 1,
"uID": 10
}, {
"id": 2,
"uID": 55
}, {
"id": 3,
"uID": 94
}]
SELECT * FROM Table WHERE Column LIKE '%"uID": 55%"uID": 94%' <- will be working
SELECT * FROM Table WHERE Column LIKE '%"uID": 94%"uID": 55%' <- will be not working
Does not work "back"

SQL query with join to get nested array of objects

Summary: I'll start with a JSON schema to describe the expectation. Notice the roles with a nested array of objects and I'm looking for a "Smart query" that can fetch it one single query.
{
"id": 1,
"first": "John",
"roles": [ // Expectation -> array of objects
{
"id": 1,
"name": "admin"
},
{
"id": 2,
"name": "accounts"
}
]
}
user
+----+-------+
| id | first |
+----+-------+
| 1 | John |
| 2 | Jane |
+----+-------+
role
+----+----------+
| id | name |
+----+----------+
| 1 | admin |
| 2 | accounts |
| 3 | sales |
+----+----------+
user_role
+---------+---------+
| user_id | role_id |
+---------+---------+
| 1 | 1 |
| 1 | 2 |
| 2 | 2 |
| 2 | 3 |
+---------+---------+
Attempt 01
In a naive approach I'd run two sql queries in my nodejs code, with the help of multipleStatements:true in connection string. Info.
User.getUser = function(id) {
const sql = "SELECT id, first FROM user WHERE id = ?; \
SELECT role_id AS id, role.name from user_role \
INNER JOIN role ON user_role.role_id = role.id WHERE user_id = ?";
db.query(sql, [id, id], function(error, result){
const data = result[0][0]; // first query result
data.roles = result[1]; // second query result, join in code.
console.log(data);
});
};
Problem: Above code produces the expected JSON schema but it takes two queries, I was able to narrow it down in a smallest possible unit of code because of multiple statements but I don't have such luxury in other languages like Java or maybe C# for instance, there I've to create two functions and two sql queries. so I'm looking for a single query solution.
Attempt 02
In an earlier attempt With the help of SO community, I was able to get close to the following using single query but it can only help to produce the array of string (not array of objects).
User.getUser = function(id) {
const sql = "SELECT user.id, user.first, GROUP_CONCAT(role.name) AS roles FROM user \
INNER JOIN user_role ON user.id = user_role.user_id \
INNER JOIN role ON user_role.role_id = role.id \
WHERE user.id = ? \
GROUP BY user.id";
db.query(sql, id, function (error, result) {
const data = {
id: result[0].id, first: result[0].first,
roles: result[0].roles.split(",") // manual split to create array
};
console.log(data);
});
};
Attempt 02 Result
{
"id": 1,
"first": "John",
"roles": [ // array of string
"admin",
"accounts"
]
}
it's such a common requirement to produce array of objects so wondering there must be something in SQL that I'm not aware of. Is there a way to better achieve this with the help of an optimum query.
Or let me know that there's no such solution, this is it and this is how it's done in production code out there with two queries.
Attempt 03
use role.id instead of role.name in GROUP_CONCAT(role.id), that way you can get hold of some id's and then use another subquery to get associated role names, just thinking...
SQL (doesn't work but just to throw something out there for some thought)
SELECT
user.id, user.first,
GROUP_CONCAT(role.id) AS role_ids,
(SELECT id, name FROM role WHERE id IN role_ids) AS roles
FROM user
INNER JOIN user_role ON user.id = user_role.user_id
INNER JOIN role ON user_role.role_id = role.id
WHERE user.id = 1
GROUP BY user.id;
Edit
Based on Amit's answer, I've learned that there's such solution in SQL Server using JSON AUTO. Yes this is something I'm looking for in MySQL.
To articulate precisely.
When you join tables, columns in the first table are generated as
properties of the root object. Columns in the second table are
generated as properties of a nested object.
User this Join Query
FOR JSON AUTO will return JSON for your query result
SELECT U.UserID, U.Name, Roles.RoleID, Roles.RoleName
FROM [dbo].[User] as U
INNER JOIN [dbo].UserRole as UR ON UR.UserID=U.UserID
INNER JOIN [dbo].RoleMaster as Roles ON Roles.RoleID=UR.RoleMasterID
FOR JSON AUTO
out put of above query is
[
{
"UserID": 1,
"Name": "XYZ",
"Roles": [
{
"RoleID": 1,
"RoleName": "Admin"
}
]
},
{
"UserID": 2,
"Name": "PQR",
"Roles": [
{
"RoleID": 1,
"RoleName": "Admin"
},
{
"RoleID": 2,
"RoleName": "User"
}
]
},
{
"UserID": 3,
"Name": "ABC",
"Roles": [
{
"RoleID": 1,
"RoleName": "Admin"
}
]
}
]
Though it is an old question, just thought might help others looking for the same issue. The below script should output the json schema you have been looking for.
SELECT roles, user.* from `user_table` AS user
INNER JOIN `roles_table` AS roles
ON user.id=roles.id

Create DF/RDD from nested other DF/RDD (Nested Json) in Spark

I'm a total newbie in Spark&Scala stuff, it would be great if someone could explain this to me.
Let's take following JSON
{
"id": 1,
"persons": [{
"name": "n1",
"lastname": "l1",
"hobbies": [{
"name": "h1",
"activity": "a1"
},
{
"name": "h2",
"activity": "a2"
}]
},
{
"name": "n2",
"lastname": "l2",
"hobbies": [{
"name": "h3",
"activity": "a3"
},
{
"name": "h4",
"activity": "a4"
}]
}]
}
I'm loading this Json to RDD via sc.parralelize(file.json) and to DF via sqlContext.sql.load.json(file.json). So far so good, this gives me RDD and DF (with schema) for mentioned Json, but I want to create annother RDD/DF from existing one that contains all distinct "hobbies" records. How can I achieve sth like that?
The only things I get from my operations are multiple WrappedArrays for Hobbies but I cannot go deeper nor assign them to DF/RDD.
Code for SqlContext I have so far
val jsonData = sqlContext.read.json("path/file.json")
jsonData.registerTempTable("jsonData") //I receive schema for whole file
val hobbies = sqlContext.sql("SELECT persons.hobbies FROM jasonData") //subschema for hobbies
hobbies.show()
That leaves me with
+--------------------+
| hobbies|
+--------------------+
|[WrappedArray([a1...|
+--------------------+
What I expect is more like:
+--------------------+-----------------+
| name | activity |
+--------------------+-----------------|
| h1| a1 |
+--------------------+-----------------+
| h2| a2 |
+--------------------+-----------------+
| h3| a3 |
+--------------------+-----------------+
| h4| a4 |
+--------------------+-----------------+
I loaded your example into the dataframe hobbies exactly as you do it and worked with it. You could run something like the following:
val distinctHobbies = hobbies.rdd.flatMap {row => row.getSeq[List[Row]](0).flatten}.map(row => (row.getString(0), row.getString(1))).distinct
val dhDF = distinctHobbies.toDF("activity", "name")
This essentially flattens your hobbies struct, transforms it into a tuple, and runs a distinct on the returned tuples. We then turn it back into a dataframe under the correct column aliases. Because we are doing this through the underlying RDD, there may also be a more efficient way to do it using just the DataFrame API.
Regardless, when I run on your example, I see:
scala> val distinctHobbies = hobbies.rdd.flatMap {row => row.getSeq[List[Row]](0).flatten}.map(row => (row.getString(0), row.getString(1))).distinct
distinctHobbies: org.apache.spark.rdd.RDD[(String, String)] = MapPartitionsRDD[121] at distinct at <console>:24
scala> val dhDF = distinctHobbies.toDF("activity", "name")
dhDF: org.apache.spark.sql.DataFrame = [activity: string, name: string]
scala> dhDF.show
...
+--------+----+
|activity|name|
+--------+----+
| a2| h2|
| a1| h1|
| a3| h3|
| a4| h4|
+--------+----+