Skip to content

Commit 20347a5

Browse files
committed
edit: AQLqueryBuilder mostly seems to produce correct aql queries.
new file: tests/bool.ts test: 2 failing
1 parent e5e0c43 commit 20347a5

File tree

6 files changed

+141
-23
lines changed

6 files changed

+141
-23
lines changed

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ export function buildAQL(query: query, limit: any = { start: 0, end: 20 }): any
99
const SEARCH = buildSearch(query)
1010
const FILTER = query.filters && buildFilters(query.filters)
1111

12+
/* FOR doc IN ${query.view} */
1213
return aql`
13-
FOR doc IN ${query.view}
14+
FOR doc IN ${aql.literal(query.view)}
1415
${SEARCH}
1516
${FILTER}
1617
LIMIT ${limit.start}, ${limit.end}

src/search.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@ export function buildSearch(query: query): any {
2525
${NOTS.phrs && NOTS.tokens ? aql.literal(' AND ') : undefined} ${NOTS.tokens}`
2626
}
2727

28-
/* if an empty query.terms string or array is passed SEARCH true*/
28+
/* if an empty query.terms string or array is passed, SEARCH true, bringing
29+
* back all documents in view */
2930
return aql`
3031
SEARCH
3132
${ANDS}
3233
${ORS}
3334
${NOTS}
3435
${(!ANDS && !ORS && !NOTS) || undefined}
35-
OPTIONS ${{ collections: query.collections }}
36+
OPTIONS ${{ collections: query.collections.map(c => c.name) }}
3637
SORT TFIDF(doc) DESC`
3738
}
3839

