Mongodb query join optimization - sql

Lets say I have 3 collections:
Grandparents
{
_id:1,
name:old foo
}
Parents
{
_id:2,
grandparentId:1,
name: foo
}
Person
{
_id: 3,
parentId:2,
name: youngfoo
}
How do i optimize a query to find all person where person's grandparent's name = x?
Name is not unique in this case.
What i can think of so far:
1. Query all grandparents with name = x
2. Query all parents where grandparent ids == grandparent ids gotten from step 1
3. Query all persons where parent ids == parent ids gotten from step 2.
Doesn't feel very efficient. Can mongodb experts out there help me? Thanks!

I think you want to do something like this. I did not test this query, but this is what i would try in your place. This is only possible on 3.6 mongodb, because it supports multiple joins. The idea is to join all 3 collections. First join is Parents and Person by Parents id, and Persons "parentsId". Second join is Parents and Grandparents. Then you filter out by grandparent name and you will get a document that contains that grandparent, his son (parent), and his grandson(person). Then you just project the person.
db.Parents.aggregate([
{
$lookup:{
from:"Person",
localField:"_id",
foreignField:"parentId",
as:"Person"
}
},
{
$unwind:"$Person"
},
{
$lookup:{
from:"Grandparents",
localField:"grandparentId",
foreignField:"_id",
as:"Grandparents"
}
},
{
$unwind:"$Grandparents"
},
{$match:{Grandparents.name:"x"}},
{$project:{Person.name:1,Person._id:1}}
}])
I think this will do the trick

Related

How to retrieve recursive childs on TypeORM

I've been experiencing some troubles lately trying to figure out a way to load recursively the childs of an entity on TypeORM (v0.3.10 on the moment I asked this question).
Right now, my objective is finding recursively all the childs from a given parentId (itself included).
For example:
Id
ParentId
1
NULL
2
1
3
2
4
NULL
Calling ID=1 should return [1, 2, 3], since the ID itself shall be included, 2 is child of 1 and 3 is child of 2.
Calling ID=2 should return [2, 3], since the ID itself shall be included and 3 is child of 2.
The entity itself looks like this at the moment:
#Entity(TableNamesConstants.GROUPS)
export class Group {
#PrimaryGeneratedColumn()
id: number;
#ManyToOne(() => Group, { nullable: true })
#JoinColumn({ name: 'parentId' })
parent?: Group;
#Column('int', { nullable: true })
parentId?: number;
}
I've tried several ways (Tree entities, trying to translate it from SQL to a TypeORM query following others suggestions, etc.) to no luck to achieve a N-depth query.
Can I have some guidance regarding this? Thanks in advance.

Sequelize Query : How to do parent and child fetch query when condition on 1 of the child is valid?

