Expect orWhere() to works with andWhere() instead where() - query-builder

I have a query:
topics = await topicRepository.createQueryBuilder('topic')
.leftJoinAndSelect('topic.user', 'user', 'topic.userId = user.id')
.where('topic.categoryId = :id', {
id: categoryId,
})
.andWhere('topic.title like :search', { search: `%${searchKey}%`})
// It should take the first where
.orWhere('user.pseudo like :search', { search: `%${searchKey}%` })
.addOrderBy(filter === 'latest' ? 'topic.created_at' : 'topic.repliesCount', 'DESC')
.take(limit)
.skip(skip)
.getMany();
Generated SQL query is:
SELECT DISTINCT distinctAlias.topic_id as \"ids_topic_id\", distinctAlias.topic_created_at FROM (SELECT topic.id AS topic_id, topic.title AS topic_title, topic.content AS topic_content, topic.created_at AS topic_created_at, topic.views AS topic_views, topic.repliesCount AS topic_repliesCount, topic.categoryId AS topic_categoryId, topic.userId AS topic_userId, topic.surveyId AS topic_surveyId, user.id AS user_id, user.email AS user_email, user.pseudo AS user_pseudo, user.password AS user_password, user.rank AS user_rank, user.avatar AS user_avatar, user.createdAt AS user_createdAt, user.lastActivity AS user_lastActivity, user.signature AS user_signature, user.post_count AS user_post_count, user.updatedAt AS user_updatedAt FROM topic topic LEFT JOIN user user ON user.id=topic.userId AND (topic.userId = user.id) WHERE topic.categoryId = '5' AND topic.title like '%admin%' OR topic.user.pseudo like '%admin%') distinctAlias ORDER BY distinctAlias.topic_created_at DESC, topic_id ASC LIMIT 20
The problem is here:
WHERE topic.categoryId = '5' AND topic.title like '%admin%' OR topic.user.pseudo like '%admin%')
I expected :
WHERE (topic.categoryId = '5' AND topic.title like '%admin%') OR (topic.categoryId = '5' AND topic.user.pseudo like '%admin%')
I want the .orWhere being OR from .andWhere instead .where
I don't find any documentation / issue about this use case.

The precedence of query conditions can be controlled by using the Brackets class:
topics = await topicRepository.createQueryBuilder('topic')
.leftJoinAndSelect('topic.user', 'user', 'topic.userId = user.id')
.where('topic.categoryId = :id', {
id: categoryId,
})
.andWhere(new Brackets(qb => {
qb.where('topic.title like :search', { search: `%${searchKey}%`})
.orWhere('user.pseudo like :search', { search: `%${searchKey}%` });
}))
.addOrderBy(filter === 'latest' ? 'topic.created_at' : 'topic.repliesCount', 'DESC')
.take(limit)
.skip(skip)
.getMany();
(edit: added period to correct syntax)

Related

Dynamo DB Query using begins_with in ruby

Below is the my dynamodb data
Table Name : Inventory
PartitionKey: Name (string)
SortKey : ID (String)
Below is sample data.
Name ID
Fruits Mango
Fruits Mango-Green
Fruits Mango-Green-10
Fruits Mango-Green-20
Fruits Apple
Fruits Apple-Red
Veggie Onion
Veggie Onion-White
Veggie Onion-White-10
How can I add the search to the below code to return all the rows that begins_with "Mango-Green" ? I cant modify the keys or the table data now.
table_name: 'Inventory',
key_condition_expression: "#Name = :Name AND #ID = :ID",
select: "ALL_ATTRIBUTES",
expression_attribute_names: {
"#ID" => "ID",
"#product" => "product"
},
expression_attribute_values: {
":ID" => ID,
":Name" => 'Fruits'
}
more info about ruby interface into code as comments
require 'dotenv/load'
require 'aws-sdk-dynamodb'
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB.html
client_dynamodb = Aws::DynamoDB::Client.new(
region: ENV['AWS_REGION'],
credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
)
# https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/DynamoDB/Client.html#query-instance_method
fruits = client_dynamodb.query({
table_name: "Inventory",
projection_expression: "name, id",
key_conditions: {
"id" => {
attribute_value_list: ["Mango-Green"],
comparison_operator: "BEGINS_WITH",
}
},
scan_index_forward: false, # true => asc ; false => desc
}).items

Wrong order of fetched data from Firestore (Vue.js)

I want to get data from Firestore COLLECTION A, where are IDs of users (createdBy and lastUpdateBy). Users data (firstName, surName...) are in COLLECTION B. If I fetch data from Firestore and use OrderBy lastUpdateBy, the order is firstly by USER (from the newest to the oldest), after that by lastUpdateAt, as I just want. Please, can you help me, what is wrong?
// Fetch Data from the Firestore
let refUsers = db.collection('users')
db.collection('campTypes').orderBy("lastUpdateAt", "asc")
.onSnapshot((snapshot) => {
snapshot.docChanges().forEach(change => {
refUsers.doc(change.doc.data().createdBy).get()
.then(createdByUser => {
var cbUser = createdByUser.data()
refUsers.doc(change.doc.data().lastUpdateBy).get()
.then(updatedByUser => {
var ubUser = updatedByUser.data()
if(change.type === 'added') {
this.campTypes.unshift({
id: change.doc.id,
name: change.doc.data().name,
status: change.doc.data().status,
createdAt: change.doc.data().createdAt,
createdBy: change.doc.data().createdBy,
createdByUser: cbUser,
lastUpdateAt: change.doc.data().lastUpdateAt,
lastUpdateBy: change.doc.data().lastUpdateBy,
lastUpdateByUser: ubUser
})
}
})
})
})
})
Image: Datatable, where the newest update isn't on the top

