Sort direct neighbor nodes (books) by attribute of 2nd degree neighbors (authors) for user book list? - sorting

By agheranimesh via Slack:
This is my graph, named LibraryGraph:
My graph query:
FOR v, e, p IN 1..2 OUTBOUND "User/001" GRAPH "LibraryGraph"
SORT p.vertices[2].Name
RETURN p.vertices[1]
It's not giving me result I want. I want a book list sorted by author name and books without author should come last (B2, B3, B1, B4, B5).
Script to re-create the data (arangosh --javascript.execute <file>):
db._createDatabase('Library')
db._useDatabase('Library')
const User = db._create('User')
const Book = db._create('Book')
const Author = db._create('Author')
const User_Book = db._createEdgeCollection('User_Book')
const Book_Author = db._createEdgeCollection('Book_Author')
User.save({ '_key': '001', 'UserName': 'U1' })
Book.save({ '_key': 'B1', 'Name': 'B1' })
Book.save({ '_key': 'B2', 'Name': 'B2' })
Book.save({ '_key': 'B3', 'Name': 'B3' })
Book.save({ '_key': 'B4', 'Name': 'B4' })
Book.save({ '_key': 'B5', 'Name': 'B5' })
Author.save({ '_key': 'A', 'Name': 'A' })
Author.save({ '_key': 'B', 'Name': 'B' })
Author.save({ '_key': 'X', 'Name': 'X' })
Author.save({ '_key': 'Y', 'Name': 'Y' })
Author.save({ '_key': 'Z', 'Name': 'Z' })
User_Book.save({ '_from': 'User/001', '_to': 'Book/B1' })
User_Book.save({ '_from': 'User/001', '_to': 'Book/B2' })
User_Book.save({ '_from': 'User/001', '_to': 'Book/B3' })
User_Book.save({ '_from': 'User/001', '_to': 'Book/B4' })
User_Book.save({ '_from': 'User/001', '_to': 'Book/B5' })
Book_Author.save({ '_from': 'Book/B2', '_to': 'Author/A' })
Book_Author.save({ '_from': 'Book/B3', '_to': 'Author/B' })
Book_Author.save({ '_from': 'Book/B1', '_to': 'Author/X' })
Book_Author.save({ '_from': 'Book/B1', '_to': 'Author/Y' })
Book_Author.save({ '_from': 'Book/B1', '_to': 'Author/Z' })
const graph_module = require('org/arangodb/general-graph')
const graph = graph_module._create('LibraryGraph')
graph._addVertexCollection('User')
graph._addVertexCollection('Book')
graph._addVertexCollection('Author')
graph._extendEdgeDefinitions(graph_module._relation('User_Book', ['User'], ['Book']))
graph._extendEdgeDefinitions(graph_module._relation('Book_Author', ['Book'], ['Author']))

Instead of a single traversal with variable depth (1..2) to cover both cases, books with and without authors, I suggest to use two traversals:
FOR book IN OUTBOUND "User/001" GRAPH "LibraryGraph"
LET author = FIRST(
FOR author IN OUTBOUND book._id GRAPH "LibraryGraph"
SORT author.Name
LIMIT 1
RETURN author.Name
) OR "\uFFFF"
SORT author
RETURN book
First we traverse from User/001 to the linked books. Then we do a second traversal from each book to the linked authors. This may return 0, 1 or multiple authors. The sub-query caps the result to the alphabetically first author (e.g. X out of X, Y, Z) and returns the name.
In the scope of the main query, we take the author name or fallback to a value that ends up last if sorted (null would end up first, which is not desired here). Then we sort the books by author name and return them:
Another way to achieve this result, yet harder to understand:
FOR v, e, p IN 1..2 OUTBOUND "User/001" GRAPH "LibraryGraph"
LET name = p.vertices[2].Name OR "\uFFFF"
COLLECT book = p.vertices[1] AGGREGATE author = MIN(name)
SORT author
RETURN book
The traversal returns paths with 2 or 3 vertices...
[0] [1] [2]
User/001 --> Book/B2
User/001 --> Book/B2 --> Author/A
User/001 --> Book/B3
User/001 --> Book/B3 --> Author/B
User/001 --> Book/B4
User/001 --> Book/B5
User/001 --> Book/B1
User/001 --> Book/B1 --> Author/Y
User/001 --> Book/B1 --> Author/X
User/001 --> Book/B1 --> Author/Z
The authors at index 2 (p.vertices[2]) or a fallback value is temporarily stored in a variable name. Then the book vertices are grouped together to eliminate duplicates (caused by the variable traversal depth, which returns e.g. 001-->B2 but also the longer path 001-->B2-->A).
Aggregation is used to pick the author name with the lowest value (MIN), which usually means the alphabetically first - it probably doesn't work correctly for some languages and character sets however, whereas SORT does sort correctly based on the rules of the set language (can only be one per DBMS instance).
The grouping result - distinct book documents - is sorted by author names and returned.

