Laravel Eloquent: with() vs join() JSON Output - json

I have these 2 queries on my Laravel project:
$questions = \App\Answer::rightJoin('questions','answers.id_question','=','questions.id_question')
->leftJoin('users','answers.id_user','=','users.id')->where([
['questions.id_question', '=', $id_question],
['questions.flg_active', '=', true],
])->orderBy('questions.created_at', 'desc')->paginate(5);
and
$questions = \App\Answer::with('question.user')
->orderBy('created_at', 'desc')->paginate(15);
Essencially the first on returns questions not yet answered and the second only returns questions already answered.
I`m trying to use the same blade view for both, but they have different JSON output structure.
The first one returns an one level JSON, while the second returns 3 levels of data.
"data": [{
"id_answer": 42,
"created_at": "2018-11-28 17:52:18",
"updated_at": "2019-05-24 15:09:14",
"id_user": 2,
"id_question": 42,
"str_answer": "onono onooono nonono onn ",
"str_question": "ononon ononon onononn ?",
"url_link": null,
"flg_active": 1,
"id": 1,
"name": "Paul",
"surname": null,
"email": "p#yahoo.com",
"certification": "CFA ...",
"about": "CS, ....."
}
and:
"data": [{
"id_answer": 64,
"created_at": "2019-05-16 15:46:54",
"updated_at": "2019-05-16 15:46:54",
"id_user": 2,
"id_question": 98,
"str_answer": "onono non ooonononn",
"question": {
"id_question": 98,
"created_at": "2019-05-16 15:06:31",
"updated_at": "2019-05-16 15:24:43",
"id_user": 2,
"str_question": "onono onnon ononon?",
"url_link": null,
"flg_active": 1,
"user": {
"id": 2,
"name": "Paul",
"surname": null,
"email": "p#outlook.com",
"created_at": "2018-12-06 05:50:09",
"updated_at": "2019-05-24 11:23:32",
"certification": null,
"about": null
}
}
}
How can I make the first one return in the same 3 levels as the second query does ?
Why this occurs?

The first one is leveraging query builder more and will execute one query which will look something like this:
SELECT * FROM answer
RIGHT JOIN questions ON answers.id_question = questions.id_question
LEFT JOIN users ON answers.id_user = users.id
WHERE questions.id_question = $id_question
AND questions.flg_active = true
ORDER BY questions.created_at desc
OFFSET 0
LIMIT 15
This query returns data from all the tables exactly how sql calculates it. Laravel / Eloquent has no idea how to reliably put this into an eloquent format.
The 2nd one will execute 3 queries which will look something like this:
SELECT * FROM answer
ORDER BY created_at desc
OFFSET 0
LIMIT 15
SELECT * FROM question
WHERE answer_Id IN ($answer_ids) // Will be like (1, 5, 6, 7). All the ids retrieve from the first query
SELECT * FROM user
WHERE queston_id IN ($user_ids)
Eloquent does 3 queries therefore it can create the structure you outlined reliably.
Ultimately it's a trade off, the first option is faster but the response is harder to work with in php which creates less readable code.
I recommend you look at the queries that are executed by each by using the built in query log.
\DB::enableQueryLog();
$builtQuery->get()
dd(\DB::getQueryLog());

This occurs because in your second query you utilize eager loading for your Eloquent relationships. Eloquent model's default toArray() method merges both it's attributes and loaded relationships into one representation.
On the other hand, your first query joins tables, but doesn't load any relationships. So the most straight-forward way you can go about this is to eager load those relationships in your first query just as you do in your second one:
$questions = \App\Answer::with('questions.user')
->rightJoin('questions','answers.id_question','=','questions.id_question')
->leftJoin('users','answers.id_user','=','users.id')->where([
['questions.id_question', '=', $id_question],
['questions.flg_active', '=', true],
])->orderBy('questions.created_at', 'desc')->paginate(5);
Note that this will produce extra SELECT ... WHERE id IN (...) queries for both Question and User models to be eager loaded into their respective relationships.

Related

Using the key from JSON in query

I'm new to MySQL and received a task which requires a complex(for me) query. I read the documentation and a few sources but I still cannot write it myself.
I'm selecting a rows from a table where in one of the cells I have JSON like this one
{
[
{
"interval" : 2,
"start": 03,
"end": 07,
"day_of_week": 3
}, {
"interval" : 8,
"start": 22,
"end": 23,
"day_of_week": 6
}
]
}
I want to check if some of the "day_of_week" values is equal to the current day of week and if so to write this value and the values of "start", "end" and "day_of_week" assoiciated with it in a variables to use them in the query.
That's not valid JSON format, so none of the MySQL JSON functions will work on it regardless. Better just fetch the whole blob of not-JSON into a client application that knows how to parse it, and deal with it there.
Even if it were valid JSON, I would ask this: why would you store data in a format you don't know how to query?
The proper solution is the following:
SELECT start, end, day_of_week
FROM mytable
WHERE day_of_week = DAYOFWEEK(CURDATE());
See how easy that is when you store data in normal rows and columns? You get to use ordinary SQL expressions, instead of wondering how you can trick MySQL into giving up the data buried in your non-JSON blob.
JSON is the worst thing to happen to relational databases.
Re your comment:
If you need to query by day of week, then you could reorganize your JSON to support that type of query:
{
"3":{
"interval" : 2,
"start": 03,
"end": 07,
"day_of_week": 3
},
"6": {
"interval" : 8,
"start": 22,
"end": 23,
"day_of_week": 6
}
}
Then it's possible to get results for the current weekday this way:
SELECT data->>'$.start' AS `start`,
data->>'$.end' AS `end`,
data->>'$.day_of_week' AS `day_of_week`
FROM (
SELECT JSON_EXTRACT(data, CONCAT('$."', DAYOFWEEK(CURDATE()), '"')) AS data
FROM mytable
) AS d;
In general, when you store data in a non-relational manner, the way to optimize it is to organize the data to support a specific query.

query from MYSQL to mongodb

This is my MYSQL query:
SELECT country, vaccines, MAX(people_fully_vaccinated_per_hundred) as vaccinated_precentage
FROM country_vaccinations
WHERE people_fully_vaccinated_per_hundred > 60
GROUP BY country, vaccines
ORDER BY MAX(people_fully_vaccinated_per_hundred) DESC;
It basically lists all countries that have fully vaccinated more than 60% of its people, and the types of vaccine offered by that country.
I am trying to do the same on MongoDB:
db.country_vaccinations.aggregate([
{$project: {_id:0,
country: 1,
vaccines: 1,
people_fully_vaccinated_per_hundred: 1},
}
{$match: {"people_fully_vaccinated_per_hundred":{$gt:60}}}
])
However, I am not sure why it returns "No Records Found" when i add in the $match to retrieve rows that have "people_fully_vaccinated_per_hundred">60%.
Can someone advise me on what is my mistake? I would really appreciate it, as I am new to noSQL and am not sure why.
I am not sure it does the same, test it before using it, and if doesn't work, give us if you can some sample data and the expected output so we can test it.
country_vaccinations.aggregate(
[{"$match": {"people_fully_vaccinated_per_hundred": {"$gt": 60}}},
{"$group":
{"_id": {"country": "$country", "vaccines": "$vaccines"},
"vaccinated_precentage": {"$max": "$people_fully_vaccinated_per_hundred"}}},
{"$sort": {"vaccinated_precentage": -1}},
{"$project":
{"_id": 0,
"country": "$_id.country",
"vaccines": "$_id.vaccines",
"vaccinated_precentage": 1}}])

Query is being cached

Hi guys I'm having a strange problem, and I'm looking for help.
I'm using laravel 7, and one of my queries apparently is being cached or something, here's my code to explain:
Here's a very simple method in one of my controllers:
public function chartEstadosVoluntarios(){
$estados = DB::table("voluntarios")
->selectRaw("estado, count(*) as total")
->whereNotNull('estado')
->groupBy("estado")
->get();
return response()->json($estados,200);
}
If I hit the route on chrome the result is:
[
{
"estado": "Derivado al 147",
"total": 2
},
{
"estado": "Derivar a mesa de ayuda",
"total": 7
},
{
"estado": "Pendiente",
"total": 3
},
{
"estado": "Reasignar Adulto Mayor",
"total": 50
},
{
"estado": "Resolución logística 24hs",
"total": 4
},
{
"estado": "Resuelto",
"total": 119
}
]
But its wrong, its not the real result, here's a screen for the same query in MySQL Workbench:
I'm using the same user in workbench and laravel.
Another thing is that If I slightly modify the query, then i get the correct result, for example:
$estados = DB::table("voluntarios")
->selectRaw("estado, count(*) as total, 'test'")//adding 'test' to select
->whereNotNull('estado')
->groupBy("estado")
->get();
Then I hit the browser again, and the result is:
[
{
"estado": "Pendiente",
"total": 9687,
"test": "test"
},
{
"estado": "Resuelto",
"total": 16415,
"test": "test"
}
]
If i go back with the modifications i get the wrong results again.
So, I really don't understand what's happening, Any point of direction will be appreciated.
EDIT, more info added from the answers:
the toSql() method throws the same query (copy pasted from the browser):
"select estado, count(*) as total from `voluntarios` where `estado` is not null group by `estado`";
The toSql() method with 'test' in select:
"select estado, count(*) as total, 'test' from `voluntarios` where `estado` is not null group by `estado`"
Same results in Tinker:
Downgraded from laravel v7.10.3 to v7.6.2 (the one that i have before running composer update today), but the problem remains.
EDIT 2
Weird thing happens, I recently use another part of the system that edits the "estado" field of table "voluntarios" to see what happen, and now the query throws the correct results. it's like something has been unlocked after editing the table.
What could it be? I check before that the user had no open transactions.
Thank's in advance.

Formatting Mysql returned Array for JSON

I am doing a mysql query that pulls relative data from 3 tables based on an ID provided. My problem comes when I try to format the sql query so that when I turn the resulting array into a JSON object the data is nested correctly.
My SQL Query as of now:
select * from Players join SchoolData on Players.School = SchoolData.ID join LocationData on SchoolData.Location = LocationData.ID where Players.ID =%d
When I JSONify the results of the above query I get:
{
"data": [
{
"City": "San Diego",
"Height": 77,
"ID": 7,
"Location": 7,
"Mascot": "Aztecs",
"Name": "San Diego State Aztecs",
"School": 11,
"State": "CA",
"Weight": 196
}
]
}
I am trying to get my results to look like the following example which nest the data that is pulled from other tables.
{
“data”:[
{
“student”:
{
“id”:xxx-xxx-xxx,
“name”: “name1”,
“school”:
{
“id”:xxxx,
“address”:
{
“id”:xxx,
“city”: “mycity”,
“state”: “OH”
},
“name”: “myschool”,
“mascot”: “wildcats”
},
“height”: 72,
“weight”: 180
},
]
}
I have been looking at the FOR JSON command (that is used in SQL) and instead of selecting * all I can use the AS command in my query which should help with my label before the value returned but not the nesting of data.
I am just not sure if this is done pre query or post.
UPDATE
I am finding the following mysql functions: https://dev.mysql.com/doc/refman/5.7/en/json-creation-functions.html but I am not sure if this is what I am looking for.
SELECT
std.id, std.name, sch.id, add.id, add.city, add.state, sch.name, sch.mascot, std.height, std.weight
FROM
student std LEFT JOIN school sch ON < join condition >
LEFT JOIN address ON < join condition >
FOR JSON AUTO
Try something like this, FOR JSON AUTO should convert it into JSON string that you want.

Using N1QL with document keys

I'm fairly new to couchbase and have tried to find the answer to a particular query I'm trying to create with not much success so far.
I've debated between using a view or N1QL for this particular case and settled with N1QL but haven't managed to get it to work so maybe a view is better after all.
Basically I have the document key (Group_1) for the following document:
Group_1
{
"cbType": "group",
"ID": 1,
"Name": "Group Atlas 3",
"StoreList": [
2,
4,
6
]
}
I also have 'store' documents, their keys are listed in this document's storelist. (Store_2, Store_4, Store_6 and they have a storeID value that is 2, 4 and 6) I basically want to obtain all 3 documents listed.
What I do have that works is I obtain this document with its id by doing:
var result = CouchbaseManager.Bucket.Get<dynamic>(couchbaseKey);
mygroup = JsonConvert.DeserializeObject<Group> (result.ToString());
I can then loop through it's storelist and obtain all it's stores in the same manner, but i don't need anything else from the group, all i want are the stores and would have prefered to do this in a single operation.
Does anyone know how to do a N1QL directly unto a specified document value?
Something like (and this is total imaginary non working code I'm just trying to clearly illustrate what I'm trying to get at):
SELECT * FROM mycouchbase WHERE documentkey IN
Group_1.StoreList
Thanks
UPDATE:
So Nic's solution does not work;
This is the closest I get to what I need atm:
SELECT b from DataBoard c USE KEYS ["Group_X"] UNNEST c.StoreList b;
"results":[{"b":2},{"b":4},{"b":6}]
Which returns the list of IDs of the Stores I want for any given group (Group_X) - I haven't found a way to get the full Stores instead of just the ID in the same statement yet.
Once I have, I'll post the full solution as well as all the speed bumps I've encountered in the process.
I apologize if I have a misunderstanding of your question, but I'm going to give it my best shot. If I misunderstood, please let me know and we'll work from there.
Let's use the following scenario:
group_1
{
"cbType": "group",
"ID": 1,
"Name": "Group Atlas 3",
"StoreList": [
2,
4,
6
]
}
store_2
{
"cbType": "store",
"ID": 2,
"name": "some store name"
}
store_4
{
"cbType": "store",
"ID": 4,
"name": "another store name"
}
store_6
{
"cbType": "store",
"ID": 6,
"name": "last store name"
}
Now lets say you wan't to query the stores from a particular group (group_1), but include no other information about the group. You essentially want to use N1QL's UNNEST and JOIN operators.
This might leave you with a query like so:
SELECT
stores.name
FROM `bucket-name-here` AS groups
UNNEST groups.StoreList AS groupstore
JOIN `bucket-name-here` AS stores ON KEYS ("store_" || groupstore.ID)
WHERE
META(groups).id = 'group_1';
A few assumptions are made in this. Both your documents exist in the same bucket and you only want to select from group_1. Of course you could use a LIKE and switch the group id to a percent wildcard.
Let me know if something doesn't make sense.
Best,
Try this query:
select Name
from buketname a join bucketname b ON KEYS a.StoreList
where Name="Group Atlas 3"
Based on your update, you can do the following:
SELECT b, s
FROM DataBoard c USE KEYS ["Group_X"]
UNNEST c.StoreList b
JOIN store_bucket s ON KEYS "Store_" || TO_STRING(b);
I have a similar requirement and I got what I needed with a query like this:
SELECT store
FROM `bucket-name-here` group
JOIN `bucket-name-here` store ON KEYS group.StoreList
WHERE group.cbType = 'group'
AND group.ID = 1