示例#1
0
 public void GetSelectorsList(WebData webData)
 {
     webData.Result.AddMessage(webData.Method, "Retrieved categories, Tags and Genres.");
     webData.Result.AddStructs(mdb.Categories.GetStructs(resultOption: ResultOption.SortAscending(nameof(MDBCategory.Name))));
     webData.Result.AddStructs(mdb.Tags.GetStructs(resultOption: ResultOption.SortAscending(nameof(MDBTag.Name))));
     webData.Result.AddStructs(mdb.Genres.GetStructs(resultOption: ResultOption.SortAscending(nameof(MDBGenre.Name))));
 }
示例#2
0
        void FilterSimple(WebData webData, ITable table, string fieldName, string filter, int page)
        {
            if (table == null)
            {
                throw new ArgumentNullException(nameof(table));
            }
            if (fieldName == null)
            {
                throw new ArgumentNullException(nameof(fieldName));
            }
            Search search;
            long   rowCount;

            if (filter == null)
            {
                search   = Search.None;
                rowCount = table.RowCount;
            }
            else
            {
                search   = Search.FieldLike(fieldName, MDBSearch.Text(filter));
                rowCount = table.Count(search);
            }
            ResultOption options = ResultOption.Limit(RowsPerPage) + ResultOption.SortAscending(fieldName);

            if (page > 0)
            {
                options += ResultOption.Offset(page * RowsPerPage);
            }
            var rows = table.GetRows(search, options);

            webData.Result.AddMessage(webData.Method, "Retrieved {0}.", table);
            AddPagination(webData, page, rowCount);
            webData.Result.AddRows(rows, table.Layout);
        }
        public void OrderByWithLimitWithIndex()
        {
            var test  = new MemoryStorage().CreateDatabase("db").CreateTable <TestStructClean>();
            var typed = new Table <long, TestStructClean>(test);

            var collisionCheck = new Set <int>();

            for (var i = 0; i < 1000; i++)
            {
                var content = string.Empty;
                while (content.Length == 0)
                {
                    content = DefaultRNG.GetPassword(DefaultRNG.UInt8 % 16, ASCII.Strings.Letters);
                }
                var integer = content.GetHashCode();
                while (collisionCheck.Contains(integer))
                {
                    integer++;
                }
                collisionCheck.Add(integer);
                typed.Replace(new TestStructClean()
                {
                    ID   = 1 + i % 100,
                    I    = integer,
                    Text = content,
                    Date = DateTime.UtcNow + new TimeSpan(integer * TimeSpan.TicksPerSecond),
                });
            }
            typed.Delete(1);

            var array = typed.GetStructs();

            CollectionAssert.AreEqual(array.OrderBy(a => a.I), typed.GetStructs(Search.None, ResultOption.SortAscending(nameof(TestStructClean.I))));
            CollectionAssert.AreEqual(array.OrderBy(a => a.Date), typed.GetStructs(Search.None, ResultOption.SortAscending(nameof(TestStructClean.Date))));
            CollectionAssert.AreEqual(array.OrderByDescending(a => a.I), typed.GetStructs(Search.None, ResultOption.SortDescending(nameof(TestStructClean.I))));
            CollectionAssert.AreEqual(array.OrderByDescending(a => a.Date), typed.GetStructs(Search.None, ResultOption.SortDescending(nameof(TestStructClean.Date))));

            CollectionAssert.AreEqual(
                array.OrderBy(a => a.I).SubRange(0, 3),
                typed.GetStructs(Search.None, ResultOption.SortAscending(nameof(TestStructClean.I)) + ResultOption.Limit(3)));

            CollectionAssert.AreEqual(
                array.OrderByDescending(a => a.I).SubRange(0, 3),
                typed.GetStructs(Search.None, ResultOption.SortDescending(nameof(TestStructClean.I)) + ResultOption.Limit(3)));

            CollectionAssert.AreEqual(
                array.OrderBy(a => a.Date).SubRange(0, 3),
                typed.GetStructs(Search.None, ResultOption.SortAscending(nameof(TestStructClean.Date)) + ResultOption.Limit(3)));

            CollectionAssert.AreEqual(
                array.OrderByDescending(a => a.Date).SubRange(0, 3),
                typed.GetStructs(Search.None, ResultOption.SortDescending(nameof(TestStructClean.Date)) + ResultOption.Limit(3)));
        }