Related

Sort a dictionary into two different lists based on group

import requests
from operator import itemgetter
foods = [{'name': 'Daisy' , 'group': 'A', 'eating': 'yes', 'feasting': 'yes', 'fasting': 'no', 'sleeping': 'no'},
{'name': 'Donny', 'group': 'B', 'eating': 'maybe', 'feasting':'maybe', 'fasting':'maybe', 'sleeping': 'maybe'},
{'name': 'Dwane', 'group': 'A', 'eating': 'no', 'feasting':'yes', 'fasting': 'no', 'sleeping': 'yes'},
{'name': 'Diana', 'group': 'B', 'eating': 'never', 'feasting':'never', 'fasting':'never', 'sleeping':'never'}]
def main():
group = sorted(foods, key=itemgetter('group'))
group_a = []
group_b = []
print(group)
main()
Hi there, I need help with the next step of this code. I would like to place the two dictionaries with group "A" in the empty list group_a. I would also like to place the two dictionaries with group "B" in the empty list group_b.
I am not sure how to go about this. Previously I tried:
for row in foods:
if 'A' in row:
group_a.append(row)
else:
group_b.append(row)
How ever that did not work.
Does anyone have an idea of how to populate these two empty lists according to group?

How to delay sequence emitting in Rxjs

I have an observable:
messages: string[] = ['a', 'b', 'c'];
const source = from(messages)
How do you delay it so when anyone subscribes to it, it will delay for n second to emit the items?
So:
source.subscribe(i => console.log(i));
// output ...n seconds... 'a' ...n seconds... 'b' ...n seconds... 'c'
You can combine the stream with an interval using zip:
zip(
from(['a', 'b', 'c', 'd']),
interval(1000),
(a, b) => a
)
.subscribe(console.log);
zip would combine the nth elements of the each stream into an array. That's way we use a selector function: (a, b) => a. It ensures that only elements from the first stream are used. The interval stream is only used for delaying emission.
I had the same problem and I solved it as the following code
const {from, of} = rxjs;
const {concatMap, delay} = rxjs.operators;
from(['a', 'b', 'c'])
.pipe(concatMap((msg) => of(msg).pipe(delay(1000))))
.subscribe(console.log);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>
You can just use the .delay() operator:
messages: string[] = ['a', 'b', 'c'];
const source = from(messages).pipe(
delay(1000)//delay for 1 second
)
Remember to import delay:
import { delay } from 'rxjs/internal/operators';
const source = from(['a', 'b', 'c', 'd']);
const delayPerElements = source
.pipe(map(v => of(v).pipe(delay(1000))), concatAll());
delayPerElements.subscribe(it => console.log(it));
// ... a ... b ... c ... d
I don't know if this is the best way but it works for me. Hope this help someone in the future.
As #Igno Burk suggestion:
const source = from(['a', 'b', 'c', 'd']);
const delayPerElements = source
.pipe(concatMap(v => of(v).pipe(delay(1000))));
delayPerElements.subscribe(it => console.log(it));
// ... a ... b ... c ... d
return delayPerElements;

ionic 2 time wise sorting

