diff --git a/hana/lib/cql-functions.js b/hana/lib/cql-functions.js index d44013f61..7d1b905c1 100644 --- a/hana/lib/cql-functions.js +++ b/hana/lib/cql-functions.js @@ -122,7 +122,8 @@ const StandardFunctions = { // if column specific value is provided, the configuration has to be defined on column level if (csnElements.some(e => e.element?.['@Search.ranking'] || e.element?.['@Search.fuzzinessThreshold'])) { csnElements.forEach(e => { - let fuzzy = `FUZZY` + const fuzziScore = e.element?.['@Search.fuzzinessThreshold'] || fuzzyIndex + let fuzzy = `${ fuzziScore === 1 ? 'EXACT' : 'FUZZY'}` // weighted search const rank = e.element?.['@Search.ranking']?.['='] switch (rank) { @@ -141,14 +142,20 @@ const StandardFunctions = { `Invalid configuration ${rank} for @Search.ranking. HIGH, MEDIUM, LOW are supported values.`, ) } - fuzzy += ` MINIMAL TOKEN SCORE ${e.element?.['@Search.fuzzinessThreshold'] || fuzzyIndex} SIMILARITY CALCULATION MODE 'search'` + if (fuzziScore === 1) + fuzzy += ` MINIMAL SCORE 1 search mode 'text'` + else + fuzzy += ` MINIMAL TOKEN SCORE ${fuzziScore} SIMILARITY CALCULATION MODE 'search'` // rewrite ref to xpr to mix in search config // ensure in place modification to reuse .toString method that ensures quoting e.xpr = [{ ref: e.ref }, fuzzy] delete e.ref }) } else { - ref = `${ref} FUZZY MINIMAL TOKEN SCORE ${fuzzyIndex} SIMILARITY CALCULATION MODE 'search'` + if (fuzzyIndex === 1) + ref = `${ref} EXACT MINIMAL SCORE 1 search mode 'text'` + else + ref = `${ref} FUZZY MINIMAL TOKEN SCORE ${fuzzyIndex} SIMILARITY CALCULATION MODE 'search'` } if (Array.isArray(arg.xpr)) { diff --git a/hana/test/fuzzy.cds b/hana/test/fuzzy.cds index 56e22edda..0991f4f9d 100644 --- a/hana/test/fuzzy.cds +++ b/hana/test/fuzzy.cds @@ -1,5 +1,10 @@ -using {sap.capire.bookshop.BooksAnnotated as BooksAnnotated} from '../../test/bookshop/db/schema.cds'; +using {sap.capire.bookshop.BooksAnnotated as BooksAnnotated, sap.capire.bookshop.Books as Books} from '../../test/bookshop/db/schema.cds'; annotate BooksAnnotated with @cds.search: {title, descr, currency.code}; annotate BooksAnnotated:title with @(Search.ranking: HIGH, Search.fuzzinessThreshold: 0.9); -annotate BooksAnnotated:descr with @(Search.ranking: LOW, Search.fuzzinessThreshold: 0.9); \ No newline at end of file +annotate BooksAnnotated:descr with @(Search.ranking: LOW, Search.fuzzinessThreshold: 0.9); + +entity BooksAnnotatedScore1 as projection on Books; +annotate BooksAnnotatedScore1 with @cds.search: {title, descr, currency.code}; +annotate BooksAnnotatedScore1:title with @(Search.ranking: HIGH, Search.fuzzinessThreshold: 0.9); +annotate BooksAnnotatedScore1:descr with @(Search.ranking: LOW, Search.fuzzinessThreshold: 1); \ No newline at end of file diff --git a/hana/test/fuzzy.test.js b/hana/test/fuzzy.test.js index b7f80aa42..0cbbef9b5 100644 --- a/hana/test/fuzzy.test.js +++ b/hana/test/fuzzy.test.js @@ -28,14 +28,24 @@ describe('search', () => { expect(res.length).to.be(2) // Eleonora and Jane Eyre }) - test('global config', async () => { + test('global config partial string', async () => { cds.env.hana.fuzzy = 1 const { Books } = cds.entities('sap.capire.bookshop') const cqn = SELECT.from(Books).search('"autobio"').columns('1') const {sql} = cqn.toSQL() - expect(sql).to.include('FUZZY MINIMAL TOKEN SCORE 1') + expect(sql).to.include('EXACT MINIMAL SCORE 1') const res = await cqn - expect(res.length).to.be(2) // Eleonora and Jane Eyre + expect(res.length).to.be(0) // must be exact match + }) + + test('global config whole string', async () => { + cds.env.hana.fuzzy = 1 + const { Books } = cds.entities('sap.capire.bookshop') + const cqn = SELECT.from(Books).search('"Jane"').columns('1') + const {sql} = cqn.toSQL() + expect(sql).to.include('EXACT MINIMAL SCORE 1') + const res = await cqn + expect(res.length).to.be(2) // Wuthering Heights and and Jane Eyre }) test('annotations', async () => { @@ -49,6 +59,18 @@ describe('search', () => { const res = await cqn expect(res.length).to.be(1) // jane eyre }) + + test('annotations with descr score 1', async () => { + const { BooksAnnotatedScore1 } = cds.entities + const cqn = SELECT.from(BooksAnnotatedScore1).search('is often').columns('1') + const {sql} = cqn.toSQL() + expect(sql).to.include('title FUZZY WEIGHT 0.8 MINIMAL TOKEN SCORE 0.9') + expect(sql).to.include('code FUZZY WEIGHT 0.5 MINIMAL TOKEN SCORE 0.7') + expect(sql).to.include('descr EXACT WEIGHT 0.3 MINIMAL SCORE 1') + + const res = await cqn + expect(res.length).to.be(2) // Eleonora and Raven + }) }) describe('like', () => {