public async Task <Page <PhraseDto> > Search(PhrasesRequestCriteria criteria)
        {
            IQueryable <Phrase> query = _dbContext.Phrases.Include(_ => _.Tags);

            if (!string.IsNullOrWhiteSpace(criteria.Q))
            {
                query = query.Where(_ => _.Text.Contains(criteria.Q));
            }

            if (criteria.TagIds?.Any() == true)
            {
                var idDict = await _dbContext.Tags
                             .Where(_ => PhraseTags.Contains(_.Type))
                             .ToDictionaryAsync(_ => _.Id, _ => _);

                var typeDict = criteria.TagIds
                               .GroupBy(_ => idDict[_].Type)
                               .ToDictionary(_ => _.Key, _ => _.ToArray());

                foreach (var ids in typeDict.Values)
                {
                    query = query.Where(p => p.Tags.Any(t => ids.Contains(t.TagId)));
                }
            }

            query = query.OrderByDescending(_ => _.UpdatedAt);

            if (criteria.FromItem != null)
            {
                var fromPhrase = await _dbContext.FindAsync <Phrase>(criteria.FromItem);

                query = query.Where(_ => _.UpdatedAt < fromPhrase.UpdatedAt);
            }

            var page = await query.GetPage(criteria);

            return(_mapper.Map <Page <PhraseDto> >(page));
        }
 public Task <Page <PhraseDto> > Search([FromQuery] PhrasesRequestCriteria criteria) => _phrasesService.Search(criteria);