Limit number of results in nested query - laravel

I have a table called comments with the following columns:
| id | user_id | post_id | parent_id | text |
I have this setup for a nested comment system, like Disqus:
If the comment has no parent comment, parent_id will be 0. But if a comment has a parent, the parent_id will be the id of the parent comment.
In my Comments.php model, I have this relationship:
public function children()
{
return $this->hasMany('App\Comment', 'parent_id', 'id')->with('children');
}
Now, if I query the comments:
$comments = Comment::where('post_id', 1)
->where('parent_id', 0)
->with('children')
->get();
The output will look something like this:
[
{
"id": 5,
"user_id": "2",
"post_id": "1",
"parent_id": "0",
"text": "Text",
"children": [
{
"id": 7,
"user_id": "1",
"post_id": "1",
"parent_id": "5",
"text": "Text",
"children": [
{
"id": 8,
"user_id": "3",
"post_id": "1",
"parent_id": "7",
"text": "Text",
"children": [
]
},
{
"id": 11,
"user_id": "3",
"post_id": "1",
"parent_id": "7",
"text": "Text",
"children": [
]
},
]
},
{
"id": 9,
"user_id": "1",
"post_id": "1",
"parent_id": "5",
"text": "Text",
"children": [
]
}
,
{
"id": 10,
"user_id": "1",
"post_id": "1",
"parent_id": "5",
"text": "Text",
"children": [
]
}
]
},
{
"id": 6,
"user_id": "3",
"post_id": "1",
"parent_id": "0",
"text": "Text",
"children": [
]
}
]
Or, in simpler terms:
- 5
-- 7
--- 8
--- 11
-- 9
-- 10
- 6
What I want to do is limit the number of results returned per depth.
So, for example, how can I change my query/code such that it returns:
Ten top-level results (parent_id of 0)
Two level-1 results
One level-2 result
So in the end, the query should output the comments like this:
- 5
-- 7
--- 8
-- 9
- 6
As you can see, there are two top-level results (up to 10 allowed), two level-1 results, and one level-2 result.
How can I do this?

Instead of using with in the model that may gets you uncontrolled retrieval of data, why not do it when you need it?
Which means, in your Comment.php, remove the with:
public function children()
{
return $this->hasMany('App\Comment', 'parent_id', 'id');
}
And in your controller/helper/service classes, only retrieve what you need? For example:
$comments = Comment::where('post_id', 1)
->where('parent_id', 0)
->with('children' => function($query) {
$query->with('children' => function($query) {
$query->with('children')
->orderBy('created_at', 'desc')
->take(1); //last 1
})
->orderBy('created_at', 'desc')
->take(2); // intermediate 2
})
->orderBy('created_at', 'desc')
->take(10) //the outermost 10
->get();
Not tested yet, but theoretically should work :)
Note: Added order by created_at because we usually want the latest few comments to comes out at the top :)

Related

Laravel nested eloquent relationships using model

I am trying to getting the more sorted object as output using laravel eloquent relationships
Section::with(['fieldTypes','fields'=>
function ($query) use ($id) {
$query->where('user_id', '=', $id);
},
'fields.fieldType'=> function ($query) {
$query->select('id','type','url','icon');
}])->get();
Output
"fields": [
{
"id": 1,
"user_id": 1,
"field_type_id": 1,
"section_id": 3,
"title": "Facebook",
"value": "inderjit",
"order": 1,
"field_type": {
"id": 1,
"type": "facebook",
"url": "https://www.facebook.com/"
}
}
]
I want output like this . I want to merge 2 objects as one
"fields": [
{
"id": 1,
"user_id": 1,
"field_type_id": 1,
"section_id": 3,
"title": "Facebook",
"value": "inderjit",
"order": 1,
"type": "facebook",
"url": "https://www.facebook.com/"
}
]
Something like this. You can add a method in your model where you trigger a leftJoin. Then you have the fields of the relation in the model.
public function scopeJoinWithFieldTypes($query)
{
return $query->leftJoin("field_types", "field_type.id", "=", "fields.id");
}

How to retrieve Json data type data in SQL using a where condition in LARAVEL

Question : How to retrieve Json data type data in SQL using a where condition in LARAVEL?
I want to display all the order that contains order->Product->user->id === 1
{
"currentUserID": 1,
"currentUserName": "Mohamed Naalir",
"order": [
{
"id": 26,
"Product": [
{
"id": 4,
"name": "Araliya Rice",
"desription": "Araliya Rice",
"salePrice": 500,
"category": "Rice",
"user": {
"id": 1,
"name": "Mohamed Naalir",
}
}
],
},
{
"id": 27,
"Product": [
{
"id": 2,
"name": "white sugar",
"desription": "aaa",
"salePrice": 100,
"category": "Sugar",
"user": {
"id": 5,
"name": "Mohamed Sharaf",
}
}
],
}
]
}
json where clauses
$orders = DB::table('orders')
->whereJsonContains('Product', [['user' => ['id' => 1]]])
->get();

