Яндекс поиск по блогам умер окончательно

Тема поиска по блогам давно потеряла актуальность. Но для истории отмечу, что с запуском "алгоритма Королёва" окончательно отвалились возможности поиска по блогам. Это во-первых, запущенные 8 лет назал EntriesAPI (http://blogs.yandex.ru/entriesapi). Во-вторых, RSS для выдачи результата поиска. Ранее RSS можно было бы получить таким вот запросом (https://yandex.ru/blogs/rss/search?text=&server=google.com&holdres=mark&lang=rus). Теперь такую информацию не извлечь даже из полу-платного Yandex.XML. Таким образом, голанлская компания Яндекс оказывается полностью бесполезной для русскоязычных блогов.


Метки:   Категории:Yandex


Очередные закрытия мониторинговых сервисов

Яндекс изменил поиск по блогам, теперь https://blogs.yandex.ru редиректит на основной поиск, там есть поиск по блогам в виде подраздела, но без возможностей RSS. Также явно показано, что ищет всего-то за месяц. Еще на днях закрылся по странным причинам поисковик topsy.com, приобретенный за 200млн.у.е. компанией Apple.

Все делается, чтобы максимально усложнить работу с социальной информацией и исключить у людей понимание о происходящем вокруг.


Метки: ,   Категории:Blogs | Yandex


Яндекс продолжает банить ссылкообменники и SEO

Новая новость от Яндекса, что теперь они будут банить сайты за продажу ссылок. До этого они начали банить сайты за покупку входящих ссылок. Такими темпами Яндекс вообще может предложить отказаться от ссылок. Зачем сейчас в интернете ссылки, когда все пользуются мобильными приложениями? В этом же направлении движется и Google, который постепенно увеличивает ранк для сайтов, у которых есть мобильная версия или мобильное приложение.

А в итоге этим поисковикам пора бы закончить дурить головы веб-мастерам и ввести ранжирование сайтов на платной основе. Те сайты, у которых установлены банеры от поисковика (Yandex.Direct и Google.AdSense - соответственно) ранжировать по величине прибыли для поисковика. Чем больше денег приносит, тем выше. Хотя это и так есть в поисковиках - покупка ключевых слов.


Метки: , ,   Категории:Yandex | Google


Жизнь в перевернутом мире по версии Яндекса

Идея того, что мы живем в "перевернутом мире", где все не так, часто упоминается в американскими агентами. Но веселее всего это отражено у Яндекса в сервисе Геокодирование. Казалось бы, уже все давно привыкли, что координаты на Земле отображаются 2 числами - широтой и долготой. Что тут можно напутать? Правильно, у сервиса Яндекса привычная нам из Google lat/lng заменена на обратную - вначале долгота(lng), а потом широта(lat). Как бы говоря, что у нас тут перевернутый мир, поэтому все задом-наперед. С ходу не сразу замечаешь почему сервис дает неправильные ответы.


Метки: ,   Категории:humor | Yandex


Рейтинг Яндекса перестал работать официально

Голландская компания Яндекс решила закрыть рейтинг блогеров. Официальный текст ниже в цитате. По сути они говорят, что писатели в блогах никому не нужны, а нужны лишь потребители информации в соц.сетях. Также они выпилили мой комментарий к их посту. Отмечу, что из-за действий Яндекса перестает работать информация о посещаемости тех или иных постов, а это может очень негативно сказаться на рейтингах типа t30p, но посмотрим через неделю.

Сегодня Яндекс закрывает часть сервиса Поиск по блогам ( http://webmaster.ya.ru/replies.xml?item_no=18640 ) - с главной страницы сервиса пропадают рейтинги блогеров и записей, а на страницах блогов перестают работать кнопки, показывающие количество читателей. При этом поисковая функциональность сервиса продолжает работать. В связи с этим с 25 апреля мы изменяем соответствующую функциональность и в API Поиска по блогам.

Станут недоступны RSS-поток рейтинга блогеров и информация о посетителях записей в API популярных записей.

Поисковая часть API продолжает работать в прежнем режиме.


Метки:   Категории:Blogs | Yandex


Темы дня Яндекса

Случайно обнаружил, что Яндекс обновил "темы дня", сделав их в стиле вымирающего pinme-абстракционизма (картинка ниже). Подобная тема оформления популярна у женской аудитории, в то время как (ну или мне так казалось), поиск по блогам Яндекса более популярен у мужской части. Да и самое главное, что стоит отметить. Какие-то слухи, да еще и какой-то поп-дивы, явно не вписываются в русскоязычную информационную повестку сегодняшнего дня.


Метки:   Категории:Blogs | Yandex


Яндекс Чунга-Чанга

Яндекс тут сделал открытие, что пора меняться и докрутил интерфейс "колдунчиков" в поиске, переименовав их в Острова. Это когда вместо поискового результата вы получаете сразу интерфейс-решение на ваш вопрос-запрос. В целом вещь удобная только для тех людей, кто ставит браузеры с именем "Интернет" от майлру. Очередная попытка подмять заменить собою интернет. Для продвинутого пользователя это все ерунда, привычнее, когда много инструментов-сайтов и каждый хорошо решает только определенную задачу. И задачу поиска давно успешнее решает Гугл. И да, рекламное видео сделано на славу, еще бы музыку поставили бы из Пиратов.

No Man Is An Island from Yandex on Vimeo.


UPDATE вот так выглядит лучше =)

video mp4

UPDATE2 Яндекс кстати ищет SMM менеджера.

Метки:   Категории:humor | Yandex


Гениальное приложение

Новое изобретение Яндекса - бесплатное приложение для продажи бесплатных приложений.
Утверждается, что пользователь получает более продвинутый поиск. Но очевидно, что самый продвинутый поиск был и есть у того, кто хостит эти приложения, то есть у Apple/Google.


Метки:   Категории:humor | Yandex


О качестве работы яндекса

Очередная ошибка в выдаче Яндекса. Почему-то две ссылки на одинаковый пост с разницей лишь в приставке "?utm_source=twitterfeed&utm_medium=twitter" 2 раза попали в поисковую выдачу.
При этом на первом месте показана ссылка на Вконтакте на удаленный пост. И как всегда глупо выглядит надпись про найденные 7 млн. ответов, когда ответ на данный поисковый вопрос может быть только 1. И тотже поисковый запрос в реал-таймовом поиске дает более красивые результаты.

Метки:   Категории:bugs | Analytics | Yandex


Контекстный поиск с русской и английской морфологией на основе проектов с открытым исходным кодом и облачных сервисов Microsoft Azure

keywords: контекстный поиск, русская морфология, поточное индексирование, облачные сервисы
abstract: В статье дается общее представление об архитектуре поискового сервиса, построенного на проектах с открытым исходным кодом и рассматриваются базовые проблемы реализации безотказной его работы в условиях поточных данных и минимальных системных ресурсах. Реализация опирается на следующие проекты:

На скриншотах присутствует программа для работы с BlobStorage - AzureStorageExplorer, которая также доступна с открытым кодом - http://azurestorageexplorer.codeplex.com/. Сам пост написан и хостится в облаке Azure с использованием BlogEngine - http://blogengine.codeplex.com/

 

Общее

Разложим задачу создания поискового сервиса на пять составляющих: 1. Сбор данных. 2. Фильтрация, токенизация, стемминг, синонимизация данных. 3. Помещение в поисковый индекс. 4. Обработка поисковых запросов. 5. Распознавание что хотел пользователь и предоставление нативного языка запросов.

В сегодняшней статье не будут разобраны пункты 2 и 5. Так как токенизация и стемминг осуществляется при помощи библиотеки Lemmatizer.dll, разработанной сотрудниками Яндекса, и рассматривалась мною 4 года назад. За прошедшее время статью открывали более 20 тыс. раз. Сама же библиотека Lemmatizer.dll перестала быть платной и с 2011 года доступна всем желающим в исходниках. Последний пункт - создание человеческого языка, запросов требует большую наработку данных уже от пользователей производящих запросы, и может дорабатываться в процессе работы поискового сервиса. На сегодняшний момент языком запросов будет являться стандартный синтаксис Lucene, который достаточно гибкий. Так например, орфографические ошибки в запросах можно исправлять с помощью нечеткого поиска "word~",

Сбор данных

В поточном режиме при помощи Twitterizer.NET подключимся к Twitter.Streaming API. Вытаскиваем все русскоязычные твиты, фотографии instagram, видео с youtube (+Vimeo), посты на стене Вконтакте, фотографии Facebook, посты GooglePlus. Для разбора данных воспользуемся библиотеками BlogsAPI, GoogleGData, FacebookSDK соответственно. Вытаскиваем из соц.сетей все данные, которые не требуют access_token - то есть присутствия пользователя. Примеры извлечения этих данных есть в примерах самих библиотек. Также важно понимать, что большинство ссылок на информацию пропущено через сервисы сокращения ссылок. Для разворачивания таких ссылок есть простая функция в BlogsAPI для большинства известных сокращателей:

Copy Source | Copy HTML
  1. Shortener shrt = Common.GetShortenerByLink(url.ExpandedUrl) as Shortener;
  2. if (shrt != null)
  3. {
  4.         fulllink = shrt.ConvertDataTo(url.ExpandedUrl, ItemType.ShortUrl, ItemType.FullUrl);
  5. }

После извлечения - данные группируются в пакеты (Batch) и отправляются на WCF-сервис индексирования. Для сложных данных, требующих дополнительных запросов используется Azure BlobQueue(примеры работы) для отложенной индексации или, как в случае Facebook и Vkontakte, группового извлечения данных, когда в одном запросе к социальной сети запрашивается сразу несколько постов.

Так как много данных не бывает, то в тотже индекс мы добавим все посты (если быть точным - большую часть) livejournal.com, liveinternet.ru, qip.ru,juick.com, blogi.mail.ru и т.д. Отдельно отмечу, что такой интернет гигант как Яндекс не выдает информации по своим блогам wow.ya.ru и видеоблогам на video.yandex.ru через API.

Индексирование

Благодаря облачным технологиям Микрософта у нас появляется замечательный объект BlobStorage, который предоставляет до 100 ТБ под хранение индекса. Чтобы разместить в нем наш поисковый индекс формируемый Lucene, достаточно воспользоваться проектом AzureDirectory. Основной индекс будет храниться в BlobStorage, а на локальном инстансе WebRole создается специальная промежуточная директория - LocalStorage, где производятся все операции с индексом по поиску и изменению его. Важно понимать, что инстансов WebRole у нас должно быть 2 и более, так как к конечный момент времени один из них заблокировал индекс на запись (добавление объектов), а с остальных идет непрерывное чтение (поисковые команды). При этом чтение на инстансе, где идет запись - невозможно. Модель "один писатель, много читателей". При завершении записи данные с локального LocalStorage копируются в центральный BlobStorage откуда синхронизируются на остальные машины. Ниже приводится класс, который реализует связь Lucene и AzureDirectory, функции работы с Lucene не приводятся, примеры этого есть в интернете.

  1. public abstract class IndexerBase
  2. {
  3.     protected IndexerBase()
  4.     {
  5.     }
  6.     /// <summary>
  7.     /// Constructor
  8.     /// </summary>
  9.     protected IndexerBase(string sDir):base()
  10.     {
  11.         IndexDir = sDir;
  12.     }
  13.     /// <summary>
  14.     /// Объект блокировки, переопределяемый дочерними классами
  15.     /// </summary>
  16.     protected static object oWriting = new object();
  17.     /// <summary>
  18.     /// Название папки с индексом
  19.     /// </summary>
  20.     protected string IndexDir = "IndexBase";
  21.     public string cachepath
  22.     {
  23.         get
  24.         {
  25. #if DEBUG
  26.             return Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), IndexDir);
  27. #else
  28.             return Path.Combine(RoleEnvironment.GetLocalResource("MyStorage").RootPath,IndexDir);
  29. #endif
  30.         }
  31.     }
  32.     private AzureDirectory _azureDirectory;
  33.     /// <summary>
  34.     /// Директория
  35.     /// </summary>
  36.     protected AzureDirectory azureDirectory
  37.     {
  38.         get
  39.         {
  40.             if (_azureDirectory != null) return _azureDirectory;
  41.             _azureDirectory = new AzureDirectory(CloudStorageAccount.Parse(
  42.                         RoleEnvironment.GetConfigurationSettingValue("SearchConnectionString")), IndexDir,
  43.                         new SimpleFSDirectory(new DirectoryInfo(cachepath)));
  44.             return _azureDirectory;
  45.         }
  46.     }
  47.     /// <summary>
  48.     /// Указывает на первое время запуска и открытия поискового индекса
  49.     /// </summary>
  50.     protected static Boolean firsttime = true;
  51.     /// <summary>
  52.     /// Статический поток занимающийся перезаписью сегментов
  53.     /// </summary>
  54.     protected static ConcurrentMergeScheduler Merger = new ConcurrentMergeScheduler();
  55.     /// <summary>
  56.     /// То, как писать
  57.     /// </summary>
  58.     private IndexWriter _MyWriter = null;
  59.     protected IndexWriter MyWriter
  60.     {
  61.         get
  62.         {
  63.             if (_MyWriter != null) return _MyWriter;
  64.             lock (oWriting)
  65.             {
  66.                 if (_MyWriter != null) return _MyWriter;
  67.                 CreateMyWriter();
  68.             }
  69.             return _MyWriter;
  70.         }
  71.         set { _MyWriter = value; }
  72.     }
  73.     private void CreateMyWriter()
  74.     {
  75.         try
  76.         {
  77.             _MyWriter = new IndexWriter(azureDirectory, MyAnalyser, false);
  78.             //важны атомарные операции, так как несмотря на lock(oWriting) в случае нескольких дочерних классов индексирования, возникнуть может многопоточность
  79.             Interlocked.Exchange(ref LockFailsCounter,  0); //успешно открылось на запись, сбросили счетчик неудач
  80.         }
  81.         catch (LockObtainFailedException e0)
  82.         {
  83.             //если уже много ошибок подряд, около часа
  84.             if (LockFailsCounter > 30)
  85.             {
  86.                 //принудительно удаляем блокировку =(
  87.                 azureDirectory.ClearLock("write.lock");
  88.                 //Trace.Write(e0);
  89.                 Trace.Write("Принудительное снятие старой блокировки на запись.");
  90.                 Thread.Sleep(60 * 1000); //1 min for unlock
  91.                 _MyWriter = new IndexWriter(azureDirectory, MyAnalyser, false);
  92.                 Interlocked.Exchange(ref LockFailsCounter,  0);//успешно открылось на запись, сбросили счетчик неудач
  93.             }
  94.             else
  95.             {
  96.                 Interlocked.Increment(ref LockFailsCounter);
  97.                 throw;
  98.                 //вызовем ошибку, чтобы запрос на индексирование был повторен через некоторое время, когда файл разблокируется
  99.             }
  100.         }
  101.         catch (Exception err)
  102.         {
  103.             Trace.WriteLine("Не удалось открыть старый индекс: " + err.Message);
  104.             if (System.IO.Directory.Exists(cachepath))
  105.             {
  106.                 System.IO.Directory.Delete(cachepath, true);
  107.                 //удаляем возможный старый хлам помешавший открытию
  108.                 System.IO.Directory.CreateDirectory(cachepath);
  109.             }
  110.             //создадим новый еще раз!
  111.             _MyWriter = new IndexWriter(azureDirectory, MyAnalyser, true);
  112.         }
  113.         _MyWriter.SetMergeScheduler(Merger);
  114.         _MyWriter.SetMergeFactor(10);
  115.         //_MyWriter.SetUseCompoundFile(false);
  116.     }
  117.     /// <summary>
  118.     /// Время открытия поискового индекса
  119.     /// </summary>
  120.     protected DateTime? SyncTime
  121.     {
  122.         get { return HttpContext.Current.Application[GetType() + "synctime"] as DateTime?; }
  123.         set { HttpContext.Current.Application[GetType() + "synctime"] = value; }
  124.     }
  125.     /// <summary>
  126.     /// Количество неудачных попыток открыть индекс на запись
  127.     /// </summary>
  128.     /// <remarks>Важно, что это один объект на все дочерние классы индексирования</remarks>
  129.     private static int LockFailsCounter =  0;
  130.     /// <summary>
  131.     /// То, как писать
  132.     /// </summary>
  133.     protected IndexSearcher _MySearcher
  134.     {
  135.         get { return HttpContext.Current.Application[GetType() + "searcher"] as IndexSearcher; }
  136.         set { HttpContext.Current.Application[GetType() + "searcher"] = value; }
  137.     }
  138.     public IndexSearcher MySearcher
  139.     {
  140.         get
  141.         {
  142.             if (SyncTime.HasValue && SyncTime.Value.AddHours(1)<DateTime.Now)//более ~  с момента последней синхронизации(!)
  143.             {
  144.                 //делаем запись только в том, случае, если индекс уже был успешно открыт ранее на запись, и сделана синхронизация с основным
  145.                 //иначе может произойти потеря
  146.                 IndexCommit();
  147.             }
  148.             if (_MySearcher != null) return _MySearcher;
  149.             lock (oWriting)
  150.             {
  151.                 if (_MySearcher != null) return _MySearcher;
  152.                 var myPerf = new PerformanceTimer();
  153.                 myPerf.StartTimer();
  154.                 try
  155.                 {
  156.                     _MySearcher = new IndexSearcher(azureDirectory, true);
  157.                 }
  158.                 catch (CorruptIndexException)
  159.                 {
  160.                     //требуется починка индекса
  161.                     FixIndex(RoleEnvironment.DeploymentId);
  162.                 }
  163.                 catch (Exception e1) //если у нас нету еще директории индекса или сбой соединения
  164.                 {
  165.                     Trace.Write(e1);
  166.                     if (_MyWriter != null)
  167.                     {
  168.                         Trace.Write("Warning: have to close MyWriter to open MySearcher!");
  169.                         _MyWriter.Commit();
  170.                         _MyWriter.Close();
  171.                         _MyWriter = null;
  172.                     }
  173.                     if (System.IO.Directory.Exists(cachepath))
  174.                     {
  175.                         System.IO.Directory.Delete(cachepath, true); //удаляем возможный старый хлам помешавший открытию
  176.                         System.IO.Directory.CreateDirectory(cachepath);
  177.                     }
  178.                     try
  179.                    &nbssearcherp;{
  180.   nbsp;запись                      //важно для всех случаев когда индекс открывается в отсутствующую папку.
  181.                         var openindex = MyWriter;
  182.                     }catch(LockObtainFailedException)
  183.                     {
  184.                         //Папка с индексом успешно открыта и создана, однако другая роль уже пишет индекс
  185.                     }
  186.                     try
  187.                     {
  188.                         _MySearcher = new IndexSearcher(azureDirectory, true);
  189.                     }catch(FileNotFoundException foi)
  190.                     {
  191.                         //индекс скопированный с мастера не содержит нужного сегмента
  192.                         if(foi.InnerException!=null && foi.InnerException.GetType() == typeof(StorageClientException))
  193.                         {
  194.                             //вызываем утилиту исправления ошибок
  195.                             FixIndex(RoleEnvironment.DeploymentId);
  196.                         }
  197.                     }
  198.                     Trace.Write(String.Format("BadMode: _MySearcher {0} loaded in {1}", GetType().Name, myPerf.StopTimer()));
  199.                 }
  200.                 if(firsttime)
  201.                 {
  202.                     firsttime = false;
  203.                     Trace.Write(String.Format("GoodMode: _MySearcher {0} loaded in {1}", GetType().Name, myPerf.StopTimer()));
  204.                 }
  205.             }
  206.             return _MySearcher;
  207.         }
  208.     }
  209.     /// <summary>
  210.     /// Морфологический анализатор
  211.     /// </summary>
  212.     internal static MorphologyAnalyzer _MyAnalyser;
  213.     protected MorphologyAnalyzer MyAnalyser
  214.     {
  215.         get
  216.         {
  217.             if (_MyAnalyser != null && _MyAnalyser.Morph != null && _MyAnalyser.Morph.isLoaded) return _MyAnalyser;
  218.             Trace.Write("Заново загружаем MorphologyAnalyzer");
  219.             _MyAnalyser = new MorphologyAnalyzer(null, false);
  220.             return _MyAnalyser;
  221.         }
  222.     }
  223.     static IndexerBase()
  224.     {
  225.         RoleEnvironment.Stopping += delegate
  226.         {
  227.             try
  228.             {
  229.                 //дожидаемся завершения записи
  230.                 lock (oWriting)
  231.                 {
  232.                     //останавливаем поток слияния
  233.                     Merger.Close();
  234.                     Merger = null;
  235.                 }
  236.             }catch(Exception e)
  237.             {
  238.                 Trace.Write(e);
  239.             }
  240.         };
  241.     }
  242.     /// <summary>
  243.     /// Запись индекса на диск и переоткрытие поискового механизма
  244.     /// </summary>
  245.     protected void IndexCommit()
  246.     {
  247.         SyncTime = DateTime.Now;
  248.         lock (oWriting)
  249.         {
  250.             if (_MyWriter != null)
  251.             {
  252.                 //закрыли текущий коммит
  253.                 try
  254.                 {
  255.                     _MyWriter.Commit();
  256.                 }catch(CorruptIndexException)
  257.                 {
  258.                     FixIndex(RoleEnvironment.DeploymentId);
  259.                 }
  260.                 _MyWriter.Close();
  261.                 _MyWriter = null;
  262.             }
  263.             if (_MySearcher != null)
  264.             {
  265.                 //закрыли тукущий поисковик
  266.                 _MySearcher.Close();
  267.                 _MySearcher = null;
  268.             }
  269.         }
  270.         //открыли новый поисковик
  271.         var opensearcher = MySearcher;
  272.     }
  273.     /// <summary>
  274.     /// Серьезный процесс по востановлению индекса 
  275.     /// </summary>
  276.     /// <param name="DeploymentId"></param>
  277.     /// <returns></returns>
  278.     public string FixIndex(string DeploymentId)
  279.     {
  280.         var myPerf = new PerformanceTimer();
  281.         myPerf.StartTimer();
  282.         Trace.WriteLine(String.Format("Вызов проверки статуса и исправления индекса со значением  {0} / {1}", DeploymentId, GetType().Name));
  283.         string lockfile = GetType().Name + "_" + DeploymentId + ".lock";
  284.         Lock indexLock = azureDirectory.MakeLock(lockfile);
  285.         if (indexLock.Obtain())//блокировка основного индекса
  286.         {
  287.             try
  288.             {
  289.                 //устроим проверку индекса!
  290.                 CheckIndex fixer = new CheckIndex(azureDirectory);
  291.                 CheckIndex.Status oStatus = fixer.CheckIndex_Renamed_Method();
  292.                 Trace.WriteLine(GetType().Name+":status=" + oStatus.numBadSegments + ":" + oStatus.totLoseDocCount);
  293.                 if (oStatus.numBadSegments != oStatus.numSegments && oStatus.numBadSegments >  0)
  294.                 {
  295.                     fixer.FixIndex(oStatus); //опасная функция перезаписи
  296.                 }
  297.                 Trace.WriteLine(GetType().Name + ":FixIndex finished");
  298.                 //убедимся, что разблокировали индекс после правки
  299.                 azureDirectory.ClearLock("write.lock");
  300.             }
  301.             catch (Exception e1)
  302.             {
  303.                 Trace.Write(e1);
  304.             }
  305.             finally
  306.             {
  307.                 indexLock.Release();
  308.                 azureDirectory.DeleteFile(lockfile);
  309.             }
  310.             //////////******************////////////
  311.         }
  312.         return myPerf.StopTimer().ToString();
  313.     }
  314. }


