Create Relationship Between Successive Elements in List - list

I need to be able to create a sequence of events with a relationship between each to describe the succession of events. I know that I can create an ordered list of their node IDs in timestamp order i.e.
MATCH (ch:Checkin)
WITH ch
ORDER BY ch.timestamp ASC
WITH collect(id(ch)) AS checkins
what I'm after is the best way to use this list of node ids to create relationships between each in succession i.e.
Checkin_0 <-FOLLOWS- Checkin_1 <-FOLLOWS- Checkin_2 and so on

You can use the RANGE and UNWIND to enumerate the checkins in the loop:
MATCH (ch:Checkin)
WITH ch ORDER BY ch.timestamp ASC
WITH COLLECT(ch) AS checkins
UNWIND RANGE(1, size(checkins)-1) AS i
WITH checkins[i-1] AS prev,
checkins[i] AS next
MERGE (prev)<-[f:FOLLOWS]-(next)
RETURN prev, f, next

You can also take a look at installing and using APOC Procedures. There's a procedure called apoc.nodes.link() that takes a collection and a relationship type string and creates those relationships between each of the nodes in sequence.
MATCH (ch:Checkin)
WITH ch
ORDER BY ch.timestamp DESC
WITH collect(ch) AS checkins
CALL apoc.nodes.link(checkins, 'FOLLOWS')
RETURN checkins

Related

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.

Postgrest filter does not seem work with fields from related table

I would like to retrieve records of kiscourse where the related joblist.job has a value that contains tech within the joblist.job string value.
This returns the expected results:
/joblist?select=job,kiscourse_id(*)&limit=10&job=ilike.*tech*
This does not:
/kiscourse?select=*,joblist(*)&limit=10&joblist.job=ilike.*tech*
And according to: https://postgrest.com/en/v4.1/api.html#embedded-filters-and-order, this seems to be the intended:
> GET /films?select=*,roles(*)&roles.character=in.Chico,Harpo,Groucho
> HTTP/1.1
Once again, this restricts the roles included to certain characters
but does not filter the films in any way. Films without any of those
characters would be included along with empty character lists.
Is there any way to accomplish the above (besides procedures)?
the joblist.job filter you have there will affect only the entities on the second level, it does not apply to the first level.
The way to read this query
/kiscourse?select=*,joblist(*)&limit=10&joblist.job=ilike.*tech*
is this:
Give me 10 rows from kiscourse with all the columns, and for each row, i want the joblists for that row that are named *tech*

django - queryset.last() not returning right order / last record

Writing code that is generating JSON. The last section of JSON has to be terminated by a ",", so in the code I have:
-- Define a queryset to retrieve distinct values of the database field:
databases_in_workload = DatabaseObjectsWorkload.objects.filter(workload=migration.workload_id).values_list('database_object__database', flat=True).distinct()
-- Then I cycle over it:
for database_wk in databases_in_workload:
... do something
if not (database_wk == databases_in_workload.last()):
job_json_string = job_json_string + '} ],'
else:
job_json_string = job_json_string + '} ]'
I want the last record to be terminated by a square bracket, the preceding by a comma. But instead, the opposite is happening.
I also looked at the database table content. The values I have for "database_wk" are user02 (for the records with a lower value of primary key) and user01 (for the records with the higher value of pk in the DB). The order (if user01 is first or last) really doesn't matter, as long as the last record is correctly identified by last() - so if I have user02, user01 in the query set iterations, I expect last() to return user01. However - this is not working correctly.
What is strange is that if in the database (Postgres) order is changed (first have user01, then user02 ordered by primary key values) then the "if" code above works, but in my situation last() seems to be returning the first record, not the last. It's as if there is one order in the database, another in the query set, and last() is taking the database order... Anybody encountered/solved this issue before? Alternatively - any other method for identifying the last record in a query set (other than last()) which I could try would also help. Many thanks in advance!
The reason is behaving the way it does is because there is no ordering specified. Try using order_by. REF
From: queryset.first()
If the QuerySet has no ordering defined, then the queryset is automatically ordered by the primary key
From: queryset.last()
Works like first(), but returns the last object in the queryset.
If you don't want to use order_by then try using queryset.latest()