示例#4
0
        void FanArtTV_CrawlArtists()
        {
            if (m_Exit)
            {
                return;
            }
            float max     = mdb.Artists.RowCount;
            long  counter = 0;

            //do not stress fanart.tv with multithreaded downloads...
            foreach (MDBArtist artist in mdb.Artists.GetStructs(Search.None, ResultOption.SortAscending(nameof(MDBArtist.Name))))
            {
                if (artist.MusicBrainzArtistGuid == null)
                {
                    continue;
                }

                try
                {
                    FanArtTV_DownloadArtist(artist);
                }
                catch (Exception ex)
                {
                    this.LogWarning(ex, "Error while crawling artist {0}", artist);
                    Thread.Sleep(1000);
                }

                progressFanArtTV = counter++ / max;
                if (m_Exit)
                {
                    return;
                }
            }
            ;
            progressFanArtTV = 1;
        }
        public void Default()
        {
            var test  = new MemoryStorage().CreateDatabase("db").CreateTable <SmallTestStruct>();
            var typed = new Table <long, SmallTestStruct>(test);

            for (var i = 0; i < 1000; i++)
            {
                typed.Insert(new SmallTestStruct()
                {
                    Content = "", DateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local).AddHours(i), Name = "host" + (i % 10), Level = TestEnum.A, Source = "this",
                });
            }
            Assert.AreEqual(1000, test.RowCount);
            Assert.AreEqual(1, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.Content))));
            Assert.AreEqual(1000, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.DateTime))));
            Assert.AreEqual(10, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.Name))));

            Assert.AreEqual(10, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.Content)) + ResultOption.Group(nameof(SmallTestStruct.Name))));

            var rows = typed.GetStructs(Search.None, ResultOption.Group(nameof(SmallTestStruct.Name)) + ResultOption.SortDescending(nameof(SmallTestStruct.Name)));

            Assert.AreEqual(10, rows.Count);
            for (var i = 0; i < 10; i++)
            {
                Assert.AreEqual("host" + (9 - i), rows[i].Name);
            }

            rows = typed.GetStructs(
                Search.FieldGreater(nameof(SmallTestStruct.DateTime), new DateTime(1970, 1, 1, 5, 0, 0, DateTimeKind.Local)) &
                Search.FieldSmallerOrEqual(nameof(SmallTestStruct.DateTime), new DateTime(1970, 1, 1, 10, 0, 0, DateTimeKind.Local)),
                ResultOption.SortDescending(nameof(SmallTestStruct.DateTime)));
            var rowsExpected = typed.GetStructs().
                               Where(i => i.DateTime > new DateTime(1970, 1, 1, 5, 0, 0, DateTimeKind.Local) &&
                                     i.DateTime <= new DateTime(1970, 1, 1, 10, 0, 0, DateTimeKind.Local)).
                               OrderBy(i => - i.DateTime.Ticks).ToList();

            CollectionAssert.AreEqual(rowsExpected, rows);

            rows = typed.GetStructs(
                Search.FieldGreaterOrEqual(nameof(SmallTestStruct.DateTime), new DateTime(1970, 1, 1, 5, 0, 0, DateTimeKind.Local)) &
                Search.FieldSmaller(nameof(SmallTestStruct.DateTime), new DateTime(1970, 1, 1, 10, 0, 0, DateTimeKind.Local)),
                ResultOption.SortAscending(nameof(SmallTestStruct.DateTime)));
            rowsExpected = typed.GetStructs().
                           Where(i => i.DateTime >= new DateTime(1970, 1, 1, 5, 0, 0, DateTimeKind.Local) &&
                                 i.DateTime < new DateTime(1970, 1, 1, 10, 0, 0, DateTimeKind.Local)).
                           OrderBy(i => i.DateTime).ToList();
            CollectionAssert.AreEqual(rowsExpected, rows);

            for (var i = 0; i < 1000; i++)
            {
                var e = new SmallTestStruct()
                {
                    ID = i + 1, Content = "Updated" + i.ToString(), DateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local).AddHours(i % 100), Name = "this", Level = TestEnum.B, Source = "this",
                };
                typed.Update(e);
                Assert.AreEqual(e, typed.GetStruct(i + 1));
            }
            Assert.AreEqual(100, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.DateTime))));
            Assert.AreEqual(1000, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.Content))));
            Assert.AreEqual(1000, test.RowCount);

            for (var i = 0; i < 1000; i++)
            {
                var e = new SmallTestStruct()
                {
                    ID = i + 1, Content = "Replaced", DateTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Local).AddHours(i), Name = "this", Level = TestEnum.B, Source = "this",
                };
                typed.Update(e);
                Assert.AreEqual(e, typed.GetStruct(i + 1));
            }
            Assert.AreEqual(1000, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.DateTime))));
            Assert.AreEqual(1, test.Count(Search.None, ResultOption.Group(nameof(SmallTestStruct.Content))));
            Assert.AreEqual(1000, test.RowCount);
        }
