2024-02-16 14:54:36 +00:00
|
|
|
package internal
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2024-02-16 20:28:26 +00:00
|
|
|
"fmt"
|
2024-05-15 09:18:01 +00:00
|
|
|
|
2024-02-16 20:51:09 +00:00
|
|
|
"git.dragse.it/anthrove/otter-space-sdk/internal/utils"
|
|
|
|
"git.dragse.it/anthrove/otter-space-sdk/pkg/models"
|
2024-02-16 14:54:36 +00:00
|
|
|
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
|
|
)
|
|
|
|
|
2024-02-17 15:36:04 +00:00
|
|
|
func CreateUserNodeWithSourceRelation(ctx context.Context, driver neo4j.DriverWithContext, anthroveUserID models.AnthroveUserID, sourceDomain string, userID string, username string) error {
|
2024-02-16 14:54:36 +00:00
|
|
|
query := `
|
|
|
|
MATCH (userNode:User {user_id: $anthrove_user_id})
|
|
|
|
MATCH (sourceNode:Source {domain: $source_domain})
|
|
|
|
MERGE (userNode)-[r:HAS_ACCOUNT_AT {username: $source_user_name, user_id: $source_user_id}]->(sourceNode)
|
|
|
|
`
|
|
|
|
params := map[string]any{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
"source_user_id": userID,
|
|
|
|
"source_user_name": username,
|
|
|
|
"source_domain": sourceDomain,
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := neo4j.ExecuteQuery(ctx, driver, query, params, neo4j.EagerResultTransformer)
|
|
|
|
if err != nil {
|
2024-02-17 15:36:04 +00:00
|
|
|
return err
|
2024-02-16 14:54:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
var anthroveUserRelationship []models.AnthroveUserRelationship
|
|
|
|
|
|
|
|
anthroveUserRelationship = append(anthroveUserRelationship, models.AnthroveUserRelationship{
|
|
|
|
UserID: userID,
|
|
|
|
Username: username,
|
|
|
|
ScrapeTimeInterval: "",
|
|
|
|
Source: models.AnthroveSource{
|
|
|
|
DisplayName: "",
|
|
|
|
Domain: sourceDomain,
|
|
|
|
Icon: "",
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
"source_user_id": userID,
|
|
|
|
"source_user_name": username,
|
|
|
|
"source_domain": sourceDomain,
|
|
|
|
}).Trace("graph: crated user with relationship")
|
|
|
|
|
2024-02-17 15:36:04 +00:00
|
|
|
return nil
|
2024-02-16 14:54:36 +00:00
|
|
|
}
|
2024-02-16 20:28:26 +00:00
|
|
|
|
|
|
|
func GetUserFavoritesCount(ctx context.Context, driver neo4j.DriverWithContext, anthroveUserID models.AnthroveUserID) (int64, error) {
|
|
|
|
var userFavoriteCount int64
|
|
|
|
|
|
|
|
query := `
|
2024-02-16 22:29:59 +00:00
|
|
|
MATCH (userNode:User {user_id: $anthrove_user_id})
|
2024-02-16 20:28:26 +00:00
|
|
|
MATCH (userNode)-[:FAV]->(favPost:AnthrovePost)
|
|
|
|
MATCH (sourceNode)-[:REFERENCE]->(favPost)
|
2024-02-16 22:44:33 +00:00
|
|
|
RETURN count( DISTINCT favPost) AS FavoritePostsCount
|
2024-02-16 20:28:26 +00:00
|
|
|
`
|
|
|
|
|
|
|
|
params := map[string]any{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := neo4j.ExecuteQuery(ctx, driver, query, params, neo4j.EagerResultTransformer)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result.Records) == 0 {
|
|
|
|
// no matches -> user does not exist, return count 0
|
|
|
|
return userFavoriteCount, err
|
|
|
|
}
|
|
|
|
|
|
|
|
record := result.Records[0]
|
|
|
|
|
|
|
|
userFavoriteCount, _, err = neo4j.GetRecordValue[int64](record, "FavoritePostsCount")
|
|
|
|
if err != nil {
|
|
|
|
return userFavoriteCount, err
|
|
|
|
}
|
|
|
|
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
"anthrove_user_fav_count": userFavoriteCount,
|
2024-02-16 20:38:35 +00:00
|
|
|
}).Trace("graph: got user favorite count")
|
2024-02-16 20:28:26 +00:00
|
|
|
|
|
|
|
return userFavoriteCount, nil
|
|
|
|
}
|
|
|
|
|
2024-02-16 20:33:46 +00:00
|
|
|
func GetUserSourceLink(ctx context.Context, driver neo4j.DriverWithContext, anthroveUserID models.AnthroveUserID) (map[string]models.AnthroveUserRelationship, error) {
|
2024-02-16 20:28:26 +00:00
|
|
|
|
|
|
|
userSource := make(map[string]models.AnthroveUserRelationship)
|
|
|
|
|
|
|
|
query := `
|
2024-02-16 22:29:59 +00:00
|
|
|
MATCH (user:User{user_id: $anthrove_user_id})-[r:HAS_ACCOUNT_AT]->(s:Source)
|
2024-05-04 21:14:52 +00:00
|
|
|
RETURN toString(r.user_id) AS sourceUserID, toString(r.username) AS sourceUsername, s as source;
|
2024-02-16 20:28:26 +00:00
|
|
|
`
|
|
|
|
params := map[string]any{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := neo4j.ExecuteQuery(ctx, driver, query, params, neo4j.EagerResultTransformer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result.Records) == 0 {
|
|
|
|
return nil, fmt.Errorf("user has no relations")
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range result.Records {
|
|
|
|
record := result.Records[i]
|
2024-05-04 22:17:18 +00:00
|
|
|
source, _, err := neo4j.GetRecordValue[neo4j.Node](record, "source")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sourceUserID, _, err := neo4j.GetRecordValue[string](record, "sourceUserID")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sourceUsername, _, err := neo4j.GetRecordValue[string](record, "sourceUsername")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
displayName := source.Props["display_name"].(string)
|
|
|
|
domain := source.Props["domain"].(string)
|
|
|
|
icon := source.Props["icon"].(string)
|
|
|
|
|
|
|
|
anthroveSourceUser := models.AnthroveUserRelationship{
|
|
|
|
UserID: sourceUserID,
|
|
|
|
Username: sourceUsername,
|
|
|
|
Source: models.AnthroveSource{
|
|
|
|
DisplayName: displayName,
|
|
|
|
Domain: domain,
|
|
|
|
Icon: icon,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
userSource[displayName] = anthroveSourceUser
|
|
|
|
}
|
|
|
|
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
"anthrove_data": userSource,
|
|
|
|
}).Trace("graph: got user favorite count")
|
|
|
|
|
|
|
|
return userSource, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetSpecifiedUserSourceLink(ctx context.Context, driver neo4j.DriverWithContext, anthroveUserID models.AnthroveUserID, sourceDisplayName string) (map[string]models.AnthroveUserRelationship, error) {
|
|
|
|
|
|
|
|
userSource := make(map[string]models.AnthroveUserRelationship)
|
|
|
|
|
|
|
|
query := `
|
|
|
|
MATCH (user:User{user_id: $anthrove_user_id})-[r:HAS_ACCOUNT_AT]->(s:Source{display_name: $source_display_name})
|
|
|
|
RETURN toString(r.user_id) AS sourceUserID, toString(r.username) AS sourceUsername, s as source;
|
|
|
|
`
|
|
|
|
params := map[string]any{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
"source_display_name": sourceDisplayName,
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := neo4j.ExecuteQuery(ctx, driver, query, params, neo4j.EagerResultTransformer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result.Records) == 0 {
|
|
|
|
return nil, fmt.Errorf("user has no relations with the source %s", sourceDisplayName)
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range result.Records {
|
|
|
|
record := result.Records[i]
|
2024-05-04 21:14:52 +00:00
|
|
|
source, _, err := neo4j.GetRecordValue[neo4j.Node](record, "source")
|
2024-02-16 20:28:26 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sourceUserID, _, err := neo4j.GetRecordValue[string](record, "sourceUserID")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sourceUsername, _, err := neo4j.GetRecordValue[string](record, "sourceUsername")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-05-04 21:14:52 +00:00
|
|
|
displayName := source.Props["display_name"].(string)
|
|
|
|
domain := source.Props["domain"].(string)
|
|
|
|
icon := source.Props["icon"].(string)
|
|
|
|
|
2024-02-16 20:28:26 +00:00
|
|
|
anthroveSourceUser := models.AnthroveUserRelationship{
|
|
|
|
UserID: sourceUserID,
|
|
|
|
Username: sourceUsername,
|
2024-05-04 21:14:52 +00:00
|
|
|
Source: models.AnthroveSource{
|
|
|
|
DisplayName: displayName,
|
|
|
|
Domain: domain,
|
|
|
|
Icon: icon,
|
|
|
|
},
|
2024-02-16 20:28:26 +00:00
|
|
|
}
|
2024-05-04 21:14:52 +00:00
|
|
|
userSource[displayName] = anthroveSourceUser
|
2024-02-16 20:28:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
"anthrove_data": userSource,
|
2024-02-16 20:38:35 +00:00
|
|
|
}).Trace("graph: got user favorite count")
|
2024-02-16 20:28:26 +00:00
|
|
|
|
|
|
|
return userSource, nil
|
|
|
|
}
|
2024-02-16 20:36:50 +00:00
|
|
|
|
|
|
|
func GetAnthroveUser(ctx context.Context, driver neo4j.DriverWithContext, anthroveUserID models.AnthroveUserID) (*models.AnthroveUser, error) {
|
|
|
|
var err error
|
|
|
|
var anthroveUser models.AnthroveUser
|
|
|
|
var userSources models.AnthroveSource
|
|
|
|
userRelationships := make([]models.AnthroveUserRelationship, 0)
|
|
|
|
|
|
|
|
query := `
|
|
|
|
MATCH (user:User{user_id: $anthrove_user_id})-[relation:HAS_ACCOUNT_AT]->(source:Source)
|
|
|
|
RETURN user as User, relation as Relation, source as Source;
|
|
|
|
`
|
|
|
|
params := map[string]any{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := neo4j.ExecuteQuery(ctx, driver, query, params, neo4j.EagerResultTransformer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result.Records) == 0 {
|
|
|
|
return nil, fmt.Errorf("user has no relations")
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range result.Records {
|
|
|
|
record := result.Records[i]
|
|
|
|
|
|
|
|
user, _, err := neo4j.GetRecordValue[neo4j.Node](record, "User")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
relation, _, err := neo4j.GetRecordValue[neo4j.Relationship](record, "Relation")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
source, _, err := neo4j.GetRecordValue[neo4j.Node](record, "Source")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
userRelationships = append(userRelationships, models.AnthroveUserRelationship{
|
|
|
|
UserID: fmt.Sprintf("%v", utils.GetOrDefault(relation.Props, "user_id", "")),
|
|
|
|
Username: utils.GetOrDefault(relation.Props, "username", "").(string),
|
|
|
|
ScrapeTimeInterval: utils.GetOrDefault(relation.Props, "scrape_time_interval", "").(string),
|
|
|
|
})
|
|
|
|
|
|
|
|
userSources = models.AnthroveSource{
|
|
|
|
DisplayName: utils.GetOrDefault(source.Props, "display_name", "").(string),
|
|
|
|
Domain: utils.GetOrDefault(source.Props, "domain", "").(string),
|
|
|
|
Icon: utils.GetOrDefault(source.Props, "icon", "").(string),
|
|
|
|
}
|
|
|
|
|
|
|
|
anthroveUser.UserID = models.AnthroveUserID(utils.GetOrDefault(user.Props, "user_id", "").(string))
|
|
|
|
anthroveUser.Relationship = userRelationships
|
|
|
|
|
|
|
|
for j := range userRelationships {
|
|
|
|
anthroveUser.Relationship[j].Source = userSources
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-02-16 20:38:35 +00:00
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
}).Trace("graph: got anthrove user")
|
|
|
|
|
2024-02-16 20:36:50 +00:00
|
|
|
return &anthroveUser, nil
|
|
|
|
|
|
|
|
}
|
2024-02-16 20:40:06 +00:00
|
|
|
|
|
|
|
func GetAllAnthroveUserIDs(ctx context.Context, driver neo4j.DriverWithContext) ([]models.AnthroveUserID, error) {
|
|
|
|
var err error
|
|
|
|
var anthroveUsers []models.AnthroveUserID
|
|
|
|
|
|
|
|
query := `
|
|
|
|
MATCH (anthroveUser:User)
|
|
|
|
RETURN anthroveUser
|
|
|
|
`
|
|
|
|
result, err := neo4j.ExecuteQuery(ctx, driver, query, nil, neo4j.EagerResultTransformer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result.Records) == 0 {
|
|
|
|
log.Warnf("No users found, this should not be happening!")
|
|
|
|
return []models.AnthroveUserID{}, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range result.Records {
|
|
|
|
record := result.Records[i]
|
|
|
|
|
|
|
|
user, _, err := neo4j.GetRecordValue[neo4j.Node](record, "anthroveUser")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
anthroveUsers = append(anthroveUsers, models.AnthroveUserID(fmt.Sprintf(user.Props["user_id"].(string))))
|
|
|
|
}
|
|
|
|
|
|
|
|
log.WithFields(log.Fields{
|
|
|
|
"anthrove_user_id_count": len(anthroveUsers),
|
|
|
|
}).Trace("graph: got al anthrove user IDs")
|
|
|
|
|
|
|
|
return anthroveUsers, nil
|
|
|
|
|
|
|
|
}
|
2024-05-10 08:57:51 +00:00
|
|
|
|
2024-05-15 09:18:01 +00:00
|
|
|
func GetUserFavoriteNodeWithPagination(ctx context.Context, driver neo4j.DriverWithContext, anthroveUserID models.AnthroveUserID, skip int, limit int) (*models.FavoriteList, error) {
|
2024-05-10 08:57:51 +00:00
|
|
|
var err error
|
2024-05-15 09:18:01 +00:00
|
|
|
var favoritePosts []models.FavoritePost
|
2024-05-10 08:57:51 +00:00
|
|
|
|
|
|
|
query := `
|
2024-05-15 09:18:01 +00:00
|
|
|
CALL {
|
|
|
|
MATCH (user:User{user_id: $anthrove_user_id})-[r:FAV]->(p:AnthrovePost)
|
|
|
|
RETURN p.post_id AS post_id
|
|
|
|
ORDER BY id(p) ASC
|
2024-05-10 08:57:51 +00:00
|
|
|
SKIP $skip
|
2024-05-15 09:18:01 +00:00
|
|
|
LIMIT $limit
|
|
|
|
}
|
|
|
|
WITH collect(post_id) AS faves
|
|
|
|
MATCH (a:AnthrovePost)<-[r:REFERENCE]-(s:Source)
|
|
|
|
WHERE a.post_id in faves
|
|
|
|
RETURN a AS anthrovePost, r AS postRelation, s AS Source
|
2024-05-10 08:57:51 +00:00
|
|
|
`
|
|
|
|
params := map[string]any{
|
|
|
|
"anthrove_user_id": anthroveUserID,
|
|
|
|
"limit": limit,
|
|
|
|
"skip": skip,
|
|
|
|
}
|
|
|
|
|
|
|
|
result, err := neo4j.ExecuteQuery(ctx, driver, query, params, neo4j.EagerResultTransformer)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(result.Records) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := range result.Records {
|
|
|
|
record := result.Records[i]
|
|
|
|
|
2024-05-15 09:18:01 +00:00
|
|
|
anthrovePost, _, err := neo4j.GetRecordValue[neo4j.Node](record, "anthrovePost")
|
2024-05-10 08:57:51 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2024-05-15 09:18:01 +00:00
|
|
|
postRelation, _, err := neo4j.GetRecordValue[neo4j.Relationship](record, "postRelation")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
source, _, err := neo4j.GetRecordValue[neo4j.Node](record, "Source")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(favoritePosts) != 0 && favoritePosts[len(favoritePosts)-1].AnthrovePost.PostID == models.AnthrovePostID(anthrovePost.Props["post_id"].(string)) {
|
|
|
|
favoritePosts[len(favoritePosts)-1].Relations = append(favoritePosts[len(favoritePosts)-1].Relations, models.FavoriteRelations{
|
|
|
|
SourcesID: source.Props["display_name"].(string),
|
|
|
|
Relations: models.AnthrovePostRelationship{
|
|
|
|
PostID: postRelation.Props["source_post_id"].(string),
|
|
|
|
Url: postRelation.Props["url"].(string),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
favoritePosts = append(favoritePosts, models.FavoritePost{
|
|
|
|
AnthrovePost: models.AnthrovePost{
|
|
|
|
PostID: models.AnthrovePostID(anthrovePost.Props["post_id"].(string)),
|
|
|
|
Rating: models.AnthroveRating(anthrovePost.Props["rating"].(string)),
|
|
|
|
},
|
|
|
|
Relations: []models.FavoriteRelations{{
|
|
|
|
SourcesID: source.Props["display_name"].(string),
|
|
|
|
Relations: models.AnthrovePostRelationship{
|
|
|
|
PostID: postRelation.Props["source_post_id"].(string),
|
|
|
|
Url: postRelation.Props["url"].(string),
|
|
|
|
},
|
|
|
|
}},
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
2024-05-10 08:57:51 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
log.WithFields(log.Fields{
|
2024-05-15 09:18:01 +00:00
|
|
|
"anthrove_user_fav_count": len(favoritePosts),
|
2024-05-10 08:57:51 +00:00
|
|
|
}).Trace("graph: got al anthrove user favorites")
|
|
|
|
|
2024-05-15 09:18:01 +00:00
|
|
|
return &models.FavoriteList{Posts: favoritePosts}, nil
|
2024-05-10 08:57:51 +00:00
|
|
|
|
|
|
|
}
|