mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Merge pull request #5380 from Ocelot-Social-Community/post-in-group
feat: 🍰 Post In Groups
This commit is contained in:
commit
51ce290195
@ -19,6 +19,7 @@ export const signupVerificationMutation = gql`
|
||||
nonce: $nonce
|
||||
termsAndConditionsAgreedVersion: $termsAndConditionsAgreedVersion
|
||||
) {
|
||||
id
|
||||
slug
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,15 +2,87 @@ import gql from 'graphql-tag'
|
||||
|
||||
// ------ mutations
|
||||
|
||||
export const createPostMutation = gql`
|
||||
mutation ($id: ID, $title: String!, $slug: String, $content: String!, $categoryIds: [ID]!) {
|
||||
CreatePost(id: $id, title: $title, slug: $slug, content: $content, categoryIds: $categoryIds) {
|
||||
id
|
||||
slug
|
||||
export const createPostMutation = () => {
|
||||
return gql`
|
||||
mutation (
|
||||
$id: ID
|
||||
$title: String!
|
||||
$slug: String
|
||||
$content: String!
|
||||
$categoryIds: [ID]
|
||||
$groupId: ID
|
||||
) {
|
||||
CreatePost(
|
||||
id: $id
|
||||
title: $title
|
||||
slug: $slug
|
||||
content: $content
|
||||
categoryIds: $categoryIds
|
||||
groupId: $groupId
|
||||
) {
|
||||
id
|
||||
slug
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
`
|
||||
}
|
||||
|
||||
// ------ queries
|
||||
|
||||
// fill queries in here
|
||||
export const postQuery = () => {
|
||||
return gql`
|
||||
query Post($id: ID!) {
|
||||
Post(id: $id) {
|
||||
id
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const filterPosts = () => {
|
||||
return gql`
|
||||
query Post($filter: _PostFilter, $first: Int, $offset: Int, $orderBy: [_PostOrdering]) {
|
||||
Post(filter: $filter, first: $first, offset: $offset, orderBy: $orderBy) {
|
||||
id
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const profilePagePosts = () => {
|
||||
return gql`
|
||||
query profilePagePosts(
|
||||
$filter: _PostFilter
|
||||
$first: Int
|
||||
$offset: Int
|
||||
$orderBy: [_PostOrdering]
|
||||
) {
|
||||
profilePagePosts(filter: $filter, first: $first, offset: $offset, orderBy: $orderBy) {
|
||||
id
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const searchPosts = () => {
|
||||
return gql`
|
||||
query ($query: String!, $firstPosts: Int, $postsOffset: Int) {
|
||||
searchPosts(query: $query, firstPosts: $firstPosts, postsOffset: $postsOffset) {
|
||||
postCount
|
||||
posts {
|
||||
id
|
||||
title
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
@ -709,7 +709,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
|
||||
await Promise.all([
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p2',
|
||||
title: `Nature Philosophy Yoga`,
|
||||
@ -718,7 +718,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
}),
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p7',
|
||||
title: 'This is post #7',
|
||||
@ -727,7 +727,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
}),
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p8',
|
||||
image: faker.image.unsplash.nature(),
|
||||
@ -737,7 +737,7 @@ const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl']
|
||||
},
|
||||
}),
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
id: 'p12',
|
||||
title: 'This is post #12',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { rule, shield, deny, allow, or } from 'graphql-shield'
|
||||
import { rule, shield, deny, allow, or, and } from 'graphql-shield'
|
||||
import { getNeode } from '../db/neo4j'
|
||||
import CONFIG from '../config'
|
||||
import { validateInviteCode } from '../schema/resolvers/transactions/inviteCodes'
|
||||
@ -221,6 +221,34 @@ const isAllowedToLeaveGroup = rule({
|
||||
}
|
||||
})
|
||||
|
||||
const isMemberOfGroup = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (_parent, args, { user, driver }) => {
|
||||
if (!(user && user.id)) return false
|
||||
const { groupId } = args
|
||||
if (!groupId) return true
|
||||
const userId = user.id
|
||||
const session = driver.session()
|
||||
const readTxPromise = session.readTransaction(async (transaction) => {
|
||||
const transactionResponse = await transaction.run(
|
||||
`
|
||||
MATCH (User {id: $userId})-[membership:MEMBER_OF]->(Group {id: $groupId})
|
||||
RETURN membership.role AS role
|
||||
`,
|
||||
{ groupId, userId },
|
||||
)
|
||||
return transactionResponse.records.map((record) => record.get('role'))[0]
|
||||
})
|
||||
try {
|
||||
const role = await readTxPromise
|
||||
return ['usual', 'admin', 'owner'].includes(role)
|
||||
} catch (error) {
|
||||
throw new Error(error)
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
})
|
||||
|
||||
const isAuthor = rule({
|
||||
cache: 'no_cache',
|
||||
})(async (_parent, args, { user, driver }) => {
|
||||
@ -271,8 +299,6 @@ export default shield(
|
||||
{
|
||||
Query: {
|
||||
'*': deny,
|
||||
findPosts: allow,
|
||||
findUsers: allow,
|
||||
searchResults: allow,
|
||||
searchPosts: allow,
|
||||
searchUsers: allow,
|
||||
@ -316,7 +342,7 @@ export default shield(
|
||||
JoinGroup: isAllowedToJoinGroup,
|
||||
LeaveGroup: isAllowedToLeaveGroup,
|
||||
ChangeGroupMemberRole: isAllowedToChangeGroupMemberRole,
|
||||
CreatePost: isAuthenticated,
|
||||
CreatePost: and(isAuthenticated, isMemberOfGroup),
|
||||
UpdatePost: isAuthor,
|
||||
DeletePost: isAuthor,
|
||||
fileReport: isAuthenticated,
|
||||
|
||||
@ -366,7 +366,7 @@ describe('slugifyMiddleware', () => {
|
||||
it('generates a slug based on title', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables,
|
||||
}),
|
||||
).resolves.toMatchObject({
|
||||
@ -382,7 +382,7 @@ describe('slugifyMiddleware', () => {
|
||||
it('generates a slug based on given slug', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
...variables,
|
||||
slug: 'the-post',
|
||||
@ -417,7 +417,7 @@ describe('slugifyMiddleware', () => {
|
||||
it('chooses another slug', async () => {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
...variables,
|
||||
title: 'Pre-existing post',
|
||||
@ -440,7 +440,7 @@ describe('slugifyMiddleware', () => {
|
||||
try {
|
||||
await expect(
|
||||
mutate({
|
||||
mutation: createPostMutation,
|
||||
mutation: createPostMutation(),
|
||||
variables: {
|
||||
...variables,
|
||||
title: 'Pre-existing post',
|
||||
|
||||
@ -31,7 +31,7 @@ const setPostCounter = async (postId, relation, context) => {
|
||||
}
|
||||
|
||||
const userClickedPost = async (resolve, root, args, context, info) => {
|
||||
if (args.id) {
|
||||
if (args.id && context.user) {
|
||||
await setPostCounter(args.id, 'CLICKED', context)
|
||||
}
|
||||
return resolve(root, args, context, info)
|
||||
|
||||
@ -261,8 +261,15 @@ export default {
|
||||
const leaveGroupCypher = `
|
||||
MATCH (member:User {id: $userId})-[membership:MEMBER_OF]->(group:Group {id: $groupId})
|
||||
DELETE membership
|
||||
WITH member, group
|
||||
OPTIONAL MATCH (p:Post)-[:IN]->(group)
|
||||
WHERE NOT group.groupType = 'public'
|
||||
WITH member, group, collect(p) AS posts
|
||||
FOREACH (post IN posts |
|
||||
MERGE (member)-[:CANNOT_SEE]->(post))
|
||||
RETURN member {.*, myRoleInGroup: NULL}
|
||||
`
|
||||
|
||||
const transactionResponse = await transaction.run(leaveGroupCypher, { groupId, userId })
|
||||
const [member] = await transactionResponse.records.map((record) => record.get('member'))
|
||||
return member
|
||||
@ -279,8 +286,22 @@ export default {
|
||||
const { groupId, userId, roleInGroup } = params
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
let postRestrictionCypher = ''
|
||||
if (['usual', 'admin', 'owner'].includes(roleInGroup)) {
|
||||
postRestrictionCypher = `
|
||||
WITH group, member, membership
|
||||
FOREACH (restriction IN [(member)-[r:CANNOT_SEE]->(:Post)-[:IN]->(group) | r] |
|
||||
DELETE restriction)`
|
||||
} else {
|
||||
postRestrictionCypher = `
|
||||
WITH group, member, membership
|
||||
FOREACH (post IN [(p:Post)-[:IN]->(group) | p] |
|
||||
MERGE (member)-[:CANNOT_SEE]->(post))`
|
||||
}
|
||||
|
||||
const joinGroupCypher = `
|
||||
MATCH (member:User {id: $userId}), (group:Group {id: $groupId})
|
||||
MATCH (member:User {id: $userId})
|
||||
MATCH (group:Group {id: $groupId})
|
||||
MERGE (member)-[membership:MEMBER_OF]->(group)
|
||||
ON CREATE SET
|
||||
membership.createdAt = toString(datetime()),
|
||||
@ -289,8 +310,10 @@ export default {
|
||||
ON MATCH SET
|
||||
membership.updatedAt = toString(datetime()),
|
||||
membership.role = $roleInGroup
|
||||
${postRestrictionCypher}
|
||||
RETURN member {.*, myRoleInGroup: membership.role}
|
||||
`
|
||||
|
||||
const transactionResponse = await transaction.run(joinGroupCypher, {
|
||||
groupId,
|
||||
userId,
|
||||
@ -313,6 +336,7 @@ export default {
|
||||
undefinedToNull: ['deleted', 'disabled', 'locationName', 'about'],
|
||||
hasMany: {
|
||||
categories: '-[:CATEGORIZED]->(related:Category)',
|
||||
posts: '<-[:IN]-(related:Post)',
|
||||
},
|
||||
hasOne: {
|
||||
avatar: '-[:AVATAR_IMAGE]->(related:Image)',
|
||||
|
||||
47
backend/src/schema/resolvers/helpers/filterInvisiblePosts.js
Normal file
47
backend/src/schema/resolvers/helpers/filterInvisiblePosts.js
Normal file
@ -0,0 +1,47 @@
|
||||
import { mergeWith, isArray } from 'lodash'
|
||||
|
||||
const getInvisiblePosts = async (context) => {
|
||||
const session = context.driver.session()
|
||||
const readTxResultPromise = await session.readTransaction(async (transaction) => {
|
||||
let cypher = ''
|
||||
const { user } = context
|
||||
if (user && user.id) {
|
||||
cypher = `
|
||||
MATCH (post:Post)<-[:CANNOT_SEE]-(user:User { id: $userId })
|
||||
RETURN collect(post.id) AS invisiblePostIds`
|
||||
} else {
|
||||
cypher = `
|
||||
MATCH (post:Post)-[:IN]->(group:Group)
|
||||
WHERE NOT group.groupType = 'public'
|
||||
RETURN collect(post.id) AS invisiblePostIds`
|
||||
}
|
||||
const invisiblePostIdsResponse = await transaction.run(cypher, {
|
||||
userId: user ? user.id : null,
|
||||
})
|
||||
return invisiblePostIdsResponse.records.map((record) => record.get('invisiblePostIds'))
|
||||
})
|
||||
try {
|
||||
const [invisiblePostIds] = readTxResultPromise
|
||||
return invisiblePostIds
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
}
|
||||
|
||||
export const filterInvisiblePosts = async (params, context) => {
|
||||
const invisiblePostIds = await getInvisiblePosts(context)
|
||||
if (!invisiblePostIds.length) return params
|
||||
|
||||
params.filter = mergeWith(
|
||||
params.filter,
|
||||
{
|
||||
id_not_in: invisiblePostIds,
|
||||
},
|
||||
(objValue, srcValue) => {
|
||||
if (isArray(objValue)) {
|
||||
return objValue.concat(srcValue)
|
||||
}
|
||||
},
|
||||
)
|
||||
return params
|
||||
}
|
||||
@ -5,6 +5,7 @@ import { UserInputError } from 'apollo-server'
|
||||
import { mergeImage, deleteImage } from './images/images'
|
||||
import Resolver from './helpers/Resolver'
|
||||
import { filterForMutedUsers } from './helpers/filterForMutedUsers'
|
||||
import { filterInvisiblePosts } from './helpers/filterInvisiblePosts'
|
||||
import CONFIG from '../../config'
|
||||
|
||||
const maintainPinnedPosts = (params) => {
|
||||
@ -20,15 +21,13 @@ const maintainPinnedPosts = (params) => {
|
||||
export default {
|
||||
Query: {
|
||||
Post: async (object, params, context, resolveInfo) => {
|
||||
params = await filterInvisiblePosts(params, context)
|
||||
params = await filterForMutedUsers(params, context)
|
||||
params = await maintainPinnedPosts(params)
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
findPosts: async (object, params, context, resolveInfo) => {
|
||||
params = await filterForMutedUsers(params, context)
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
profilePagePosts: async (object, params, context, resolveInfo) => {
|
||||
params = await filterInvisiblePosts(params, context)
|
||||
params = await filterForMutedUsers(params, context)
|
||||
return neo4jgraphql(object, params, context, resolveInfo)
|
||||
},
|
||||
@ -77,13 +76,37 @@ export default {
|
||||
},
|
||||
Mutation: {
|
||||
CreatePost: async (_parent, params, context, _resolveInfo) => {
|
||||
const { categoryIds } = params
|
||||
const { categoryIds, groupId } = params
|
||||
const { image: imageInput } = params
|
||||
delete params.categoryIds
|
||||
delete params.image
|
||||
delete params.groupId
|
||||
params.id = params.id || uuid()
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
let groupCypher = ''
|
||||
if (groupId) {
|
||||
groupCypher = `
|
||||
WITH post MATCH (group:Group { id: $groupId })
|
||||
MERGE (post)-[:IN]->(group)`
|
||||
const groupTypeResponse = await transaction.run(
|
||||
`
|
||||
MATCH (group:Group { id: $groupId }) RETURN group.groupType AS groupType`,
|
||||
{ groupId },
|
||||
)
|
||||
const [groupType] = groupTypeResponse.records.map((record) => record.get('groupType'))
|
||||
if (groupType !== 'public')
|
||||
groupCypher += `
|
||||
WITH post, group
|
||||
MATCH (user:User)-[membership:MEMBER_OF]->(group)
|
||||
WHERE group.groupType IN ['closed', 'hidden']
|
||||
AND membership.role IN ['usual', 'admin', 'owner']
|
||||
WITH post, collect(user.id) AS userIds
|
||||
OPTIONAL MATCH path =(restricted:User) WHERE NOT restricted.id IN userIds
|
||||
FOREACH (user IN nodes(path) |
|
||||
MERGE (user)-[:CANNOT_SEE]->(post)
|
||||
)`
|
||||
}
|
||||
const categoriesCypher =
|
||||
CONFIG.CATEGORIES_ACTIVE && categoryIds
|
||||
? `WITH post
|
||||
@ -103,9 +126,10 @@ export default {
|
||||
MATCH (author:User {id: $userId})
|
||||
MERGE (post)<-[:WROTE]-(author)
|
||||
${categoriesCypher}
|
||||
${groupCypher}
|
||||
RETURN post {.*}
|
||||
`,
|
||||
{ userId: context.user.id, params, categoryIds },
|
||||
{ userId: context.user.id, categoryIds, groupId, params },
|
||||
)
|
||||
const [post] = createPostTransactionResponse.records.map((record) => record.get('post'))
|
||||
if (imageInput) {
|
||||
@ -367,6 +391,7 @@ export default {
|
||||
author: '<-[:WROTE]-(related:User)',
|
||||
pinnedBy: '<-[:PINNED]-(related:User)',
|
||||
image: '-[:HERO_IMAGE]->(related:Image)',
|
||||
group: '-[:IN]->(related:Group)',
|
||||
},
|
||||
count: {
|
||||
commentsCount:
|
||||
|
||||
1516
backend/src/schema/resolvers/postsInGroups.spec.js
Normal file
1516
backend/src/schema/resolvers/postsInGroups.spec.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -72,19 +72,19 @@ const signupCypher = (inviteCode) => {
|
||||
(inviteCode:InviteCode {code: $inviteCode})<-[:GENERATED]-(host:User)
|
||||
`
|
||||
optionalMerge = `
|
||||
MERGE(user)-[:REDEEMED { createdAt: toString(datetime()) }]->(inviteCode)
|
||||
MERGE(host)-[:INVITED { createdAt: toString(datetime()) }]->(user)
|
||||
MERGE(user)-[:FOLLOWS { createdAt: toString(datetime()) }]->(host)
|
||||
MERGE(host)-[:FOLLOWS { createdAt: toString(datetime()) }]->(user)
|
||||
MERGE (user)-[:REDEEMED { createdAt: toString(datetime()) }]->(inviteCode)
|
||||
MERGE (host)-[:INVITED { createdAt: toString(datetime()) }]->(user)
|
||||
MERGE (user)-[:FOLLOWS { createdAt: toString(datetime()) }]->(host)
|
||||
MERGE (host)-[:FOLLOWS { createdAt: toString(datetime()) }]->(user)
|
||||
`
|
||||
}
|
||||
const cypher = `
|
||||
MATCH(email:EmailAddress {nonce: $nonce, email: $email})
|
||||
MATCH (email:EmailAddress {nonce: $nonce, email: $email})
|
||||
WHERE NOT (email)-[:BELONGS_TO]->()
|
||||
${optionalMatch}
|
||||
CREATE (user:User)
|
||||
MERGE(user)-[:PRIMARY_EMAIL]->(email)
|
||||
MERGE(user)<-[:BELONGS_TO]-(email)
|
||||
MERGE (user)-[:PRIMARY_EMAIL]->(email)
|
||||
MERGE (user)<-[:BELONGS_TO]-(email)
|
||||
${optionalMerge}
|
||||
SET user += $args
|
||||
SET user.id = randomUUID()
|
||||
@ -95,6 +95,13 @@ const signupCypher = (inviteCode) => {
|
||||
SET user.showShoutsPublicly = false
|
||||
SET user.sendNotificationEmails = true
|
||||
SET email.verifiedAt = toString(datetime())
|
||||
WITH user
|
||||
OPTIONAL MATCH (post:Post)-[:IN]->(group:Group)
|
||||
WHERE NOT group.groupType = 'public'
|
||||
WITH user, collect(post) AS invisiblePosts
|
||||
FOREACH (invisiblePost IN invisiblePosts |
|
||||
MERGE (user)-[:CANNOT_SEE]->(invisiblePost)
|
||||
)
|
||||
RETURN user {.*}
|
||||
`
|
||||
return cypher
|
||||
|
||||
@ -23,12 +23,15 @@ const postWhereClause = `WHERE score >= 0.0
|
||||
AND NOT (
|
||||
author.deleted = true OR author.disabled = true
|
||||
OR resource.deleted = true OR resource.disabled = true
|
||||
OR (:User {id: $userId})-[:MUTED]->(author)
|
||||
)`
|
||||
) AND block IS NULL AND restriction IS NULL`
|
||||
|
||||
const searchPostsSetup = {
|
||||
fulltextIndex: 'post_fulltext_search',
|
||||
match: 'MATCH (resource:Post)<-[:WROTE]-(author:User)',
|
||||
match: `MATCH (resource:Post)<-[:WROTE]-(author:User)
|
||||
MATCH (user:User {id: $userId})
|
||||
OPTIONAL MATCH (user)-[block:MUTED]->(author)
|
||||
OPTIONAL MATCH (user)-[restriction:CANNOT_SEE]->(resource)
|
||||
WITH user, resource, author, block, restriction`,
|
||||
whereClause: postWhereClause,
|
||||
withClause: `WITH resource, author,
|
||||
[(resource)<-[:COMMENTS]-(comment:Comment) | comment] AS comments,
|
||||
@ -116,8 +119,8 @@ export default {
|
||||
Query: {
|
||||
searchPosts: async (_parent, args, context, _resolveInfo) => {
|
||||
const { query, postsOffset, firstPosts } = args
|
||||
const { id: userId } = context.user
|
||||
|
||||
let userId = null
|
||||
if (context.user) userId = context.user.id
|
||||
return {
|
||||
postCount: getSearchResults(
|
||||
context,
|
||||
@ -177,7 +180,8 @@ export default {
|
||||
},
|
||||
searchResults: async (_parent, args, context, _resolveInfo) => {
|
||||
const { query, limit } = args
|
||||
const { id: userId } = context.user
|
||||
let userId = null
|
||||
if (context.user) userId = context.user.id
|
||||
|
||||
const searchType = query.replace(/^([!@#]?).*$/, '$1')
|
||||
const searchString = query.replace(/^([!@#])/, '')
|
||||
|
||||
@ -39,6 +39,8 @@ type Group {
|
||||
categories: [Category] @relation(name: "CATEGORIZED", direction: "OUT")
|
||||
|
||||
myRole: GroupMemberRole # if 'null' then the current user is no member
|
||||
|
||||
posts: [Post] @relation(name: "IN", direction: "IN")
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -81,6 +81,7 @@ input _PostFilter {
|
||||
emotions_none: _PostEMOTEDFilter
|
||||
emotions_single: _PostEMOTEDFilter
|
||||
emotions_every: _PostEMOTEDFilter
|
||||
group: _GroupFilter
|
||||
}
|
||||
|
||||
enum _PostOrdering {
|
||||
@ -167,6 +168,8 @@ type Post {
|
||||
emotions: [EMOTED]
|
||||
emotionsCount: Int!
|
||||
@cypher(statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)")
|
||||
|
||||
group: Group @relation(name: "IN", direction: "OUT")
|
||||
}
|
||||
|
||||
input _PostInput {
|
||||
@ -184,6 +187,7 @@ type Mutation {
|
||||
language: String
|
||||
categoryIds: [ID]
|
||||
contentExcerpt: String
|
||||
groupId: ID
|
||||
): Post
|
||||
UpdatePost(
|
||||
id: ID!
|
||||
@ -225,18 +229,4 @@ type Query {
|
||||
PostsEmotionsCountByEmotion(postId: ID!, data: _EMOTEDInput!): Int!
|
||||
PostsEmotionsByCurrentUser(postId: ID!): [String]
|
||||
profilePagePosts(filter: _PostFilter, first: Int, offset: Int, orderBy: [_PostOrdering]): [Post]
|
||||
findPosts(query: String!, limit: Int = 10, filter: _PostFilter): [Post]!
|
||||
@cypher(
|
||||
statement: """
|
||||
CALL db.index.fulltext.queryNodes('post_fulltext_search', $query)
|
||||
YIELD node as post, score
|
||||
MATCH (post)<-[:WROTE]-(user:User)
|
||||
WHERE score >= 0.2
|
||||
AND NOT user.deleted = true AND NOT user.disabled = true
|
||||
AND NOT post.deleted = true AND NOT post.disabled = true
|
||||
AND NOT user.id in COALESCE($filter.author_not.id_in, [])
|
||||
RETURN post
|
||||
LIMIT $limit
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
@ -186,18 +186,6 @@ type Query {
|
||||
blockedUsers: [User]
|
||||
isLoggedIn: Boolean!
|
||||
currentUser: User
|
||||
findUsers(query: String!,limit: Int = 10, filter: _UserFilter): [User]!
|
||||
@cypher(
|
||||
statement: """
|
||||
CALL db.index.fulltext.queryNodes('user_fulltext_search', $query)
|
||||
YIELD node as post, score
|
||||
MATCH (user)
|
||||
WHERE score >= 0.2
|
||||
AND NOT user.deleted = true AND NOT user.disabled = true
|
||||
RETURN user
|
||||
LIMIT $limit
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
enum Deletable {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user