Calcite LogicalAggregate - apache-calcite

What is a proper way to associate an AggregateCall that is part of a HAVING expression with a corresponding field in a RelRecordType for the LogicalAggregate? If the AggregateCall is not part of the SELECT clause, the LogicalAggregate's RelRecordType still has it, but the AggregateCall's name attribute is set to NULL and RelRecordType.getField(AggregateCall.getName()) can't be used in this case. If the AggregateCall is a part of the final output, its name is set and RelRecordType.getField(AggregateCall.getName()) returns the right field.

Use field ordinals rather than names.
In the world of Calcite RelNodes and RexNodes, field names are not that important; they exist mainly to help you understand the purpose of fields when debugging. Names of AggregateCalls are even less important; they exist so that Aggregate can give reasonable names to its fields, and if they don't exist, that's fine.
If your SELECT has N fields (numbered 0 .. N-1) and a HAVING clause, you will likely add the HAVING predicate as field N, apply a Filter relational operator, then apply a Project so that only fields 0 .. N-1 are returned. I'm pretty sure that this is what SqlToRelConverter does already.

Related

Doctrine returning strange field names from query

I am using "doctrine/doctrine-orm-module": "^2.1" (it is a module for zend framework 3). I want to create a query which will return rows with field names (trivial, right?). But instead of exact names of fields I am getting this query result:
SELECT
u0_.id AS id_0, u0_.username AS username_1, u0_.email AS email_2,
u0_.first_name AS first_name_3, u0_.last_name AS last_name_4,
u0_.password AS password_5, u0_.status AS status_6, u0_.created AS created_7,
u0_.modified AS modified_8
FROM
user_item u0_
ORDER BY
u0_.id DESC
This query is generated by this code:
$entityManager = $this->getEntityManager();
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->select('u')
->from(UserItem::class, 'u')
->orderBy('u.id', 'DESC')
;
$query = $queryBuilder->getQuery();
echo $query->getSql();
print_r($query->getParameters());
die('|||');
What is the "0_" appending to the table name? What is appendings "_x" to the fields name?
How can I get normal fields and tables names without appended "_x"?
Just names, I'm assuming both the first_name and last_name as shown in that generated SQL, right?
I changed the order below, makes it easier to read / understand.
What you want to do is (pseudo code): Select from UserItem all the first & last names
So, write the code that way :)
$queryBuilder
->from(UserItem::class, 'u')
->select(['u.first_name', 'u.last_name'])
->orderBy('u.id', 'DESC'); // Might want to sort by either u.first_name or u.last_name
What's in the QueryBuilder?
->from(UserItem::class, 'u') - First parameter is the FQCN (Fully Qualified Class Name) of the Entity you wish to use with the QueryBuilder. Not required is the second parameter, which is an alias to use for this instance of the QueryBuilder to recognize the FQCN defined class by. (Off of the top of my head it defaults to snake_case'd names of the class, in this case "user_item")
->select(['u.first_name', 'u.last_name']) - Function takes a "mixed" param. Click through to its definition and you'll see the following in the function:
$selects = is_array($select) ? $select : func_get_args();
Which indicates that it will always pass the "$selects" on the next bit as an array. (Another hint is that $selects is plural)
->orderBy('u.id', 'DESC') - Creates a rule to order results by. If you click through to this function, you'll see that this one ends like so:
return $this->add('orderBy', $orderBy);
Meaning: you can add more than 1 order by.
When it comes to the generated DQL:
u0_ is the table alias as defined in the DQL, from your question: FROM user_item u0_, this will later be transformed to MySQL (usually) which will be the same. It sets u0_ as an alias for user_item.
The _* appended to property names is just plain the order of the columns as they've been created in the database (have a look, they'll be in that order).
Lastly, the fact you were receiving entire entities and not just the names (first_name & last_name) is due to ->select('u'). Because no property (or properties as shown above) is defined, Doctrine assumes you wish to receive the whole enchalada. Doing ->select('u.first_name') would then get you just the first names, and using an array as above would get you more than 1 property.
Hope that helped you out :)

Annotate one part of a range to a new field

