Skip to content

Commit

Permalink
Merge pull request #3302 from SeedCompany/neo4j/filter-perf
Browse files Browse the repository at this point in the history
Neo4j Filter Performance
  • Loading branch information
CarsonF authored Oct 4, 2024
2 parents f292865 + 15cd13e commit fba9266
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 230 deletions.
124 changes: 59 additions & 65 deletions src/components/engagement/engagement.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -699,41 +699,6 @@ export const engagementFilters = filter.define(() => EngagementFilters, {
),
}),
status: filter.stringListProp(),
name: filter.fullText({
index: () => NameIndex,
matchToNode: (q) =>
q.match([
node('node', 'Engagement'),
relation('either', '', undefined, ACTIVE),
node('', 'BaseNode'),
relation('out', '', undefined, ACTIVE),
node('match'),
]),
// UI joins project & language/intern names with dash
// Remove it from search if users type it
normalizeInput: (v) => v.replaceAll(/ -/g, ''),
// Treat each word as a separate search term
// Each word could point to a different node
// i.e. "project - language"
separateQueryForEachWord: true,
minScore: 0.9,
}),
engagedName: filter.fullText({
index: () => EngagedNameIndex,
matchToNode: (q) =>
q.match([
node('node', 'Engagement'),
relation('out', '', undefined, ACTIVE),
node('', 'BaseNode'),
relation('out', '', undefined, ACTIVE),
node('match'),
]),
// Treat each word as a separate search term
// Each word could point to a different node
// i.e. "first - last"
separateQueryForEachWord: true,
minScore: 0.9,
}),
projectId: filter.pathExists((id) => [
node('node'),
relation('in', '', 'engagement'),
Expand All @@ -753,36 +718,6 @@ export const engagementFilters = filter.define(() => EngagementFilters, {
relation('out', '', 'language'),
node('', 'Language', { id }),
]),
project: filter.sub(
() => projectFilters,
'requestingUser',
)((sub) =>
sub
.with('node as eng, requestingUser')
.match([
node('eng'),
relation('in', '', 'engagement'),
node('node', 'Project'),
]),
),
language: filter.sub(() => languageFilters)((sub) =>
sub
.with('node as eng')
.match([
node('eng'),
relation('out', '', 'language'),
node('node', 'Language'),
]),
),
intern: filter.sub(() => userFilters)((sub) =>
sub
.with('node as eng')
.match([
node('eng'),
relation('out', '', 'intern'),
node('node', 'User'),
]),
),
startDate: filter.dateTime(({ query }) => {
query.optionalMatch([
[
Expand Down Expand Up @@ -817,6 +752,65 @@ export const engagementFilters = filter.define(() => EngagementFilters, {
]);
return coalesce('endDateOverride.value', 'mouEnd.value');
}),
name: filter.fullText({
index: () => NameIndex,
matchToNode: (q) =>
q.match([
node('node', 'Engagement'),
relation('either', '', undefined, ACTIVE),
node('', 'BaseNode'),
relation('out', '', undefined, ACTIVE),
node('match'),
]),
// UI joins project & language/intern names with dash
// Remove it from search if users type it
normalizeInput: (v) => v.replaceAll(/ -/g, ''),
// Treat each word as a separate search term
// Each word could point to a different node
// i.e. "project - language"
separateQueryForEachWord: true,
minScore: 0.9,
}),
engagedName: filter.fullText({
index: () => EngagedNameIndex,
matchToNode: (q) =>
q.match([
node('node', 'Engagement'),
relation('out', '', undefined, ACTIVE),
node('', 'BaseNode'),
relation('out', '', undefined, ACTIVE),
node('match'),
]),
// Treat each word as a separate search term
// Each word could point to a different node
// i.e. "first - last"
separateQueryForEachWord: true,
minScore: 0.9,
}),
project: filter.sub(
() => projectFilters,
'requestingUser',
)((sub) =>
sub.match([
node('outer'),
relation('in', '', 'engagement'),
node('node', 'Project'),
]),
),
language: filter.sub(() => languageFilters)((sub) =>
sub.match([
node('outer'),
relation('out', '', 'language'),
node('node', 'Language'),
]),
),
intern: filter.sub(() => userFilters)((sub) =>
sub.match([
node('outer'),
relation('out', '', 'intern'),
node('node', 'User'),
]),
),
});

export const engagementSorters = defineSorters(IEngagement, {
Expand Down
36 changes: 17 additions & 19 deletions src/components/language/language.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,15 +280,7 @@ export class LanguageRepository extends DtoRepository<
}

export const languageFilters = filter.define(() => LanguageFilters, {
name: filter.fullText({
index: () => NameIndex,
matchToNode: (q) =>
q.match([
node('node', 'Language'),
relation('out', '', undefined, ACTIVE),
node('match'),
]),
}),
pinned: filter.isPinned,
sensitivity: filter.stringListProp(),
leastOfThese: filter.propVal(),
isSignLanguage: filter.propVal(),
Expand All @@ -308,21 +300,27 @@ export const languageFilters = filter.define(() => LanguageFilters, {
relation('out', '', 'partner', ACTIVE),
node('', 'Partner', { id }),
]),
name: filter.fullText({
index: () => NameIndex,
matchToNode: (q) =>
q.match([
node('node', 'Language'),
relation('out', '', undefined, ACTIVE),
node('match'),
]),
}),
ethnologue: filter.sub(() => ethnologueFilters)((sub) =>
sub.match([
node('outer'),
relation('out', '', 'ethnologue'),
node('node', 'EthnologueLanguage'),
]),
),
presetInventory: ({ value, query }) => {
query.apply(isPresetInventory).with('*');
const condition = equals('true', true);
return { presetInventory: value ? condition : not(condition) };
},
pinned: filter.isPinned,
ethnologue: filter.sub(() => ethnologueFilters)((sub) =>
sub
.with('node as lang')
.match([
node('lang'),
relation('out', '', 'ethnologue'),
node('node', 'EthnologueLanguage'),
]),
),
});

const ethnologueFilters = filter.define(() => EthnologueLanguageFilters, {
Expand Down
10 changes: 5 additions & 5 deletions src/components/location/location.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ export class LocationRepository extends DtoRepository(Location) {
export const locationSorters = defineSorters(Location, {});

export const locationFilters = filter.define(() => LocationFilters, {
fundingAccountId: filter.pathExists((id) => [
node('node'),
relation('out', '', 'fundingAccount', ACTIVE),
node('', 'FundingAccount', { id }),
]),
name: filter.fullText({
index: () => NameIndex,
matchToNode: (q) =>
Expand All @@ -254,11 +259,6 @@ export const locationFilters = filter.define(() => LocationFilters, {
node('match'),
]),
}),
fundingAccountId: filter.pathExists((id) => [
node('node'),
relation('out', '', 'fundingAccount', ACTIVE),
node('', 'FundingAccount', { id }),
]),
});

const NameIndex = FullTextIndex({
Expand Down
12 changes: 5 additions & 7 deletions src/components/partner/partner.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -312,13 +312,11 @@ export const partnerFilters = filters.define(() => PartnerFilters, {
node('', 'User', { id }),
]),
organization: filter.sub(() => organizationFilters)((sub) =>
sub
.with('node as partner')
.match([
node('partner'),
relation('out', '', 'organization'),
node('node', 'Organization'),
]),
sub.match([
node('outer'),
relation('out', '', 'organization'),
node('node', 'Organization'),
]),
),
});

Expand Down
12 changes: 5 additions & 7 deletions src/components/partnership/partnership.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,13 +429,11 @@ export const partnershipFilters = filter.define(() => PartnershipFilters, {
projectId: filter.skip,
types: filter.intersectsProp(),
partner: filter.sub(() => partnerFilters)((sub) =>
sub
.with('node as partnership')
.match([
node('partnership'),
relation('out', '', 'partner'),
node('node', 'Partner'),
]),
sub.match([
node('outer'),
relation('out', '', 'partner'),
node('node', 'Partner'),
]),
),
});

Expand Down
26 changes: 14 additions & 12 deletions src/components/periodic-report/periodic-report.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,18 +190,7 @@ export class PeriodicReportRepository extends DtoRepository<
const result = await this.db
.query()
.matchNode('node', 'PeriodicReport')
.apply(
filter.builder(filters, {
parent: filter.pathExists((id) => [
node('', 'BaseNode', { id }),
relation('out', '', 'report', ACTIVE),
node('node'),
]),
start: filter.dateTimeProp(),
end: filter.dateTimeProp(),
type: ({ value }) => ({ node: hasLabel(`${value}Report`) }),
}),
)
.apply(periodicReportFilters(filters))
.apply(sorting(resource, input))
.apply(paginate(input, this.hydrate(session)))
.first();
Expand Down Expand Up @@ -460,6 +449,19 @@ export const matchCurrentDue =
])
.limit(1);

export const periodicReportFilters = filter.define<
Pick<PeriodicReportListInput, 'type' | 'start' | 'end' | 'parent'>
>(() => undefined as any, {
type: ({ value }) => ({ node: hasLabel(`${value}Report`) }),
parent: filter.pathExists((id) => [
node('', 'BaseNode', { id }),
relation('out', '', 'report', ACTIVE),
node('node'),
]),
start: filter.dateTimeProp(),
end: filter.dateTimeProp(),
});

export const periodicReportSorters = defineSorters(IPeriodicReport, {});

export const progressReportSorters = defineSorters(ProgressReport, {
Expand Down
23 changes: 15 additions & 8 deletions src/components/product/product.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
ProducibleType,
Product,
ProductCompletionDescriptionSuggestionsInput,
ProductFilters,
ProductListInput,
ProgressMeasurement,
UpdateDirectScriptureProduct,
Expand Down Expand Up @@ -505,14 +506,10 @@ export class ProductRepository extends CommonRepository {
...(approach ? ApproachToMethodologies[approach] : []),
...(methodology ? [methodology] : []),
];
filter.builder(
{ ...rest, ...(merged.length ? { methodology: merged } : {}) },
{
engagementId: filter.skip,
placeholder: filter.isPropNotNull('placeholderDescription'),
methodology: filter.propVal(),
},
)(q);
productFilters({
...rest,
...(merged.length ? { methodology: merged } : {}),
})(q);
})
.apply(sorting(Product, input))
.apply(paginate(input, this.hydrate(session)))
Expand Down Expand Up @@ -583,6 +580,16 @@ export class ProductRepository extends CommonRepository {
}
}

export const productFilters = filter.define<
Omit<ProductFilters, 'approach' | 'methodology'> & {
methodology?: Methodology[];
}
>(() => undefined as any, {
engagementId: filter.skip,
placeholder: filter.isPropNotNull('placeholderDescription'),
methodology: filter.stringListProp(),
});

const ProductCompletionDescriptionIndex = FullTextIndex({
indexName: 'ProductCompletionDescription',
labels: 'ProductCompletionDescription',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,7 @@ export class ProgressReportMediaRepository extends DtoRepository<
relation('out', '', 'child', ACTIVE),
node('node', this.resource.dbLabel),
])
.apply(
filter.builder(
{ variants: args.variants },
{
variants: ({ value }) => ({
'node.variant': inArray(value.map((v) => v.key)),
}),
},
),
)
.apply(progressReportMediaFilters({ variants: args.variants }))
.match(requestingUser(session))
.apply(projectFromProgressReportChild)
.apply(
Expand Down Expand Up @@ -298,3 +289,11 @@ export class ProgressReportMediaRepository extends DtoRepository<
);
}
}

export const progressReportMediaFilters = filter.define<
Pick<ListArgs, 'variants'>
>(() => ListArgs, {
variants: ({ value }) => ({
'node.variant': inArray(value.map((v) => v.key)),
}),
});
Loading

0 comments on commit fba9266

Please sign in to comment.