app/api/search/specs/search.relationships.v2.spec.ts
import { getFixturesFactory } from 'api/utils/fixturesFactory';
import { testingEnvironment } from 'api/utils/testingEnvironment';
import testingDB from 'api/utils/testing_db';
import { elastic } from '../elastic';
import { search } from '../search';
import { fixturesTimeOut } from './fixtures_elastic';
const fixturesFactory = getFixturesFactory();
const elasticIndex = 'relationship_v2_testing_index';
beforeEach(async () => {
await testingEnvironment.setUp(
{
templates: [
fixturesFactory.template('template1', [
fixturesFactory.property('relProp1', 'newRelationship', {
denormalizedProperty: 'textProp1',
query: [],
}),
fixturesFactory.property('relProp2', 'newRelationship', {
denormalizedProperty: 'dateProp1',
query: [],
}),
fixturesFactory.property('relProp3', 'newRelationship', {
query: [],
filter: true,
}),
fixturesFactory.property('nullprop', 'newRelationship', {
query: [],
}),
fixturesFactory.property('relProp4', 'newRelationship', {
denormalizedProperty: 'select',
query: [],
filter: true,
}),
]),
fixturesFactory.template('template2', [fixturesFactory.property('textProp1', 'text', {})]),
fixturesFactory.template('template3', [fixturesFactory.property('dateProp1', 'date', {})]),
fixturesFactory.template('template4', [
fixturesFactory.property('select', 'select', {
content: fixturesFactory.id('thesauri1'),
}),
]),
],
dictionaries: [fixturesFactory.thesauri('thesauri1', ['value1', 'value2'])],
entities: [
fixturesFactory.entity(
'entity1',
'template1',
{
relProp1: [
{
value: 'entity2',
label: 'entity2',
inheritedValue: [{ value: 'text_content' }],
inheritedType: 'text',
},
],
relProp2: [
{
value: 'entity3',
label: 'entity3',
inheritedValue: [{ value: 1676688700080 }],
inheritedType: 'date',
},
],
relProp3: [
{
value: 'entity3',
label: 'entity3',
inheritedValue: [{ value: 'entity3', label: 'entity3' }],
inheritedType: 'text',
},
],
// @ts-ignore
nullprop: null,
relProp4: [
{
value: 'entity4',
label: 'entity4',
inheritedValue: [{ value: 'value1', label: 'value1' }],
inheritedType: 'select',
},
],
},
{ published: true }
),
fixturesFactory.entity(
'entity2',
'template2',
{
textProp1: [fixturesFactory.metadataValue('text_content')],
},
{ published: true }
),
fixturesFactory.entity(
'entity3',
'template3',
{
dateProp1: [fixturesFactory.metadataValue(1676688700080)],
},
{ published: true }
),
fixturesFactory.entity(
'entity4',
'template4',
{
select: [{ value: 'value1' }],
},
{ published: true }
),
],
settings: [
{
features: {
newRelationships: true,
},
},
],
},
elasticIndex
);
}, fixturesTimeOut);
afterAll(async () => {
await testingEnvironment.tearDown();
});
describe('mapping', () => {
it('should map the property as if it was of the type of the denormalized property', async () => {
const mapping = await elastic.indices.getMapping();
const mappedProps = mapping.body[elasticIndex].mappings.properties.metadata.properties;
expect(mappedProps.relProp1).toMatchObject(mappedProps.textProp1);
expect(mappedProps.relProp2).toMatchObject(mappedProps.dateProp1);
expect(mappedProps.relProp3).toMatchObject({
properties: { value: { type: 'keyword' }, label: { type: 'text' } },
});
});
});
describe('indexing', () => {
it('should index the relationship metadata property using as contents the denormalized values', async () => {
const body = await elastic.search({});
const entityWithRelationships = body.body.hits.hits.find(
e => e._source.sharedId === 'entity1'
)!._source;
expect(entityWithRelationships.metadata!.relProp1).toMatchObject([{ value: 'text_content' }]);
expect(entityWithRelationships.metadata!.relProp2).toMatchObject([{ value: 1676688700080 }]);
expect(entityWithRelationships.metadata!.relProp3).toMatchObject([
{ label: 'entity3', value: 'entity3' },
]);
});
});
describe('searching', () => {
it('should filter the relationship metadata as the type of the denormalized values', async () => {
let results = await search.search({ filters: { relProp2: { from: 1676688700080 } } }, 'en');
expect(results.rows).toEqual([expect.objectContaining({ sharedId: 'entity1' })]);
results = await search.search({ filters: { relProp1: 'text_content' } }, 'en');
expect(results.rows).toEqual([expect.objectContaining({ sharedId: 'entity1' })]);
results = await search.search({ filters: { relProp3: { values: ['entity3'] } } }, 'en');
expect(results.rows).toEqual([expect.objectContaining({ sharedId: 'entity1' })]);
});
it('should transform the relationship properties back to the shape of the database for compatibility', async () => {
const results = await search.search({ ids: 'entity1' }, 'en');
expect(results.rows).toMatchObject([
{
sharedId: 'entity1',
metadata: {
relProp1: [
{ value: 'entity2', label: 'entity2', inheritedValue: [{ value: 'text_content' }] },
],
relProp2: [
{ value: 'entity3', label: 'entity3', inheritedValue: [{ value: 1676688700080 }] },
],
relProp3: [{ value: 'entity3', label: 'entity3' }],
},
},
]);
});
it('should not transform fields that are not new relationships', async () => {
const results = await search.search({}, 'en');
expect(results.rows).toMatchObject([
{ sharedId: 'entity1' },
{
sharedId: 'entity2',
metadata: {
textProp1: [{ value: 'text_content' }],
},
},
{
sharedId: 'entity3',
metadata: {
dateProp1: [{ value: 1676688700080 }],
},
},
{
sharedId: 'entity4',
metadata: {
select: [{ value: 'value1' }],
},
},
]);
});
it('should add the obsoleteMetadata information to the result', async () => {
// Marking the metadata here so it's not indexed.
await testingDB.mongodb
?.collection('entities')
.updateMany(
{ template: fixturesFactory.id('template1') },
{ $set: { obsoleteMetadata: ['relProp1'] } }
);
await testingDB.mongodb
?.collection('entities')
.updateMany(
{ template: fixturesFactory.id('template2') },
{ $set: { obsoleteMetadata: ['someOtherProp'] } }
);
const results = await search.search({}, 'en');
expect(results.rows).toMatchObject([
{
sharedId: 'entity1',
obsoleteMetadata: ['relProp1'],
},
{
sharedId: 'entity2',
obsoleteMetadata: ['someOtherProp'],
},
{
sharedId: 'entity3',
obsoleteMetadata: [],
},
{
sharedId: 'entity4',
obsoleteMetadata: [],
},
]);
});
});
describe('search aggreagations', () => {
let results: any;
beforeEach(async () => {
results = await search.search({ types: [fixturesFactory.id('template1').toHexString()] }, 'en');
});
it('should return aggregations for relationship properties denormalizing the title', async () => {
expect(results.aggregations.all.relProp3.buckets).toMatchObject([
{ label: 'entity3', filtered: { doc_count: 1 } },
{ label: 'Any', filtered: { doc_count: 1 } },
]);
});
it('should return aggregations for relationship properties denormalizing a select property', async () => {
results = await search.search({ types: [fixturesFactory.id('template1').toHexString()] }, 'en');
expect(results.aggregations.all.relProp4.buckets).toMatchObject([
{ label: 'value1', filtered: { doc_count: 1 } },
{ label: 'Any', filtered: { doc_count: 1 } },
]);
});
});