|
|
@ -27,12 +27,12 @@ import {
|
|
|
|
} from '../../../../common/entities/SearchQueryDTO';
|
|
|
|
} from '../../../../common/entities/SearchQueryDTO';
|
|
|
|
import {GalleryManager} from './GalleryManager';
|
|
|
|
import {GalleryManager} from './GalleryManager';
|
|
|
|
import {ObjectManagers} from '../../ObjectManagers';
|
|
|
|
import {ObjectManagers} from '../../ObjectManagers';
|
|
|
|
import {Utils} from '../../../../common/Utils';
|
|
|
|
|
|
|
|
import {PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
|
|
|
import {PhotoDTO} from '../../../../common/entities/PhotoDTO';
|
|
|
|
import {DatabaseType} from '../../../../common/config/private/PrivateConfig';
|
|
|
|
import {DatabaseType} from '../../../../common/config/private/PrivateConfig';
|
|
|
|
import {ISQLGalleryManager} from './IGalleryManager';
|
|
|
|
import {ISQLGalleryManager} from './IGalleryManager';
|
|
|
|
import {ISQLSearchManager} from './ISearchManager';
|
|
|
|
import {ISQLSearchManager} from './ISearchManager';
|
|
|
|
import {MediaDTO} from '../../../../common/entities/MediaDTO';
|
|
|
|
import {MediaDTO} from '../../../../common/entities/MediaDTO';
|
|
|
|
|
|
|
|
import {Utils} from '../../../../common/Utils';
|
|
|
|
|
|
|
|
|
|
|
|
export class SearchManager implements ISQLSearchManager {
|
|
|
|
export class SearchManager implements ISQLSearchManager {
|
|
|
|
|
|
|
|
|
|
|
@ -139,8 +139,7 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async search(queryIN: SearchQueryDTO): Promise<SearchResultDTO> {
|
|
|
|
async search(queryIN: SearchQueryDTO): Promise<SearchResultDTO> {
|
|
|
|
let query = this.flattenSameOfQueries(queryIN);
|
|
|
|
const query = await this.prepareQuery(queryIN);
|
|
|
|
query = await this.getGPSData(query);
|
|
|
|
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
|
|
|
|
|
|
|
|
const result: SearchResultDTO = {
|
|
|
|
const result: SearchResultDTO = {
|
|
|
@ -225,8 +224,7 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public async getPreview(queryIN: SearchQueryDTO): Promise<MediaDTO> {
|
|
|
|
public async getPreview(queryIN: SearchQueryDTO): Promise<MediaDTO> {
|
|
|
|
let query = this.flattenSameOfQueries(queryIN);
|
|
|
|
const query = await this.prepareQuery(queryIN);
|
|
|
|
query = await this.getGPSData(query);
|
|
|
|
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
const connection = await SQLConnection.getConnection();
|
|
|
|
|
|
|
|
|
|
|
|
return await connection
|
|
|
|
return await connection
|
|
|
@ -239,6 +237,28 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
.getOne();
|
|
|
|
.getOne();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private async prepareQuery(queryIN: SearchQueryDTO): Promise<SearchQueryDTO> {
|
|
|
|
|
|
|
|
let query: SearchQueryDTO = this.assignQueryIDs(Utils.clone(queryIN)); // assign local ids before flattening SOME_OF queries
|
|
|
|
|
|
|
|
query = this.flattenSameOfQueries(query);
|
|
|
|
|
|
|
|
query = await this.getGPSData(query);
|
|
|
|
|
|
|
|
return query;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
|
|
* Assigning IDs to search queries. It is a help / simplification to typeorm,
|
|
|
|
|
|
|
|
* so less parameters are needed to pass down to SQL.
|
|
|
|
|
|
|
|
* Witch SOME_OF query the number of WHERE constrains have O(N!) complexity
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
private assignQueryIDs(queryIN: SearchQueryDTO, id = {value: 1}): SearchQueryDTO {
|
|
|
|
|
|
|
|
if ((queryIN as SearchListQuery).list) {
|
|
|
|
|
|
|
|
(queryIN as SearchListQuery).list.forEach(q => this.assignQueryIDs(q, id));
|
|
|
|
|
|
|
|
return queryIN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
(queryIN as SearchQueryDTOWithID).queryId = id.value;
|
|
|
|
|
|
|
|
id.value++;
|
|
|
|
|
|
|
|
return queryIN;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Returns only those part of a query tree that only contains directory related search queries
|
|
|
|
* Returns only those part of a query tree that only contains directory related search queries
|
|
|
|
*/
|
|
|
|
*/
|
|
|
@ -297,16 +317,17 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
* @param directoryOnly Only builds directory related queries
|
|
|
|
* @param directoryOnly Only builds directory related queries
|
|
|
|
* @private
|
|
|
|
* @private
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
private buildWhereQuery(query: SearchQueryDTO, directoryOnly = false, paramCounter = {value: 0}): Brackets {
|
|
|
|
private buildWhereQuery(query: SearchQueryDTO, directoryOnly = false): Brackets {
|
|
|
|
|
|
|
|
const queryId = (query as SearchQueryDTOWithID).queryId;
|
|
|
|
switch (query.type) {
|
|
|
|
switch (query.type) {
|
|
|
|
case SearchQueryTypes.AND:
|
|
|
|
case SearchQueryTypes.AND:
|
|
|
|
return new Brackets((q): any => {
|
|
|
|
return new Brackets((q): any => {
|
|
|
|
(query as ANDSearchQuery).list.forEach((sq): any => q.andWhere(this.buildWhereQuery(sq, directoryOnly, paramCounter)));
|
|
|
|
(query as ANDSearchQuery).list.forEach((sq): any => q.andWhere(this.buildWhereQuery(sq, directoryOnly)));
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
case SearchQueryTypes.OR:
|
|
|
|
case SearchQueryTypes.OR:
|
|
|
|
return new Brackets((q): any => {
|
|
|
|
return new Brackets((q): any => {
|
|
|
|
(query as ANDSearchQuery).list.forEach((sq): any => q.orWhere(this.buildWhereQuery(sq, directoryOnly, paramCounter)));
|
|
|
|
(query as ANDSearchQuery).list.forEach((sq): any => q.orWhere(this.buildWhereQuery(sq, directoryOnly)));
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
@ -340,21 +361,20 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
|
|
|
|
|
|
|
|
return new Brackets((q): any => {
|
|
|
|
return new Brackets((q): any => {
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
paramCounter.value++;
|
|
|
|
textParam['maxLat' + queryId] = maxLat;
|
|
|
|
textParam['maxLat' + paramCounter.value] = maxLat;
|
|
|
|
textParam['minLat' + queryId] = minLat;
|
|
|
|
textParam['minLat' + paramCounter.value] = minLat;
|
|
|
|
textParam['maxLon' + queryId] = maxLon;
|
|
|
|
textParam['maxLon' + paramCounter.value] = maxLon;
|
|
|
|
textParam['minLon' + queryId] = minLon;
|
|
|
|
textParam['minLon' + paramCounter.value] = minLon;
|
|
|
|
|
|
|
|
if (!(query as DistanceSearch).negate) {
|
|
|
|
if (!(query as DistanceSearch).negate) {
|
|
|
|
q.where(`media.metadata.positionData.GPSData.latitude < :maxLat${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.positionData.GPSData.latitude < :maxLat${queryId}`, textParam);
|
|
|
|
q.andWhere(`media.metadata.positionData.GPSData.latitude > :minLat${paramCounter.value}`, textParam);
|
|
|
|
q.andWhere(`media.metadata.positionData.GPSData.latitude > :minLat${queryId}`, textParam);
|
|
|
|
q.andWhere(`media.metadata.positionData.GPSData.longitude < :maxLon${paramCounter.value}`, textParam);
|
|
|
|
q.andWhere(`media.metadata.positionData.GPSData.longitude < :maxLon${queryId}`, textParam);
|
|
|
|
q.andWhere(`media.metadata.positionData.GPSData.longitude > :minLon${paramCounter.value}`, textParam);
|
|
|
|
q.andWhere(`media.metadata.positionData.GPSData.longitude > :minLon${queryId}`, textParam);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
q.where(`media.metadata.positionData.GPSData.latitude > :maxLat${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.positionData.GPSData.latitude > :maxLat${queryId}`, textParam);
|
|
|
|
q.orWhere(`media.metadata.positionData.GPSData.latitude < :minLat${paramCounter.value}`, textParam);
|
|
|
|
q.orWhere(`media.metadata.positionData.GPSData.latitude < :minLat${queryId}`, textParam);
|
|
|
|
q.orWhere(`media.metadata.positionData.GPSData.longitude > :maxLon${paramCounter.value}`, textParam);
|
|
|
|
q.orWhere(`media.metadata.positionData.GPSData.longitude > :maxLon${queryId}`, textParam);
|
|
|
|
q.orWhere(`media.metadata.positionData.GPSData.longitude < :minLon${paramCounter.value}`, textParam);
|
|
|
|
q.orWhere(`media.metadata.positionData.GPSData.longitude < :minLon${queryId}`, textParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
@ -370,11 +390,9 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
const relation = (query as TextSearch).negate ? '<' : '>=';
|
|
|
|
const relation = (query as TextSearch).negate ? '<' : '>=';
|
|
|
|
|
|
|
|
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
textParam['from' + paramCounter.value] = (query as FromDateSearch).value;
|
|
|
|
textParam['from' + queryId] = (query as FromDateSearch).value;
|
|
|
|
q.where(`media.metadata.creationDate ${relation} :from${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.creationDate ${relation} :from${queryId}`, textParam);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paramCounter.value++;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
@ -389,10 +407,9 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
const relation = (query as TextSearch).negate ? '>' : '<=';
|
|
|
|
const relation = (query as TextSearch).negate ? '>' : '<=';
|
|
|
|
|
|
|
|
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
textParam['to' + paramCounter.value] = (query as ToDateSearch).value;
|
|
|
|
textParam['to' + queryId] = (query as ToDateSearch).value;
|
|
|
|
q.where(`media.metadata.creationDate ${relation} :to${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.creationDate ${relation} :to${queryId}`, textParam);
|
|
|
|
|
|
|
|
|
|
|
|
paramCounter.value++;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
@ -408,10 +425,9 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
const relation = (query as TextSearch).negate ? '<' : '>=';
|
|
|
|
const relation = (query as TextSearch).negate ? '<' : '>=';
|
|
|
|
|
|
|
|
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
textParam['min' + paramCounter.value] = (query as MinRatingSearch).value;
|
|
|
|
textParam['min' + queryId] = (query as MinRatingSearch).value;
|
|
|
|
q.where(`media.metadata.rating ${relation} :min${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.rating ${relation} :min${queryId}`, textParam);
|
|
|
|
|
|
|
|
|
|
|
|
paramCounter.value++;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
case SearchQueryTypes.max_rating:
|
|
|
|
case SearchQueryTypes.max_rating:
|
|
|
@ -427,10 +443,9 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
|
|
|
|
|
|
|
|
if (typeof (query as MaxRatingSearch).value !== 'undefined') {
|
|
|
|
if (typeof (query as MaxRatingSearch).value !== 'undefined') {
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
textParam['max' + paramCounter.value] = (query as MaxRatingSearch).value;
|
|
|
|
textParam['max' + queryId] = (query as MaxRatingSearch).value;
|
|
|
|
q.where(`media.metadata.rating ${relation} :max${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.rating ${relation} :max${queryId}`, textParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
paramCounter.value++;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
@ -446,11 +461,10 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
const relation = (query as TextSearch).negate ? '<' : '>=';
|
|
|
|
const relation = (query as TextSearch).negate ? '<' : '>=';
|
|
|
|
|
|
|
|
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
textParam['min' + paramCounter.value] = (query as MinResolutionSearch).value * 1000 * 1000;
|
|
|
|
textParam['min' + queryId] = (query as MinResolutionSearch).value * 1000 * 1000;
|
|
|
|
q.where(`media.metadata.size.width * media.metadata.size.height ${relation} :min${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.size.width * media.metadata.size.height ${relation} :min${queryId}`, textParam);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
paramCounter.value++;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
@ -466,10 +480,9 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
const relation = (query as TextSearch).negate ? '>' : '<=';
|
|
|
|
const relation = (query as TextSearch).negate ? '>' : '<=';
|
|
|
|
|
|
|
|
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
textParam['max' + paramCounter.value] = (query as MaxResolutionSearch).value * 1000 * 1000;
|
|
|
|
textParam['max' + queryId] = (query as MaxResolutionSearch).value * 1000 * 1000;
|
|
|
|
q.where(`media.metadata.size.width * media.metadata.size.height ${relation} :max${paramCounter.value}`, textParam);
|
|
|
|
q.where(`media.metadata.size.width * media.metadata.size.height ${relation} :max${queryId}`, textParam);
|
|
|
|
|
|
|
|
|
|
|
|
paramCounter.value++;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
@ -483,7 +496,6 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
q.where('media.metadata.size.width <= media.metadata.size.height');
|
|
|
|
q.where('media.metadata.size.width <= media.metadata.size.height');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
paramCounter.value++;
|
|
|
|
|
|
|
|
return q;
|
|
|
|
return q;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
@ -514,26 +526,25 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
const whereFNRev = (query as TextSearch).negate ? 'orWhere' : 'andWhere';
|
|
|
|
const whereFNRev = (query as TextSearch).negate ? 'orWhere' : 'andWhere';
|
|
|
|
|
|
|
|
|
|
|
|
const textParam: any = {};
|
|
|
|
const textParam: any = {};
|
|
|
|
paramCounter.value++;
|
|
|
|
textParam['text' + queryId] = createMatchString((query as TextSearch).text);
|
|
|
|
textParam['text' + paramCounter.value] = createMatchString((query as TextSearch).text);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (query.type === SearchQueryTypes.any_text ||
|
|
|
|
if (query.type === SearchQueryTypes.any_text ||
|
|
|
|
query.type === SearchQueryTypes.directory) {
|
|
|
|
query.type === SearchQueryTypes.directory) {
|
|
|
|
const dirPathStr = ((query as TextSearch).text).replace(new RegExp('\\\\', 'g'), '/');
|
|
|
|
const dirPathStr = ((query as TextSearch).text).replace(new RegExp('\\\\', 'g'), '/');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
textParam['fullPath' + paramCounter.value] = createMatchString(dirPathStr);
|
|
|
|
textParam['fullPath' + queryId] = createMatchString(dirPathStr);
|
|
|
|
q[whereFN](`directory.path ${LIKE} :fullPath${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
q[whereFN](`directory.path ${LIKE} :fullPath${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
|
|
|
|
|
|
|
|
const directoryPath = GalleryManager.parseRelativeDirePath(dirPathStr);
|
|
|
|
const directoryPath = GalleryManager.parseRelativeDirePath(dirPathStr);
|
|
|
|
q[whereFN](new Brackets((dq): any => {
|
|
|
|
q[whereFN](new Brackets((dq): any => {
|
|
|
|
textParam['dirName' + paramCounter.value] = createMatchString(directoryPath.name);
|
|
|
|
textParam['dirName' + queryId] = createMatchString(directoryPath.name);
|
|
|
|
dq[whereFNRev](`directory.name ${LIKE} :dirName${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
dq[whereFNRev](`directory.name ${LIKE} :dirName${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
if (dirPathStr.includes('/')) {
|
|
|
|
if (dirPathStr.includes('/')) {
|
|
|
|
textParam['parentName' + paramCounter.value] = createMatchString(directoryPath.parent);
|
|
|
|
textParam['parentName' + queryId] = createMatchString(directoryPath.parent);
|
|
|
|
dq[whereFNRev](`directory.path ${LIKE} :parentName${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
dq[whereFNRev](`directory.path ${LIKE} :parentName${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dq;
|
|
|
|
return dq;
|
|
|
@ -541,21 +552,21 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((query.type === SearchQueryTypes.any_text && !directoryOnly) || query.type === SearchQueryTypes.file_name) {
|
|
|
|
if ((query.type === SearchQueryTypes.any_text && !directoryOnly) || query.type === SearchQueryTypes.file_name) {
|
|
|
|
q[whereFN](`media.name ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
q[whereFN](`media.name ${LIKE} :text${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((query.type === SearchQueryTypes.any_text && !directoryOnly) || query.type === SearchQueryTypes.caption) {
|
|
|
|
if ((query.type === SearchQueryTypes.any_text && !directoryOnly) || query.type === SearchQueryTypes.caption) {
|
|
|
|
q[whereFN](`media.metadata.caption ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
q[whereFN](`media.metadata.caption ${LIKE} :text${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if ((query.type === SearchQueryTypes.any_text && !directoryOnly) || query.type === SearchQueryTypes.position) {
|
|
|
|
if ((query.type === SearchQueryTypes.any_text && !directoryOnly) || query.type === SearchQueryTypes.position) {
|
|
|
|
q[whereFN](`media.metadata.positionData.country ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
q[whereFN](`media.metadata.positionData.country ${LIKE} :text${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam)
|
|
|
|
textParam)
|
|
|
|
[whereFN](`media.metadata.positionData.state ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
[whereFN](`media.metadata.positionData.state ${LIKE} :text${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam)
|
|
|
|
textParam)
|
|
|
|
[whereFN](`media.metadata.positionData.city ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
[whereFN](`media.metadata.positionData.city ${LIKE} :text${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -563,22 +574,22 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
const matchArrayField = (fieldName: string): void => {
|
|
|
|
const matchArrayField = (fieldName: string): void => {
|
|
|
|
q[whereFN](new Brackets((qbr): void => {
|
|
|
|
q[whereFN](new Brackets((qbr): void => {
|
|
|
|
if ((query as TextSearch).matchType !== TextSearchQueryMatchTypes.exact_match) {
|
|
|
|
if ((query as TextSearch).matchType !== TextSearchQueryMatchTypes.exact_match) {
|
|
|
|
qbr[whereFN](`${fieldName} ${LIKE} :text${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
qbr[whereFN](`${fieldName} ${LIKE} :text${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
qbr[whereFN](new Brackets((qb): void => {
|
|
|
|
qbr[whereFN](new Brackets((qb): void => {
|
|
|
|
textParam['CtextC' + paramCounter.value] = `%,${(query as TextSearch).text},%`;
|
|
|
|
textParam['CtextC' + queryId] = `%,${(query as TextSearch).text},%`;
|
|
|
|
textParam['Ctext' + paramCounter.value] = `%,${(query as TextSearch).text}`;
|
|
|
|
textParam['Ctext' + queryId] = `%,${(query as TextSearch).text}`;
|
|
|
|
textParam['textC' + paramCounter.value] = `${(query as TextSearch).text},%`;
|
|
|
|
textParam['textC' + queryId] = `${(query as TextSearch).text},%`;
|
|
|
|
textParam['text_exact' + paramCounter.value] = `${(query as TextSearch).text}`;
|
|
|
|
textParam['text_exact' + queryId] = `${(query as TextSearch).text}`;
|
|
|
|
|
|
|
|
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :CtextC${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :CtextC${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :Ctext${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :Ctext${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :textC${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :textC${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :text_exact${paramCounter.value} COLLATE utf8_general_ci`,
|
|
|
|
qb[whereFN](`${fieldName} ${LIKE} :text_exact${queryId} COLLATE utf8_general_ci`,
|
|
|
|
textParam);
|
|
|
|
textParam);
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -626,14 +637,41 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
} as ANDSearchQuery);
|
|
|
|
} as ANDSearchQuery);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const combinations: SearchQueryDTO[][] = Utils.getAnyX(someOfQ.min, (query as SearchListQuery).list);
|
|
|
|
const getAllCombinations = (num: number, arr: SearchQueryDTO[], start = 0): SearchQueryDTO[] => {
|
|
|
|
|
|
|
|
if (num <= 0 || num > arr.length || start >= arr.length) {
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num <= 1) {
|
|
|
|
|
|
|
|
return arr.slice(start);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num === arr.length - start) {
|
|
|
|
|
|
|
|
return arr.slice(start);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const ret: ANDSearchQuery[] = [];
|
|
|
|
|
|
|
|
for (let i = start; i < arr.length - num + 1; ++i) {
|
|
|
|
|
|
|
|
const subRes = getAllCombinations(num - 1, arr, i + 1);
|
|
|
|
|
|
|
|
const and: ANDSearchQuery = {
|
|
|
|
|
|
|
|
type: SearchQueryTypes.AND,
|
|
|
|
|
|
|
|
list: [
|
|
|
|
|
|
|
|
arr[i],
|
|
|
|
|
|
|
|
subRes.length === 1 ? subRes[0] : (
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
type: SearchQueryTypes.OR,
|
|
|
|
|
|
|
|
list: subRes
|
|
|
|
|
|
|
|
} as ORSearchQuery)
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ret.push(and);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return this.flattenSameOfQueries({
|
|
|
|
return this.flattenSameOfQueries({
|
|
|
|
type: SearchQueryTypes.OR,
|
|
|
|
type: SearchQueryTypes.OR,
|
|
|
|
list: combinations.map((c): ANDSearchQuery => ({
|
|
|
|
list: getAllCombinations(someOfQ.min, (query as SearchListQuery).list)
|
|
|
|
type: SearchQueryTypes.AND, list: c
|
|
|
|
|
|
|
|
} as ANDSearchQuery))
|
|
|
|
|
|
|
|
} as ORSearchQuery);
|
|
|
|
} as ORSearchQuery);
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -650,3 +688,7 @@ export class SearchManager implements ISQLSearchManager {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export interface SearchQueryDTOWithID extends SearchQueryDTO {
|
|
|
|
|
|
|
|
queryId: number;
|
|
|
|
|
|
|
|
}
|
|
|
|