Find where results in columns from an array of column names in TypeORM

At the moment I have the following query:
return await this.siteRepository.find({
where: [{ id: Like(`%${q}%`) }, { name: Like(`%${q}%`) }]
});
But I would like to be able to pass a list of column names to be used for the query from an array and not write each one of them manually.
const columns = ["id","name", "lastName", "age"]
const query = {};
return await this.siteRepository.find({
where: columns.map(column=>{(query[`${column}`] = `Like("%${q}%")}`)})
});
Is this even possible? I'm starting to feel like it currently is not.
I didn't manage to accomplish what I wanted with the Repository TypeORM methods but I did manage to do it with the QueryBuilder
Here is my solution
const res = ['id', 'name'].map(item => `${item} LIKE :q`).join(' OR ');
return await this.siteRepository
.createQueryBuilder()
.where(res, { q: `%${q}%` })
.getMany();
Which yields a query of
SELECT `User`.`id` AS `User_id`, `User`.`name` AS `User_name`, `User`.`lastName` AS `User_lastName`, `User`.`active` AS `User_active` FROM `user` `User` WHERE name LIKE ? OR id LIKE ?

How can i search by field of joined table in graphql and nestjs

i create two table tag and tagTranslation.
following is field of each
Tag
id, type, transloations, creaed_at, updated_at
TagTranslation
id, tag_id, name, language
I use graphql, i want to get tag list by type, name and language
{ tags(name:"tag1", language:"en, type:3){
id,
type,
translations{
id,
name,
language,
}
}
}
so I create resolver like following
#Query(returns => [Tag])
tags(#Args() tagArgs: TagArgs): Promise<Tag[]> {
const where = {
...(tagArgs.type) && {type: tagArgs.type}
};
const include_where = {
...(tagArgs.name) && {name: { [Op.like]: `%${tagArgs.name}%` }},
...(tagArgs.language) && {language: tagArgs.language}
};
return this.tagService.findAll({
where: where,
include: {
as: 'translations',
model: TagTranslation,
where: include_where,
required: true,
}
});
}
#Query(returns => Tag)
tag(#Args({name: 'id', type: ()=> Int}) id: number): Promise<Tag>{
return this.tagService.get(id)
}
#ResolveProperty()
async translations(#Parent() tag): Promise<TagTranslation[]>{
const { id } = tag;
return await this.tagTranslationService.findAll({tag_id: id});
}
when i call tags, the query is called twice
first, A query is executed to get the results I want.
but second,
SELECT `id`, `tag_id`, `name`, `language`, `created_at`, `updated_at` FROM `tag_translation` AS `TagTranslation` WHERE `TagTranslation`.`tag_id` = 1;
query is called once more, so i can't get results what i want.
I think second query is called because of ResolveProperty, I remove ResolveProperty. after that, tag query is not include tagtranslation info...
how can i solve that problem ? or is there another idea??
how can i solve that problem ? or is there another idea??
Relations between entities should be resolved on a field resolver (#ResolveProperty()) level because when someone requests only id and type, you will still perform additional, not needed join on TagTranslation in sql query.

Using subscribe on a GroupedObservable in map

I have an array of objects like the follwing:
private questions: Question[] = [
{
title: "...",
category: "Technologie",
answer: `...`
},
{
title: "...",
category: "Technologie",
answer: `...`
},
{
title: "...",
category: "eID",
answer: `...`
}
];
And I would like to group them by categories, filter them based on a value and return the result as an array. Currently, I'm using this:
Observable
.from(this.questions)
.groupBy(q => q.category)
.map(go =>
{
let category: Category = { title: go.key, questions: [] };
go.subscribe(d => category.questions.push(d));
return category;
})
.filter(c => c.title.toLowerCase().indexOf(value.toLowerCase()) >= 0 || c.questions.filter(q => q.title.toLowerCase().indexOf(value.toLowerCase()) >= 0).length > 0)
.toArray()
This finds the question with the value in the category title but not the one with the value in the question title. I think that's because I'm using a subscribe in map, therefore, the questions are not yet available in the filter method, so I was wondering if there's a possibility to wait for the subscribe to end before going into filter. My research pointed me to flatMap but I can't get it to do what I want.
EDIT
I figured out that I can fix the issue like this:
Observable
.from(this.questions)
.filter(q => q.category.toLowerCase().indexOf(value.toLowerCase()) >= 0 || q.title.toLowerCase().indexOf(value.toLowerCase()) >= 0)
.groupBy(q => q.category)
.map(go =>
{
let category: Category = { title: go.key, questions: [] };
go.subscribe(d => category.questions.push(d));
return category;
})
.toArray()
But I'm still interested in the answer.
When you use groupBy, you get a grouped observable that can be flattened with operators like concatMap, mergeMap, switchMap etc. Within those operators, grouped observables can be transformed separately for each category, i.e. collect the questions together into an array with reduce, and then create the desired object with map.
Observable
.from(questions)
.groupBy(q => q.category)
.mergeMap(go => {
return go.reduce((acc, question) => { acc.push(question); return acc; }, [])
.map(questions => ({ title: go.key, questions }));
})
.filter(c => "...")
.toArray()

Resources