Doctrine ORM Pagination count of returned results - doctrine-orm

Have got Doctrine pagination working perfectly, but one of the requirements is at the bottom of the table of results to show Displaying X of X results.
Getting the total results is not a problem as you can simply do:
$paginator = new Paginator($query);
echo $paginator->count(); // total results for the query
I can get a count of the currently displayed results using:
$paginator = new Paginator($query);
echo $paginator->getIterator()->count();
But this results in 2 additional queries being executed, I'd love a way to get this count with only 3 queries instead of 5!
Any suggestions?

One solution I have found has been to extend the doctrine paginator as followed which works but I feel theres a better solution?
use Doctrine\ORM\Tools\Pagination\Paginator as DoctrinePaginator;
class Paginator extends DoctrinePaginator
{
protected $iterator;
public function getIterator()
{
if (is_null($this->iterator)) {
$this->iterator = parent::getIterator();
}
return $this->iterator;
}
}

Related

Populate Model from a Query with a One-to-Many Relationship

I have two tables:
surveyTemplateHeader
surveyTemplateQuestions
What I was looking to do was get the information from both tables:
var q = dao.getSurveyTemplateDetails( surveyTemplateID = ARGUMENTS.surveyTemplateID );
Then use Coldbox to populate my model, surveyTemplate, with the query data:
var surveyTemplateObj = populator.populateFromQuery( beanFactory.getInstance("surveyTemplate"), q );
This works, however, the model is only populated with one question. The surveyTemplate I am testing has three questions.
I have tried to set up a property name correctly in my model to use fieldtype="one-to-many" but that does not appear to have made a difference.
property name="surveyTemplateQuestionID" fieldtype="one-to-many";
While Coldbox's documentation for models overall is quite good, I am not able to find an answer for this, which probably means I am off track a good bit in my implementation of this.
Any insight would be appreciated.
So, what I did for this is I injected a surveyTemplateQuestions model into my surveyTemplate model:
property name="surveyTemplateQuestions" inject="surveyTemplateQuestions";
then I set the surveyTemplateQuestions property with the questions query:
surveyTemplateObj.setSurveyTemplateQuestions(qQuestions);
Not exactly what I was looking for but it works for now.
You can loop over query to build Arrays of objects. populateFromQuery method takes query row-number to get data from query.
var surveyTemplateObj = [];
for(var row in q){
ArrayAppend(surveyTemplateObj, populator.populateFromQuery( beanFactory.getInstance("surveyTemplate"), q , row.currentRow));
}
API Doc info
http://apidocs.ortussolutions.com/coldbox/4.3.0/coldbox/system/core/dynamic/BeanPopulator.html#populateFromQuery()

How expensive are `count` calls for Django querysets?

