MM-15542 Change getPostsBefore/After to include all posts in each thread (#10859)
* MM-15542 Add new tests for GetPostsBefore and GetPostsAfter * MM-15542 Change getPostsBefore/After to include all posts in each thread
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
a5cbe97d31
Коммит
869e8eae26
@@ -598,15 +598,15 @@ func (s *SqlPostStore) GetPostsSince(channelId string, time int64, allowFromCach
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlPostStore) GetPostsBefore(channelId string, postId string, numPosts int, offset int) store.StoreChannel {
|
func (s *SqlPostStore) GetPostsBefore(channelId string, postId string, limit int, offset int) store.StoreChannel {
|
||||||
return s.getPostsAround(channelId, postId, numPosts, offset, true)
|
return s.getPostsAround(channelId, postId, limit, offset, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlPostStore) GetPostsAfter(channelId string, postId string, numPosts int, offset int) store.StoreChannel {
|
func (s *SqlPostStore) GetPostsAfter(channelId string, postId string, limit int, offset int) store.StoreChannel {
|
||||||
return s.getPostsAround(channelId, postId, numPosts, offset, false)
|
return s.getPostsAround(channelId, postId, limit, offset, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SqlPostStore) getPostsAround(channelId string, postId string, numPosts int, offset int, before bool) store.StoreChannel {
|
func (s *SqlPostStore) getPostsAround(channelId string, postId string, limit int, offset int, before bool) store.StoreChannel {
|
||||||
return store.Do(func(result *store.StoreResult) {
|
return store.Do(func(result *store.StoreResult) {
|
||||||
var direction string
|
var direction string
|
||||||
var sort string
|
var sort string
|
||||||
@@ -621,47 +621,52 @@ func (s *SqlPostStore) getPostsAround(channelId string, postId string, numPosts
|
|||||||
var posts []*model.Post
|
var posts []*model.Post
|
||||||
var parents []*model.Post
|
var parents []*model.Post
|
||||||
_, err1 := s.GetReplica().Select(&posts,
|
_, err1 := s.GetReplica().Select(&posts,
|
||||||
`(SELECT
|
`SELECT
|
||||||
*
|
*
|
||||||
FROM
|
FROM
|
||||||
Posts
|
Posts
|
||||||
WHERE
|
WHERE
|
||||||
(CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = :PostId)
|
CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = :PostId)
|
||||||
AND ChannelId = :ChannelId
|
AND ChannelId = :ChannelId
|
||||||
AND DeleteAt = 0)
|
AND DeleteAt = 0
|
||||||
ORDER BY CreateAt `+sort+`
|
ORDER BY CreateAt `+sort+`
|
||||||
LIMIT :NumPosts
|
LIMIT :Limit
|
||||||
OFFSET :Offset)`,
|
OFFSET :Offset`,
|
||||||
map[string]interface{}{"ChannelId": channelId, "PostId": postId, "NumPosts": numPosts, "Offset": offset})
|
map[string]interface{}{"ChannelId": channelId, "PostId": postId, "Limit": limit, "Offset": offset})
|
||||||
_, err2 := s.GetReplica().Select(&parents,
|
_, err2 := s.GetReplica().Select(&parents,
|
||||||
`(SELECT
|
`SELECT
|
||||||
*
|
q2.*
|
||||||
FROM
|
FROM
|
||||||
Posts
|
Posts q2
|
||||||
WHERE
|
INNER JOIN
|
||||||
Id
|
(SELECT DISTINCT
|
||||||
IN
|
q3.Id, q3.RootId
|
||||||
(SELECT * FROM (SELECT
|
|
||||||
RootId
|
|
||||||
FROM
|
FROM
|
||||||
Posts
|
(SELECT
|
||||||
WHERE
|
Id, RootId
|
||||||
(CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = :PostId)
|
FROM
|
||||||
|
Posts
|
||||||
|
WHERE
|
||||||
|
CreateAt `+direction+` (SELECT CreateAt FROM Posts WHERE Id = :PostId)
|
||||||
AND ChannelId = :ChannelId
|
AND ChannelId = :ChannelId
|
||||||
AND DeleteAt = 0)
|
AND DeleteAt = 0
|
||||||
ORDER BY CreateAt `+sort+`
|
ORDER BY CreateAt `+sort+`
|
||||||
LIMIT :NumPosts
|
LIMIT :Limit OFFSET :Offset) q3 -- q3 contains the Id and RootId of every post in posts
|
||||||
OFFSET :Offset)
|
) q1 -- q1 is q3 with the duplicates removed
|
||||||
temp_tab))
|
ON q1.RootId = q2.Id -- This is the root post of a thread that appears in posts
|
||||||
|
OR q1.Id = q2.RootId -- This is a comment on a post in posts
|
||||||
|
OR (q2.RootId != '' AND q1.RootId = q2.RootId) -- This is a comment on a thread that appears in posts
|
||||||
|
WHERE
|
||||||
|
ChannelId = :ChannelId
|
||||||
|
AND DeleteAt = 0
|
||||||
ORDER BY CreateAt DESC`,
|
ORDER BY CreateAt DESC`,
|
||||||
map[string]interface{}{"ChannelId": channelId, "PostId": postId, "NumPosts": numPosts, "Offset": offset})
|
map[string]interface{}{"ChannelId": channelId, "PostId": postId, "Limit": limit, "Offset": offset})
|
||||||
|
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
result.Err = model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, "channelId="+channelId+err1.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get.app_error", nil, "channelId="+channelId+err1.Error(), http.StatusInternalServerError)
|
||||||
} else if err2 != nil {
|
} else if err2 != nil {
|
||||||
result.Err = model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get_parent.app_error", nil, "channelId="+channelId+err2.Error(), http.StatusInternalServerError)
|
result.Err = model.NewAppError("SqlPostStore.GetPostContext", "store.sql_post.get_posts_around.get_parent.app_error", nil, "channelId="+channelId+err2.Error(), http.StatusInternalServerError)
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
list := model.NewPostList()
|
list := model.NewPostList()
|
||||||
|
|
||||||
// We need to flip the order if we selected backwards
|
// We need to flip the order if we selected backwards
|
||||||
@@ -712,14 +717,14 @@ func (s *SqlPostStore) getParentsPosts(channelId string, offset int, limit int)
|
|||||||
q3.RootId
|
q3.RootId
|
||||||
FROM
|
FROM
|
||||||
(SELECT
|
(SELECT
|
||||||
RootId
|
RootId
|
||||||
FROM
|
FROM
|
||||||
Posts
|
Posts
|
||||||
WHERE
|
WHERE
|
||||||
ChannelId = :ChannelId1
|
ChannelId = :ChannelId1
|
||||||
AND DeleteAt = 0
|
AND DeleteAt = 0
|
||||||
ORDER BY CreateAt DESC
|
ORDER BY CreateAt DESC
|
||||||
LIMIT :Limit OFFSET :Offset) q3
|
LIMIT :Limit OFFSET :Offset) q3
|
||||||
WHERE q3.RootId != '') q1
|
WHERE q3.RootId != '') q1
|
||||||
ON q1.RootId = q2.Id OR q1.RootId = q2.RootId
|
ON q1.RootId = q2.Id OR q1.RootId = q2.RootId
|
||||||
WHERE
|
WHERE
|
||||||
|
|||||||
@@ -709,107 +709,187 @@ func testPostStoreGetPostsWithDetails(t *testing.T, ss store.Store) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
|
func testPostStoreGetPostsBeforeAfter(t *testing.T, ss store.Store) {
|
||||||
o0 := &model.Post{}
|
t.Run("without threads", func(t *testing.T) {
|
||||||
o0.ChannelId = model.NewId()
|
channelId := model.NewId()
|
||||||
o0.UserId = model.NewId()
|
userId := model.NewId()
|
||||||
o0.Message = "zz" + model.NewId() + "b"
|
|
||||||
_ = (<-ss.Post().Save(o0)).Data.(*model.Post)
|
|
||||||
time.Sleep(2 * time.Millisecond)
|
|
||||||
|
|
||||||
o1 := &model.Post{}
|
var posts []*model.Post
|
||||||
o1.ChannelId = model.NewId()
|
for i := 0; i < 10; i++ {
|
||||||
o1.UserId = model.NewId()
|
post := store.Must(ss.Post().Save(&model.Post{
|
||||||
o1.Message = "zz" + model.NewId() + "b"
|
ChannelId: channelId,
|
||||||
o1 = (<-ss.Post().Save(o1)).Data.(*model.Post)
|
UserId: userId,
|
||||||
time.Sleep(2 * time.Millisecond)
|
Message: "message",
|
||||||
|
})).(*model.Post)
|
||||||
|
|
||||||
o2 := &model.Post{}
|
posts = append(posts, post)
|
||||||
o2.ChannelId = o1.ChannelId
|
|
||||||
o2.UserId = model.NewId()
|
|
||||||
o2.Message = "zz" + model.NewId() + "b"
|
|
||||||
o2.ParentId = o1.Id
|
|
||||||
o2.RootId = o1.Id
|
|
||||||
o2 = (<-ss.Post().Save(o2)).Data.(*model.Post)
|
|
||||||
time.Sleep(2 * time.Millisecond)
|
|
||||||
|
|
||||||
o2a := &model.Post{}
|
time.Sleep(time.Millisecond)
|
||||||
o2a.ChannelId = o1.ChannelId
|
}
|
||||||
o2a.UserId = model.NewId()
|
|
||||||
o2a.Message = "zz" + model.NewId() + "b"
|
|
||||||
o2a.ParentId = o1.Id
|
|
||||||
o2a.RootId = o1.Id
|
|
||||||
o2a = (<-ss.Post().Save(o2a)).Data.(*model.Post)
|
|
||||||
time.Sleep(2 * time.Millisecond)
|
|
||||||
|
|
||||||
o3 := &model.Post{}
|
t.Run("should not return anything before the first post", func(t *testing.T) {
|
||||||
o3.ChannelId = o1.ChannelId
|
res := <-ss.Post().GetPostsBefore(channelId, posts[0].Id, 10, 0)
|
||||||
o3.UserId = model.NewId()
|
assert.Nil(t, res.Err)
|
||||||
o3.Message = "zz" + model.NewId() + "b"
|
|
||||||
o3.ParentId = o1.Id
|
|
||||||
o3.RootId = o1.Id
|
|
||||||
o3 = (<-ss.Post().Save(o3)).Data.(*model.Post)
|
|
||||||
time.Sleep(2 * time.Millisecond)
|
|
||||||
|
|
||||||
o4 := &model.Post{}
|
postList := res.Data.(*model.PostList)
|
||||||
o4.ChannelId = o1.ChannelId
|
assert.Equal(t, []string{}, postList.Order)
|
||||||
o4.UserId = model.NewId()
|
assert.Equal(t, map[string]*model.Post{}, postList.Posts)
|
||||||
o4.Message = "zz" + model.NewId() + "b"
|
})
|
||||||
o4 = (<-ss.Post().Save(o4)).Data.(*model.Post)
|
|
||||||
time.Sleep(2 * time.Millisecond)
|
|
||||||
|
|
||||||
o5 := &model.Post{}
|
t.Run("should return posts before a post", func(t *testing.T) {
|
||||||
o5.ChannelId = o1.ChannelId
|
res := <-ss.Post().GetPostsBefore(channelId, posts[5].Id, 10, 0)
|
||||||
o5.UserId = model.NewId()
|
assert.Nil(t, res.Err)
|
||||||
o5.Message = "zz" + model.NewId() + "b"
|
|
||||||
o5.ParentId = o4.Id
|
|
||||||
o5.RootId = o4.Id
|
|
||||||
_ = (<-ss.Post().Save(o5)).Data.(*model.Post)
|
|
||||||
|
|
||||||
r1 := (<-ss.Post().GetPostsBefore(o1.ChannelId, o1.Id, 4, 0)).Data.(*model.PostList)
|
postList := res.Data.(*model.PostList)
|
||||||
|
assert.Equal(t, []string{posts[4].Id, posts[3].Id, posts[2].Id, posts[1].Id, posts[0].Id}, postList.Order)
|
||||||
|
assert.Equal(t, map[string]*model.Post{
|
||||||
|
posts[0].Id: posts[0],
|
||||||
|
posts[1].Id: posts[1],
|
||||||
|
posts[2].Id: posts[2],
|
||||||
|
posts[3].Id: posts[3],
|
||||||
|
posts[4].Id: posts[4],
|
||||||
|
}, postList.Posts)
|
||||||
|
})
|
||||||
|
|
||||||
if len(r1.Posts) != 0 {
|
t.Run("should limit posts before", func(t *testing.T) {
|
||||||
t.Fatal("Wrong size")
|
res := <-ss.Post().GetPostsBefore(channelId, posts[5].Id, 2, 0)
|
||||||
}
|
assert.Nil(t, res.Err)
|
||||||
|
|
||||||
r2 := (<-ss.Post().GetPostsAfter(o1.ChannelId, o1.Id, 4, 0)).Data.(*model.PostList)
|
postList := res.Data.(*model.PostList)
|
||||||
|
assert.Equal(t, []string{posts[4].Id, posts[3].Id}, postList.Order)
|
||||||
|
assert.Equal(t, map[string]*model.Post{
|
||||||
|
posts[3].Id: posts[3],
|
||||||
|
posts[4].Id: posts[4],
|
||||||
|
}, postList.Posts)
|
||||||
|
})
|
||||||
|
|
||||||
if r2.Order[0] != o4.Id {
|
t.Run("should not return anything after the last post", func(t *testing.T) {
|
||||||
t.Fatal("invalid order")
|
res := <-ss.Post().GetPostsAfter(channelId, posts[len(posts)-1].Id, 10, 0)
|
||||||
}
|
assert.Nil(t, res.Err)
|
||||||
|
|
||||||
if r2.Order[1] != o3.Id {
|
postList := res.Data.(*model.PostList)
|
||||||
t.Fatal("invalid order")
|
assert.Equal(t, []string{}, postList.Order)
|
||||||
}
|
assert.Equal(t, map[string]*model.Post{}, postList.Posts)
|
||||||
|
})
|
||||||
|
|
||||||
if r2.Order[2] != o2a.Id {
|
t.Run("should return posts after a post", func(t *testing.T) {
|
||||||
t.Fatal("invalid order")
|
res := <-ss.Post().GetPostsAfter(channelId, posts[5].Id, 10, 0)
|
||||||
}
|
assert.Nil(t, res.Err)
|
||||||
|
|
||||||
if r2.Order[3] != o2.Id {
|
postList := res.Data.(*model.PostList)
|
||||||
t.Fatal("invalid order")
|
assert.Equal(t, []string{posts[9].Id, posts[8].Id, posts[7].Id, posts[6].Id}, postList.Order)
|
||||||
}
|
assert.Equal(t, map[string]*model.Post{
|
||||||
|
posts[6].Id: posts[6],
|
||||||
|
posts[7].Id: posts[7],
|
||||||
|
posts[8].Id: posts[8],
|
||||||
|
posts[9].Id: posts[9],
|
||||||
|
}, postList.Posts)
|
||||||
|
})
|
||||||
|
|
||||||
if len(r2.Posts) != 5 {
|
t.Run("should limit posts after", func(t *testing.T) {
|
||||||
t.Fatal("wrong size")
|
res := <-ss.Post().GetPostsAfter(channelId, posts[5].Id, 2, 0)
|
||||||
}
|
assert.Nil(t, res.Err)
|
||||||
|
|
||||||
r3 := (<-ss.Post().GetPostsBefore(o3.ChannelId, o3.Id, 2, 0)).Data.(*model.PostList)
|
postList := res.Data.(*model.PostList)
|
||||||
|
assert.Equal(t, []string{posts[7].Id, posts[6].Id}, postList.Order)
|
||||||
|
assert.Equal(t, map[string]*model.Post{
|
||||||
|
posts[6].Id: posts[6],
|
||||||
|
posts[7].Id: posts[7],
|
||||||
|
}, postList.Posts)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
if r3.Order[0] != o2a.Id {
|
t.Run("with threads", func(t *testing.T) {
|
||||||
t.Fatal("invalid order")
|
channelId := model.NewId()
|
||||||
}
|
userId := model.NewId()
|
||||||
|
|
||||||
if r3.Order[1] != o2.Id {
|
// This creates a series of posts that looks like:
|
||||||
t.Fatal("invalid order")
|
// post1
|
||||||
}
|
// post2
|
||||||
|
// post3 (in response to post1)
|
||||||
|
// post4 (in response to post2)
|
||||||
|
// post5
|
||||||
|
// post6 (in response to post2)
|
||||||
|
|
||||||
if len(r3.Posts) != 3 {
|
post1 := store.Must(ss.Post().Save(&model.Post{
|
||||||
t.Fatal("wrong size")
|
ChannelId: channelId,
|
||||||
}
|
UserId: userId,
|
||||||
|
Message: "message",
|
||||||
|
})).(*model.Post)
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
|
||||||
if r3.Posts[o1.Id].Message != o1.Message {
|
post2 := store.Must(ss.Post().Save(&model.Post{
|
||||||
t.Fatal("Missing parent")
|
ChannelId: channelId,
|
||||||
}
|
UserId: userId,
|
||||||
|
Message: "message",
|
||||||
|
})).(*model.Post)
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
|
||||||
|
post3 := store.Must(ss.Post().Save(&model.Post{
|
||||||
|
ChannelId: channelId,
|
||||||
|
UserId: userId,
|
||||||
|
ParentId: post1.Id,
|
||||||
|
RootId: post1.Id,
|
||||||
|
Message: "message",
|
||||||
|
})).(*model.Post)
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
|
||||||
|
post4 := store.Must(ss.Post().Save(&model.Post{
|
||||||
|
ChannelId: channelId,
|
||||||
|
UserId: userId,
|
||||||
|
RootId: post2.Id,
|
||||||
|
ParentId: post2.Id,
|
||||||
|
Message: "message",
|
||||||
|
})).(*model.Post)
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
|
||||||
|
post5 := store.Must(ss.Post().Save(&model.Post{
|
||||||
|
ChannelId: channelId,
|
||||||
|
UserId: userId,
|
||||||
|
Message: "message",
|
||||||
|
})).(*model.Post)
|
||||||
|
time.Sleep(time.Millisecond)
|
||||||
|
|
||||||
|
post6 := store.Must(ss.Post().Save(&model.Post{
|
||||||
|
ChannelId: channelId,
|
||||||
|
UserId: userId,
|
||||||
|
ParentId: post2.Id,
|
||||||
|
RootId: post2.Id,
|
||||||
|
Message: "message",
|
||||||
|
})).(*model.Post)
|
||||||
|
|
||||||
|
// Adding a post to a thread changes the UpdateAt timestamp of the parent post
|
||||||
|
post1.UpdateAt = post3.UpdateAt
|
||||||
|
post2.UpdateAt = post6.UpdateAt
|
||||||
|
|
||||||
|
t.Run("should return each post and thread before a post", func(t *testing.T) {
|
||||||
|
res := <-ss.Post().GetPostsBefore(channelId, post4.Id, 2, 0)
|
||||||
|
assert.Nil(t, res.Err)
|
||||||
|
|
||||||
|
postList := res.Data.(*model.PostList)
|
||||||
|
assert.Equal(t, []string{post3.Id, post2.Id}, postList.Order)
|
||||||
|
assert.Equal(t, map[string]*model.Post{
|
||||||
|
post1.Id: post1,
|
||||||
|
post2.Id: post2,
|
||||||
|
post3.Id: post3,
|
||||||
|
post4.Id: post4,
|
||||||
|
post6.Id: post6,
|
||||||
|
}, postList.Posts)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("should return each post and the root of each thread after a post", func(t *testing.T) {
|
||||||
|
res := <-ss.Post().GetPostsAfter(channelId, post4.Id, 2, 0)
|
||||||
|
assert.Nil(t, res.Err)
|
||||||
|
|
||||||
|
postList := res.Data.(*model.PostList)
|
||||||
|
assert.Equal(t, []string{post6.Id, post5.Id}, postList.Order)
|
||||||
|
assert.Equal(t, map[string]*model.Post{
|
||||||
|
post2.Id: post2,
|
||||||
|
post4.Id: post4,
|
||||||
|
post5.Id: post5,
|
||||||
|
post6.Id: post6,
|
||||||
|
}, postList.Posts)
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testPostStoreGetPostsSince(t *testing.T, ss store.Store) {
|
func testPostStoreGetPostsSince(t *testing.T, ss store.Store) {
|
||||||
|
|||||||
Ссылка в новой задаче
Block a user