how to short my list time wise ascending/ descending order
i have array like this:
this.array= [
{name: A, time: 10:00am},
{name: b, time: 10:05am},
{name: c, time: 10:02am},
{name: e, time: 09:00am}
]
i want to show this array time wise acceding order like:
this.array= [
{name: e, time: 09:00am},
{name: A, time: 10:00am},
{name: c, time: 10:02am},
{name: b, time: 10:05am},
]
The best way would be to use MomentJS:
array= [
{'name': 'A', 'time': '10:00am'},
{'name': 'b', 'time': '10:05am'},
{'name': 'c', 'time': '10:02am'},
{'name': 'e', 'time': '09:00am'}
]
sorted = array.sort(function(a, b) {
aT = new moment(a.time, 'HH:mm:ss a');
bT = new moment(b.time, 'HH:mm:ss a');
return aT.isBefore(bT) ? -1 : bT.isBefore(aT) ? 1 : 0;
});
To get MomentJS do:
npm install --save moment
then use it:
import * as moment from 'moment';
See https://momentjs.com/docs/#/use-it/typescript/.

Replay series of events with timestamps using RxJS

If I have an array of events that include a utc timestamp and event data like as follows:
[{utcts: , data: , ... ];
how would you use RxJS to "replay" those events with the correct time differentials between each item in the array? Assume the array is ordered by the utcts field so the first item has the lowest value.
here is a very basic set of data to get started:
var testdata = [
{utcts: 1, data: 'a'},
{utcts: 4, data: 'b'},
{utcts: 6, data: 'c'},
{utcts: 10, data: 'd'}
];
Assume the utcts is just the number of seconds from the start of replaying the event which starts at 0 seconds.
Use delayWhen to give you timed replay.
Since utcts given is relative (not absolute) time, don't need to refresh the timestamp inside the data object.
I have added a timestamp to the console log so we can see the elapsed output time.
Note the extra few milliseconds is typical of rxjs process time.
console.clear()
const testdata = [
{utcts: 1, data: 'a'},
{utcts: 4, data: 'b'},
{utcts: 6, data: 'c'},
{utcts: 10, data: 'd'}
];
const replayData = (data) => Rx.Observable.from(data)
.delayWhen(event => Rx.Observable.of(event).delay(event.utcts * 1000))
// Show replay items with output time (in milliseconds)
const start = new Date()
replayData(testdata)
.timestamp()
.subscribe(x => console.log(x.value, 'at', x.timestamp - start, 'ms'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.6/Rx.js"></script>
Ref delayWhen, timestamp
This also works, arguably simpler, not sure which is best.
mergeMap() flattens the inner observable, which is necessary to apply the delay.
const replayData = (data) => Rx.Observable.from(data)
.mergeMap(event => Rx.Observable.of(event).delay(event.utcts * 1000))
Rough pseudo (comes out of my head directly without running to verify) might be something similar to
Observable.scan((acc, value) => ({
delay: delay === NaN ? value.utcts - delay,
value
}), { delay: NaN, value: null })
.mergeMap(({delay, value}) => Observable.from(value).delay(delay))
scan operator is similar to reduce, but emits intermediate values. Using that compute diff between times to get delay those values, then emit values per given delayed time. There are couple of other approaches could work in same way.
This should work in https://rxviz.com (copy-paste there):
const { delay, mergeMap } = RxOperators;
const { from, Observable, of } = Rx;
const testdata = [
{utcts: 0.2, data: 'a'},
{utcts: 2.0, data: 'b'},
{utcts: 2.8, data: 'c'},
{utcts: 4.0, data: 'd'}
];
from(testdata).pipe(
mergeMap(event => of(event).pipe(
delay(event.utcts * 1000)
))
)

Can I use inline views with the criteria API?

Does NHibernate support inline views using criterias? Google doesn't seem to return any relevant results. Here is the query I need to convert preferably using criterias.
SELECT COUNT (incident_count) AS incident_count,
SUM (total_customers) AS total_customers,
MAX (longest_etr) AS longest_etr,
COUNT (DISTINCT crew_count) AS crew_count
FROM (SELECT l.incident_id AS incident_count,
i.downstream_cust_qty_total AS total_customers,
TO_CHAR (MAX (l.etr_datetime),
'MM/DD/YYYY HH24:mi:ss'
) AS longest_etr,
ca.crew_no AS crew_count
FROM district d,
LOCATION l,
ZONE z,
incident_device ID,
incident i,
crew_action ca
WHERE l.dist_no = d.dist_no
AND d.zone_id NOT IN (1008, 1010)
AND ID.location_id = l.location_id
AND ID.incident_id = i.incident_id
AND l.location_id = i.location_id
AND ca.incident_id = i.incident_id
AND ca.location_id = l.location_id
AND ID.call_type_cd IN ('ELEC', 'PLAN')
AND ID.clue_cd NOT IN (248, 258, 975)
AND l.fac_job_status_cd IN ('A', 'D', 'F', 'G', 'P', 'U', 'W')
AND z.zone_id = d.zone_id
AND ca.crew_action_id = l.crew_action_id
AND l.dist_no = 24
AND l.primary_loc_flg = 'T'
GROUP BY l.incident_id, i.downstream_cust_qty_total, ca.crew_no)
I already have everything converted in the where clause. That part was no problem. Which translates into something like.
GetSession().CreateCriteria(typeof (Incident), () => incidentAlias)
// Projection
.SetProjection(
Projections.ProjectionList()
.Add(LambdaProjection.Count<Incident>(i => incidentAlias.IncidentId).As(() => IncidentCount))
.Add(LambdaProjection.Sum<Incident>(i => incidentAlias.DownstreamCustQtyTotal).As(() => TotalCustomers))
.Add(LambdaProjection.Max<Location>(l => locationAlias.EtrDatetime).As(() => LongestEtr))
.Add(LambdaProjection.CountDistinct<CrewAction>(ca => crewActionAlias.CrewNo).As(() => CrewCount))
.Add(LambdaProjection.GroupProperty(() => incidentAlias.IncidentId))
.Add(LambdaProjection.GroupProperty(() => incidentAlias.DownstreamCustQtyTotal))
.Add(LambdaProjection.GroupProperty(() => crewActionAlias.CrewNo))
)
// Aliases
.CreateAlias(() => incidentAlias.Locations, () => locationAlias)
.CreateAlias(() => incidentAlias.IncidentDevices, () => incidentDeviceAlias)
.CreateAlias(() => incidentAlias.District, () => districtAlias)
.CreateAlias(() => districtAlias.Zone, () => zoneAlias)
.CreateAlias(() => locationAlias.CrewAction, () => crewActionAlias)
// Criterias
.Add(() => locationAlias.PrimaryLocFlg == "T")
.Add(() => locationAlias.DistNo == districtNumber)
.Add(() => zoneAlias.ZoneId != 1008)
.Add(() => zoneAlias.ZoneId != 1010)
.Add(SqlExpression.In(() => locationAlias.FacJobStatusCd, new[] { "A", "D", "F", "G", "P", "U", "W" }))
.Add(SqlExpression.In(() => incidentDeviceAlias.CallTypeCd, new [] { "ELEC", "PLAN" }))
.Add(() => incidentDeviceAlias.ClueCd != "248")
.Add(() => incidentDeviceAlias.ClueCd != "258")
.Add(() => incidentDeviceAlias.ClueCd != "975")
.SetResultTransformer(Transformers.AliasToBean<Dto>())
.List<Dto>();
Note that I'm using the Lambda criteria extension. Alternatively, I suppose I could create an additional Dto to select all columns with no aggregate functions then use Linq to do the count/sum/max/count distinct.
I just tried it with HQL and it does not work (would be the same with the Criteria API). What does work, however, is the following:
select
(select count(*) from Table1 t1),
(select sum(*) from Table2 t2)
from DummyTable dt
where rownum <= 1
DummyTable is not doing anything other than being there so NHibernate doesn't cry about it, and rownum <= 1 is there to ensure NHibernate doesn't try to return a list of objects. ;-)

Resources