bilibili-backup/app/interface/main/app-intl/service/search/search.go
2019-04-22 02:59:20 +00:00

545 lines
15 KiB
Go

package search
import (
"context"
"time"
tag "go-common/app/interface/main/app-interface/model/tag"
"go-common/app/interface/main/app-intl/conf"
accdao "go-common/app/interface/main/app-intl/dao/account"
arcdao "go-common/app/interface/main/app-intl/dao/archive"
artdao "go-common/app/interface/main/app-intl/dao/article"
bgmdao "go-common/app/interface/main/app-intl/dao/bangumi"
resdao "go-common/app/interface/main/app-intl/dao/resource"
srchdao "go-common/app/interface/main/app-intl/dao/search"
tagdao "go-common/app/interface/main/app-intl/dao/tag"
"go-common/app/interface/main/app-intl/model"
"go-common/app/interface/main/app-intl/model/bangumi"
"go-common/app/interface/main/app-intl/model/search"
article "go-common/app/interface/openplatform/article/model"
account "go-common/app/service/main/account/model"
"go-common/app/service/main/archive/api"
"go-common/library/log"
"go-common/library/sync/errgroup"
)
var (
_emptyResult = &search.Result{
NavInfo: []*search.NavInfo{},
Page: 0,
}
)
// Service is search service
type Service struct {
c *conf.Config
srchDao *srchdao.Dao
accDao *accdao.Dao
arcDao *arcdao.Dao
artDao *artdao.Dao
// artDao *artdao.Dao
resDao *resdao.Dao
tagDao *tagdao.Dao
bgmDao *bgmdao.Dao
// config
seasonNum int
movieNum int
seasonShowMore int
movieShowMore int
upUserNum int
uvLimit int
userNum int
userVideoLimit int
biliUserNum int
biliUserVideoLimit int
iPadSearchBangumi int
iPadSearchFt int
}
// New is search service initial func
func New(c *conf.Config) (s *Service) {
s = &Service{
c: c,
srchDao: srchdao.New(c),
accDao: accdao.New(c),
arcDao: arcdao.New(c),
artDao: artdao.New(c),
// artDao: artdao.New(c),
resDao: resdao.New(c),
tagDao: tagdao.New(c),
bgmDao: bgmdao.New(c),
seasonNum: c.Search.SeasonNum,
movieNum: c.Search.MovieNum,
seasonShowMore: c.Search.SeasonMore,
movieShowMore: c.Search.MovieMore,
upUserNum: c.Search.UpUserNum,
uvLimit: c.Search.UVLimit,
userNum: c.Search.UpUserNum,
userVideoLimit: c.Search.UVLimit,
biliUserNum: c.Search.BiliUserNum,
biliUserVideoLimit: c.Search.BiliUserVideoLimit,
iPadSearchBangumi: c.Search.IPadSearchBangumi,
iPadSearchFt: c.Search.IPadSearchFt,
}
return
}
// Search get all type search data.
func (s *Service) Search(c context.Context, mid, zoneid int64, mobiApp, device, platform, buvid, keyword, duration, order, filtered, lang, fromSource, recommend string, plat int8, rid, highlight, build, pn, ps int, now time.Time) (res *search.Result, err error) {
var (
aids []int64
am map[int64]*api.Arc
owners []int64
follows map[int64]bool
seasonIDs []int64
bangumis map[string]*bangumi.Card
)
var (
seasonNum int
movieNum int
)
seasonNum = s.seasonNum
movieNum = s.movieNum
all, code, err := s.srchDao.Search(c, mid, zoneid, mobiApp, device, platform, buvid, keyword, duration, order, filtered, fromSource, recommend, plat, seasonNum, movieNum, s.upUserNum, s.uvLimit, s.userNum, s.userVideoLimit, s.biliUserNum, s.biliUserVideoLimit, rid, highlight, build, pn, ps, now)
if err != nil {
log.Error("%+v", err)
return
}
if code == model.ForbidCode || code == model.NoResultCode {
res = _emptyResult
return
}
res = &search.Result{}
res.Trackid = all.Trackid
res.Page = all.Page
res.Array = all.FlowPlaceholder
res.Attribute = all.Attribute
res.NavInfo = s.convertNav(all, plat, build, lang)
if len(all.FlowResult) != 0 {
var item []*search.Item
for _, v := range all.FlowResult {
switch v.Type {
case search.TypeUser, search.TypeBiliUser:
owners = append(owners, v.User.Mid)
for _, vr := range v.User.Res {
aids = append(aids, vr.Aid)
}
case search.TypeVideo:
aids = append(aids, v.Video.ID)
case search.TypeMediaBangumi, search.TypeMediaFt:
seasonIDs = append(seasonIDs, v.Media.SeasonID)
}
}
g, ctx := errgroup.WithContext(c)
if len(owners) != 0 {
if mid > 0 {
g.Go(func() error {
follows = s.accDao.Relations3(ctx, owners, mid)
return nil
})
}
}
if len(aids) != 0 {
g.Go(func() (err error) {
if am, err = s.arcDao.Archives(ctx, aids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(seasonIDs) != 0 {
g.Go(func() (err error) {
if bangumis, err = s.bgmDao.Card(ctx, mid, seasonIDs); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
if all.SuggestKeyword != "" && pn == 1 {
i := &search.Item{Title: all.SuggestKeyword, Goto: model.GotoSuggestKeyWord}
item = append(item, i)
}
for _, v := range all.FlowResult {
i := &search.Item{TrackID: v.TrackID, LinkType: v.LinkType, Position: v.Position}
switch v.Type {
case search.TypeVideo:
i.FromVideo(v.Video, am[v.Video.ID])
case search.TypeMediaBangumi:
i.FromMedia(v.Media, "", model.GotoBangumi, bangumis)
case search.TypeMediaFt:
i.FromMedia(v.Media, "", model.GotoMovie, bangumis)
case search.TypeSpecial:
i.FromOperate(v.Operate, model.GotoSpecial)
case search.TypeBanner:
i.FromOperate(v.Operate, model.GotoBanner)
case search.TypeUser:
if follows[v.User.Mid] {
i.Attentions = 1
}
i.FromUser(v.User, am)
case search.TypeBiliUser:
if follows[v.User.Mid] {
i.Attentions = 1
}
i.FromUpUser(v.User, am)
case search.TypeSpecialS:
i.FromOperate(v.Operate, model.GotoSpecialS)
case search.TypeQuery:
i.Title = v.TypeName
i.FromQuery(v.Query)
case search.TypeConverge:
var (
avids, artids []int64
avm map[int64]*api.Arc
artm map[int64]*article.Meta
)
for _, c := range v.Operate.ContentList {
switch c.Type {
case 0:
avids = append(avids, c.ID)
case 2:
artids = append(artids, c.ID)
}
}
g, ctx := errgroup.WithContext(c)
if len(aids) != 0 {
g.Go(func() (err error) {
if avm, err = s.arcDao.Archives(ctx, avids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if len(artids) != 0 {
g.Go(func() (err error) {
if artm, err = s.artDao.Articles(ctx, artids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
continue
}
i.FromConverge(v.Operate, avm, artm)
case search.TypeTwitter:
i.FromTwitter(v.Twitter)
}
if i.Goto != "" {
item = append(item, i)
}
}
res.Item = item
if all.EggInfo != nil {
res.EasterEgg = &search.EasterEgg{ID: all.EggInfo.Source, ShowCount: all.EggInfo.ShowCount}
}
return
}
// archive
for _, v := range all.Result.Video {
aids = append(aids, v.ID)
}
if duration == "0" && order == "totalrank" && rid == 0 {
for _, v := range all.Result.Movie {
if v.Type == "movie" {
aids = append(aids, v.Aid)
}
}
}
if pn == 1 {
for _, v := range all.Result.User {
for _, vr := range v.Res {
aids = append(aids, vr.Aid)
}
}
for _, v := range all.Result.BiliUser {
for _, vr := range v.Res {
aids = append(aids, vr.Aid)
}
owners = append(owners, v.Mid)
}
}
g, ctx := errgroup.WithContext(c)
if len(owners) != 0 {
if mid > 0 {
g.Go(func() error {
follows = s.accDao.Relations3(ctx, owners, mid)
return nil
})
}
}
if len(aids) != 0 {
g.Go(func() (err error) {
if am, err = s.arcDao.Archives(ctx, aids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
return
}
// SearchByType is tag bangumi movie upuser video search
func (s *Service) SearchByType(c context.Context, mid, zoneid int64, mobiApp, device, platform, buvid, sType, keyword, filtered, order string, plat int8, build, highlight, categoryID, userType, orderSort, pn, ps int, now time.Time) (res *search.TypeSearch, err error) {
switch sType {
case "upper":
if res, err = s.upper(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, s.biliUserVideoLimit, highlight, build, userType, orderSort, pn, ps, now); err != nil {
return
}
case "article":
if res, err = s.article(c, mid, zoneid, highlight, keyword, mobiApp, device, platform, buvid, filtered, order, sType, plat, categoryID, build, pn, ps, now); err != nil {
return
}
case "season2":
if res, err = s.srchDao.Season2(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
return
}
case "movie2":
if res, err = s.srchDao.MovieByType2(c, mid, keyword, mobiApp, device, platform, buvid, highlight, build, pn, ps); err != nil {
return
}
case "tag":
if res, err = s.channel(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps, highlight); err != nil {
return
}
}
if res == nil {
res = &search.TypeSearch{Items: []*search.Item{}}
}
return
}
// Suggest3 for search suggest
func (s *Service) Suggest3(c context.Context, mid int64, platform, buvid, keyword string, build, highlight int, mobiApp string, now time.Time) (res *search.SuggestionResult3) {
var (
suggest *search.Suggest3
err error
aids []int64
am map[int64]*api.Arc
)
res = &search.SuggestionResult3{}
if suggest, err = s.srchDao.Suggest3(c, mid, platform, buvid, keyword, build, highlight, mobiApp, now); err != nil {
log.Error("%+v", err)
return
}
for _, v := range suggest.Result {
if v.TermType == search.SuggestionJump {
if v.SubType == search.SuggestionAV {
aids = append(aids, v.Ref)
}
}
}
g, ctx := errgroup.WithContext(c)
if len(aids) != 0 {
g.Go(func() (err error) {
if am, err = s.arcDao.Archives(ctx, aids); err != nil {
log.Error("%+v", err)
err = nil
}
return
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, v := range suggest.Result {
si := &search.Item{}
si.FromSuggest3(v, am)
res.List = append(res.List, si)
}
res.TrackID = suggest.TrackID
return
}
// convertNav deal with old search pageinfo to new.
func (s *Service) convertNav(all *search.Search, plat int8, build int, lang string) (nis []*search.NavInfo) {
const (
_showHide = 0
)
var (
season = "番剧"
upper = "用户"
movie = "影视"
article = "专栏"
)
if lang == model.Hant {
season = "番劇"
upper = "UP主"
movie = "影視"
article = "專欄"
}
nis = make([]*search.NavInfo, 0, 4)
// season
// media season
if all.PageInfo.MediaBangumi != nil {
var nav = &search.NavInfo{
Name: season,
Total: all.PageInfo.MediaBangumi.NumResults,
Pages: all.PageInfo.MediaBangumi.Pages,
Type: 7,
}
if all.PageInfo.MediaBangumi.NumResults > s.seasonNum {
nav.Show = s.seasonShowMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
// upper
if all.PageInfo.BiliUser != nil {
var nav = &search.NavInfo{
Name: upper,
Total: all.PageInfo.BiliUser.NumResults,
Pages: all.PageInfo.BiliUser.Pages,
Type: 2,
}
nis = append(nis, nav)
}
// media movie
if all.PageInfo.MediaFt != nil {
var nav = &search.NavInfo{
Name: movie,
Total: all.PageInfo.MediaFt.NumResults,
Pages: all.PageInfo.MediaFt.Pages,
Type: 8,
}
if all.PageInfo.MediaFt.NumResults > s.movieNum {
nav.Show = s.movieShowMore
} else {
nav.Show = _showHide
}
nis = append(nis, nav)
}
if all.PageInfo.Article != nil {
var nav = &search.NavInfo{
Name: article,
Total: all.PageInfo.Article.NumResults,
Pages: all.PageInfo.Article.Pages,
Type: 6,
}
nis = append(nis, nav)
}
return
}
// upper search for upper
func (s *Service) upper(c context.Context, mid int64, keyword, mobiApp, device, platform, buvid, filtered, order string, biliUserVL, highlight, build, userType, orderSort, pn, ps int, now time.Time) (res *search.TypeSearch, err error) {
var (
owners []int64
follows map[int64]bool
)
if res, err = s.srchDao.Upper(c, mid, keyword, mobiApp, device, platform, buvid, filtered, order, biliUserVL, highlight, build, userType, orderSort, pn, ps, now); err != nil {
return
}
if res == nil || len(res.Items) == 0 {
return
}
owners = make([]int64, 0, len(res.Items))
for _, item := range res.Items {
owners = append(owners, item.Mid)
}
if len(owners) != 0 {
g, ctx := errgroup.WithContext(c)
if mid > 0 {
g.Go(func() error {
follows = s.accDao.Relations3(ctx, owners, mid)
return nil
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, item := range res.Items {
if follows[item.Mid] {
item.Attentions = 1
}
}
}
return
}
// article search for article
func (s *Service) article(c context.Context, mid, zoneid int64, highlight int, keyword, mobiApp, device, platform, buvid, filtered, order, sType string, plat int8, categoryID, build, pn, ps int, now time.Time) (res *search.TypeSearch, err error) {
if res, err = s.srchDao.ArticleByType(c, mid, zoneid, keyword, mobiApp, device, platform, buvid, filtered, order, sType, plat, categoryID, build, highlight, pn, ps, now); err != nil {
log.Error("%+v", err)
return
}
if res != nil && len(res.Items) != 0 {
return
}
var mids []int64
for _, v := range res.Items {
mids = append(mids, v.Mid)
}
var infom map[int64]*account.Info
if infom, err = s.accDao.Infos3(c, mids); err != nil {
log.Error("%+v", err)
err = nil
return
}
for _, item := range res.Items {
if info, ok := infom[item.Mid]; ok {
item.Name = info.Name
}
}
return
}
// channel search for channel
func (s *Service) channel(c context.Context, mid int64, keyword, mobiApp, platform, buvid, device, order, sType string, build, pn, ps, highlight int) (res *search.TypeSearch, err error) {
var (
g *errgroup.Group
ctx context.Context
tags []int64
tagMyInfos []*tag.Tag
)
if res, err = s.srchDao.Channel(c, mid, keyword, mobiApp, platform, buvid, device, order, sType, build, pn, ps, highlight); err != nil {
return
}
if res == nil || len(res.Items) == 0 {
return
}
tags = make([]int64, 0, len(res.Items))
for _, item := range res.Items {
tags = append(tags, item.ID)
}
if len(tags) != 0 {
g, ctx = errgroup.WithContext(c)
if mid > 0 {
g.Go(func() error {
tagMyInfos, _ = s.tagDao.TagInfos(ctx, tags, mid)
return nil
})
}
if err = g.Wait(); err != nil {
log.Error("%+v", err)
return
}
for _, item := range res.Items {
for _, myInfo := range tagMyInfos {
if myInfo != nil && myInfo.TagID == item.ID {
item.IsAttention = myInfo.IsAtten
break
}
}
}
}
return
}