So we've been using a DateTimeRangeField in a booking model to denote start and end. The rationale for this might not have been great —separate start and end fields might have been better in hindsight— but we're over a year into this now and there's no going back.
It's generally been fine except I need to annotate just the end datetime onto a related model's query. And I can't work out the syntax.
Here's a little toy example where I want a list of Employees with end of their last booking annotated on.
class Booking(models.Model):
timeframe = DateTimeRangeField()
employee = models.ForeignKey('Employee')
sq = Booking.objects.filter(employee=OuterRef('pk')).values('timeframe')
Employee.objects.annotate(last_on_site=Subquery(sq, output_field=DateTimeField()))
That doesn't work because the annotated value is the range, not the single value. I've tried a heap of modifiers (egs __1 .1 but nothing works).
Is there a way to get just the one value? I guess you could simulate this without the complication of the subquery just doing a simple values lookup. Booking.objects.values('timeframe__start') (or whatever). That's essentially what I'm trying to do here.
Thanks to some help in IRC, it turns out you can use the RangeStartsWith and RangeEndsWith model transform classes directly. These are the things that are normally just registered to provide you with a __startswith filter access to range values, but directly they can pull back the value.
In my example, that means just modifying the annotation slightly:
from django.contrib.postgres.fields.ranges import RangeEndsWith
sq = Booking.objects.filter(employee=OuterRef('pk')).values('timeframe')
Employee.objects.annotate(last_on_site=RangeEndsWith(Subquery(sq[:1])))

Django annotate and LEFT OUTER JOIN with desired WHERE Clause

Django 1.10.6
Asset.objects.annotate(
coupon_saved=Count(
Q(coupons__device_id='8ae83c6fa52765061360f5459025cb85e6dc8905')
)
).all().query
produces the following query:
SELECT
"assets_asset"."id",
"assets_asset"."title",
"assets_asset"."description",
"assets_asset"."created",
"assets_asset"."modified",
"assets_asset"."uid",
"assets_asset"."org_id",
"assets_asset"."subtitle",
"assets_asset"."is_active",
"assets_asset"."is_generic",
"assets_asset"."file_standalone",
"assets_asset"."file_ios",
"assets_asset"."file_android",
"assets_asset"."file_preview",
"assets_asset"."json_metadata",
"assets_asset"."file_icon",
"assets_asset"."file_image",
"assets_asset"."video_mobile",
"assets_asset"."video_standalone",
"assets_asset"."file_coupon",
"assets_asset"."where_to_buy",
COUNT("games_coupon"."device_id" = 8ae83c6fa52765061360f5459025cb85e6dc8905) AS "coupon_saved"
FROM
"assets_asset"
LEFT OUTER JOIN
"games_coupon"
ON ("assets_asset"."id" = "games_coupon"."asset_id")
GROUP BY
"assets_asset"."id"
I need to get that device_id=X into LEFT OUTER JOIN definition below.
How to achieve?
TL;DR:
The condition should be in filter.
qs = (
Asset.objects
.filter(coupons__device_id='8ae83c6fa52765061360f5459025cb85e6dc8905')
.annotate(coupon_saved=Count('coupons'))
)
If you want only count > 0 then it can be filtered.
qs = qs.filter(coupon_saved__gt=0)
Footnotes: A one to many query is compiled to LEFT OUTER JOIN in order to be possible to get also base objects (Asset) with zero children. JOINs in Django are based every times on a ForeignKey to the primary key or similarly on OneToOne or ManyToMany, other conditions are compiled to WHERE.
Conditions in annotation (that you used) are possible e.g. as part of Conditional Expressions but it is more complicated to be used correctly and useful e.g. if you want to get many aggregations with many conditions by one query without subqueries and if a full scan is acceptable. This is probably not a subject of a question.

Use and (&) in queryset only when the parameters extists Django