示例#6
0
        public void SearchAlbums(WebData webData, long artistID = 0, string filter = null, int page = 0, long categoryID = 0, long genreID = 0, long tagID = 0, string genre = null, string tag = null)
        {
            ICollection <long> albumIDs = null;

            //select audio files
            if (genreID != 0 || categoryID != 0 || tagID != 0 || genre != null || tag != null)
            {
                Search s = Search.None;
                if (genreID != 0)
                {
                    s &= Search.FieldEquals(nameof(MDBAudioFile.GenreID), genreID);
                }
                if (tagID != 0)
                {
                    s &= Search.FieldEquals(nameof(MDBAudioFile.TagID), tagID);
                }
                if (genre != null)
                {
                    s &= Search.FieldLike(nameof(MDBAudioFile.Genres), MDBSearch.Text("%" + genre + "%"));
                }
                if (tag != null)
                {
                    s &= Search.FieldLike(nameof(MDBAudioFile.Tags), MDBSearch.Text("%" + tag + "%"));
                }
                if (categoryID > 0)
                {
                    s &= GetCategorySearch(categoryID);
                }
                int fieldIndex = mdb.AudioFiles.Layout.GetFieldIndex(nameof(MDBAudioFile.AlbumID));
                albumIDs = mdb.AudioFiles.GetRows(s).Select(r => (long)r.GetValue(fieldIndex)).ToList();
            }

            //select artists
            IList <MDBAlbum> albums;
            long             rowCount;
            {
                Search search = Search.None;
                if (filter != null)
                {
                    search &= Search.FieldLike(nameof(MDBAlbum.Name), MDBSearch.Text("%" + filter + "%"));
                }
                if (albumIDs != null)
                {
                    search &= Search.FieldIn(nameof(MDBAlbum.ID), albumIDs);
                }
                if (artistID != 0)
                {
                    search &= Search.FieldEquals(nameof(MDBAlbum.ArtistID), artistID);
                }
                if (search.Mode == SearchMode.None)
                {
                    rowCount = mdb.Albums.RowCount;
                }
                else
                {
                    rowCount = mdb.Albums.Count(search);
                }
                albums = mdb.Albums.GetStructs(search, ResultOption.SortAscending(nameof(MDBAlbum.Name)) + ResultOption.Offset(page * RowsPerPage) + ResultOption.Limit(RowsPerPage));
            }

            //join
            var result = albums.Select(i => RPCAlbum.Load(mdb, i));

            //return
            webData.Result.AddMessage(webData.Method, "Retrieved Albums.");
            AddPagination(webData, page, rowCount);
            webData.Result.AddStructs(result);
        }
