How do you perform projections in RethinkDB? - rethinkdb

Given the following data in two tables:
Employees
[
{"name": "person 1", "dept": 1},
{"name": "person 2", "dept": 1},
{"name": "person 3", "dept": 2}
]
Departments
[
{"name": "design", "id": 1},
{"name": "development", "id": 2}
]
To run a query with a join:
r.db('mydb').table('employees').eqJoin('dept', r.db('mydb').table('departments')).zip();
This will merge the results and I end up with a single 'name' property from the departments overwriting the name from the employees table.
How to you handle projections in RethinkDB to handle this to that I can end up with the name from both the Employee and Department tables?

You can manually map
r.db('mydb').table('employees')
.eqJoin('dept', r.db('mydb').table('departments'))
.map(function(doc) {
return {
personName: left("name"),
departmentName: right("name"),
// etc.
}
})

Related

Get certain attribute data from the result of a recursive query

The following example is used to populate a tree and use a table with a parent_id column.
The data is obtained with a recursive query.
$data = [{
"id": 1,
"name": "parent 1"
"note": "note 1",
}, {
"id": 2,
"name": " parent 2",
"note": "note 2",
"children": [{
"id": 21,
"name": "child A of 2",
"note": "note A of 2",
},{
"id": 22,
"name": "child B of 2",
"note": "note B of 2",
},{
"id": 23,
"name": "child C of 2",
"note": "note C of 2",
"children": [{
"id": 231,
"name": "child A of 23",
"note": "note A of 23",
"children": [{
"id": 2311,
"name": "child A of 231",
"note": "note A of 231",
"children": []
}]
}]
}]
}];
And the query:
$myData= Hierarchy::whereNull('parent_id')
->with('children')
->get();
So far so good.
Problem to solve:
It is necessary to obtain a simple (non-hierarchical) list of the id and name attributes of the parents and children.
Example:
"id": 1,
"name": "parent 1",
"id": 2,
"name": " parent 2",
"id": 21,
"name": "child A of 2",
"id": 23,
"name": "child C of 2",
"id": 231,
"name": "child A of 23",
"id": 2311,
"name": "child A of 231"
While this can be solved on the client side with javascript, I intended to do it with eloquent or PHP functions.
I made some attempts with the array_walk() and array_walk_recursive() PHP functions (without success).
Is there any way to solve with eloquent, bearing in mind that the number of children nodes can be infinite?
Thanks.
EDITED:
Example attempt with array_walk_recursive() PHP function
public function getList()
{
$myData= Hierarchy::whereNull('parent_id')
->with('children')
->get();
$data = array_walk_recursive($myData, "self::myFunction");
return response()->json(['success' => true, "data" => $data]);
}
public function myFunction($item, $key){
???
}
You can use the API Resouce recursively or use the recursive function to generate the hierarchy array.
Example with recursive function:
function makeHierarchy($values)
{
$result = [];
foreach($values as $item) {
$result[] = [
'id' => $item->id,
'name' => $item->name,
'children' => makeHierarchy($item->children),
];
}
return $result;
}
$values = Hierarchy::whereNull('parent_id')->with('children')->get();
$hierarchical = makeHierarchy($values);
If you want to get all values as a flat list:
$values = Hierarchy::get();
$result = [];
foreach($values as $item) {
$result[] = [
'id' => $item->id,
'name' => $item->name,
];
}
# now the result contains all the parents and children in a flat list
In the cleaner way:
$result = Hierarchy::select(['id', 'name'])->all();

Laravel, get relation row instead of id

I want to get author row instead of author_id. I could this with add collection and change one by one but has Laravel any function for this? Well, I want make this one line
Can i use something like this
Book::where('id',$bid)->with('author')->first('author_id AS author'); //Changes only coulmn name :(
model
public function author()
{
return $this->hasOne(Author::class,'id','author_id');
}
query
Book::where('id',$bid)->with('author')->first()
Output
{
"id": 1,
"name": "Book 1",
"author_id": 3,
"category_id": 2,
"level_id": 1,
"book_language_id": 1,
"book_length": 0,
"img": "book1.png",
"summary": "Summary 1",
"rate_avg": "2.75",
"created_at": "2022-03-04T18:46:32.000000Z",
"updated_at": "2022-03-04T18:52:28.000000Z",
"author": {
"id": 3,
"name": "Author 3",
"created_at": "2022-03-04T18:46:32.000000Z",
"updated_at": "2022-03-04T18:46:32.000000Z"
}
}
Want
{
"id": 1,
"name": "Book 1",
"author": {
"id": 3,
"name": "Author 3",
"created_at": "2022-03-04T18:46:32.000000Z",
"updated_at": "2022-03-04T18:46:32.000000Z"
},
"category_id": 2,
"level_id": 1,
"book_language_id": 1,
"book_length": 0,
"img": "book1.png",
"summary": "Summary 1",
"rate_avg": "2.75",
"created_at": "2022-03-04T18:46:32.000000Z",
"updated_at": "2022-03-04T18:52:28.000000Z",
}
in your query
Book::where('id',$bid)->with('author')->first()
you are getting the book that has that id and you are eager loading the author relation, so in order to get the author you have to access the author field:
Book::where('id',$bid)->with('author')->first()->author
As I said I can this with collection like this:
$b=Book::where('id',$bid)->with('author')->first();
$book=collect([
'id'=>$b->id,
'name'=>$b->name,
'author'=>$b->author,
'category_id'=>$b->category_id,
'level_id'=>$b->level_id,
'book_language_id'=>$b->book_language_id,
'book_length'=>$b->book_length,
'summary'=>$b->summary,
'rate_avg'=>$b->rate_avg,
]);
But this method seems unnecessary
In your example the only difference between the output and what you want is "author_id": 3, as been deleted.
So if you don't want a column or rename a column, you need to use ->select and take all the field you want. And you can also rename with something like that
-> select(DB::raw('author_id as auhtor'), 'books.*')