Поясню основные моменты:
1. Класс абстрактный, так как от него наследуется WCF сервис, который может содержать свой индекс. То есть один инстанс может работать с несколькими индексами Lucene которые хранятся в одном BlobStorage в разных папках.
2. В Azure любой инстанс может быть выключен в любую секунду, поэтому важно обрабатывать событие остановки и корректно завершать работу отдельного потока ConcurrentMergeScheduler занимающегося оптимизацией индекса.
3. Объект чтения MySearcher у нас открыт всегда, чтобы минимизировать время поиска. Объект записи MyWriter создается только при получении данных на запись и соответственно закрывает и блокирует чтение для текущего индекса на инстансе.
4. Очень важна отказоустойчивость, поэтому такая детальная обработка ошибок LockObtainFailedException - когда пришли данные на запись, а основной индекс в BlogStorage заблокирован. Например, другим инстансом, который после этого падает по OutOfMemoryException и не снимает блокировки. Соответственно через несколько таких ошибок блокировка снимается автоматически и буфера поставщиков данных не успевают переполниться и потери данных не происходит.
5. Другая стандартная ошибка - CorruptIndexException - вполне может быть следствием сбоем сети при передачи больших файлов. При этом последняя операция записи отменяется, однако это занимает значительное время и блокирует другие операции записи. Ниже, как это выглядит в логах.




