/// <summary>
        /// Finds the gallery objects with the specified rating. Guaranteed to not return null. Albums cannot be
        /// rated and are thus not returned. Only items the current user is authorized to view are returned.
        /// </summary>
        /// <returns><see cref="IEnumerable&lt;IGalleryObject&gt;" />.</returns>
        private IEnumerable <IGalleryObject> FindMediaObjectsMatchingRating()
        {
            var galleryObjects = new GalleryObjectCollection();

            if (SearchOptions.Filter != GalleryObjectType.Album)
            {
                galleryObjects.AddRange(GetRatedMediaObjects(SearchOptions.MaxNumberResults));
            }

            var filteredGalleryObjects = FilterGalleryObjects(galleryObjects);

            if (filteredGalleryObjects.Count != galleryObjects.Count && filteredGalleryObjects.Count < SearchOptions.MaxNumberResults && galleryObjects.Count >= SearchOptions.MaxNumberResults)
            {
                // We lost some objects in the filter and now we have less than the desired MaxNumberResults. Get more.
                // Note: Performance can be very poor for large galleries when using a filter. For example, a gallery where 20 videos
                // were added and then 200,000 images were added, a search for the most recent 20 videos causes this algorithm
                // to load all 200,000 images into memory before finding the videos. The good news is that by default the filter
                // is for media objects, which will be very fast. If filters end up being commonly used, this algorithm should be improved.
                var       max      = SearchOptions.MaxNumberResults * 2;
                var       skip     = SearchOptions.MaxNumberResults;
                const int maxTries = 5;

                for (var i = 0; i < maxTries; i++)
                {
                    // Add items up to maxTries times, each time doubling the number of items to retrieve.
                    filteredGalleryObjects.AddRange(GetRatedMediaObjects(max, skip));

                    filteredGalleryObjects = FilterGalleryObjects(filteredGalleryObjects);

                    if (filteredGalleryObjects.Count >= SearchOptions.MaxNumberResults)
                    {
                        break;
                    }

                    if (i < (maxTries - 1))
                    {
                        skip = skip + max;
                        max  = max * 2;
                    }
                }

                if (filteredGalleryObjects.Count < SearchOptions.MaxNumberResults)
                {
                    // We still don't have enough objects. Search entire set of albums and media objects.
                    filteredGalleryObjects.AddRange(GetRatedMediaObjects(int.MaxValue, skip));

                    filteredGalleryObjects = FilterGalleryObjects(filteredGalleryObjects);
                }
            }

            if (SearchOptions.MaxNumberResults > 0 && filteredGalleryObjects.Count > SearchOptions.MaxNumberResults)
            {
                return(filteredGalleryObjects.OrderByDescending(g => g.DateAdded).Take(SearchOptions.MaxNumberResults));
            }

            return(filteredGalleryObjects);
        }
        /// <summary>
        /// Finds the gallery objects matching tags. Guaranteed to not return null. Call this function only when the search type
        /// is <see cref="GalleryObjectSearchType.SearchByTag" /> or <see cref="GalleryObjectSearchType.SearchByPeople" />.
        /// Only items the user has permission to view are returned.
        /// </summary>
        /// <returns>An instance of <see cref="IGalleryObjectCollection" />.</returns>
        private IEnumerable <IGalleryObject> FindItemsMatchingTags()
        {
            var galleryObjects = new GalleryObjectCollection();

            if (SearchOptions.Filter == GalleryObjectType.All || SearchOptions.Filter == GalleryObjectType.Album)
            {
                galleryObjects.AddRange(GetAlbumsHavingTags());
            }

            if (SearchOptions.Filter != GalleryObjectType.Album)
            {
                galleryObjects.AddRange(GetMediaObjectsHavingTags());
            }

            var filteredGalleryObjects = FilterGalleryObjects(galleryObjects);

            return(SearchOptions.MaxNumberResults > 0 ? filteredGalleryObjects.ToSortedList().Take(SearchOptions.MaxNumberResults) : filteredGalleryObjects);
        }