Inner join in JPA using native query

I have two tables -
Podcasts
Categories
Podcast columns - podcast_id, podcast_autho, podcast_category_id, podcast_description, podcast_owner, podcast_subcategory_id, podcast_title
Category columns - category_id, category_name, category_owner
podcast_category_id has a value from the Categories table and so does podcast_subcategory_id.
Now, I have an SQL statement which inner joins the two table to produce category name instead of id in the output -
SELECT p.*, c.category_name, c2.category_name AS sub_category_name FROM podcasts p
LEFT JOIN categories c ON p.podcast_category_id = c.category_id
LEFT JOIN categories c2 ON p.podcast_subcategory_id = c2.category_id;
I use the same query in my JpaRepository class to get the output -
interface PodcastRepository: JpaRepository<Podcast, Long> {
#Query(value = "SELECT p.*, c.category_name, c2.category_name AS sub_category_name FROM podcasts p " +
"LEFT JOIN categories c ON p.podcast_category_id = c.category_id " +
"LEFT JOIN categories c2 ON p.podcast_subcategory_id = c2.category_id",
nativeQuery = true)
fun getPodcastsByOwner(owner: Long): List<Any>
}
I do get an output but it is not in JSON format unlike other cases where I don't user Any (Object in Java) but the specific Entity class.
What I get -
[
[
8,
"krktush",
2,
"World War 1",
1,
3,
"What Went Wrong",
"General",
"Specific"
],
[
9,
"krktush",
2,
"World War 2",
1,
3,
"What went right",
"General",
"Specific"
]
]
What I expect -
[
{
"id": 16,
"author": "krtkush",
"title": "World War 1",
"description": "What Went Wrong",
"category": "2",
"subCategory": "",
"owner": 1,
"categoryName": General,
"subCategoryName": Specific
},
{
"id": 22,
"author": "krtkush",
"title": "World War 2",
"description": "What Went Right",
"category": "20",
"subCategory": "",
"owner": 1,
"categoryName": General,
"subCategoryName": Specific
}
]
How do I achieve this?

Laravel hasOne get only one field instead of object