@@ -63,7 +64,7 @@ function buildOPS(collections: collection[], terms: term[], op: string): any {
6364

6465
function buildPhrase(phrase: term, collections: collection[]): any {
6566
return collections.map(coll => {
66-
return aql`PHRASE(doc.text, ${phrase.val.slice(1, -1)}, ${coll.analyzer}`
67+
return aql`PHRASE(doc.text, ${phrase.val.slice(1, -1)}, ${coll.analyzer})`
6768
})
6869
}
6970

tests/bool.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { expect } from 'chai'
2+
import { Database, DocumentCollection, aql } from 'arangojs'
3+
4+
import { buildAQL } from '../src/index'
5+
6+
const ARANGO_URL = process.env.TEST_ARANGODB_URL || "http://localhost:8529"
7+
8+
describe("boolean search logic", () => {
9+
let db: Database
10+
let collection: DocumentCollection<any>
11+
let view
12+
const dbName = `testdb_${Date.now()}`
13+
const collectionName = `coll_${Date.now()}`
14+
15+
16+
before(async () => {
17+
18+
/* create db */
19+
db = new Database({ url: ARANGO_URL })
20+
await db.createDatabase(dbName)
21+
db.useDatabase(dbName)
22+
23+
/* create coll */
24+
collection = db.collection(collectionName)
25+
await collection.create()
26+
27+
/* create view */
28+
view = db.arangoSearchView(`view_${Date.now()}`)
29+
const links = {}
30+
links[collectionName] = {
31+
fields: { text: { analyzers: ['text_en'] } }
32+
}
33+
await view.create({ links })
34+
let info = await view.get()
35+
expect(info.name).to.equal(view.name)
36+
37+
/* add documents */
38+
const doc1 = { title: 'doc A', text: 'words in text in document' }
39+
const doc2 = { title: 'doc A', text: 'sample word string to search across' }
40+
await db.query(aql`INSERT ${doc1} INTO ${collection}`)
41+
await db.query(aql`INSERT ${doc2} INTO ${collection}`)
42+
})
43+
after(async () => {
44+
try {
45+
db.useDatabase("_system")
46+
await db.dropDatabase(dbName)
47+
await view.drop()
48+
} finally {
49+
db.close()
50+
}
51+
})
52+
53+
describe('ANDS', () => {
54+
it(`should exclude all results that do not match required terms`, async () => {
55+
const query = {
56+
view: view.name,
57+
collections: [{
58+
name: collectionName,
59+
analyzer: 'text_en'
60+
}],
61+
terms: '+word'
62+
}
63+
const aqlQuery = buildAQL(query)
64+
65+
expect(Object.keys(aqlQuery.bindVars)).to.have.length(7)
66+
expect(aqlQuery.bindVars.value0).to.equal('word')
67+
expect(aqlQuery.bindVars.value1).to.equal('text_en')
68+
expect(aqlQuery.bindVars.value2).to.equal('text')
69+
expect(aqlQuery.bindVars.value3).to.equal(1)
70+
expect(aqlQuery.bindVars.value4).to.deep.equal({ collections: [collectionName] })
71+
expect(aqlQuery.bindVars.value5).to.equal(0)
72+
expect(aqlQuery.bindVars.value6).to.equal(20)
73+
expect(aqlQuery.query).to.equal(`
74+
FOR doc IN ${view.name}
75+
76+
SEARCH
77+
MIN_MATCH(
78+
ANALYZER(
79+
TOKENS(@value0, @value1)
80+
ALL IN doc.@value2, @value1),
81+
@value3)
82+
83+
84+
85+
OPTIONS @value4
86+
SORT TFIDF(doc) DESC
87+
88+
LIMIT @value5, @value6
89+
RETURN doc`)
90+
91+
const cursor = await db.query(aqlQuery)
92+
let has = cursor.hasNext()
93+
expect(has).to.be.ok
94+
95+
let result = await cursor.next()
96+
expect(result).to.deep.equal({ a: 1 })
97+
})
98+
})
99+
})

tests/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,17 @@ describe('buildAQL', () => {
2323
expect(builtAQL).to.be.an('object')
2424

2525
expect(builtAQL.query).to.equal(`
26-
FOR doc IN @value0
26+
FOR doc IN view
2727
2828
SEARCH
2929
3030
3131
32-
@value1
33-
OPTIONS @value2
32+
@value0
33+
OPTIONS @value1
3434
SORT TFIDF(doc) DESC
3535
36-
LIMIT @value3, @value4
36+
LIMIT @value2, @value3
3737
RETURN doc`
3838
)
3939
})

tests/parse.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import {expect} from 'chai'
2-
import {parseQuery} from '../src/parse'
1+
import { expect } from 'chai'
2+
import { parseQuery } from '../src/parse'
33

44
describe('parse.ts', () => {
55
it('should export a function parseQuery', () => {
@@ -15,12 +15,12 @@ describe('parse.ts', () => {
1515
when a string is passed with no op hints`, () => {
1616
const parsedQuery = parseQuery('terms "a and b" c')
1717
expect(parsedQuery).to.be.an('array').that.has.lengthOf(3)
18-
expect(parsedQuery[0]).to.deep.equal({
18+
expect(parsedQuery[ 0 ]).to.deep.equal({
1919
op: "?",
2020
type: "tok",
2121
val: "terms",
2222
})
23-
expect(parsedQuery[1]).to.deep.equal({
23+
expect(parsedQuery[ 1 ]).to.deep.equal({
2424
op: "?",
2525
type: "phr",
2626
val: '"a and b"',
@@ -33,13 +33,13 @@ describe('parse.ts', () => {
3333

3434
expect(parsedQuery).to.be.an('array').that.has.lengthOf(4)
3535

36-
expect(parsedQuery[1]).to.deep.equal({
36+
expect(parsedQuery[ 1 ]).to.deep.equal({
3737
op: "+",
3838
type: "phr",
3939
val: '"must have"',
4040
})
4141

42-
expect(parsedQuery[2]).to.deep.equal({
42+
expect(parsedQuery[ 2 ]).to.deep.equal({
4343
op: "-",
4444
type: "tok",
4545
val: 'cannot',

tests/search.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,33 @@ import { expect } from 'chai'
22
import { buildSearch } from '../src/search'
33

44
describe('search.js', () => {
5-
it('should export a function named buildSearch', () => {
6-
expect(buildSearch).to.be.a('function')
7-
})
5+
it('should export a function named buildSearch', () => {
6+
expect(buildSearch).to.be.a('function')
7+
})
88

9-
it('should return an array of aql objects', () => {
10-
const query = { view: 'search_view', collections: [ { name: 'coll', analyzer: 'text_en' } ], terms: '-a +"query string" ?token' }
11-
const builtSearch = buildSearch(query)
12-
expect(builtSearch).to.be.an('object')
9+
it('should return SEARCH true when terms: is an empty string', () => {
10+
const query = { view: 'search_view', collections: [{ name: 'coll', analyzer: 'text_en' }], terms: '' }
11+
const builtSearch = buildSearch(query)
1312

14-
expect(builtSearch.query).to.equal(`
13+
expect(builtSearch).to.be.an('object')
14+
expect(Object.keys(builtSearch.bindVars)).to.have.length(2)
15+
expect(builtSearch.bindVars.value0).to.equal(true)
16+
expect(builtSearch.bindVars.value1).to.deep.equal({ collections: ['coll'] })
17+
})
18+
19+
it('should return an array of aql objects', () => {
20+
const query = { view: 'search_view', collections: [{ name: 'coll', analyzer: 'text_en' }], terms: '-a +"query string" ?token' }
21+
const builtSearch = buildSearch(query)
22+
23+
expect(Object.keys(builtSearch.bindVars)).to.have.length(7)
24+
expect(builtSearch.bindVars.value0[0].query).to.equal('PHRASE(doc.text, @value0, @value1)')
25+
expect(builtSearch.bindVars.value1).to.deep.equal('token')
26+
expect(builtSearch.bindVars.value2).to.deep.equal('text_en')
27+
expect(builtSearch.bindVars.value3).to.deep.equal('text')
28+
expect(builtSearch.bindVars.value4).to.deep.equal(1)
29+
expect(builtSearch.bindVars.value5).to.deep.equal('a')
30+
expect(builtSearch.bindVars.value6).to.deep.equal({ collections: [query.collections[0].name] })
31+
expect(builtSearch.query).to.equal(`
1532
SEARCH
1633
@value0 OR (@value0 AND MIN_MATCH(
1734
ANALYZER(
@@ -29,5 +46,5 @@ describe('search.js', () => {
2946
3047
OPTIONS @value6
3148
SORT TFIDF(doc) DESC`)
32-
})
49+
})
3350
})

0 commit comments

Comments
 (0)