I have a parent Table "Orders" and child table "Items". They have one to many relationships. I need to do the query using the "Orders" table. I need the Items in the "include". I want to query the order which has a particular Item. But if it also has other items those too should get selected.
A toned-down version of my query looks something like this:
let term = 'phone';
Orders.findAll({
include: [
model: Item,
where: {itemName: { [Op.like]: `%${term}%` }}
]
})
The query happens but if an order has multiple items and if the item name doesn't have the 'term' in it then that particular item gets excluded. Only the item with the term is returned with the order record.
I want if the where condition is true for 1 of the items, all the other items should also get included.
For example, let's say I search for the term 'phone', I want to get the orders which looks something like the below:
Order No: 123ABC
Order Items:
Android Phone
Smart Watch
In my case, the smartwatch doesn't come with the "include" option.
I will be grateful if anyone can help me find a solution.
Thank You.
If this sql can help you..
select *
from Order o
inner join item i on i.orderId = o.id and i.itemName like %term%
inner join item i2 on i2.orderId = o.id
you can use sequelize like this!
let term = 'phone';
Orders.findAll({
include: [
{
model: Item,
as : 'searchItem',
attributes : [],
where: {itemName: { [Op.like]: `%${term}%` }},
{
model: Item,
}
]
})

SPARQL Query to find someone's grandfather

I'm just starting out learning SPARQL and I'm finding it a little difficult to navigate finding items and properties. Could anyone advise me on how to write a query which returns a person's grandfather? E.g. Tom Cruise's grandfather
Thanks!
May be you can try one of these:
select * where {
:personID :hasParent/:hasFather ?grandFather
}
select * where {
:personID :hasFather/:hasFather ?grandFather_fatherSide
}
select * where {
:personID :hasMother/:hasFather ?grandFather_motherSide
}
Legend:
:personID = a specific ID
:hasParent = relations a person with her father or mother
:hasFather = relations a person with her father
:hasMother = relations a person with her mother

get JOIN table as array of results with PostgreSQL/NodeJS

I'm creating an app where users are able to create questions, and others can upvote/downvote them.
The following is a part of my sql schema:
CREATE TABLE "questions" (
id SERIAL,
content VARCHAR(511) NOT NULL,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
CONSTRAINT pk_question PRIMARY KEY (id)
);
CREATE TABLE "votes" (
id SERIAL,
value INT,
question_id INT NOT NULL,
CONSTRAINT pk_vote PRIMARY KEY (id),
CONSTRAINT fk_question_votes FOREIGN KEY (question_id) REFERENCES questions (id) MATCH SIMPLE ON UPDATE CASCADE ON DELETE CASCADE
);
What I would like to have is Postgres giving me each question with an array of votes, like that:
[{ // a question
id: 1,
content: 'huh?',
votes: [{ // a vote
id: 1,
value: 1
}, { // another vote
id: 2,
value: -1
}]
}, { /*another question with votes*/ }]
I looked at aggregate functions (like array_agg()) but it gave me only the values. A JOIN gave me a question joined with a vote, and would force me to do server side operations, which I would prefer not to.
Is there any way to do that? Is my reasoning regarding what I want to obtain wrong?
Thanks for your time.
This is easy to do with pg-promise:
function buildTree(t) {
const v = q => t.any('SELECT id, value FROM votes WHERE question_id = $1', q.id)
.then(votes => {
q.votes = votes;
return q;
});
return t.map('SELECT * FROM questions', undefined, v).then(a => t.batch(a));
}
db.task(buildTree)
.then(data => {
console.log(data); // your data tree
})
.catch(error => {
console.log(error);
});
The same as above, but using ES7 async/await syntax:
await db.task(async t => {
const questions = await t.any('SELECT * FROM questions');
for(const q of questions) {
q.votes = await t.any('SELECT id, value FROM votes WHERE question_id = $1', [q.id]);
}
return questions;
});
// method "task" resolves with the correct data tree
API: map, any, task, batch
Related questions:
Get a parents + children tree with pg-promise
Conditional task with pg-promise
And if you want to use just a single query, then using PostgreSQL 9.4 and later syntax you can do the following:
SELECT json_build_object('id', q.id, 'content', q.content, 'votes',
(SELECT json_agg(json_build_object('id', v.id, 'value', v.value))
FROM votes v WHERE q.id = v.question_id))
FROM questions q
And then your pg-promise example would be:
const query =
`SELECT json_build_object('id', q.id, 'content', q.content, 'votes',
(SELECT json_agg(json_build_object('id', v.id, 'value', v.value))
FROM votes v WHERE q.id = v.question_id)) json
FROM questions q`;
const data = await db.map(query, [], a => a.json);
And you definitely will want to keep such complex queries in external SQL files. See Query Files.
Conclusion
The choice between the two approaches presented above should be based on the performance requirements of your application:
The single-query approach is faster, but is somewhat difficult to read or extend, being fairly verbose
The multi-query approach is easier to understand and to extend, but it is not great for performance, due to dynamic number of queries executed.
UPDATE-1
The following related answer offers more options, by concatenating child queries, which will give a much improved performance: Combine nested loop queries to parent result pg-promise.
UPDATE-2
Another example added, using ES7 async/await approach.
Please think simple way, May be I am right, I use knex js
let allpost = knex
.select([
'questions.id',
'question.content',
knex.raw('json_agg(v.*) as votes')
])
.from('questions')
.leftJoin('votes as v', 'questions.id', 'v.question_id')
.groupBy('questions.id');
sql-toolkit does exactly this. It's a node library built for pg-promise which allows you to write regular native SQL and receive back properly structured (nested) pure business objects, without either having to split up the query or rewrite it with json_build_object.
For example:
class Article extends BaseDAO {
getBySlug(slug) {
const query = `
SELECT
${Article.getSQLSelectClause()},
${Person.getSQLSelectClause()},
${ArticleTag.getSQLSelectClause()},
${Tag.getSQLSelectClause()}
FROM article
JOIN person
ON article.author_id = person.id
LEFT JOIN article_tags
ON article.id = article_tags.article_id
LEFT JOIN tag
ON article_tags.tag_id = tag.id
WHERE article.slug = $(slug);
`;
return this.one(query, { slug });
// OUTPUT: Article {person: Person, tags: Tags[Tag, Tag, Tag]}
}
The select clause uses the business object "getSQLSelectClause" methods to save tedium in typing the columns, as well as ensure no collisions of names (nothing magical going on, and could just be written out instead).
The this.one is a call into sql-toolkits base DAO class. It is responsible for structuring the flat result records into a nice nested structure.
(Also notice that it is "one" which matches our mental model for the SQL. The DAO methods for one, oneOrNone, many, and any ensure their count against the number of generated top level business objects - not the number of rows the sql expression returns!)
Check out the repository for details on how to set it up on top of pg-promise. It's strictly an enhancement, and does not seek to abstract out pg-promise (you still set up pg-promise and can use it directly). (Disclamer, I am the author of sql-toolkit.)

Count one-to-one relationship in grails

I have a problem doing a query. That I want to do is access with a query to the data in my "String code" in MyDomainA through a query from MyDomainB. The relationship between the tables is unidirectional one to one.
Is possible use a gorm way to do this??
Domain A:
class LicenceType {
String code
String description
Double price
static constraints = {
}
}
TABLE DOMAIN A
code description
A this is A
B this is B
C this is C
Domain B: (have the unidirectional relationship)
class VoiceUser {
LicenceType licenceType
String username
String email
String nameID
}
TABLE DOMAIN B
User
1
2
3
4
That I want to do is know how many users have the same code(code is a column of DomainA and both tables have a unidirectional relationship as I indicated before).
This is that I'm trying to do that is wrong...
Controller:
def resulta = VoiceUser.executeQuery('SELECT a.code, COUNT(b.nameID) FROM VoiceUser AS b INNER JOIN b.licenceType AS a GROUP BY a.code')
def resultCount = resulta[0]
This is some example result that I hope...
Users with code A = 2
Users with code B = 2
Users with code C = o
The trick is to do a group by on the code and a count() on the user. You can do this using either HQL or a criteria query.
HQL
Here's an example in HQL:
VoiceUser.executeQuery('SELECT licence.code, COUNT(user) FROM VoiceUser AS user INNER JOIN user.licenceType AS licence GROUP BY licence.code')
If you're familiar with SQL, most of this should make sense right away. An important difference is the syntax for joining domain classes. HQL deals with domain classes, not tables.
Criteria query
And here's the equivalent criteria query.
VoiceUser.withCriteria {
projections {
licenceType {
groupProperty('code')
}
count('id')
}
}
Alternative queries
The queries shown above return a List<List> like this:
[
['A', 2],
['B', 2],
['C', 0]
]
If you provide a LicenceType (or its code) as input to the query, then you can get the count for just that LicenceType. For instance, here are examples which retrieve the user count for licence code 'A'.
HQL
def result = VoiceUser.executeQuery('SELECT COUNT(user) FROM VoiceUser AS user INNER JOIN user.licenceType AS licence WHERE licence.code = :code', [code: 'A'])[0]
Criteria query
def result = VoiceUser.createCriteria().get {
licenceType {
eq('code', 'A')
}
projections {
count('id')
}
}
Additional resources
I've got a series of articles which explain HQL, criteria, and where queries in detail; such as how to use projections and joins. Feel free to check them out.