I have a list of "posts" I have to render. For each post, I must do three filter querysets, OR them together, and then count the number of objects. Is this reasonable? What factors might make this slow?
This is roughly my code:
def viewable_posts(request, post):
private_posts = post.replies.filter(permissions=Post.PRIVATE, author_profile=request.user.user_profile).order_by('-modified_date')
community_posts = post.replies.filter(permissions=Post.COMMUNITY, author_profile__in=request.user.user_profile.following.all()).order_by('-modified_date')
public_posts = post.replies.filter(permissions=Post.PUBLIC).order_by('-modified_date')
mixed_posts = private_posts | community_posts | public_posts
return mixed_posts
def viewable_posts_count(request, post):
return viewable_posts(request, post).count()
The biggest factor I can see is that you have filter actions on each post. If possible, you should query the results associated with each post in ONE query. As of the count, it's the most efficient way of getting the number of results from a query, so it's likely not a problem.
Try the following code:
def viewable_posts(request, post):
private_posts = post.replies.filter(permissions=Post.PRIVATE, author_profile=request.user.user_profile).values_list('id',flat=True)
community_posts = post.replies.filter(permissions=Post.COMMUNITY, author_profile__in=request.user.user_profile.following.values_list('id',flat=True)
public_posts = post.replies.filter(permissions=Post.PUBLIC).values_list('id',flat=True)
Lposts_id = private_posts
Lposts_id.extend(community_posts)
Lposts_id.extend(public_posts)
viewable_posts = post.filter(id__in=Lposts_id).order_by('-modified_date')
viewable_posts_count = post.filter(id__in=Lposts_id).count()
return viewable_posts,viewable_posts_count
It should improve the following things:
order_by once, instead of three times
The count method runs on a query with only the index field
django uses a faster filter with "values", both for the count and the filtering.
Depends on your database, the db own cache may pick the last queried posts for viewable_posts, and use it for viewable_posts_count
Indeed, if you can squeeze the first three filter queries into one, you will save time as well.

is search query set's __contains filter of any use?

I am running django-haystack v2.0.0 with pyelasticsearch v0.3 in one of my projects. I have a SearchView that renders a listing template and i want to integrate result filtering. The search runs fine, but the filters i am using are not working at all ? I use the following filters __contains, __lte, __gte but none of them seems to make a difference to the result list.
So say, I have 10 results rendered from the SearchView based on a search term. On the same template i have a filter form (that uses the GET method) and calls the same SearchView. Now under the SearchView I have a custom private _filter_results method defined as follows.
def _filter_results(results, filters):
"""
This method would return the filtered search results, based on
the filters applied by a user.
"""
for item in filters:
if item == 'location':
results = results.filter(locations__contains=filters[item])
print results
if item == 'age_min':
results = results.filter(workex_min__gte=filters[item])
if item == 'age_max':
results = results.filter(workex_max__lte=filters[item])
return results
Now, I pass the results to the template as follows:
context {'results' : _filter_results(self.results, self.result_filters)}
self.result_filters is a dictionary of filters that I set in SearchView's __call__ method and it look something like this.
{
'location' : request.GET.get('location'),
'age_min' : request.GET.get('age_min'),
'age_max' : request.GET.get('age_max'),
}
I do not get any errors, and I have double checked that each filter value is being passed to the _filter_results method. But the results still stay the same.
If on a results list of 10 entries i try to filter with a location where none of them contains the location, I still get a result of the same 10 entries back. What could be the reason ?
I havent used Haystack with ElasticSearch but have used it extensively with Solr, but it should work the same with ElasticSearch as well.
You should be using results.narrow() method instead of filter. Filter method will essentially append the filters with the main initially query term and run a 'OR' query between them as the default operator is set as 'OR' in haystack.
The narrow() method is specifically there for filtering the initial set of results and refine them further, also its beneficial to run filter query using narrow as the query constructed in SearchEngine is different and makes better use of inbuilt caching.
def _filter_results(results, filters):
"""
This method would return the filtered search results, based on
the filters applied by a user.
"""
for item in filters:
if item == 'location':
results = results.narrow('locations:%s' % filters[item])
print results
if item == 'age_min':
results = results.narrow('workex_min:[%s TO *]' % filters[item])
if item == 'age_max':
results = results.narrow('workex_max:[* TO %s]' % filters[item])
return results
Again this is how it works with Solr but haystack method concepts are same across and the only difference lies in the backends file for each SearchEngine. You should look at haystack/backends/elasticsearch_backend.py for better understanding.
Also have a look at FacetedSearchView and FacetedSearchForm

Preventing lazy-loading gives me 1 "subentity"

I have a problem with Doctrine (Symfony2.1). I want to prevent lazy-loading by join fetching subentities (OneToMany relation) but I got only one result for those subentities.
For example:
public function getSingleProjectQuery($project){
$query = $this->createQueryBuilder('p')
->select(array("p", "fb"))
->where('p.id = :project_id')->setParameter('project_id', $project)
->leftJoin('p.feedbacks', 'fb')
->groupBy('p.id')
->getQuery();
return $query;
}
In this example Doctrine returns me the "Project"-object and one single "feedback" object (but there are more than one feedbacks...).
When I replace the select to this: ->select(array("p"))
I got all the "Feedback" objects but there are then lazy-loaded (many queries).
see http://docs.doctrine-project.org/en/latest/reference/dql-doctrine-query-language.html#joins
You should remove the groupBy clause.

Get multiple rows with one query in django?

How can I get build a QuerySet that gets multiple rows from django? I thought filter() would work, but it seems to be worse off.
For example, I have two rows in the model Car, with two text attributes (license and vin). Now say I want to print the licenses and vins from these cars. How can I do that with one database call?
Here's an answer that will make two database calls:
#using get(), two total queries
a = Car.objects.get(id=1) #query here
b = Car.objects.get(id=2) #query here
print(a.license + a.vin) #no query
print(b.license + b.vin) #no query
That obviously didn't work because I made two get() queries. So next I'll try filter():
#using filter(), four total queries
c = Car.objects.filter(id__in=(1,2)) #no query
print(c[0].license + c[0].vin) #two queries
print(c[1].license + c[1].vin) #two queries
Hmmm, that's weird, why is making four database calls? Is there a way I can make it get the two in one database call?
It's seems weird because of how indexing into a queryset works.
c = list(Car.objects.filter(id__in=(1,2))) # query
print(c[0].license + c[0].vin) #no query
print(c[1].license + c[1].vin) #no query
If you do the following, you'll only have one query too:
for car in Car.objects.filter(id__in=(1,2)):
print(car.license + car.vin)
As #Torsten said, in your situation it appears like you're simply trying to get all the cars you've created. This can be achieved via the all() method:
for car in Car.objects.all():
print(car.license + car.vin)
Great example. A typo I think though in your last codeblock. Should be:
for car in Car.objects.filter(id__in=(1,2)):
print(car.license + car.vin)
How does that method stack up