示例#7
0
        public void GetPlayerState(WebData webData, int hash = 0, long streamID = 0)
        {
            //default is JukeBob
            if (streamID == 0)
            {
                streamID = (long)MDBStreamType.JukeBob;
            }

            MDBNowPlaying nowPlaying = mdb.NowPlaying.TryGetStruct(1);
            var           myHash     = nowPlaying.GetHashCode() ^ mdb.PlayListSequenceNumber ^ -1;
            DateTime      timeout    = DateTime.UtcNow.AddSeconds(30 + DefaultRNG.UInt8 % 30);

            while (hash == myHash && DateTime.UtcNow < timeout)
            {
                Thread.Sleep(200);
                nowPlaying = mdb.NowPlaying.TryGetStruct(1);
                myHash     = nowPlaying.GetHashCode() ^ mdb.PlayListSequenceNumber ^ -1;
            }

            var playList = new List <MDBPlayListItem>();

            playList.AddRange(mdb.PlayListItems.GetStructs(
                                  Search.FieldEquals(nameof(MDBPlayListItem.StreamID), streamID) & Search.FieldGreater(nameof(MDBPlayListItem.OwnerID), 0),
                                  ResultOption.SortAscending(nameof(MDBPlayListItem.Added))));
            playList.AddRange(mdb.PlayListItems.GetStructs(
                                  Search.FieldEquals(nameof(MDBPlayListItem.StreamID), streamID) & Search.FieldEquals(nameof(MDBPlayListItem.OwnerID), 0),
                                  ResultOption.SortAscending(nameof(MDBPlayListItem.Added))));

            var audioFileIDs = new Set <long>(nowPlaying.AudioFileID, playList.Select(i => i.AudioFileID));
            var files        = mdb.AudioFiles.GetStructs(audioFileIDs);
            var albums       = mdb.Albums.GetStructs(files.Select(f => f.AlbumID));
            var artistIDs    = new Set <long>(files.Select(f => f.AlbumArtistID), files.Select(f => f.SongArtistID));
            var artists      = mdb.Artists.GetStructs(artistIDs);
            var users        = authTables.Users.GetStructs(playList.Select(i => i.OwnerID).Where(i => i > 0)).Select(u => u.ClearPrivateFields());

            var subsetIDs = playList.Where(i => i.SubsetID > 0).Select(i => i.SubsetID).ToSet();

            if (nowPlaying.SubsetID > 0)
            {
                subsetIDs.Include(nowPlaying.SubsetID);
            }
            var subsets = mdb.Subsets.GetStructs(subsetIDs);

            long ownerID;

            if (webData.Session.IsAuthenticated())
            {
                ownerID = webData.Session.GetUser().ID;
            }
            else
            {
                ownerID = -webData.Session.ID;
            }

            webData.Result.AddMessage(webData.Method, "Retrieved JukeBob NowPlaying");
            webData.Result.AddStructs(playList);
            webData.Result.AddStructs(files);
            webData.Result.AddStructs(albums);
            webData.Result.AddStructs(artists);
            webData.Result.AddStructs(subsets);
            webData.Result.AddStructs(users);
            webData.Result.AddStruct(new MDBPlayerState()
            {
                ID = ownerID, Hash = myHash, StreamType = MDBStreamType.JukeBob
            });
            nowPlaying.UpdateDateTime = DateTime.UtcNow;
            webData.Result.AddStruct(nowPlaying);
        }
