Lets assume the following model in OrientDB graph:
I have a Profile vertex.
Profiles are connected with 2 edges: Liked and Commented. Both edges have a "value" field indicating the count of the action (or the "weight" of the edge).
So, if user A commented 3 times on posts by user B there will be a Commented edge from user A to user B with value = 3.
Now, say I want to get all the users that interacted with user B (either liked or commented), sorted by the weight of the interaction.
I can do that with the following SQL:
select * from (traverse out from
(select out, sum(value) as value from
(traverse * from (select from Profile where username="B") while $depth < 3)
where #class="Liked" or #class="Commented" group by out order by value desc)
while $depth < 2 ) where #class="Profile" )
But what if I want to know also the weight of the interaction? How do I propagate up the "value" while doing the last traverse?
Edit
According to the suggestion, a simplified version of this query will be:
select expand(out) from (
select out, sum(value) as value from (
select expand(inE("Liked", "Commented")) from Profile
where username="B"
) group by out order by value desc
)
But I still can't find a way to use LET to insert the value into the outer expanded object. $parent does not seem to point to the object that is expanded on the most outer select.
Edit 2
I'm Playing with $parent in every way I can think of. I don't see how you can use it in this case. Again - the problem I'm trying to solve is how to pass the sum(value) to the outer result set. I don't see a way of using LET for it when doing a GROUP BY, and I also don't see a way of using LET when the outer most select is doing an expand (since you can't do other projections together with expand).
Also, the results of using $current do not seem to be what is expected. For example, the following query:
select expand($v) from
(select from
(select expand(inE("Liked", "Commented")) from Profile where #rid=#11:0)
let $v = $current
)
Returns this:
{
"result" : [{
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}, {
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}, {
"#type" : "d",
"#rid" : "#14:4",
"#version" : 2,
"#class" : "Commented",
"value" : 1,
"out" : "#11:165",
"in" : "#11:0"
}
]
}
The same node over and over again, instead of all the edges, which is what I would expect.
I see you're using an old version of OrientDB. With more recent versions you can simplify it by. Example: original query:
select * from (
traverse out from (
select out, sum(value) as value from (
traverse * from (
select from Profile where username="B"
) while $depth < 3
) where #class="Liked" or #class="Commented" group by out order by value desc
) while $depth < 2
) where #class="Profile" )
You could skip some step by using out()/in()/both() passing the Edge's labels/class like:
select expand( out(["Liked","Commented]) ) from Profile where username="B"
However to pass the value you can use variables with LET clause. Example:
select from XXX let $parent.a = value
In this way you set the variable "a" into the upper level context, but you could do also:
select from XXX let $parent.$parent.a = value
To set it 2 levels up.
I haven't tried this with a group by yet, but you should be able to group the result using a sub query. This works for me, where prop1, prop2, prop3, etc are properties of the vertices coming out of the edge (the columns resulting from select out() Profile where username="B")
select outV().prop1, outV().prop2, outV().prop3, value from (
select expand(inE("Liked", "Commented")) from Profile
where username="B"
) order by value desc
Related
Using postgres version 10.13
This is my datatable jsongraphs
id
jsongraph
1
{ "data": {"scopes_by_id": { "121": { "id": 121, "pk": 121, "name": "Prework" } }, "commonsites_by_id": {"123": {"id": 123, "pk": 123, "name": "Somewhere over the rainbow"}}}}
2
{ "data": {"scopes_by_id": { "156": { "id": 156, "pk": 156, "name": "ABC" } }, "commonsites_by_id": {"123": {"id": 123, "pk": 123, "name": "Somewhere over the rainbow"}}}}
I want the distinct values of scope id and site id which should be (121, 123), (156,123)
So I tried
SELECT DISTINCT
jsongraph->'data'->'scopes_by_id'->>'pk' ,
jsongraph->'data'->'commonsites_by_id'->>'pk' from jsongraphs;
This won't work because the path should be like data->scopes_by_id->121->>pk but I cannot know beforehand the value of 121 in between.
Is there a way to get the values of what I need by filling in some kind of wildcard in the path?
E.g.data->scopes_by_id->{*}->>pk like that?
ANd because this is legacy data, it's also hard to change the data itself.
As the nesting level seems to be fixed, you could do something like this:
select j.id, scopes.*, commonsites.*
from jsongraphs j
cross join lateral (
select jsonb_agg(j.jsongraph #> array['data','scopes_by_id', t1.scope_id, 'pk']) as scope_ids
from jsonb_each_text(j.jsongraph #> '{data,scopes_by_id}') as t1(scope_id)
) scopes
cross join lateral (
select jsonb_agg(j.jsongraph #> array['data','commonsites_by_id', t2.site_id, 'pk']) as common_ids
from jsonb_each_text(j.jsongraph #> '{data,commonsites_by_id}') as t2(site_id)
) commonsites
order by id;
The sub-queries extract all key below the respective part (e.g. scopes_by_id) and then uses the #>' operator to access the path for each id inside the original JSON value. And finally all PK values are aggregated back into a single array.
This returns the PK values from each part separately as an array in order to handle the situation where you have a different number of "scope ids" and "commonsite ids"
If you just want "the first" id from each section, you can remove the aggregation and use a LIMIT clause:
select j.id, scopes.*, commonsites.*
from jsongraphs j
cross join lateral (
select j.jsongraph #> array['data','scopes_by_id', t1.scope_id, 'pk'] as scope_id
from jsonb_each_text(j.jsongraph #> '{data,scopes_by_id}') as t1(scope_id)
limit 1
) scopes
cross join lateral (
select j.jsongraph #> array['data','commonsites_by_id', t2.site_id, 'pk'] as common_id
from jsonb_each_text(j.jsongraph #> '{data,commonsites_by_id}') as t2(site_id)
limit 1
) commonsites
order by id;
Not sure on which level you want to apply the "distinct" part for this.
In Postgres 12 or later, you could achieve the same with:
select id,
jsonb_path_query_array(j.jsongraph, 'strict $.data.scopes_by_id.**.pk') as scopes,
jsonb_path_query_array(j.jsongraph, 'strict $.data.commonsites_by_id.**.pk') as common
from jsongraphs ;
order by id;
Online example
SELECT
OUT.EMP_ID,
OUT.DT_TM "DateTimeOut",
IN.DT_TM "DateTimeIn",
cast(timestampdiff( 4, char(timestamp(IN.DT_TM) - timestamp(OUT.DT_TM))) as decimal(30,1))/60 "Duration Out"
FROM (
select
e1.EMP_ID,
e1.DT_TM
from
hr.timeout e1
WHERE
month(e1.DT_TM)=09
and year(e1.DT_TM)=2016
AND e1.CD='OUT'
) OUT
LEFT JOIN (
select
e2.EMP_ID,
e2.DT_TM
from
hr.timeout e2
WHERE
month(e2.dt_tm)=09
and year(e2.dt_tm)=2016
AND e2.CD='IN'
) IN
on out.EMP_ID=in.EMP_ID
Trying to get the closest DateTimeIn match with the DateTimeOut.
Currently it repeats the same DateTimeOut and DateTimeIn multiple times.
I think its normal because your table dont have constraint unique on emp_id, dt_tm and cd. But if you want unique result try this :
With Period as (
select e1.EMP_ID, e1.DT_TM from hr.timeout e1
WHERE month(e1.DT_TM)=09 and year(e1.DT_TM)=2016
)
Select distinct OUT.EMP_ID, IN.DT_TM as DateTimeIn, OUT.DT_TM as DateTimeOut,
TIMESTAMPDIFF(2 , CAST(timestamp(IN.DT_TM) - timestamp(ifnull(OUT.DT_TM, current date)) AS CHAR(22)) ) as DurationSecond
from Period in left outer join Period out
on out.EMP_ID=in.EMP_ID and out.CD='OUT' and in.CD='IN'
order by 1, 2, 3
Like you can see, i use timestampdiff with '2' like first parameter for second (you divided by 60), i use ifnull because you do a left outer join (out.DT_TM can be null) and i do a distinct for unique result
I have a column in my table having values of format:
COURSE_214/MODULE_5825/SUBMODULE_123/..../GOAL_124/ACTIVITY_125.
I need value for goal i.e 124 from goal_124. I am planning to use 'regexp_split_to_array' but don't know how to use elements from array.
I am using postgres 9.2.
You can use split_part like so:
select split_part(split_part('COURSE_214/MODULE_5825/SUBMODULE_123/..../GOAL_124/ACTIVITY_125', '/GOAL_', 2), '/', 1)
i.e.
select split_part(split_part(fieldname, '/GOAL_', 2), '/', 1)
Result:
124
Using json_object():
select json_object(string_to_array(translate(params, '_', '/'), '/'))
from test
json_object
------------------------------------------------------------------------------------------------
{"COURSE" : "214", "MODULE" : "5825", "SUBMODULE" : "123", "GOAL" : "124", "ACTIVITY" : "125"}
(1 row)
select json_object(string_to_array(translate(params, '_', '/'), '/'))->>'GOAL' as goal
from test
goal
------
124
(1 row)
The column has a format suitable for json. I would suggest to change the type of the column to jsonb. The first query may be used as a converter.
After the conversion you would access the parameters in an easy way, e.g.:
select *
from test
where params->>'COURSE' = '214'
and (params->>'GOAL')::int > 120;
Simple select of all GOAL_ parameters (if there are more than one)
select ltrim(elem, 'GOAL_')
from (
select unnest(string_to_array(params, '/')) elem
from test
) sub
where elem like 'GOAL_%'
You may try using regular expressions, getting the string between slashes
select substring(your_column from '^.*/(.*)/.*$') from your_table
If you expect to find in that part the GOAL value, use
select substring(your_column from '/GOAL_(.*)/') from your_table
I got the following code ( no chance to ask the person who wrote it ) and don't see the need of the inner join on deleted. Do I miss something?
I thought, the deleted-table is used in instead-of-delete triggers. In instead-of-inserted triggers it's empty everytime, isn't it in instead-of-update triggers?
CREATE TRIGGER "someTrigger" ON "dbo"."someView"
INSTEAD OF UPDATE
AS
BEGIN
INSERT INTO "someOtherTable"
(
"id"
, "startTime"
, "endTime"
, "duration"
, "resourceId"
, "resourceLocked"
, "timeLocked"
, "groupPrefix"
)
SELECT
"id" = i."id"
, "startTime" = i."startTime"
, "endTime" = i."endTime"
, "duration" = ISNULL( i."duration", 0 )
, "resourceId" = i."resourceId"
, "resourceLocked" = ISNULL( i."resourceLocked", 0 )
, "timeLocked" = ISNULL( i."timeLocked", 0 )
, "groupPrefix" = N'gp'
FROM inserted AS i
INNER JOIN deleted AS d
ON d."id" = i."id"
WHERE ( i."jobType" != 3 )
OR ( i."jobType" = 3 AND i."startTime" != d."startTime" );
END;
In an update trigger, both inserted and deleted are populated. inserted contains the new row values, deleted contains the old row values, i.e. the values from before the UPDATE statement executed.
Here, it seems to be being used in the WHERE clause:
WHERE ( i."jobType" != 3 )
OR ( i."jobType" = 3 AND i."startTime" != d."startTime" );
So, if jobType is 3, we only do the insert if the startTime has been changed.
It should be noted that correlating the rows between inserted and deleted is only possible if at least one key's column(s) are not subject to update. Here, id appears to be a key. Hopefully that id column isn't subject to update or else this trigger may misbehave when such updates occur.
Drools Planner used this to select two distinct objects, ensuring that a rule did not fire twice for a given pair of objects. If o1 and o2 made a match, it fired only once, not twice for (o1,o2) and (o2,01).
when
$stp1 : SubjectTeacherPeriod( $slno : slNo,
$id : id,
$period_day_order : period.dayOrder
)
$stp2 : SubjectTeacherPeriod( slNo > $slno,
id == $id,
period.dayOrder == $period_day_order
)
How do I select a set of three distinct objects? What is a good selection criteria for that?
Same approach should work:
$f1 : SomeFact( ... )
$f2 : SomeFact( id > $f1.id, ... )
$f3 : SomeFact( id > $f2.id, ... )