Еще раз перечислю основные проблемы, к которым нужно быть готовыми: одновременный приход запросов на индексирование, произвольное выключение инстанса, нечитаемость индекса, нехватка памяти, невозможность эксклюзивно заблокировать индекс, очищение LocalStorage. Последние подразумевает то, что согласно документации, подключаемый к инстансу диск несмотря на настройку cleanOnRecycle="false" всеравно не является величиной постоянной и может быть очищен в любой момент. Последнее означает повторную синхронизацию с центральным BlobStorage и открытие объектов чтения и записи займет дольше обычного (2-3 сек) на несколько минут. В итоге все работает стабильно и загрузка одного индексирующего инстанса показана ниже. К слову, мы используем всего-лишь два инстанса с минимальными мощностями, так как сильно ограничены в ресурсах. Пикам по 100% соответствует обработка пакета на индексацию. С уверенностью можно сказать, что за простой ресурсов мы не платим.


Вторая картинка приведена для сравнения, и формируется на системном портале Azure как среднее по WebRoles в Endpoint. Очевидно, что она не дает представления о том, что твориться с инстансами.

Выполнение поисковых запросов

В условиях, когда мы не знаем в каком состоянии находится инстанс, пишет он или нет, единственный правильный вариант - отправлять поисковый запрос на все инстансы, и брать первый ненулевой ответ. При наличии ресурсов можно было бы выделить отдельную Endpoint и WebRole, которая работала бы только на чтение. Но мы не ищем простых путей, поэтому на каждой webrole с индексом имеется следующая поисковая функция, выполняющаяся только если инстанс не пишет в индекс

