Orientdb fetch relationship as array in json - orientdb

I want to fetch the vertices connected by the edge to be returned as an array as a property in json.
Eg: If a POST has 10 comments the query should return something like this.
{
#class: Post,
postTitle: "Some title",
comments: [
{
#class: Comment,
content: "First Comment,
someKey: "Some Value"
},
{
#class: Comment,
content: "Second Comment
someKey: "Some Value"
}
]
}
It is possible to get one property of the vertices in an array by this query
select *, out('HAS_COMMENT').content as comments from POST
This will result in an array which has only the value of 'content' property in the Comment class
I need to fetch the full record as a nested json.
UPDATE
If I just use out('HAS_COMMENT') in the query instead of out('HAS_COMMENT').content , it returns the #rid field instead of full record.

I tried your case with this structure :
create class Post extends V
create class Comment extends V
create class HAS_COMMENT extends E
create property Post.postTitle String
create property Comment.content String
create property Comment.someKey Integer
create vertex Post set postTitle="First"
create vertex Post set postTitle="Second"
create vertex Comment set content="First Comment", someKey="1"
create vertex Comment set content="Second Comment", someKey="2"
create vertex Comment set content="Third Comment", someKey="3"
create vertex Comment set content="Fourth Comment", someKey="4"
create vertex Comment set content="Fifth Comment", someKey="5"
create vertex Comment set content="Sixth Comment", someKey="6"
create vertex Comment set content="Seventh Comment", someKey="7"
create vertex Comment set content="Eighth Comment", someKey="8"
create vertex Comment set content="Ninth Comment", someKey="9"
create vertex Comment set content="Tenth Comment", someKey="10"
create vertex Comment set content="Eleventh Comment", someKey="11"
create vertex Comment set content="Twelfth Comment", someKey="12"
create vertex Comment set content="Thirteenth Comment", someKey="13"
create vertex Comment set content="Fourteenth Comment", someKey="14"
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="First Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Second Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Third Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Fourth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Fifth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Sixth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Seventh Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Eighth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Ninth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="First") to (select from Comment where content="Tenth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="Second") to (select from Comment where content="Eleventh Comment")
create edge HAS_COMMENT from (select from Post where postTitle="Second") to (select from Comment where content="Twelfth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="Second") to (select from Comment where content="Thirteenth Comment")
create edge HAS_COMMENT from (select from Post where postTitle="Second") to (select from Comment where content="Fourteenth Comment")
To get the result you want, you can use the following query:
select expand($ris)
let $a = (select from Post where postTitle = 'First'),
$b = (select from Comment where in('HAS_COMMENT').postTitle in $a.postTitle),
$ris = unionAll($a,$b)
Studio:
Console output:
----+-----+-------+---------+---------------+---------------+-------+--------------
# |#RID |#CLASS |postTitle|out_HAS_COMMENT|content |someKey|in_HAS_COMMENT
----+-----+-------+---------+---------------+---------------+-------+--------------
0 |#12:0|Post |First |[size=10] |null |null |null
1 |#13:0|Comment|null |null |First Comment |1 |[size=1]
2 |#13:1|Comment|null |null |Second Comment |2 |[size=1]
3 |#13:2|Comment|null |null |Third Comment |3 |[size=1]
4 |#13:3|Comment|null |null |Fourth Comment |4 |[size=1]
5 |#13:4|Comment|null |null |Fifth Comment |5 |[size=1]
6 |#13:5|Comment|null |null |Sixth Comment |6 |[size=1]
7 |#13:6|Comment|null |null |Seventh Comment|7 |[size=1]
8 |#13:7|Comment|null |null |Eighth Comment |8 |[size=1]
9 |#13:8|Comment|null |null |Ninth Comment |9 |[size=1]
10 |#13:9|Comment|null |null |Tenth Comment |10 |[size=1]
----+-----+-------+---------+---------------+---------------+-------+--------------
About your question underlined in your UPDATE, to get the full record/s from the #rid you can use the expand() function.
Example:
Getting all of the comments connected with the vertex Post where postTitle = 'Second'
Query: select expand(out('HAS_COMMENT')) from Post where postTitle = 'Second'
Studio:
Console output:
----+------+-------+------------------+-------+--------------
# |#RID |#CLASS |content |someKey|in_HAS_COMMENT
----+------+-------+------------------+-------+--------------
0 |#13:10|Comment|Eleventh Comment |11 |[size=1]
1 |#13:11|Comment|Twelfth Comment |12 |[size=1]
2 |#13:12|Comment|Thirteenth Comment|13 |[size=1]
3 |#13:13|Comment|Fourteenth Comment|14 |[size=1]
----+------+-------+------------------+-------+--------------
Hope it helps
EDITED
Query:
select *, $a as comments from Post
let $a = (select #class, content, someKey from Comment where in('HAS_COMMENT').postTitle in $parent.current.postTitle)
Studio:

Related

UPDATE in a specific order

So let's say I have a table:
SELECT * from test_table order by name;
----|----
name|ord
----|----
a |4
a |5
b |2
c |3
d |1
And I want to change the ord such that it matches the alphabetized result of the "order by name" clause. My goal, therefore, is:
SELECT * from test_table order by name;
----|----
name|ord
----|----
a |1
a |2
b |3
c |4
d |5
Is there a good way in Postgres to do this? I have a new sequence I can pull from, I'm just not sure how to do this cleanly in-place, or if that's even possible. Or should I just store the results of the selection, then iterate over and select each name, assigning a new ord value to them? (They all have unique IDs, so the repeat shouldn't matter)
You don't need any sequence for this.
The first step is determinate the new data:
SELECT
*
FROM test_table AS test_table_old
LEFT JOIN (
SELECT
*, row_number() OVER () AS ord_new
FROM test_table
ORDER BY name, ord
) AS test_table_new USING (name, ord)
;
Then convert this to an update:
UPDATE test_table SET
ord = test_table_new.ord_new
FROM test_table AS test_table_old
LEFT JOIN (
SELECT
*, row_number() OVER () AS ord_new
FROM test_table
ORDER BY name, ord
) AS test_table_new USING (name, ord)
WHERE (test_table.name, test_table.ord) = (test_table_old.name, test_table_old.ord)
;
If you need a new sequence, then replace "row_numer() OVER ()" to "nextval('the_new_sequence_name')".

Select only the rows with the latest date in postgres

I only want the latest date for each row (house) the number of entries per house varies sometimes there might be one sale sometimes multiple.
Date of sale | house number | street | price |uniqueref
-------------|--------------|--------|-------|----------
15-04-1990 |1 |castle |100000-| 1xzytt
15-04-1995 |1 |castle |200000-| 2jhgkj
15-04-2005 |1 |castle |800000-| 3sdfsdf
15-04-1995 |2 |castle |200000-| 2jhgkj
15-04-2005 |2 |castle |800000-| 3sdfsdf
What I have working is as follows
Creating VIEW as (v_orderedhouses) ORDER BY house number, street with date ordered on DESCso that latest date is first returned.
I then feed that into another VIEW (v_latesthouses) using DISTINCT ON (house number, street). Which gives me;
Date of sale | house number | street | price |uniqueref
-------------|--------------|--------|-------|----------
15-04-2005 |1 |castle |800000-| 3sdfsdf
15-04-2005 |2 |castle |800000-| 3sdfsdf
This works but seems like there should be a more elegant solution. Can I get to the filtered view in one step?
You do not need to create a bunch of views, just:
select distinct on(street, house_number)
*
from your_table
order by
street, house_number, -- those fields should be in the "order by" clause because it is in the "distinct on" expression
date_of_sale desc;
To make this query faster you could to create an index according to the order by:
create index index_name on your_table(street, house_number, date_of_sale desc);
Do not forget to analyse your tables regularly (depending on the grown speed):
analyse your_table;
You can use window function row_number for this
select * from (
select your_table.*, row_number() over(partition by house_number order by Date_of_sale desc) as rn from your_table
) tt
where rn = 1
This is what I use and it works fast(is a generic solution, as far as I tested every database software can do this):
SELECT t1.date_of_sale, t1.house_number
FROM table t1
LEFT JOIN table t2 ON (t2.house_number = t1.house_number AND t2.date_of_sale>t1.date_of_sale)
WHERE t2.pk IS NULL
GROUP BY t1.date_of_sale, t1.house_number

How to view linked data in OrientDB

I have two tables, described as below.
Table 1: countries
c_id, int
c_name, varchar(20) (PK)
Sample records in this table are.
c_id | c_name
1 | USA
2 | UK
3 | PAK
Table 2: immigrants
i_id, int
i_name, varchar(20)
i_country, int (FK)
Sample records in this table are.
i_id | i_name | i_country
1 | John | 1
2 | Graham | 2
3 | Ali | 3
Question 1:
I want to create two classes (tables) in OrientDB but I need to know what should be the data type of the FK field and what to insert in it. I mean what should I write in query to insert the id of the PK table. Does it need to be #rid? How?
QUESTION 2:
What is the OrientDB SQL for producing the following output.
i_id | i_name | i_country | c_id | c_name
1 | John | 1 | 1 | USA
2 | Graham | 2 | UK
3 | Ali 3 | PAK
with OrientDB you can avoid the creation of FK fields and join operations by using direct Edge links between records. For example:
create class Country extends V;
create class Immigrant extends V;
create class comesFrom extends E;
create property Country.c_id integer;
create property Country.c_name String;
create property Immigrant.i_id integer;
create property Immigrant.i_name String;
insert into Country(c_id, c_name) values (1, USA);
insert into Country(c_id, c_name) values (2, UK);
insert into Country(c_id, c_name) values (3, PAK);
insert into Immigrant(i_id, i_name) values (1, John);
insert into Immigrant(i_id, i_name) values (2, Graham);
insert into Immigrant(i_id, i_name) values (3, Ali);
Now you can connect directly the record you want (I used an Edge called 'comesFrom' and subqueries to link the id fields but you could also directly use the #RID field)
create edge comesFrom from (select from Immigrant where i_id = 1) to (select from Country where c_id = 1);
create edge comesFrom from (select from Immigrant where i_id = 2) to (select from Country where c_id = 2);
create edge comesFrom from (select from Immigrant where i_id = 3) to (select from Country where c_id = 3);
Finally you can query the fields you want without join operations:
select i_id, i_name, out('comesFrom').c_id as c_id, out('comesFrom').c_name as c_name
from Immigrant unwind c_id, c_name
----+------+----+------+----+------
# |#CLASS|i_id|i_name|c_id|c_name
----+------+----+------+----+------
0 |null |1 |John |1 |USA
1 |null |2 |Graham|2 |UK
2 |null |3 |Ali |3 |PAK
----+------+----+------+----+------
In Orient Studio you should obtain a graph like this:
OrientDB does not support join.
You should declare the i_country field in class Immigrant as Link to Country class, or you can use Graph Model if you want bidirectional relationship.
If you want to use Direct link
you can insert immigrants like this
insert into Immigrant set id=1, name ='John', country = (select from country where id = 1 )
then
select id,name,country.id,country.name form Immigrant
you could speed up the insertion and link operations with a javascript function like this:
so the edge is automatically created when you decide to create a new person. You can also decide where to place the person:
Could it be helpful for you ?
I think that you have created a Document DB because I reproduced the issue by creating a Document DB and not a Graph DB and I get, like you, the same exception. This is because if you want to work with Vertices and Edges you must use the Graph DB type.

How can I dynamically choose which column to join on in SQL?

Say I have the following table in SQL Server (2008):
Person
|PersonID|NickName|FirstName|LastName|
|1 |Jim |James |Leahy |
|2 |Mike |Michael |Ross |
|3 |Bob |Robert |Helberg |
I want to know if the following is possible in SQL. I have a main table and I would like to find matches on another table based on the NickName and FirstName columns. However, I want the columns to be joined to in a specific order.
I want to join on the first column from above (NickName or FirstName) which will match the identifier in the table below
|Identifier|PersonId|
|Jim |1 | <- should return PersonId = 1
|Michael |2 | <- should return PersonId = 2
So if there is a match on NickName then choose the row. If there is no match on NickName then look at FirstName.
Is there any way I can query on NickName and FirstName columns in a particular order?
I don't think COALESCE will work since we are not guaranteed that any of the columns will be NULL - we only know that a match may not occur on the column instead.
Please let me know if you need clarification; I may not have worded this well.
Since you didn't specify the schema of main table I assume it has Identifier varchar(100) field, which can contain either nickname or firstname. In this case the query should look like:
select m.identifier,
PersonId = isnull(p1.PersonID,p2.PersonID)
from maintable m
left join persons p1 on p1.nickname = m.identifier
left join persons p2 on p2.firstname = m.identifier

How to group by in DB2 IBM and get the first item in each group?

I have a table like this:
|sub_account|name|email|
|-----------|----|-----|
// same account and same name: email different
|a1 |n1 |e1 |
|a1 |n1 |e2 |
// same account, name and email
|a2 |n2 |e3 |
|a2 |n2 |e3 |
I would like a query to get a table like this:
|sub_account|name|email|
|-----------|----|-----|
// nothing to do here
|a1 |n1 |e1 |
|a1 |n1 |e2 |
// remove the one that is exactly the same, but leave at least one
|a2 |n2 |e3 |
I've tried:
select sub_account, name, first(email)
from table
group by sub_account, name
but as you know "first" doesn't exists in the DB2; what is the alternative to it?
thanks
select sub_account, name, email
from table
group by sub_account, name, email
I am not sure in DB2. In SQL server, you can use DISTINCT for your issue.. You may try.
SELECT DISTINCT sub_acount, name, email
from TABLE
Create a subquery with the table values + a counter (pos) that gets increased for each row and gets reset to 1 each time a new sub-account+name is reached.
The final query filters out all results from the subquery other than those with pos 1 (i.e. first entries of the group):
select *
from (
select sub_account, name, email,
ROW_NUMBER() OVER (PARTITION BY sub_account, name
ORDER BY email DESC) AS pos
from table
)
where pos = 1
I found a way:
SELECT sub_account,
name,
CASE WHEN split_index=0 THEN MyList ELSE SUBSTR(MyList,1,LOCATE('|',MyList)-1) END
FROM (select sub_account, name, LISTAGG(email,'|') as MyList, LOCATE('|',LISTAGG(LB_ARTICLE_CAISSE,'|')) AS split_index
from TABLE
group by sub_account, name) AS TABLEA
This function will aggregate your mail and after split it and take the first one