I don't know if there's a quick way to do this, but what I want to do is a search according to six parameters, where 5 of them come from Select Inputs, and the other one from an Input text, the search needs to be more specific when more options are selected.
Is there a way to do this with lookups? Or I need to make an if for every combination of parameters? If I have to write all the if's, what is the more efficient way? Because if I'll have to do 2^6 if statements to cover all the combinations, that will be a little bit slow. Consider that the parameters doesn't have an specific order, so any combination is possible.
I was thinking in Q objects but I don't know if they work for that, I have only used them for OR satements, I know that they can be used for and statements too, but I don't know if they are different than using comma. Probably something like this:
result = Product.objects.filter(Q(field1=param1)&Q(field2=param2)&Q(field3=param3)&Q(field4=param4)&Q(field5=param5)&Q(field6=param6))
It seems like you probably have access to a form's cleaned_data and you're looking to do something like an "advanced search." In that case, given that all of the form fields are relevant to the search, you could do something like
result = Product.objects.filter(**form.cleaned_data)
since you want to only include fields that are specified you can then add a filtering statement to ensure you're only looking at the relevant fields. Unfortunately, that depends a lot on how your form looks. If your form's non-empty values are all truthy, you could do something like
result = Product.objects.filter(**{k: v for k, v in form.cleaned_data.items() if v})
If on the other hand, you only want to apply filters where the fields have changed from what they populated the form with, you might be well served by
result = Product.objects.filter(**{k: v for k, v in form.cleaned_data.items() if form.changed_data})
Unfortunately, if you have a checkbox named turtle_safe, you'll need to record whether someone's indicated that they want results that are safe for turtles or results that are not safe for turtles. Both of the follow up approaches I've suggested would not apply a filter for "not safe for turtles" and would instead filter for "no preference for turtle safety" (assuming the default is False in the option with changed_data).
If that isn't sufficient, you may be able to avoid posting fields that the user hasn't deliberately selected by disabling / enabling the fields with javascript until the user adds them for their search.
If you end up with that, you may want to set the default values to None and use the second check (so long as None isn't a valid option!).
You do not have to use Q and &. Every filter operation does and with previous. So You will have the same number of if operations as parameters.
q = Product.objects.all()
if param1 != None:
q = q.filter(field1=param1)
if param2 != None:
q = q.filter(field2=param2)
#and so on
It is equivalent to your code but with if.

Change the way the Django Admin sorts integers?

The model has an IntegerField with null=True, blank=True, so it allows the field to have a value of None. When the column is sorted, the Nones are always treated as the lowest values. Is it possible to make Django treat them as the highest values? So when sorted in descending order, instead of:
100 43 7 None
It would go:
None 100 43 7
I realize I could assign an extremely high number instead of None, but for neatness' sake, I was wondering if there were any other options.
Thanks!
Daniel is correct in that the database determines the sort order, and different databases treat the ordering of NULLs differently. However, there are ways to get the DB to give you the order you want.
PostgreSQL is nice enough to actually allow you to append "NULLS FIRST" or "NULLS LAST" to your query (ref).
SELECT * FROM table_name ORDER BY int_field DESC NULLS FIRST;
For MySQL and SQLite, there's an alternative (which will also work in PostgreSQL), as described here. Essentially, if you want nulls last, you would do:
SELECT *, int_field IS NULL AS is_null FROM table_name ORDER BY is_null DESC, int_field DESC;
However, getting Django to execute these queries is a different story alltogether. In Django 1.2, model managers now have a raw() method, documented here, which returns a RawQuerySet, which is like a QuerySet, but can't be stacked (e.g. you can't add a filter() call in there). Of course, instead of stacking, you can just add your lookup parameters to the SQL. Whether or not this functionality is useful to you depends on what you're trying to accomplish. If you simply want to fetch your models in that order then pass the queryset to a view or something, you can do:
YourModel.objects.raw('SELECT *, int_field IS NULL AS is_null FROM table_name ORDER BY is_null DESC, int_field DESC')
If however you want this to be the default ordering for use in the admin and such, you'll need a different approach, perhaps via overriding the manager.
It's not Django that determines the sort order, but the database. And databases have their own rules about how to sort NULLs.
One (rather complicated) possibility would be to implement a custom field that uses a custom database type to sort in the correct order. The details of how you would do this are likely to depend on your database.
Since I'm using Django 1.1 and couldn't use raw(), the simplest way turned out to be to create a "int_sort" field, and populate it with the value of the IntegerField, unless it encountered a None, in which case it would take the value of sys.maxint.
Then, in admin.py, I set the admin_order_field to be the "int_sort" field.