Copy Source | Copy HTML
  1. /// <summary>
  2. /// Контекстный поиск с проверкой на поток записи, если открыта запись, то возвращаем NULL
  3. /// </summary>
  4. /// <param name="opts">Слова и параметры поиска</param>
  5. /// <param name="ignorelock">Не проверять идет ли запись</param>
  6. /// <returns>найденные результаты, CAN BE NULL</returns>
  7. public IndexedDocument[] SearchForAdvanced(SearchForOptions opts, bool ignorelock)
  8. {
  9.     //без проверки на блокировку
  10.     if (ignorelock) return SearchFor(opts);
  11.     bool isNotLocked = false;
  12.     try
  13.     {
  14.         isNotLocked = Monitor.TryEnter(oWriting);
  15.     }
  16.     finally
  17.     {
  18.         if (isNotLocked)
  19.         {
  20.             //важно освободить блокировку на поиск как можно скорее
  21.             Monitor.Exit(oWriting);
  22.         }
  23.     }
  24.     if (isNotLocked)
  25.     {
  26.         return SearchFor(opts);
  27.     }
  28.     //заблокировано
  29.     return null;
  30. }

И соответственно на поисковом клиенте реализуется многопоточная отправка с ожидаем быстрейшего ненулевого результата через ThreadPool.QueueUserWorkItem(state => SearchForAdvancedAsync(oData));

