Why is double underscore needed here for accessing table field? - postgresql

I came across a GitHub issue about sorting rows with TypeORM. I found this comment did work for my problem.
Quote:
async sortWithRelations(entityRepository) {
// Assuming repository classname is RepoX
let repoOptions = {
relations: ['relationA', 'relationB'],
where: qb => {
// Filter if required
qb.where('RepoX__relationA.fieldY = :val', {val: 'searchedValue'});
// Then sort
qb.orderBy({
'RepoX__relationA.fieldYYY': 'DESC',
'RepoX__relationB.fieldZZZ': 'DESC'
// '{repositoryClassName}__{relationName}.fieldName
// [+ __{childRelations} for every child relations]
});
}
};
However, I have no idea why RepositoryClassName__ accompanied with double underscore is needed to access the table column?
'RelationName.FieldName': 'DESC' will result in error instead.

Related

Modify Solr Query by adding additional filters in TYPO3

I'm trying to add custom filters to the query (TYPO3 v10, EXT:solr 11.2). However, this doesn't want to work.
After I simplified the use-case significantly and debugged it, I'm still not further, but rather more confused.
Filter works, if added via TypoScript:
plugin.tx_solr {
search {
filter {
jobTitle = title:Dev*
}
}
}
The same filter added via modifySearchQuery-hook does not work:
public function modifyQuery(Query $query)
{
$filterQuery = new FilterQuery([
'key' => 'jobTitle2',
'value' => 'title:Dev*',
]);
return $query->addFilterQuery($filterQuery);
}
When debugging the query, both filters look the same.
Thanks to Guido, who hit me on the right point: sometimes, keys are keys.
In the hook, the array-keys for FilterQuery have to be key and query (not value, as I've used)
public function modifyQuery(Query $query)
{
$filterQuery = new FilterQuery([
'key' => 'jobTitle2',
'query' => 'title:Dev*', // <-- correct key
]);
return $query->addFilterQuery($filterQuery);
}

Concat two observables for firestore query on multiple fields

I am trying to get a user search functionality working in my AngularFire app.
As firestore doesn't support these queries I thought it would be enough to query the fields separately
getUsersByName(searchValue: string) {
const firstNames = this.afs.collection<IUser>('user', ref => ref.orderBy('firstname').startAt(searchValue).endAt(searchValue+'\uf8ff')).valueChanges({ idField: 'id' });
const lastNames = this.afs.collection<IUser>('user', ref => ref.orderBy('lastname').startAt(searchValue).endAt(searchValue+'\uf8ff')).valueChanges({ idField: 'id' });
return concat(firstNames, lastNames);
}
This only works for the firstNames though. Only the first Observable is being used. I think I don't understand the concat operator but it's not clear to me according the docs what the current best solution would be for this problem.
you could use zip operator
const firstNames: Observable<string>
const lastNames: Observable<string>
zip(firstNames,lastNames).subscribe(
([firstName,lastName]) => { console.log(firstName,lastName);}
)
if firstNames and lastNames emit only one item, combineLatest([firstNames,lastNames]) will be more readable
great link to learn how to use these operators https://indepth.dev/posts/1114/learn-to-combine-rxjs-sequences-with-super-intuitive-interactive-diagrams
The reason this only works for first name is because of how concat works; it will only use one observable at a time until it completes, but the firestore observables are long lived and will not complete.
You should use merge instead of concat.
import { merge } from 'rxjs';
getUsersByName(searchValue: string) {
const firstNames = this.afs.collection<IUser>('user', ref => ref.orderBy('firstname').startAt(searchValue).endAt(searchValue+'\uf8ff')).valueChanges({ idField: 'id' });
const lastNames = this.afs.collection<IUser>('user', ref => ref.orderBy('lastname').startAt(searchValue).endAt(searchValue+'\uf8ff')).valueChanges({ idField: 'id' });
return merge(firstNames, lastNames);
}

Selectet data and data from with(), Laravel

I want to select some columns and some data from with(), the problem is that I get only data from select().
$today = date('Y-m-d', strtotime('-7 days'));
$contracts = Contract::select('
'contracts.id',
'contracts.contract_value_exc_VAT_total',
'customers.account_name',
'users.name',
)
->whereHas('dates', function($q) use($today){
return $q->whereDate('date', '>=', $today)
->where(function($q) {
$q->where('lkp_contract_date_tag_id', 4)
->orwhere('lkp_contract_date_tag_id', 7);
});
})
->with(['dates' => function($q){
$q->select('id', 'date');
}])
->join('customers','contracts.customer_id', 'customers.id')
->leftJoin('users','contracts.account_manager_select', 'users.id')
->get();
return response()->json($contracts);
From response, dates are null
//date....
dates: []
//date...
You can do it without using the select()
You can have the relations in the ContractModel. You can always process the data after getting from the database and manipulate it in a format you want to return.
There are two options to do that
Do it here in the controller itself.
Create an API resource for the Contract. (https://laravel.com/docs/7.x/eloquent-resources#introduction)
I would suggest the latter as it's more convenient.
For both of them you need to do this first. Make some changes in the contract model.
ContractModel.php
// I'm assuming that you have dates relation in the contract(because you've added it in the `with()` for eager loading.)
public function dates(){
...
}
// Instead of joining while doing the query, add the following
// relations in the contract as well.
public function customer(){
return $belongsTo('App\Customer', 'customer_id', 'id');
}
public function accountManagerSelect(){
return $belongsTo('App\User', 'account_manager_select', 'id');
}
This is how you go with the API resource approach.
Create the Contract Api Resource. And this is how the toArray() method should be.
toArray() {
// Get the dates in the format you want. I'have added the below
// format by considering the 'select' statement you added for
// dates.
$dates = [];
// will use the relation dates, to get the associated dates.
foreach($this->dates as $date){
array_push($dates, [
'id' => $date->id,
'date' => $date->date,
]);
}
return [
'id' => $this->id, // id of the contract.
'contract_value_exc_VAT_total' => $this
->contract_value_exc_VAT_total,
'account_name' => $this->account_name,
// This will use the accountManagerSelect relation to get the
// User instance and then you can access the name from that.
'name' => $this->accountManagerSelect->name,
'dates' => $dates, // The dates variable that we created earlier.
];
}
All you need to do is return using API resource in your controller.
instead of doing this
return response()->json($contracts);
Use Api resource
return ContractResource::collection($contracts);

Objection.js alias to relationMapping

I have a working relation mapping with Objection / Knex / Sql server that is causing a problem when the results are paged.
components: {
relation: BaseModel.HasManyRelation,
modelClass: Component,
join: {
from: 'vehicle.id',
to: 'component.vehicleID'
}
}
When I use withGraphFetched to get related components for every vehicle, the query fails if I include the 'vehicle.id' in the original select.
static getFieldList() {
return [
'id',
'mark',
'model'
].
}
static getPagedList(page, pagelength) {
return this.query()
.select(this.getFieldList())
.withGraphFetched('components')
.page(page, pagelength)
}
Now, when paging is done, Objection / Knex runs a second query after the primary one to fetch the total number of rows. Objection adds 'vehicle.id' from the relation mapping to the query, thus causing the query to fail because the column 'id' is now fetched twice for the subquery.
exec sp_executesql #statement=N'select count(*) as [count] from (select [id], [mark], [model], [id] from [vehicle]) as [temp]'
My question is, how can this be avoided? Can I use some alias in the relation mapping? I tried 'vehicle.id as vehicleFK' in the relation mapping but that caused the withGraphFetched to not run at all.
there might be two ways to try to solve your issue
get rid of component's id column
const componentsSelectList = ()=>{
// select columns you need from components without the id column
return [
'column1', // columns you need
'column2'
]
}
static getPagedList(page, pagelength) {
return this.query()
.select(this.getFieldList())
.withGraphFetched('components',{minimize: true})
.modifyGraph('components',builder => builder.select(componentsSelectList()))
.page(page, pagelength)
}
use ref function from objection to reference the id column from which table
const {ref} = require('objection')
...
static getFieldList() {
return [
ref('vehicle.id'),
'mark',
'model'
].
}
static getPagedList(page, pagelength) {
return this.query()
.select(this.getFieldList())
.withGraphFetched('components')
.page(page, pagelength)
}
...

Sequelize migration add "IF NOT EXISTS" to addIndex and addColumn

Is there a way to force Sequelize.js to add IF NOT EXISTS to the Postgres SQL created by the queryInterface.addColumn and queryInterface.addIndex methods?
According to the Postgres Docs this is supported for Alter Table Add Column as well as Create Index
I have looked through the Sequelize.js docs without any luck, and I have tried to go through the code to figure out how the SQL is generated, but I have not had any luck yet.
A bit of background, or "Why"
I am trying to create a migration strategy for an existing postgres instance, and I have currently created a Sequelize migration set which migrates from "nothing" to the current schema. Now I would like to simply get this up and running on my production server where all of the data already exists such that the next time I create a migration, I can run it.
All of this works well for every queryInterface.createTable because the IF NOT EXISTS is automatically added.
I had a similar issue, except in my case I was only interested in addColumn IF NOT EXIST.
You can achieve this with a two step solution, using queryInterface.describeTable.
Given the table name the function will return the table definition which contains all the existing columns. If the column you need to add does not exist then call the queryInterface.addColumn function.
const tableName = 'your_table_name';
queryInterface.describeTable(tableName)
.then(tableDefinition => {
if (tableDefinition.yourColumnName) {
return Promise.resolve();
}
return queryInterface.addColumn(
tableName,
'your_column_name',
{ type: Sequelize.STRING } // or a different column
);
});
addColumn function comes from queryGenerator method called addColumnQuery, which accepts three parameters - table, key and dataType. With use of them it creates a query, which looks like that
let query = `ALTER TABLE ${quotedTable} ADD COLUMN ${quotedKey} ${definition};`;
So, as you can see, there is no option to add the IF NOT EXISTS clause to the query string. The same concerns the addIndex method unfortunately. However, you can use plain query in order to perform some atypical operations
queryInterface.sequelize.query(...);
The statement if (!tableDefinition.yourColumnName) won't be able to check if column exists.
Correct way is
return queryInterface.describeTable(tableName).then(tableDefinition => {
if (!tableDefinition[columnName]){
return queryInterface.addColumn(tableName, columnName, {
type: Sequelize.JSON
});
} else {
return Promise.resolve(true);
}
});
A small working example:
module.exports = {
/**
* #description Up.
* #param {QueryInterface} queryInterface
* #return Promise<void>
*/
up: async (queryInterface) => {
const tableDefinition = await queryInterface.describeTable('group');
const promises = [];
return queryInterface.sequelize.transaction((transaction) => {
if (!tableDefinition.column1) {
promises.push(queryInterface.addColumn(
'group',
'column1',
{
type: queryInterface.sequelize.Sequelize.STRING,
allowNull: true,
},
{transaction},
));
}
if (!tableDefinition.oauth2_token_expire_at) {
promises.push(queryInterface.addColumn(
'group',
'column2',
{
type: queryInterface.sequelize.Sequelize.DATE,
allowNull: true,
},
{transaction},
));
}
return Promise.all(promises);
});
},
/**
* #description Down.
* #param {QueryInterface} queryInterface
* #return Promise<void>
*/
down: (queryInterface) => {
...
},
};