示例#8
0
        public void SearchAudioFiles(WebData webData, int page = 0, long artistID   = 0, long albumID    = 0, long categoryID  = 0, long genreID    = 0, long tagID    = 0,
                                     string filter             = null, string title = null, string album = null, string artist = null, string genre = null, string tag = null)
        {
            if (title == null)
            {
                title = filter;
            }

            Search search = Search.None;

            if (artistID > 0)
            {
                if (artist != null)
                {
                    throw new WebServerException(WebError.InvalidParameters, 0, "Cannot use artist search and artistID at the same time!");
                }
                search &=
                    Search.FieldEquals(nameof(MDBAudioFile.AlbumArtistID), artistID) |
                    Search.FieldEquals(nameof(MDBAudioFile.SongArtistID), artistID);
            }
            else if (artist != null)
            {
                var ids = mdb.Artists.FindRows(Search.FieldLike(nameof(MDBArtist.Name), artist));
                search &=
                    Search.FieldIn(nameof(MDBAudioFile.AlbumArtistID), ids) |
                    Search.FieldIn(nameof(MDBAudioFile.SongArtistID), ids);
            }

            if (albumID > 0)
            {
                search &= Search.FieldEquals(nameof(MDBAudioFile.AlbumID), albumID);
                if (album != null)
                {
                    throw new WebServerException(WebError.InvalidParameters, 0, "Cannot use album search and albumID at the same time!");
                }
            }
            else if (album != null)
            {
                var ids = mdb.Albums.FindRows(Search.FieldLike(nameof(MDBArtist.Name), album));
                search &= Search.FieldIn(nameof(MDBAudioFile.AlbumID), ids);
            }

            if (categoryID > 0)
            {
                search &= GetCategorySearch(categoryID);
            }
            if (genreID > 0)
            {
                search &= Search.FieldEquals(nameof(MDBAudioFile.GenreID), genreID);
            }
            if (tagID > 0)
            {
                search &= Search.FieldEquals(nameof(MDBAudioFile.TagID), tagID);
            }
            if (genre != null)
            {
                search &= Search.FieldLike(nameof(MDBAudioFile.Genres), MDBSearch.Text("%" + genre + "%"));
            }
            if (tag != null)
            {
                search &= Search.FieldLike(nameof(MDBAudioFile.Tags), MDBSearch.Text("%" + tag + "%"));
            }
            if (title != null)
            {
                search &= Search.FieldLike(nameof(MDBAudioFile.Title), MDBSearch.Text("%" + title + "%"));
            }

            {
                long rowCount = mdb.AudioFiles.Count(search);
                var  ids      = mdb.AudioFiles.FindRows(search, ResultOption.Limit(RowsPerPage) + ResultOption.Offset(page * RowsPerPage) + ResultOption.SortAscending(nameof(MDBAudioFile.Title)));
                webData.Result.AddMessage(webData.Method, "Found {0} matching AudioFiles.", rowCount);
                AddPagination(webData, page, rowCount);
                var files = ids.Select(i => RPCAudioFile.Load(mdb, i));
                webData.Result.AddStructs(files);
            }
        }
        /// <summary>Selects the next file.</summary>
        /// <param name="streamID">The stream identifier.</param>
        /// <returns>Returns true on success, false otherwise</returns>
        public MDBFileSelection SelectNextFile(long streamID)
        {
            var       config = mdb.GetStreamSettings(streamID);
            MDBSubset subset = mdb.Subsets.TryGetStruct(config.SubsetID);

            if (subset.ID == 0)
            {
                subset.Name = "Undefined";
            }
            int seqNumber = mdb.Subsets.SequenceNumber ^ mdb.SubsetFilters.SequenceNumber ^ mdb.AudioFiles.SequenceNumber;

            if (audioFileIDs == null || seqNumber != sequenceNumber)
            {
                audioFileIDs = mdb.GetSubsetAudioFileIDs(subset.ID, config.MinimumLength, config.MaximumLength);
                if (subset.TitleCount != audioFileIDs.Count)
                {
                    subset.TitleCount = audioFileIDs.Count;
                    if (subset.ID > 0)
                    {
                        mdb.Subsets.Update(subset);
                    }
                }
                sequenceNumber = seqNumber;
                this.LogInfo("Reloaded subset {0} at player", subset);
            }

            if (subset.ID > 0 && subset.TitleCount != audioFileIDs.Count)
            {
                subset.TitleCount = audioFileIDs.Count;
                try
                {
                    mdb.Subsets.Update(subset);
                    this.LogInfo("Subset {0} title count updated!", subset);
                }
                catch { }
            }

            if (audioFileIDs.Count == 0)
            {
                this.LogDebug("No subset defined or subset result empty for stream <red>{0}<default> selecting random titles.", streamID);
                audioFileIDs = mdb.AudioFiles.IDs;
            }
            if (audioFileIDs.Count == 0)
            {
                this.LogDebug("No audio files found!");
                Selection = null;
                return(null);
            }

            var listSearch = Search.FieldEquals(nameof(MDBPlayListItem.StreamID), streamID);

            while (true)
            {
                Func <long> getCount = () => mdb.PlayListItems.Count(listSearch);

                //fill playlist
                for (int n = 0; getCount() < config.MinimumTitleCount; n++)
                {
                    //max 4 tries per slot
                    if (n > config.MinimumTitleCount * 4)
                    {
                        break;
                    }
                    int  i      = (int)((rnd.Next() * (long)rnd.Next()) % audioFileIDs.Count);
                    long nextID = audioFileIDs[i];
                    //audiofile valid ?
                    MDBAudioFile audioFile;
                    if (!mdb.AudioFiles.TryGetStruct(nextID, out audioFile))
                    {
                        continue;
                    }

                    //file does not exist
                    if (!File.Exists(mdb.Files.TryGetStruct(audioFile.FileID).GetFullPath(mdb)))
                    {
                        mdb.AudioFiles.TryDelete(audioFile.FileID);
                        mdb.Files.TryDelete(audioFile.FileID);
                        this.LogError("AudioFile <red>{0}<default> removed (inaccessible).", audioFile);
                        continue;
                    }

                    //yes, playlist contains id already ?
                    if (mdb.PlayListItems.Exist(listSearch & Search.FieldEquals(nameof(MDBPlayListItem.AudioFileID), nextID)))
                    {
                        continue;
                    }
                    //no add
                    mdb.PlayListItems.Insert(new MDBPlayListItem()
                    {
                        AudioFileID = audioFile.FileID,
                        StreamID    = streamID,
                        SubsetID    = subset.ID,
                        Added       = DateTime.UtcNow.AddTicks(DefaultRNG.Int8),
                    });
                    this.LogInfo("Added audio file {0} from subset {1} to {2} playlist.", audioFile, subset, streamID);
                }

                mdb.Save();

                //get current entry
                try
                {
                    var items = mdb.PlayListItems.GetStructs(
                        listSearch & Search.FieldGreater(nameof(MDBPlayListItem.OwnerID), 0),
                        ResultOption.SortAscending(nameof(MDBPlayListItem.Added)) + ResultOption.Limit(1));
                    if (items.Count == 0)
                    {
                        items = mdb.PlayListItems.GetStructs(
                            listSearch & Search.FieldEquals(nameof(MDBPlayListItem.OwnerID), 0),
                            ResultOption.SortAscending(nameof(MDBPlayListItem.Added)) + ResultOption.Limit(1));
                    }
                    var item = items.FirstOrDefault();
                    if (item.ID == 0)
                    {
                        continue;
                    }

                    try
                    {
                        var result = Selection = MDBFileSelection.Load(mdb, item);
                        return(result);
                    }
                    finally
                    {
                        //always remove playlistitem (even on errors)
                        mdb.PlayListItems.TryDelete(item.ID);
                    }
                }
                catch (Exception ex)
                {
                    this.LogError(ex, "Cannot start stream {0}!", streamID, ex.Message);
                    Selection = null;
                    return(null);
                }
            }
        }