Copy Source | Copy HTML
  1. /// <summary>
  2. /// Объет параллельного запроса
  3. /// </summary>
  4. public class SearchAsyncObject
  5. {
  6.     /// <summary>
  7.     /// Число параллельных запросов
  8.     /// </summary>
  9.     public readonly int Count = 3;
  10.     /// <summary>
  11.     /// Число обработанных вызовов
  12.     /// </summary>
  13.     public int Finished =  0;
  14.     /// <summary>
  15.     /// Поисковые параметры
  16.     /// </summary>
  17.     public SearchForOptions opts = null;
  18.     /// <summary>
  19.     /// Результат поиска
  20.     /// </summary>
  21.     public IndexedDocument[] Founds = null;
  22.     /// <summary>
  23.     /// Mutex
  24.     /// </summary>
  25.     public ManualResetEvent mre = new ManualResetEvent(false);
  26. }
  27. /// <summary>
  28. /// Функция завершения поиска
  29. /// </summary>
  30. private static void SearchForAdvancedAsync(SearchAsyncObject data)
  31. {
  32.     try
  33.     {
  34.         IndexClient service = Indexer.GetIndexClient();
  35.         IndexedDocument[] founds = service.SearchForAdvanced(data.opts, false);
  36.         if (founds != null && data.Founds == null) //найден результат(!)
  37.         {
  38.             data.Founds = founds;
  39.             data.mre.Set();
  40.         }else if (Interlocked.Increment(ref data.Finished) >= data.Count){
  41.             data.mre.Set();//все потоки завершили работу
  42.         }
  43.     }
  44.     catch (Exception e1)
  45.     {
  46.         Trace.Write(e1);
  47.     }
  48. }

