mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge pull request #3297 from Human-Connection/2143-search-for-hashtags
feat: Search for Hashtags
This commit is contained in:
commit
576b511d03
@ -12,6 +12,7 @@ class Store {
|
||||
[
|
||||
'CALL db.index.fulltext.createNodeIndex("post_fulltext_search",["Post"],["title", "content"])',
|
||||
'CALL db.index.fulltext.createNodeIndex("user_fulltext_search",["User"],["name", "slug"])',
|
||||
'CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"])',
|
||||
].map(statement => txc.run(statement)),
|
||||
)
|
||||
})
|
||||
|
||||
@ -40,6 +40,7 @@ export async function down(next) {
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
import { getDriver } from '../../db/neo4j'
|
||||
|
||||
export const description =
|
||||
'This migration adds a fulltext index for the tags in order to search for Hasthags.'
|
||||
|
||||
export async function up(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
await transaction.run(`
|
||||
CALL db.index.fulltext.createNodeIndex("tag_fulltext_search",["Tag"],["id"])
|
||||
`)
|
||||
await transaction.commit()
|
||||
next()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(next) {
|
||||
const driver = getDriver()
|
||||
const session = driver.session()
|
||||
const transaction = session.beginTransaction()
|
||||
|
||||
try {
|
||||
// Implement your migration here.
|
||||
await transaction.run(`
|
||||
CALL db.index.fulltext.drop("tag_fulltext_search")
|
||||
`)
|
||||
await transaction.commit()
|
||||
next()
|
||||
} catch (error) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(error)
|
||||
await transaction.rollback()
|
||||
// eslint-disable-next-line no-console
|
||||
console.log('rolled back')
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,16 @@ export default {
|
||||
RETURN resource {.*, __typename: labels(resource)[0]}
|
||||
LIMIT $limit
|
||||
`
|
||||
const tagCypher = `
|
||||
CALL db.index.fulltext.queryNodes('tag_fulltext_search', $query)
|
||||
YIELD node as resource, score
|
||||
MATCH (resource)
|
||||
WHERE score >= 0.0
|
||||
AND NOT (resource.deleted = true OR resource.disabled = true)
|
||||
RETURN resource {.*, __typename: labels(resource)[0]}
|
||||
LIMIT $limit
|
||||
`
|
||||
|
||||
const myQuery = queryString(query)
|
||||
|
||||
const session = context.driver.session()
|
||||
@ -55,14 +65,25 @@ export default {
|
||||
limit,
|
||||
thisUserId,
|
||||
})
|
||||
return Promise.all([postTransactionResponse, userTransactionResponse])
|
||||
const tagTransactionResponse = transaction.run(tagCypher, {
|
||||
query: myQuery,
|
||||
limit,
|
||||
})
|
||||
return Promise.all([
|
||||
postTransactionResponse,
|
||||
userTransactionResponse,
|
||||
tagTransactionResponse,
|
||||
])
|
||||
})
|
||||
|
||||
try {
|
||||
const [postResults, userResults] = await searchResultPromise
|
||||
const [postResults, userResults, tagResults] = await searchResultPromise
|
||||
log(postResults)
|
||||
log(userResults)
|
||||
return [...postResults.records, ...userResults.records].map(r => r.get('resource'))
|
||||
log(tagResults)
|
||||
return [...postResults.records, ...userResults.records, ...tagResults.records].map(r =>
|
||||
r.get('resource'),
|
||||
)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
|
||||
@ -41,6 +41,9 @@ const searchQuery = gql`
|
||||
slug
|
||||
name
|
||||
}
|
||||
... on Tag {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
@ -439,6 +442,28 @@ und hinter tausend Stäben keine Welt.`,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('adding a tag', () => {
|
||||
beforeAll(async () => {
|
||||
await Factory.build('tag', { id: 'myHashtag' })
|
||||
})
|
||||
|
||||
describe('query the first four characters of the tag', () => {
|
||||
it('finds the tag', async () => {
|
||||
variables = { query: 'myha' }
|
||||
await expect(query({ query: searchQuery, variables })).resolves.toMatchObject({
|
||||
data: {
|
||||
findResources: [
|
||||
{
|
||||
__typename: 'Tag',
|
||||
id: 'myHashtag',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
union SearchResult = Post | User
|
||||
union SearchResult = Post | User | Tag
|
||||
|
||||
type Query {
|
||||
findResources(query: String!, limit: Int = 5): [SearchResult]!
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<ds-tag>
|
||||
<ds-tag class="hc-hashtag">
|
||||
<nuxt-link :to="hashtagUrl">#{{ id }}</nuxt-link>
|
||||
</ds-tag>
|
||||
</template>
|
||||
|
||||
@ -106,6 +106,16 @@ describe('SearchableInput.vue', () => {
|
||||
params: { id: 'u2', slug: 'bob-der-baumeister' },
|
||||
})
|
||||
})
|
||||
|
||||
it('pushes hashtag query params', async () => {
|
||||
select.element.value = 'Hash'
|
||||
select.trigger('input')
|
||||
const tags = wrapper.findAll('.hc-hashtag')
|
||||
const tag = tags.filter(item => item.text().match(/#Hashtag/))
|
||||
tag.trigger('click')
|
||||
await Vue.nextTick()
|
||||
expect(mocks.$router.push).toHaveBeenCalledWith('?hashtag=Hashtag')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -106,6 +106,10 @@ export const searchResults = [
|
||||
name: 'Tonya Mohr',
|
||||
slug: 'tonya-mohr',
|
||||
},
|
||||
{
|
||||
id: 'Hashtag',
|
||||
__typename: 'Tag',
|
||||
},
|
||||
]
|
||||
|
||||
storiesOf('Search Field', module)
|
||||
|
||||
@ -35,6 +35,12 @@
|
||||
>
|
||||
<search-post :option="option" />
|
||||
</p>
|
||||
<p
|
||||
v-if="option.__typename === 'Tag'"
|
||||
:class="{ 'option-with-heading': isFirstOfType(option) }"
|
||||
>
|
||||
<hc-hashtag :id="option.id" />
|
||||
</p>
|
||||
</template>
|
||||
</ds-select>
|
||||
<base-button v-if="isActive" icon="close" circle ghost size="small" @click="clear" />
|
||||
@ -45,6 +51,7 @@
|
||||
import { isEmpty } from 'lodash'
|
||||
import SearchHeading from '~/components/generic/SearchHeading/SearchHeading.vue'
|
||||
import SearchPost from '~/components/generic/SearchPost/SearchPost.vue'
|
||||
import HcHashtag from '~/components/Hashtag/Hashtag.vue'
|
||||
import UserTeaser from '~/components/UserTeaser/UserTeaser.vue'
|
||||
|
||||
export default {
|
||||
@ -52,6 +59,7 @@ export default {
|
||||
components: {
|
||||
SearchHeading,
|
||||
SearchPost,
|
||||
HcHashtag,
|
||||
UserTeaser,
|
||||
},
|
||||
props: {
|
||||
@ -138,12 +146,19 @@ export default {
|
||||
isPost(item) {
|
||||
return item.__typename === 'Post'
|
||||
},
|
||||
isTag(item) {
|
||||
return item.__typename === 'Tag'
|
||||
},
|
||||
goToResource(item) {
|
||||
this.$nextTick(() => {
|
||||
this.$router.push({
|
||||
name: this.isPost(item) ? 'post-id-slug' : 'profile-id-slug',
|
||||
params: { id: item.id, slug: item.slug },
|
||||
})
|
||||
if (!this.isTag(item)) {
|
||||
this.$router.push({
|
||||
name: this.isPost(item) ? 'post-id-slug' : 'profile-id-slug',
|
||||
params: { id: item.id, slug: item.slug },
|
||||
})
|
||||
} else {
|
||||
this.$router.push('?hashtag=' + item.id)
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
@ -19,6 +19,9 @@ export const findResourcesQuery = gql`
|
||||
... on User {
|
||||
...user
|
||||
}
|
||||
... on Tag {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
@ -599,6 +599,7 @@
|
||||
"failed": "Nichts gefunden",
|
||||
"heading": {
|
||||
"Post": "Beiträge",
|
||||
"Tag": "Hashtags",
|
||||
"User": "Benutzer"
|
||||
},
|
||||
"hint": "Wonach suchst Du?",
|
||||
|
||||
@ -599,6 +599,7 @@
|
||||
"failed": "Nothing found",
|
||||
"heading": {
|
||||
"Post": "Posts",
|
||||
"Tag": "Hashtags",
|
||||
"User": "Users"
|
||||
},
|
||||
"hint": "What are you searching for?",
|
||||
|
||||
@ -130,6 +130,7 @@ export default {
|
||||
return this.$apollo.loading || (this.posts && this.posts.length > 0)
|
||||
},
|
||||
},
|
||||
watchQuery: ['hashtag'],
|
||||
methods: {
|
||||
...mapMutations({
|
||||
selectOrder: 'posts/SELECT_ORDER',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user