示例#10
0
        void FilterList(WebData webData, ITable table, string nameField, string guidField, string text)
        {
            var ids = new Set <long>();

            if (text == null)
            {
                ids.IncludeRange(table.FindRows(Search.None, ResultOption.Limit(20)));
            }
            else
            {
                ids.AddRange(table.FindRows(Search.FieldLike(nameField, MDBSearch.Text(text + "%")) & Search.FieldNotEquals(guidField, null), ResultOption.SortAscending(nameField) + ResultOption.Group(nameField) + ResultOption.Group(guidField) + ResultOption.Limit(20)));
                if (ids.Count < 20)
                {
                    ids.IncludeRange(table.FindRows(Search.FieldLike(nameField, MDBSearch.Text("% " + text + "%")) & Search.FieldNotEquals(guidField, null), ResultOption.SortAscending(nameField) + ResultOption.Group(nameField) + ResultOption.Group(guidField) + ResultOption.Limit(20)));
                }
                if (ids.Count < 20)
                {
                    ids.IncludeRange(table.FindRows(Search.FieldLike(nameField, MDBSearch.Text(text + "%")) & Search.FieldEquals(guidField, null), ResultOption.SortAscending(nameField) + ResultOption.Group(nameField) + ResultOption.Limit(20 - ids.Count)));
                }
                if (ids.Count < 20)
                {
                    ids.IncludeRange(table.FindRows(Search.FieldLike(nameField, MDBSearch.Text("% " + text + "%")) & Search.FieldEquals(guidField, null), ResultOption.SortAscending(nameField) + ResultOption.Group(nameField) + ResultOption.Limit(20 - ids.Count)));
                }
            }
            var json = new JsonWriter();

            json.BeginArray("results");
            if (ids.Count > 0)
            {
                //get items
                var values = table.GetValues <string>(nameField, false, ids.SubRange(0, Math.Min(20, ids.Count)));
                foreach (var value in values)
                {
                    json.BeginObject();
                    json.String("id", value);
                    json.String("text", value);
                    json.EndObject();
                }
            }
            json.EndArray();
            var message = WebMessage.Create(webData.Method, $"Filter {nameField} {text}");

            webData.Answer = WebAnswer.Json(webData.Request, message, json.ToString());
        }