Функции добавления документов в индекс и выполнение операций поиска уже на конкретной машине выполняются как описано в документации Lucene.

 

Результат

Построенный поисковый сервис используется для выявления наиболее интересной информации в русскоязычных социальных сетях и ранжирования этой информации для формирования топа событий на сайте http://t30p.ru. Полученное таким образом самостоятельное независимое СМИ сильно снижает стоимость и увеличивает качество интернет журналистики. В поисковый индекс можно добавлять новые индексные поля и обновлять код без нарушения работы сервиса или потери данных (проверено). Сейчас в индексе более 200 млн. объектов от 2,5 млн. социальных аккаунтов; примерно 2,5 млн. новых объектов в сутки; размер индекса в пределах 100 GB; время выполнения запросов - 0,5 сек; к индексу выполняется примерно 1000 запросов в час, в том числе внутре-технические. Ожидаемое время запаздывания между нахождением данных и появлением их в поисковой выдаче - 5-15 мин. (в частности, задержку дает "кэширующий" CDN на поисковой странице).

Проблемы и решения

Более int.MaxValue объектов
Поисковый индекс Lucene ограничен примерно в 2 млрд. объектов. Поэтому для работы с большим числом объектов на помощь приходит MultiSeacher, который еще не портирован в .Net версию. Он позволяет производить поиск по конечному набору поисковых индексов при этом теряется скорость.
Нехватка места или превышение 100ТБ
При использовании MultiSeacher для поиска по нескольким поисковым индексам каждый индекс может быть привязан к своему диску или BlobStorage, что решает проблему с ограничением места.
Дублирование информации
Несмотря на обработку поточных данных от самих социальных сервисов, в них часто происходят сбои и разрывы соединений, что приводит к разного рода ошибкам. Так возможно повторное индексирование постов. Поэтому на поисковом клиенте производится проверка уникальности ссылки на пост в результате. В теории эту задачу можно переложить на фильтр DuplicateFilter
Lucene 4.0 имеет классы встроенной русскоязычной морфологии
Какой бы не была встроенная морфология, я бы рекомендовал использовать более тяжеловесный Lemmatizer, библиотеки которого в архиве zip занимают 100МБ, что говорит о серьезности подхода к составлению словаря.
И куда потом все эти собранные данные?
Например, можно создать сервис в Azure Marketplace и продавать доступ к ним по фиксированной цене. Там же есть интересный платный сервис перевода текстов на многие языки, что позволит в автоматическом режиме перевести всю собранную информацию на любой язык и сделать международный топ русскоязычных социальных новостей.