Extract keywords from fields

I want to write a query to analyze one or more fields ?
i.e. current analyzers require text to function, instead of passing text I want to pass a field value.
If I have a document like this
{
"desc": "A document description",
"name": "This name is not original",
"amount": 3000
}
I would like to return something like the below
{
"desc": ["document", "description"],
"name": ["name", "original"],
"amount": 3000
}
You can use Term Vectors or Multi Term Vectors to achieve what you're looking for:
https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-multi-termvectors.html
You'd have to specify the Ids of the fields you want as well as the fields and it will return an array of analyzed tokens for each document you have as well as certain other info which you can easily disable.
GET /exampleindex/_doc/_mtermvectors
{
"ids": [
"1","2"
],
"parameters": {
"fields": [
"*"
]
}
}
Will return something along the lines of:
"docs": [
{
"_index": "exampleindex",
"_type": "_doc",
"_id": "1",
"_version": 2,
"found": true,
"took": 0,
"term_vectors": {
"desc": {
"field_statistics": {
"sum_doc_freq": 5,
"doc_count": 2,
"sum_ttf": 5
},
"terms": {
"amazing": {
"term_freq": 1,
"tokens": [
{
"position": 1,
"start_offset": 3,
"end_offset": 10
}
]
},
"an": {
"term_freq": 1,
"tokens": [
{
"position": 0,
"start_offset": 0,
"end_offset": 2
}
]
}

Object Array search support in Elasticsearch

I have a array of object in elastic search.
I would like to search if a particular field value appears in top 2 position of the array without using script.
Imagine my ES data is as follows
[
{
"_id": "TestID1",
"data": [
{
"name": "Test1",
"priority": 2
},
{
"name": "Test2",
"priority": 3
},
{
"name": "Test3",
"priority": 4
}
]
},
{
"_id": "TestID2",
"data": [
{
"name": "Test3",
"priority": 2
},
{
"name": "Test9",
"priority": 3
},
{
"name": "Test5",
"priority": 4
},
{
"name": "Test10",
"priority": 5
}
]
},
{
"_id": "TestID3",
"data": [
{
"name": "Test1",
"priority": 2
},
{
"name": "Test2",
"priority": 3
},
{
"name": "Test3",
"priority": 6
}
]
}
]
Here I would like to make a query which searches for _Test3_ ONLY within the top 2 elements of the data array.
Searching here would return the result
_id: TestID2's data
because only TestID2 has Test3 in the top 2 of the data array.
You will not be able to perform such request directly without using script. The only solution that I can think of is to create a copy of the array field containing only the first 2 elements. You will then be able to search on this field.
You can add an ingest pipeline to trim your array automatically.
PUT /_ingest/pipeline/top2_elements
{
"description": "Create a top2 field containing only the first two values of an array",
"processors": [
{
"script": {
"source": "ctx.top2 = [ctx.data[0], ctx.data[1]]"
}
}
]
}

How to get parent objects and nested children objects by parent and children ids

I have a parent object (blogpost) and nested items (comments)
The parent and nested objects are both keyed by an id
So if I fetch the parent object, i will get all the children too
GET /my_index/blogpost/1
{
"id": 1,
"title": "Nest eggs",
"body": "Making your money work...",
"tags": [ "cash", "shares" ],
"comments": [
{
"id": 2,
"comment": "Great article",
"age": 28,
"stars": 4,
"date": "2014-09-01"
},
{
"id": 4,
"comment": "More like this please",
"age": 31,
"stars": 5,
"date": "2014-10-22"
}
]
}
Question
However, I only want to fetch the parent and a subset of children based on children ids
e.g. My desired behaviour is this:
GET /my_index/blogpost/1?onlyGetCommentIds=4
{
"id": 1,
"title": "Nest eggs",
"body": "Making your money work...",
"tags": [ "cash", "shares" ],
"comments": [
{
"id": 4,
"comment": "More like this please",
"age": 31,
"stars": 5,
"date": "2014-10-22"
}
]
}
See in the above example that only comment id == 4 is returned along with the parent object.
How do I construct this query?
You need to use nested inner_hits in order to achieve what you want
POST /my_index/blogpost/_search
{
"_source": {
"exclude": "comments"
},
"query": {
"bool": {
"filter": [
{
"term": {
"id": 1
}
},
{
"nested": {
"path": "comments",
"query": {
"term": {
"comments.id": 4
}
},
"inner_hits": {}
}
}
]
}
}
}
The response will include the parent object (without the comments nested objects) and another inner_hits section containing only the desired nested comment with id = 4.

Resources