There two models user and also address, which contains country, city and etc. I need to get a list of users with the city, not with the whole address. Relation is oneToOne. The only thing I can get, using select['user_id', 'city']:
{
"id": 1,
"name": "John",
"city": {
"user_id": 3,
"city": "Paris"
},
but I need:
{
"id": 1,
"name": "John",
"city": "Paris"
}
Of course, I can use a loop and do something like $user->city = $user->address->city but maybe there is a better way to solve this problem. Laravel 5.4
You can use Accessor,
And append the attribute to json appending-values-to-json:
In your User model:
protected $appends = ['city'];
public function getCityAttribute()
{
return $this->city->city;
}
Test it like this:
User::with('city')->get()->toJson();
// It will return:
// [{"id": 1, "name": "John", "city": "Paris", "city" : {"user_id": 3, "city": "Paris"}}, {...}]

CosmosDB - SubDocument Delselecting - LINQ Query

I have a ProductDocument model in CosmosDB, which represents a Product. Within that model there is a subdocument contributors which holds who has contributed to the Product. Each contributor has a role.
Now I have been experimenting with a query that needs to:
Only select ProductDocument with a contributor.roleDescription of Author
Only select ProductDocument with a division of Pub 1
Only include contributors sub documents with a contributor.roleDescription of Author in the result set.
Now I'm struggling with:
Part 3 of select above. How do I accomplish this bit as my result set is including both contributor.roleDescription of Author AND Illustrator
Example Cosmos Model:
[
{
"id": "1",
"coverTitle": "A Title",
"pubPrice": 2.99,
"division" :"Pub 1",
"Availability": {
"code": "20",
"description": "Digital No Stock"
},
"contributors": [
{
"id": 1,
"firstName": "Brad",
"lastName": "Smith",
"roleDescription": "Author",
"roleCode": "A01"
},
{
"id": 2,
"firstName": "Steve",
"lastName": "Bradley",
"roleDescription": "Illustrator",
"roleCode": "A12"
}
]
},
{
"id": "2",
"coverTitle": "Another Title",
"division" :"Pub 2",
"pubPrice": 2.99,
"Availability": {
"code": "50",
"description": "In Stock"
},
"contributors": [
{
"id": 1,
"firstName": "Gareth Bradley",
"lastName": "Smith",
"roleDescription": "Author",
"roleCode": "A01"
}
]
}]
Here is my SQL which I have been playing around with in the Data Explorer:
SELECT VALUE p
FROM Products p
JOIN c IN p.contributors
WHERE c.roleDescription = 'Author'
AND p.division = 'Pub 1'
Here is my LINQ query from my service:
var query = client.CreateDocumentQuery<ProductDocument>(
UriFactory.CreateDocumentCollectionUri("BiblioAPI", "Products"),
new FeedOptions
{
MaxItemCount = -1,
EnableCrossPartitionQuery = true
}
)
.SelectMany(product => product.Contributors
.Where(contributor => contributor.RoleDescription == "Author")
.Select(c => product)
.Where(p => product.Division == "Pub 1"))
.AsDocumentQuery();
List<ProductDocument> results = new List<ProductDocument>();
while (query.HasMoreResults)
{
results.AddRange(await query.ExecuteNextAsync<ProductDocument>());
}
It selects the correct records, but how do I de-select the Illustrator sub document of contributor, because at the moment I get the following:
{
"id": "1",
"coverTitle": "A Title",
"pubPrice": 2.99,
"division" :"Pub 1",
"Availability": {
"code": "20",
"description": "Digital No Stock"
},
"contributors": [
{
"id": 1,
"firstName": "Brad",
"lastName": "Smith",
"roleDescription": "Author",
"roleCode": "A01"
},
{
"id": 2,
"firstName": "Steve",
"lastName": "Bradley",
"roleDescription": "Illustrator",
"roleCode": "A12"
}
]
}
But the following output is what I want, excluding the Illustrator contributor sub document:
{
"id": "1",
"coverTitle": "A Title",
"pubPrice": 2.99,
"division" :"Pub 1",
"Availability": {
"code": "20",
"description": "Digital No Stock"
},
"contributors": [
{
"id": 1,
"firstName": "Brad",
"lastName": "Smith",
"roleDescription": "Author",
"roleCode": "A01"
}
]
}
EDIT:
I would like to filter on Product if one of the subdocument contributor.roleDescription equals Author. So if the Product record doesn't include a Author contributor I don't want it
I want to include each contributor subdocument that equals Author. So if there are multiple Author contributor subdocuments for a Product, I want to include them, but exclude the Illustrator ones.
You could have a Collection of ProductDocuments.
Help on the fluent LINQ syntax would help greatly.
Azure CosmosDB now supports subqueries. Using subqueries, you could do this in two ways, with minor differences:
You could utilize the ARRAY expression with a subquery in your projection, filtering out contributors that you don’t want, and projecting all your other attributes. This query assumes that you need a select list of attributes to project apart from the array.
SELECT c.id, c.coverTitle, c.division, ARRAY(SELECT VALUE contributor from contributor in c.contributors WHERE contributor.roleDescription = "Author") contributors
FROM c
WHERE c.division="Pub 1"
This assumes that you need to filter on division "Pub 1" first followed by the subquery with the ARRAY expression.
Alternately, if you want the entire document along with the filtered contributors, you could do this:
SELECT c, ARRAY(SELECT VALUE contributor from contributor in c.contributors WHERE contributor.roleDescription = "Author") contributors
FROM c
WHERE c.division="Pub 1"
This will project the original document with a "Pub 1" division in the property labeled "c", along with a filtered contributor array separately in the property labeled "contributors". You could refer this contributor array for your filtered contributors and ignore the one in the document.
This will do what you want, but obviously if you have multiple contributors you want to show it might not do quite what you are after - it's hard to tell with your question if that is what you want exactly
SELECT p.id, p.coverTitle, p.pubPrice, p.division, p.Availability, c as contributors
FROM Products p
JOIN c IN p.contributors
WHERE c.roleDescription = 'Author'
AND p.division = 'Pub 1'
and the output is:
[
{
"id": "1",
"coverTitle": "A Title",
"pubPrice": 2.99,
"division": "Pub 1",
"Availability": {
"code": "20",
"description": "Digital No Stock"
},
"contributors": {
"id": 1,
"firstName": "Brad",
"lastName": "Smith",
"roleDescription": "Author",
"roleCode": "A01"
}
}
]
Note that contributors is not a list, it's a single value, so if multiple contributors match the filter, then you will get the same product returned multiple times.

Resources