Метки: , , , , , ,   Категории:Blogs | Csharp | Yandex | microsoft | Code


Яндекс Ошибки

Интересная ошибка висит на Яндексе. С одной стороны маловероятно, чтобы роботом составляющий префиксы к текстам допустил такую ошибку в названии страны. С другой, не исключено, что слово с заглавной буквы было воспринято, как имя собственное. Но я склоняюсь, что это скорее человеческий фактор, так как ошибка роботом на этом участке работы проявилась бы уже давно и была бы своевременно исправлена.

Метки:   Категории:bugs | Yandex


YAC2012 - регистрация

Открылась регистрация на ежегодную конференцию Яндекса. Надо прийти пораньше, чтобы футболки достались. В программе мероприятия пока ничего необычного кроме "12:20 Запуск секретного продукта" - наверняка что-нить мобильное.

Метки:   Категории:Yandex


Blogs Yandex Update -

Только что обновилась страница поиска по блогам. 2 заметных изменения - 1) пропали рейтинги фильмов и театров, видимо тут нет смысла конкурировать со всякими афишами и нет человека, который бы все это модерировал. 2) пропала ссылка на рейтинг рейтингов и описание разных API. Судя по всему, это не окончательное изменения, а какое-то промежуточное.

Метки:   Категории:Yandex


Результаты выдачи яндекса используют твиттер

Может и старая новость, но выглядит интересно, как яндекс формирует выдачу с использованием ретвитов сообщений. Примечетельно, что на рисунке, где у обоих записей написано "сегодня", на самом деле должно было стоять уже "вчера". Видимо популярность твиттера продолжит расти, даже без нано-президента.
Поисковый запрос

Метки:   Категории:Yandex


Yandex blocks bit.ly

Узнал, что некоторые пользователи не могут пользоваться топом, так как подлый яндекс блокирует им переход на сервис сокращения ссылок - bit.ly. Примерно такая же проблема до этого была с вконтактиком. Примерное описание ситуации ниже. Вот наверное к чему приводит установка всяких Яндекс.баров, что Яндекс пытается вести себя как антивирус.
при нажатии на ссылку вылезает окошко с текстом: Имеется информация, что эта веб-страница атакует компьютеры! Имеется информация о том, что веб-страница bit.ly используется для атак на компьютеры пользователей. В соответствии с вашими настройками безопасности она была заблокирована. Веб-страницы, атакующие компьютеры, пытаются установить программное обеспечение, которое похищает персональную информацию, вредит вашей системе или использует ваш компьютер для атак на другие компьютеры. Некоторые страницы намеренно созданы для распространения вредоносного программного обеспечения, однако многие страницы были взломаны и делают это без ведома или разрешения своих владельцев. далее есть кнопка "Почему эта страница была заблокирована?" при нажатии на нее вылезает вот эта страница: http://yandex.ru/infected?l10n=ru&url=http://bit.ly/IOX16k браузер у меня мозилла

