You've already forked pigallery2
mirror of
https://github.com/bpatrik/pigallery2.git
synced 2025-12-09 23:32:14 +02:00
Add directory filtering for listing #1015
This commit is contained in:
@@ -6,6 +6,7 @@ export class SessionContext {
|
|||||||
user: ContextUser;
|
user: ContextUser;
|
||||||
// New structured projection with prebuilt SQL and params
|
// New structured projection with prebuilt SQL and params
|
||||||
projectionQuery?: Brackets;
|
projectionQuery?: Brackets;
|
||||||
|
projectionQueryForSubDir?: Brackets; // only the directory part of the query, where it filters 'directories' instead of 'directory' aliases
|
||||||
hasDirectoryProjection: boolean;
|
hasDirectoryProjection: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {DuplicatesDTO} from '../../../common/entities/DuplicatesDTO';
|
|||||||
import {ReIndexingSensitivity} from '../../../common/config/private/PrivateConfig';
|
import {ReIndexingSensitivity} from '../../../common/config/private/PrivateConfig';
|
||||||
import {DiskManager} from '../fileaccess/DiskManager';
|
import {DiskManager} from '../fileaccess/DiskManager';
|
||||||
import {SessionContext} from '../SessionContext';
|
import {SessionContext} from '../SessionContext';
|
||||||
import {ProjectedDirectoryCacheEntity} from './enitites/ProjectedDirectoryCacheEntity';
|
|
||||||
|
|
||||||
const LOG_TAG = '[GalleryManager]';
|
const LOG_TAG = '[GalleryManager]';
|
||||||
|
|
||||||
@@ -339,16 +338,6 @@ export class GalleryManager {
|
|||||||
partialDirId: number
|
partialDirId: number
|
||||||
): Promise<ParentDirectoryDTO> {
|
): Promise<ParentDirectoryDTO> {
|
||||||
|
|
||||||
const select = [
|
|
||||||
'directory',
|
|
||||||
'directories',
|
|
||||||
'cover.name',
|
|
||||||
'coverDirectory.name',
|
|
||||||
'coverDirectory.path',
|
|
||||||
'dcover.name',
|
|
||||||
'dcoverDirectory.name',
|
|
||||||
'dcoverDirectory.path',
|
|
||||||
];
|
|
||||||
const query = connection
|
const query = connection
|
||||||
.getRepository(DirectoryEntity)
|
.getRepository(DirectoryEntity)
|
||||||
.createQueryBuilder('directory')
|
.createQueryBuilder('directory')
|
||||||
@@ -375,6 +364,12 @@ export class GalleryManager {
|
|||||||
'dcoverDirectory.path',
|
'dcoverDirectory.path',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// search does not return a directory if that is recursively having 0 media
|
||||||
|
// gallery listing should otherwise, we won't be able to trigger lazy indexing
|
||||||
|
// this behavior lets us explicitly hid a directory if it is explicitly blocked
|
||||||
|
if(session.projectionQueryForSubDir) {
|
||||||
|
query.andWhere(session.projectionQueryForSubDir);
|
||||||
|
}
|
||||||
if (!session.projectionQuery) {
|
if (!session.projectionQuery) {
|
||||||
query.leftJoinAndSelect('directory.media', 'media');
|
query.leftJoinAndSelect('directory.media', 'media');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -478,11 +478,13 @@ export class SearchManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async prepareAndBuildWhereQuery(
|
public async prepareAndBuildWhereQuery(
|
||||||
queryIN: SearchQueryDTO,
|
queryIN: SearchQueryDTO, directoryOnly = false,
|
||||||
directoryOnly = false
|
aliases: { [key: string]: string } = {}): Promise<Brackets> {
|
||||||
): Promise<Brackets> {
|
let query = await this.prepareQuery(queryIN);
|
||||||
const query = await this.prepareQuery(queryIN);
|
if (directoryOnly) {
|
||||||
return this.buildWhereQuery(query, directoryOnly);
|
query = this.filterDirectoryQuery(query);
|
||||||
|
}
|
||||||
|
return this.buildWhereQuery(query, directoryOnly, aliases);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async prepareQuery(queryIN: SearchQueryDTO): Promise<SearchQueryDTO> {
|
public async prepareQuery(queryIN: SearchQueryDTO): Promise<SearchQueryDTO> {
|
||||||
@@ -496,11 +498,13 @@ export class SearchManager {
|
|||||||
* Builds the SQL Where query from search query
|
* Builds the SQL Where query from search query
|
||||||
* @param query input search query
|
* @param query input search query
|
||||||
* @param directoryOnly Only builds directory related queries
|
* @param directoryOnly Only builds directory related queries
|
||||||
|
* @param aliases for SQL alias mapping
|
||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
public buildWhereQuery(
|
public buildWhereQuery(
|
||||||
query: SearchQueryDTO,
|
query: SearchQueryDTO,
|
||||||
directoryOnly = false
|
directoryOnly = false,
|
||||||
|
aliases: { [key: string]: string } = {}
|
||||||
): Brackets {
|
): Brackets {
|
||||||
const queryId = (query as SearchQueryDTOWithID).queryId;
|
const queryId = (query as SearchQueryDTOWithID).queryId;
|
||||||
switch (query.type) {
|
switch (query.type) {
|
||||||
@@ -1039,10 +1043,10 @@ export class SearchManager {
|
|||||||
new RegExp('\\\\', 'g'),
|
new RegExp('\\\\', 'g'),
|
||||||
'/'
|
'/'
|
||||||
);
|
);
|
||||||
|
const alias = aliases['directory'] ?? 'directory';
|
||||||
textParam['fullPath' + queryId] = createMatchString(dirPathStr);
|
textParam['fullPath' + queryId] = createMatchString(dirPathStr);
|
||||||
q[whereFN](
|
q[whereFN](
|
||||||
`directory.path ${LIKE} :fullPath${queryId} COLLATE ` + SQL_COLLATE,
|
`${alias}.path ${LIKE} :fullPath${queryId} COLLATE ` + SQL_COLLATE,
|
||||||
textParam
|
textParam
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1053,7 +1057,7 @@ export class SearchManager {
|
|||||||
directoryPath.name
|
directoryPath.name
|
||||||
);
|
);
|
||||||
dq[whereFNRev](
|
dq[whereFNRev](
|
||||||
`directory.name ${LIKE} :dirName${queryId} COLLATE ${SQL_COLLATE}`,
|
`${alias}.name ${LIKE} :dirName${queryId} COLLATE ${SQL_COLLATE}`,
|
||||||
textParam
|
textParam
|
||||||
);
|
);
|
||||||
if (dirPathStr.includes('/')) {
|
if (dirPathStr.includes('/')) {
|
||||||
@@ -1061,7 +1065,7 @@ export class SearchManager {
|
|||||||
directoryPath.parent
|
directoryPath.parent
|
||||||
);
|
);
|
||||||
dq[whereFNRev](
|
dq[whereFNRev](
|
||||||
`directory.path ${LIKE} :parentName${queryId} COLLATE ${SQL_COLLATE}`,
|
`${alias}.path ${LIKE} :parentName${queryId} COLLATE ${SQL_COLLATE}`,
|
||||||
textParam
|
textParam
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {ANDSearchQuery, SearchQueryDTO, SearchQueryTypes} from '../../../common/
|
|||||||
import {SharingEntity} from './enitites/SharingEntity';
|
import {SharingEntity} from './enitites/SharingEntity';
|
||||||
import {ObjectManagers} from '../ObjectManagers';
|
import {ObjectManagers} from '../ObjectManagers';
|
||||||
import {Logger} from '../../Logger';
|
import {Logger} from '../../Logger';
|
||||||
|
import {Utils} from '../../../common/Utils';
|
||||||
|
|
||||||
const LOG_TAG = '[SessionManager]';
|
const LOG_TAG = '[SessionManager]';
|
||||||
|
|
||||||
@@ -68,6 +69,9 @@ export class SessionManager {
|
|||||||
// Build the Brackets-based query
|
// Build the Brackets-based query
|
||||||
context.projectionQuery = await ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(finalQuery);
|
context.projectionQuery = await ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(finalQuery);
|
||||||
context.hasDirectoryProjection = ObjectManagers.getInstance().SearchManager.hasDirectoryQuery(finalQuery);
|
context.hasDirectoryProjection = ObjectManagers.getInstance().SearchManager.hasDirectoryQuery(finalQuery);
|
||||||
|
if (context.hasDirectoryProjection) {
|
||||||
|
context.projectionQueryForSubDir = await ObjectManagers.getInstance().SearchManager.prepareAndBuildWhereQuery(finalQuery, true, {directory: 'directories'});
|
||||||
|
}
|
||||||
context.user.projectionKey = this.createProjectionKey(finalQuery);
|
context.user.projectionKey = this.createProjectionKey(finalQuery);
|
||||||
if (SearchQueryUtils.isQueryEmpty(finalQuery)) {
|
if (SearchQueryUtils.isQueryEmpty(finalQuery)) {
|
||||||
Logger.silly(LOG_TAG, 'Empty Projection query.');
|
Logger.silly(LOG_TAG, 'Empty Projection query.');
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import {Utils} from './Utils';
|
|||||||
|
|
||||||
export const SearchQueryUtils = {
|
export const SearchQueryUtils = {
|
||||||
negate: (query: SearchQueryDTO): SearchQueryDTO => {
|
negate: (query: SearchQueryDTO): SearchQueryDTO => {
|
||||||
|
query = Utils.clone(query)
|
||||||
switch (query.type) {
|
switch (query.type) {
|
||||||
case SearchQueryTypes.AND:
|
case SearchQueryTypes.AND:
|
||||||
query.type = SearchQueryTypes.OR;
|
query.type = SearchQueryTypes.OR;
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ describe('GalleryManager', (sqlHelper: DBTestHelper) => {
|
|||||||
|
|
||||||
it('should return all media when no projection query is provided', async () => {
|
it('should return all media when no projection query is provided', async () => {
|
||||||
// Setup a session with no projection query
|
// Setup a session with no projection query
|
||||||
const session = DBTestHelper.defaultSession
|
const session = DBTestHelper.defaultSession;
|
||||||
|
|
||||||
|
|
||||||
// Get the directory contents without any filtering using our directory's name and path
|
// Get the directory contents without any filtering using our directory's name and path
|
||||||
@@ -142,7 +142,33 @@ describe('GalleryManager', (sqlHelper: DBTestHelper) => {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('blocks child directory when blockQuery contains normal directory search', async () => {
|
||||||
|
const root = sqlHelper.testGalleyEntities.dir;
|
||||||
|
const child = sqlHelper.testGalleyEntities.subDir;
|
||||||
|
|
||||||
|
const blockQuery = {
|
||||||
|
type: SearchQueryTypes.directory,
|
||||||
|
matchType: TextSearchQueryMatchTypes.like,
|
||||||
|
text: child.name
|
||||||
|
} as any;
|
||||||
|
|
||||||
|
const session = await ObjectManagers.getInstance().SessionManager.buildContext({
|
||||||
|
blockQuery,
|
||||||
|
overrideAllowBlockList: true
|
||||||
|
} as any);
|
||||||
|
|
||||||
|
const dirInfo = await galleryManager.getDirIdAndTime(connection, root.name, root.path);
|
||||||
|
const directory = await galleryManager.getParentDirFromId(connection, session, dirInfo.id);
|
||||||
|
|
||||||
|
expect(((await galleryManager.getParentDirFromId(connection, DBTestHelper.defaultSession, dirInfo.id)).directories || [])
|
||||||
|
.map(d => d.name)).to.include(child.name);
|
||||||
|
expect(((await galleryManager.getParentDirFromId(connection, session, dirInfo.id)).directories || [])
|
||||||
|
.map(d => d.name)).to.not.include(child.name);
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
describe('GalleryManager.listDirectory - reindexing severities and projection behavior', () => {
|
describe('GalleryManager.listDirectory - reindexing severities and projection behavior', () => {
|
||||||
const origStatSync = fs.statSync;
|
const origStatSync = fs.statSync;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user