mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
search spec starts doing what it should
This commit is contained in:
parent
f9b6fb95ab
commit
a6a2ac4fbe
@ -1,22 +1,18 @@
|
||||
import log from './helpers/databaseLogger'
|
||||
|
||||
// see http://lucene.apache.org/core/8_3_1/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
findResources: async (_parent, args, context, _resolveInfo) => {
|
||||
const { query, limit } = args
|
||||
const { id: thisUserId } = context.user
|
||||
// see http://lucene.apache.org/core/8_3_1/queryparser/org/apache/lucene/queryparser/classic/package-summary.html#package.description
|
||||
const myQuery = query
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/[[@#:*~\\$|^\]?/"'(){}+?!,.-;]/g, '')
|
||||
.split(' ')
|
||||
.map(s => (s.toLowerCase().match(/^(not|and|or)$/) ? '"' + s + '"' : s + '*'))
|
||||
.join(' ')
|
||||
|
||||
const postCypher = `
|
||||
CALL db.index.fulltext.queryNodes('post_fulltext_search', $query)
|
||||
YIELD node as resource, score
|
||||
MATCH (resource)<-[:WROTE]-(author:User)
|
||||
WHERE score >= 0.5
|
||||
WHERE score >= 0.2
|
||||
AND NOT (
|
||||
author.deleted = true OR author.disabled = true
|
||||
OR resource.deleted = true OR resource.disabled = true
|
||||
@ -53,7 +49,7 @@ export default {
|
||||
thisUserId,
|
||||
})
|
||||
const userTransactionResponse = transaction.run(userCypher, {
|
||||
query: myQuery,
|
||||
query: createUserQuery(query),
|
||||
limit,
|
||||
thisUserId,
|
||||
})
|
||||
@ -64,6 +60,8 @@ export default {
|
||||
const [postResults, userResults] = await searchResultPromise
|
||||
log(postResults)
|
||||
log(userResults)
|
||||
// console.log(postResults.summary.query.parameters)
|
||||
// console.log(userResults)
|
||||
return [...postResults.records, ...userResults.records].map(r => r.get('resource'))
|
||||
} finally {
|
||||
session.close()
|
||||
@ -72,16 +70,32 @@ export default {
|
||||
},
|
||||
}
|
||||
|
||||
function createUserQuery(str) {
|
||||
// match the whole text
|
||||
const normalizedString = normalizeWhitespace(str)
|
||||
const escapedString = escapeSpecialCharacters(normalizedString)
|
||||
const result = normalizedString.includes(' ') ? quoteString(escapedString) : escapedString
|
||||
// console.log('"' + + '"')
|
||||
return result
|
||||
}
|
||||
|
||||
function createPostQuery(str) {
|
||||
// match the whole text
|
||||
// console.log('"' + escapeSpecialCharacters(normalizeWhitespace(str)) + '"')
|
||||
return '"' + escapeSpecialCharacters(normalizeWhitespace(str)) + '"'
|
||||
const normalizedString = normalizeWhitespace(str)
|
||||
const escapedString = escapeSpecialCharacters(normalizedString)
|
||||
const result = normalizedString.includes(' ') ? quoteString(escapedString) : escapedString
|
||||
// console.log('"' + + '"')
|
||||
return result
|
||||
}
|
||||
|
||||
function normalizeWhitespace(str) {
|
||||
return str.replace(/\s+/g, ' ')
|
||||
}
|
||||
|
||||
function quoteString(str) {
|
||||
return '"' + str + '"'
|
||||
}
|
||||
|
||||
function escapeSpecialCharacters(str) {
|
||||
return str.replace(/(["[\]&|\\{}+!()^~*?:/-])/g, '\\$1')
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ beforeAll(async () => {
|
||||
})
|
||||
|
||||
afterAll(async () => {
|
||||
await cleanDatabase()
|
||||
// await cleanDatabase()
|
||||
})
|
||||
|
||||
const searchQuery = gql`
|
||||
@ -47,73 +47,149 @@ const searchQuery = gql`
|
||||
}
|
||||
`
|
||||
|
||||
const nothingFound = { data: { findResources: [] } }
|
||||
|
||||
const createExpectedObject = array => {
|
||||
return { data: { findResources: array } }
|
||||
}
|
||||
|
||||
const addPostToDB = post => {
|
||||
return Factory.build('post', {
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
content: post.content,
|
||||
})
|
||||
}
|
||||
|
||||
const addUserToDB = user => {
|
||||
return Factory.build('user', {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
slug: user.slug,
|
||||
})
|
||||
}
|
||||
|
||||
const createDataObject = (obj, type) => {
|
||||
return { __typename: type, ...obj }
|
||||
}
|
||||
|
||||
const createPostObject = post => {
|
||||
return createDataObject(post, 'Post')
|
||||
}
|
||||
|
||||
const createUserObject = user => {
|
||||
return createDataObject(user, 'User')
|
||||
}
|
||||
|
||||
// see data at the end of the file
|
||||
|
||||
let user
|
||||
|
||||
describe('resolvers', () => {
|
||||
describe('searches', () => {
|
||||
beforeAll(async () => {
|
||||
const user = await Factory.build('user', {
|
||||
id: 'a-user',
|
||||
name: 'John Doe',
|
||||
slug: 'john-doe',
|
||||
})
|
||||
await Factory.build('post', {
|
||||
id: 'a-post',
|
||||
title: 'Beitrag',
|
||||
content: 'Ein erster Beitrag',
|
||||
})
|
||||
user = await addUserToDB(aUser)
|
||||
await addPostToDB(aPost)
|
||||
authenticatedUser = await user.toJson()
|
||||
})
|
||||
|
||||
let variables
|
||||
|
||||
describe('basic searches', () => {
|
||||
it('finds the post', async () => {
|
||||
variables = { query: 'Beitrag' }
|
||||
const res = await query({ query: searchQuery, variables })
|
||||
// console.log(res)
|
||||
expect(res.data.findResources).toHaveLength(1)
|
||||
const expected = createExpectedObject([aPost])
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(expected)
|
||||
})
|
||||
|
||||
it('does not find the post', async () => {
|
||||
variables = { query: 'Unfug' }
|
||||
const res = await query({ query: searchQuery, variables })
|
||||
expect(res.data.findResources).toHaveLength(0)
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(nothingFound)
|
||||
})
|
||||
|
||||
it('finds the user', async () => {
|
||||
variables = { query: 'John' }
|
||||
const res = await query({ query: searchQuery, variables })
|
||||
// console.log(res)
|
||||
expect(res.data.findResources).toHaveLength(1)
|
||||
const expected = createExpectedObject([aUser])
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(expected)
|
||||
})
|
||||
|
||||
it('does not find the user', async () => {
|
||||
variables = { query: 'Unfug' }
|
||||
const res = await query({ query: searchQuery, variables })
|
||||
expect(res.data.findResources).toHaveLength(0)
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(nothingFound)
|
||||
})
|
||||
})
|
||||
|
||||
describe('more data added', () => {
|
||||
beforeAll(async () => {
|
||||
await Promise.all([
|
||||
Factory.build('post', {
|
||||
id: 'b-post',
|
||||
title: 'Aufruf',
|
||||
content: 'Jeder sollte seinen Beitrag leisten.',
|
||||
}),
|
||||
Factory.build('post', {
|
||||
id: 'c-post',
|
||||
title: 'Die binomischen Formeln',
|
||||
content: `
|
||||
1. binomische Formel: (a + b)² = a² + 2ab + b²
|
||||
addPostToDB(bPost),
|
||||
addPostToDB(cPost),
|
||||
addPostToDB(dPost),
|
||||
addPostToDB(ePost),
|
||||
addPostToDB(fPost),
|
||||
addPostToDB(gPost),
|
||||
addUserToDB(bUser),
|
||||
addUserToDB(cUser),
|
||||
addUserToDB(dUser),
|
||||
addUserToDB(eUser),
|
||||
addUserToDB(fUser),
|
||||
addUserToDB(gUser),
|
||||
])
|
||||
})
|
||||
|
||||
it('finds the AK-47', async () => {
|
||||
variables = { query: 'AK-47' }
|
||||
const expected = createExpectedObject([gPost])
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(expected)
|
||||
})
|
||||
|
||||
it('finds more than one post', async () => {
|
||||
variables = { query: 'Beitrag' }
|
||||
const expected = createExpectedObject([aPost, bPost])
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(expected)
|
||||
})
|
||||
|
||||
it('finds more than one user by slug', async () => {
|
||||
variables = { query: '-maria-' }
|
||||
const expected = createExpectedObject([dUser, cUser])
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(expected)
|
||||
})
|
||||
|
||||
it('finds Russian text', async () => {
|
||||
variables = { query: 'Калашникова' }
|
||||
const expected = createExpectedObject([gPost])
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
// data section
|
||||
|
||||
const aPost = createPostObject({
|
||||
id: 'a-post',
|
||||
title: 'Beitrag',
|
||||
content: 'Ein erster Beitrag',
|
||||
})
|
||||
|
||||
const bPost = createPostObject({
|
||||
id: 'b-post',
|
||||
title: 'Aufruf',
|
||||
content: 'Jeder sollte seinen Beitrag leisten.',
|
||||
})
|
||||
|
||||
const cPost = createPostObject({
|
||||
id: 'c-post',
|
||||
title: 'Die binomischen Formeln',
|
||||
content: `1. binomische Formel: (a + b)² = a² + 2ab + b²
|
||||
2. binomische Formel: (a - b)² = a² - 2ab + b²
|
||||
3. binomische Formel: (a + b)(a - b) = a² - b²
|
||||
`,
|
||||
}),
|
||||
Factory.build('post', {
|
||||
id: 'd-post',
|
||||
title: 'Der Panther',
|
||||
content: `
|
||||
Sein Blick ist vom Vorübergehn der Stäbe
|
||||
3. binomische Formel: (a + b)(a - b) = a² - b²`,
|
||||
})
|
||||
|
||||
const dPost = createPostObject({
|
||||
id: 'd-post',
|
||||
title: 'Der Panther',
|
||||
content: `Sein Blick ist vom Vorübergehn der Stäbe
|
||||
so müd geworden, daß er nichts mehr hält.
|
||||
Ihm ist, als ob es tausend Stäbe gäbe
|
||||
und hinter tausend Stäben keine Welt.
|
||||
@ -126,69 +202,66 @@ in der betäubt ein großer Wille steht.
|
||||
Nur manchmal schiebt der Vorhang der Pupille
|
||||
sich lautlos auf –. Dann geht ein Bild hinein,
|
||||
geht durch der Glieder angespannte Stille –
|
||||
und hört im Herzen auf zu sein.
|
||||
`,
|
||||
}),
|
||||
Factory.build('post', {
|
||||
id: 'e-post',
|
||||
title: 'Typographie',
|
||||
content: `
|
||||
Gelegentlich können sowohl der angeführte Text als auch der Begleitsatz mit Frage- oder Ausrufezeichen enden (§ 91):
|
||||
Gefällt dir der Roman „Quo vadis?“? Lass doch dieses ewige „Ich will nicht!“!
|
||||
`,
|
||||
}),
|
||||
Factory.build('post', {
|
||||
id: 'f-post',
|
||||
title: 'Typographie II',
|
||||
content: `
|
||||
Der Gedankenstrich kann als Auslassungszeichen (Auslassungsstrich) eine längere Pause oder eine Ellipse darstellen: „Du willst doch wohl nicht etwa –“, „Mein Gott, woher nehm ich bloß –?“
|
||||
`,
|
||||
}),
|
||||
Factory.build('post', {
|
||||
id: 'g-post',
|
||||
title: 'AK-47',
|
||||
content: `
|
||||
Vom AK-47 Typ I existiert eine Version mit unter die Waffe klappbarer Schulterstütze, das AKS-47 (russisch Автомат Калашникова складной образца 1947 года, transkr.: Avtomat Kalašnikova skladnoj obrazca 1947 goda, dt. Automat Kalaschnikow klappbar Modell 1947tes Jahr) genannt wird, seltener auch AK-47s.
|
||||
`,
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'b-user',
|
||||
name: 'Johnannes der Täufer',
|
||||
slug: 'johnannes-der-taufer',
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'c-user',
|
||||
name: 'Rainer Maria Rilke',
|
||||
slug: 'rainer-maria-rilke',
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'd-user',
|
||||
name: 'Erich Maria Remarque',
|
||||
slug: 'erich-maria-remarque',
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'e-user',
|
||||
name: 'Klaus Dieter',
|
||||
slug: 'kd',
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'f-user',
|
||||
name: 'Sluggy',
|
||||
slug: '_',
|
||||
}),
|
||||
Factory.build('user', {
|
||||
id: 'g-user',
|
||||
name: 'AKK',
|
||||
slug: 'akk',
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
it('finds the AK-47', async () => {
|
||||
variables = { query: 'AK-47' }
|
||||
const res = await query({ query: searchQuery, variables })
|
||||
expect(res.data.findResources).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
und hört im Herzen auf zu sein.`,
|
||||
})
|
||||
|
||||
const ePost = createPostObject({
|
||||
id: 'e-post',
|
||||
title: 'Typographie',
|
||||
content: `Gelegentlich können sowohl der angeführte Text als auch der Begleitsatz mit Frage- oder Ausrufezeichen enden (§ 91):
|
||||
Gefällt dir der Roman „Quo vadis?“? Lass doch dieses ewige „Ich will nicht!“!`,
|
||||
})
|
||||
|
||||
const fPost = createPostObject({
|
||||
id: 'f-post',
|
||||
title: 'Typographie II',
|
||||
content: `Der Gedankenstrich kann als Auslassungszeichen (Auslassungsstrich) eine längere Pause oder eine Ellipse darstellen: „Du willst doch wohl nicht etwa –“, „Mein Gott, woher nehm ich bloß –?“`,
|
||||
})
|
||||
|
||||
const gPost = createPostObject({
|
||||
id: 'g-post',
|
||||
title: 'AK-47',
|
||||
content: `Vom AK-47 Typ I existiert eine Version mit unter die Waffe klappbarer Schulterstütze, das AKS-47 (russisch Автомат Калашникова складной образца 1947 года, transkr.: Avtomat Kalašnikova skladnoj obrazca 1947 goda, dt. Automat Kalaschnikow klappbar Modell 1947tes Jahr) genannt wird, seltener auch AK-47s.`,
|
||||
})
|
||||
|
||||
const aUser = createUserObject({
|
||||
id: 'a-user',
|
||||
name: 'John Doe',
|
||||
slug: 'john-doe',
|
||||
})
|
||||
|
||||
const bUser = createUserObject({
|
||||
id: 'b-user',
|
||||
name: 'Johnannes der Täufer',
|
||||
slug: 'johnannes-der-taufer',
|
||||
})
|
||||
|
||||
const cUser = createUserObject({
|
||||
id: 'c-user',
|
||||
name: 'Rainer Maria Rilke',
|
||||
slug: 'rainer-maria-rilke',
|
||||
})
|
||||
|
||||
const dUser = createUserObject({
|
||||
id: 'd-user',
|
||||
name: 'Erich Maria Remarque',
|
||||
slug: 'erich-maria-remarque',
|
||||
})
|
||||
|
||||
const eUser = createUserObject({
|
||||
id: 'e-user',
|
||||
name: 'Klaus Dieter',
|
||||
slug: 'kd',
|
||||
})
|
||||
|
||||
const fUser = createUserObject({
|
||||
id: 'f-user',
|
||||
name: 'Sluggy',
|
||||
slug: '_',
|
||||
})
|
||||
|
||||
const gUser = createUserObject({
|
||||
id: 'g-user',
|
||||
name: 'AKK',
|
||||
slug: 'akk',
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user