Метки:   Категории:Yandex


Поиск Общепита?

Как стало известно яндекс готовит к запуску новый сервис. Как догадались пользователи твиттера - это станет поиск мест питания. Судя по логотипу тарелки и сольницы:

Метки:   Категории:Yandex


Исследование частоты имен

Яндекс опубликовал исследование по популярности имен в российских городах. Кавказ особенно интересен.


Метки:   Категории:Analytics | Yandex


Социальные результаты в основной выдаче яндекса

Что-то странное выползло на главную страницу результатов поиска в Яндексе. Назвалось оно "Ответы". По факту, это вхождения поискового запроса в упоминания в социальных сетях АКА "яндекс.блоги", однако среди них как то затесался и т30п, получился совершенно левый результат при поиске по имени сайта Прохорова mdp2012.ru:

Метки:   Категории:Yandex


Yandex Twitter 2

Итак, кроме поиска в соцаильных профилях яндекс вплотную занялся твиттером, интегрировавшись с ним. Что получили?
1) Отдельную страницу поиска в твиттере, но в стиле яндекса.
2) Новый раздел справа в поиске по блогам для выбора поиска только по Твиттеру.
3) Новые данные, что русскоязычных твиттерян у нас 2,2 млн., хотя по моим оценкам их уже 2,4 млн.

Метки:   Категории:Twitter | Yandex


Yandex Blogs UPDATE

Кроме инициативы по ПСИ яндекс сегодня ночью внес изменения в выдаваемые результаты по твиттеру. Стало красивее. Особенно радует что пропали заголовки у твиттов.

Метки:   Категории:Yandex


Кто я?

Программист. Я слежу за блогосферой и знаю, как будет развиваться интернет. Когда у меня есть время я даже прилагаю для этого усилия. Подробнее

Последние комментарии

Topbot at FeedsBurner

Мои Твиты

Twitter сентября 24, 19:17
@SielenaSemper по какой причине эта трансляция более недоступна?

Twitter сентября 23, 14:26
Разбор сериала "Конец детства" http://dlvr.it/Ppxjly https://twitter.com/f1ashr/status/911597559604420608/photo/1

Twitter сентября 22, 17:41
Спустя 18 лет в ЖЖ запустили поиск по постам http://dlvr.it/PpmtD1

Twitter сентября 22, 16:01
Интернет ищет инопланетян (экзопланеты) http://dlvr.it/PpltJF

Twitter сентября 22, 16:01
dlvrit вводит лимиты http://dlvr.it/PpltJf

Twitter сентября 22, 14:24
Iron Sky 3 - новый трейлер на 9 мая http://dlvr.it/PpkrvS https://twitter.com/f1ashr/status/911234743747805184/photo/1

Twitter сентября 22, 14:24
Про терракты в Лондоне http://dlvr.it/Ppkrv7

Twitter сентября 22, 14:24
Коломенский кремль: Альтернативная история http://dlvr.it/Ppkrrx https://twitter.com/f1ashr/status/911234723476676608/photo/1

Twitter сентября 22, 14:24
Паштет из мяса медведя с брусникой http://dlvr.it/PpkrcB https://twitter.com/f1ashr/status/911234712026234882/photo/1

Twitter сентября 22, 14:24
Рейтинг каналов и ботов Телеграмм http://dlvr.it/PpkrQw https://twitter.com/f1ashr/status/911234670553063424/photo/1

Twitter сентября 22, 14:24
Facebook опять забанил http://dlvr.it/PpkrPG

Twitter сентября 22, 14:24
Звездные войны Изгой Один http://dlvr.it/PpkrDL https://twitter.com/f1ashr/status/911234655310905344/photo/1

Twitter сентября 21, 15:26
Робот играющий в гольф http://dlvr.it/PpWS1G

Twitter сентября 21, 14:53
Мобильное приложение t30p.ru в AppStore http://dlvr.it/PpW5Qk

Twitter сентября 21, 14:53
Что будет 22 сентября? http://dlvr.it/PpW5Lb https://twitter.com/f1ashr/status/910879685042618368/photo/1

Twitter сентября 21, 14:20
Битва за Мосул в 360 с вертолета http://dlvr.it/PpVlXD https://twitter.com/f1ashr/status/910871392970608640/photo/1

Twitter сентября 21, 14:20
Очередной летающий электро-автомобиль http://dlvr.it/PpVlWl https://twitter.com/f1ashr/status/910871386507186177/photo/1

Twitter сентября 21, 14:20
Яндекс атаковал Израиль http://dlvr.it/PpVlX9

Twitter сентября 21, 14:20
Мелькает число 35 в международных новостях http://dlvr.it/PpVlWZ

Twitter сентября 21, 14:20
Информационные аккаунты в Твиттере http://dlvr.it/PpVlWF

Мой твиттер

Копирайт

Все мысли, высказанные в блоге, являются моим мнением и за это мнение меня никто не забанит! Кроме того, никто не имеет право копировать материалы блога без использования ctrl+C/V!

© Copyright 2008