Django - how to filter a queryset on multiple reverse lookup matches

I have two models: Order and OrderStatus.
Don't worry about Order, but OrderStatus has the following fields:
order = models.ForiegnKey(Order)
status = models.CharField (choice that can be either ORDERED, IN_TRANSIT, or RECEIVED)
OrderStatuses are created when the Order changes status, so initially there's just an ORDERED status, then later an ORDERED and IN_TRANSIT status, then later an ORDERED, IN_TRANSIT, and RECEIVED status all exist as foriegn keys to one Order. This is to keep track of timings, etc.
I want to find all Orders which have all three statuses. In other words, all orders that have been received and are valid because they have the other two statuses.
This is returning an empty set:
Order.objects.filter(Q(orderstatus__status=OrderStatus.ORDERED) &
Q(orderstatus__status=OrderStatus.IN_TRANSIT) &
Q(orderstatus__status=OrderStatus.RECEIVED))):
... but this is working fine:
Order.objects.filter(orderstatus__status=OrderStatus.ORDERED)
.filter(orderstatus__status=OrderStatus.IN_TRANSIT)
.filter(orderstatus__status=OrderStatus.RECEIVED)
What's the difference here? Is there any way to simplify? I thought this was what Q objects are for.
This means a query where all the fields are required
Order.objects.filter(Q(orderstatus__status=OrderStatus.ORDERED) &
Q(orderstatus__status=OrderStatus.IN_TRANSIT) &
Q(orderstatus__status=OrderStatus.RECEIVED))):
This means that the third filter is applying on the result of second filter and the second filter is applying on the result of first filter
Order.objects.filter(orderstatus__status=OrderStatus.ORDERED)
.filter(orderstatus__status=OrderStatus.IN_TRANSIT)
.filter(orderstatus__status=OrderStatus.RECEIVED)
If you want to do something where you want to get the Order objects if their status is ORDERED, RECEIVED OR IN_TRANSIT you can also do something like this
Order.objects.filter(orderstatus__status__in=[OrderStatus.ORDERED, OrderStatus.IN_TRANSIT, OrderStatus.RECEIVED])

How to create a conversation inbox in Django

I have a Message class which has fromUser, toUser, text and createdAt fields.
I want to imitate a whatsapp or iMessage or any SMS inbox, meaning I want to fetch the last message for each conversation.
I tried:
messages = Message.objects.order_by('createdAt').distinct('fromUser', 'toUser')
But this doesn't work because of SELECT DISTINCT ON expressions must match initial ORDER BY expressions error.
I don't really understand what it means, I also tried:
messages = Message.objects.order_by('fromUser','toUser','createdAt').distinct('fromUser', 'toUser')
and such but let me not blur the real topic here with apparently meaningless code pieces. How can I achieve this basic or better said, general well-known, result?
Your second method is correct. From the Django docs:
When you specify field names, you must provide an order_by() in the QuerySet, and the fields in order_by() must start with the fields in distinct(), in the same order.
For example, SELECT DISTINCT ON (a) gives you the first row for each value in column a. If you don’t specify an order, you’ll get some arbitrary row.
This means that you must include the same columns in your order_by() method that you want to use in the distinct() method. Indeed, your second query correctly includes the columns in the order_by() method:
messages = Message.objects.order_by('fromUser','toUser','createdAt').distinct('fromUser', 'toUser')
In order to fetch the latest record, you need to order the createdAt column by descending order. The way to specify this order is to include a minus sign on the column name in the order_by() method (there is an example of this in the docs here). Here's the final form that you should use to get your list of messages in latest-first order:
messages = Message.objects.order_by('fromUser','toUser','-createdAt').distinct('fromUser', 'toUser')