مطالب
یک سؤال در مورد WordPress

کسی می‌دونه چرا تمام tag های سایت wordpress فیلتر شده؟
یکی از خاصیت‌های جالب این سایت جستجوی یک واژه‌ی کلیدی به صورت زیر است:
http://en.wordpress.com/tag/keyword

به جای keyword عبارت مورد نظر خود را قرار دهید.
مثلا:


به این صورت می‌شود کل وبلاگ‌های سایت را یکجا تحت نظر داشت یا حتی روی این‌ برچسب‌ها فید هم تعریف می‌شود و در نرم افزارهای فید خوان قابل استفاده خواهد بود.

در کل بجای فیلتر کردن چند عبارت خاص نباید کل برچسب‌ها رو یکجا فیلتر می‌کردند.


اشتراک‌ها
آموزش Kendo UI

بررسی مفاهیم مقدماتی کار با Kendo UI

آموزش Kendo UI
اشتراک‌ها
لینک visual studio 2013 update 4
November 12, 2014

Today, we are happy to announce the availability of Visual Studio 2013 and Team Foundation Server 2013 Update 4 Release to Manufacturing (RTM).
This update is the latest in a cumulative series of feature additions and bug fixes for Visual Studio 2013.

More information:
http://www.visualstudio.com/news/vs2013-update4-rtm-vs

Download full ISO:
http://go.microsoft.com/fwlink/?LinkId=512594

SHA-1 Values:
http://www.visualstudio.com/en-us/downloads/visual-studio-2013-iso-sha1-vs

They also released the new Visual Studio Community 2013:
http://www.visualstudio.com/products/visual-studio-community-vs

 
لینک visual studio 2013 update 4
اشتراک‌ها
کالکشن ISet

کالکشنی بسیار عالی برای مجموعه‌ها که عمدتا به بررسی این موضوع که آیا مجموعه A زیر مجموعه B است یا نه میپردازد . 

در مورد اصطلاحاتی مثل Proper Subset  , Subset ,  Proper Superset  , Superset  می‌توانید از مقالات 

^ , ^   و ^ کمک بگیرید
کالکشن ISet
مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 9 - بررسی تغییرات مسیریابی
فعال سازی تنظیمات مسیریابی

یکی دیگر از تغییرات عمده‌ی ASP.NET Core با نگارش‌های قبلی آن، نحوه‌ی مدیریت مسیریابی‌های سیستم است. در نگارش‌های قبلی مبتنی بر HTTP Moduleها، مسیریابی‌ها توسط یک HTTP Module مخصوص، با pipeline اصلی ASP.NET یکپارچه شده‌اند و زمانیکه مسیر درخواستی با تنظیمات سیستم تطابق داشته باشد، پردازش کار به HTTP Handler مخصوص ASP.NET MVC منتقل می‌شود:


اما در ASP.NET Core مبتنی بر میان افزارها، زیر ساخت مسیریابی به صورت زیر تغییر کرده‌است:


میان افزار ASP.NET MVC را که در قسمت قبل فعال کردیم، باید بتواند کنترلر و اکشن متد متناظر با URL درخواستی را مشخص کند. این تصمیم گیری نیز بر اساس تنظیماتی به نام Routing انجام می‌شود. در قسمت قبل، حالت ساده و پیش فرض این تنظیمات را مورد استفاده قرار دادیم
 app.UseMvcWithDefaultRoute();
که مطابق سورس ASP.NET Core، معادل است با فراخوانی متد app.UseMvc، با قالب پیش فرضی به صورت زیر:
    public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app)
    {
      if (app == null)
        throw new ArgumentNullException("app");
      return app.UseMvc((Action<IRouteBuilder>) (routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}")));
    }
قالب مشخص شده‌ی در اینجا به ASP.NET MVC می‌گوید که از کدام قسمت‌های URL باید نام کلاس کنترلر (کلاس ویژه‌ای که به کلمه‌ی Controller ختم می‌شود) و نام اکشن متد متناظر با آن‌را انتخاب کند (اکشن متد، متدی است عمومی در آن کلاس).
روش دیگر معرفی این تنظیمات، استفاده از Attribute routing است:
 [Route("[controller]/[action]")]


مسیریابی‌های قراردادی

در قسمت قبل، یک POCO Controller را به صورت ذیل تعریف کردیم و این کنترلر، بدون تعریف هیچ نوع مسیریابی خاصی در دسترس بود:
namespace Core1RtmEmptyTest.Controllers
{
  public class HomeController
  {
   public string Index()
   {
    return "Running a POCO controller!";
   }
  }
}
علت کار کردن مسیریابی آن نیز به ذکر متد app.UseMvcWithDefaultRoute در کلاس آغازین برنامه بر می‌گردد و همانطور که عنوان شد، این فراخوانی را می‌توان با فراخوانی واضح‌تر ذیل جایگزین کرد:
app.UseMvc(routes =>
{
  routes.MapRoute(
   name: "default",
   template: "{controller=Home}/{action=Index}/{id?}");
});
پارامتر این متد که جایگزین متد ConfigureRoutes، در نگارش‌های قبلی ASP.NET MVC شده‌است، از نوع IRouteBuilder می‌باشد.
در این تعاریف، هر کدام از قسمت‌های قرارگرفته‌ی داخل {}، مشخص کننده‌ی قسمتی از URL دریافتی بوده و نام‌های controller و action در اینجا جزو نام‌های از پیش مشخص شده هستند و برای نگاشت اطلاعات مورد استفاده قرار می‌گیرند. برای مثال اگر آدرس home/index/ درخواست شد، برنامه به کلاس HomeController و متد عمومی Index آن هدایت می‌شود. همچنین قسمت آخر این پردازش به ?id ختم شده‌است. وجود  ?، به معنای اختیاری بودن این پارامتر است و اگر در URL ذکر شود، به پارامتر id این اکشن متد، نگاشت خواهد شد. مواردی که پس از = ذکر شده‌اند، مقادیر پیش فرض مسیریابی هستند. برای مثال اگر صرفا آدرس home/ درخواست شود، مقدار اکشن متد آن با مقدار پیش فرض index جایگزین خواهد شد و اگر تنها مسیر / درخواست شود، کنترل Home و اکشن متد Index آن پردازش می‌شوند.
در اینجا به هر تعدادی که نیاز است می‌توان متدهای routes.MapRoute را فراخوانی و استفاده کرد؛ اما ترتیب تعریف آن‌ها حائز اهمیت است. هر مسیریابی که در ابتدای لیست اضافه شود، حق تقدم بالاتری خواهد داشت و هر تطابقی با یکی از مسیریابی‌های تعریف شده، در همان سطح سبب خاتمه‌ی پردازش سایر مسیریابی‌ها می‌شود.


استفاده از Attributes برای تعریف مسیریابی‌ها

بجای تعریف قرار دادهای پیش فرض مسیریابی در کلاس آغازین برنامه، می‌توان از ویژگی Route نیز استفاده کرد. هرچند روش تعریف مسیریابی‌های قراردادی، از نگارش‌های آغازین ASP.NET MVC به همراه آن بوده‌اند، اما با زیاد شدن تعداد کنترلرها و مسیریابی‌های سفارشی هر کدام، اینبار با نگاه کردن به یک کنترلر، سریع نمی‌توان تشخیص داد که چه مسیریابی‌های خاصی به آن مرتبط هستند. برای ساده سازی مدیریت برنامه‌های بزرگ و ساده سازی تعاریف مسیریابی‌های خاص آن‌ها، استفاده از ویژگی Route نیز به ASP.NET MVC اضافه شده‌است.
یک مثال: کنترلر About را درنظر بگیرید:
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
  public class AboutController : Controller
  {
   public ActionResult Hello()
   {
    return Content("Hello from DNT!");
   }
 
   public ActionResult SiteName()
   {
    return Content("DNT");
   }
  }
}
این کلاس و کنترلر، به صورت پیش فرض نیاز به تعریف هیچ نوع مسیریابی جدیدی ندارد. همان مسیریابی پیش فرض ثبت شده‌ی در کلاس آغازین برنامه، تمام متدهای عمومی آن یا همان اکشن متدهای آن‌را پوشش می‌دهد. برای مثال جهت رسیدن به اکشن متد SiteName آن، می‌توان آدرس /About/SiteName/ را درخواست داد.
اما اگر آدرس /About/ را درخواست دهیم چطور؟ چون در مسیریابی پیش فرض، تعریف {action=Index} را داریم، یعنی هر زمانیکه در URL درخواستی، قسمت action آن ذکر نشد، آن‌را با index جایگزین کن و این کنترلر دارای متد Index نیست. در ادامه اگر بخواهیم متد Hello را تبدیل به متد پیش فرض این کنترلر کنیم، می‌توان با استفاده از ویژگی Route به صورت ذیل عمل کرد:
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
  [Route("About")]
  public class AboutController : Controller
  {
   [Route("")]
   public ActionResult Hello()
   {
    return Content("Hello from DNT!");
   }
 
   [Route("SiteName")]
   public ActionResult SiteName()
   {
    return Content("DNT");
   }
  }
}
در اینجا با اولین Route تعریف شده، مشخص کرده‌ایم که اگر قسمت اول URL درخواستی معادل about بود، پردازش برنامه باید به این کنترلر هدایت شود. بدیهی است الزامی به یکی بودن نام Route، با نام کنترلر، وجود ندارد. همچنین Route تعریف شده‌ی با رشته‌ی خالی، به معنای مسیریابی پیش فرض است. یعنی اگر آدرس /about/ درخواست داده شد، اکشن متد پیش فرض آن، متد Hello خواهد بود. در این حالت، ذکر Route بعدی برای اکشن متد SiteName الزامی است و اگر این‌کار صورت نگیرد، به استثنای ذیل خواهیم رسید:
 AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:

Core1RtmEmptyTest.Controllers.AboutController.Hello (Core1RtmEmptyTest)
Core1RtmEmptyTest.Controllers.AboutController.SiteName (Core1RtmEmptyTest)
که عنوان کرده‌است در این حالت مشخص نیست که اکشن متد پیش فرض، کدام است.

روش بهتر و refactoring friendly آن نیز به صورت ذیل است:
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
  [Route("[controller]")]
  public class AboutController : Controller
  {
   [Route("")]
   public ActionResult Hello()
   {
    return Content("Hello from DNT!");
   }
 
   [Route("[action]")]
   public ActionResult SiteName()
   {
    return Content("DNT");
   }
  }
}
عموما مرسوم است که نام مسیریابی کنترلر همان نام کنترلر باشد و نام مسیریابی اکشن متد، همان نام اکشن متد مربوطه. به همین جهت می‌توان از توکن‌های ویژه‌ی [controller] و [action] نیز در اینجا استفاده کرد که دقیقا به همان نام کنترلر و اکشن متد متناظر با آن‌ها تفسیر خواهند شد. مزیت این‌کار این است که در صورت تغییر نام متدها یا کنترلرها، دیگر نیازی نیست تا نام‌های تعریف شده‌ی در ویژگی‌های Route را نیز تغییر داد.

یک نکته: در حین تعریف مسیریابی یک کنترلر می‌توان پیشوندهایی را نیز ذکر کرد؛ برای مثال:
 [Route("api/[controller]")]
وجود api در اینجا به این معنا است که از این پس تنها آدرس /api/about/ پردازش خواهد شد و اگر صرفا آدرس /about/ درخواست شود، با خطای 404 و یا یافت نشد، کار خاتمه می‌یابد.


تعریف قیود، برای مسیریابی‌های تعریف شده

فرض کنید به کنترلر About فوق، اکشن متد ذیل را که یک خروجی JSON را بازگشت می‌دهد، اضافه کرده‌ایم:
//[Route("/Users/{userid}")]
[Route("Users/{userid}")]
public IActionResult GetUsers(int userId)
{
    return Json(new { userId = userId });
}
در اینجا تعریف مسیریابی آن با users/ و user معانی کاملا متفاوتی را دارند. اگر مسیریابی Users/{userid}/ را تعریف کنیم، یعنی مسیر ذیل از ریشه‌ی سایت باید درخواست شود: http://localhost:7742/users/1
و اگر مسیریابی Users/{userid} را تعریف کنیم، یعنی این مسیریابی پس از ذکر کنترلر about، به عنوان یک اکشن متد آن مفهوم پیدا می‌کند:
http://localhost:7742/about/users/1
در هر دو حالت، ذکر پارامتر userid الزامی است (چون با ? مشخص نشده‌است)؛ مانند:
[Route("/Users/{userid:int?}")]
در اینجا اگر بخواهیم نوع پارامتر درخواستی را نیز دقیقا مشخص و مقید کنیم، می‌توان به صورت ذیل عمل کرد:
 [Route("Users/{userid:int}")]
اگر این کار را انجام ندهیم، با درخواست مسیر http://localhost:7742/dnt/about/users/test مقدار صفر به userId ارسال می‌شود (چون پارامتر test عددی نیست). اگر تنظیم فوق را انجام دهیم، کاربر خطای 404 را دریافت می‌کند.

قیودی را که در اینجا می‌توان ذکر کرد به شرح زیر هستند:
• alpha - معادل است با  (a-z, A-Z).
• bool - برای تطابق با مقادیر بولی.
• datetime - برای تطابق با تاریخ میلادی.
• decimal - برای تطابق با ورودی‌های اعشاری.
• double - برای تطابق با اعداد اعشاری 64 بیتی.
• float - برای تطابق با اعداد اعشاری 32 بیتی.
• guid - برای تطابق با GUID ها
• int - برای تطابق با اعداد صحیح 32 بیتی.
• length - برای تعیین طول رشته.
• long - برای تطابق با اعداد صحیح 64 بیتی.
• max - برای ذکر حداکثر مقدار یک عدد صحیح.
• maxlength - جهت ذکر حداکثر طول رشته‌ی مجاز ورودی.
• min - برای ذکر حداقل مقدار یک عدد صحیح.
• minlength - جهت ذکر حداقل طول رشته‌ی مجاز ورودی.
• range - ذکر بازه‌ی اعداد صحیح مجاز.
• regex - ذکر یک عبارت با قاعده جهت مشخص سازی الگوی قابل پذیرش.

برای ترکیب چندین قید مختلف نیز می‌توان از : استفاده کرد:
 [Route("/Users/{userid:int:max(1000):min(10)}")]


ذکر نام Route برای ساده سازی تعریف آدرسی به آن

در حین تعریف یک Route می‌توان نام دلخواهی را نیز به آن انتساب داد (همانند نام default مسیریابی ثبت شده‌ی در کلاس آغازین برنامه):
 [Route("/Users/{userid:int}", Name="GetUserById")]
مزیت آن این است که اکنون برای اشاره‌ی به این مسیریابی خاص می‌توان از این نام تعریف شده استفاده کرد:
 string uri = Url.Link("GetUserById", new { userid = 1 });
پارامتر اول ذکر شده، نام مسیریابی و پارامتر دوم، پارامترهای مرتبط با این مسیریابی هستند.


مشخص سازی ترتیب پردازش مسیریابی‌ها

ترتیب مسیریابی‌های ثبت شده‌ی در کلاس آغازین برنامه، همان ترتیب افزوده شدن و ذکر آن‌ها است.
در اینجا می‌توان از خاصیت order نیز استفاده کرد و اعداد کوچکتر، ابتدا پردازش می‌شوند (مقدار پیش فرض آن نیز صفر است):
 [Route("/Users/{userid:int}", Name = "GetUserById", Order = 1)]


امکان تعریف قیود سفارشی

اگر قیودی که تا اینجا ذکر شدند، برای کار شما مناسب نبودند و نیاز بود تا الگوریتم خاصی را جهت محدود سازی دسترسی به یک مسیریابی خاص پیاده سازی کنید، می‌توان به صورت ذیل عمل کرد:
using System;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
 
namespace Core1RtmEmptyTest
{
  public class CustomRouteConstraint : IRouteConstraint
  {
   public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values,
    RouteDirection routeDirection)
   {
    object value;
    if (!values.TryGetValue(routeKey, out value) || value == null)
    {
      return false;
    }
 
    long longValue;
    if (value is long)
    {
      longValue = (long)value;
      return longValue != 10;
    }
 
    var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
    if (long.TryParse(valueString, NumberStyles.Integer,
      CultureInfo.InvariantCulture, out longValue))
    {
      return longValue != 10;
    }
    return false;
   }
  }
}
در اینجا یک کلاس جدید را که اینترفیس IRouteConstraint را پیاده سازی می‌کند تعریف کرده‌ایم:
public class CustomRouteConstraint : IRouteConstraint
سپس در متد match آن بررسی کرده‌ایم که اگر userid=10 بود، خطای 404 صادر شود.
در آخر برای ثبت و معرفی آن باید به متد ConfigureServices کلاس آغازین برنامه مراجعه کرد:
public void ConfigureServices(IServiceCollection services)
{
    services.AddRouting(options =>options.ConstraintMap.Add("Custom", typeof(CustomRouteConstraint)));
پس از آن، این نام جدید ثبت شده‌ی در اینجا، به نحو ذیل قابل استفاده است:
 [Route("/Users/{userid:int:custom}")]
به این ترتیب userid باید از نوع int بوده و همچنین قید custom را نیز پوشش دهد (یعنی userid=10 نباشد).

یک نکته:  اگر به سورس ASP.NET Core مراجعه کنید ، تمام قیودی را که پیشتر نام بردیم (مانند int، guid و امثال آن) نیز به همین روش تعریف و پیشتر ثبت شده‌اند.


معرفی بسته‌ی نیوگت Microsoft.AspNetCore.SpaServices

مسیریابی‌های پیش فرض ASP.NET Core با مسیریابی‌های برنامه‌های SPA مانند AngularJS (و امثال آن) تداخل دارند؛ از این جهت که درخواست‌های رسیده‌ی به سرور، ابتدا به موتور پردازشی ASP.NET وارد می‌شوند و اگر یافت نشدند، کاربر با پیام 404 مواجه خواهد شد و دیگر در اینجا برنامه به مسیریابی خاص مثلا AngularJS 2.0 هدایت نمی‌شود.
برای این موارد مرسوم است که یک fallback route را در انتهای مسیریابی‌های موجود اضافه کنند (به آن catch all هم می‌گویند)
app.UseMvc(routes =>
{
  routes.MapRoute(
   name: "default",
   template: "{controller=Home}/{action=Index}/{id?}");
 
  routes.MapRoute(
   name: "spa-fallback",
   template: "{*url}",
   defaults: new { controller = "Home", action = "Index" });
});
در اینجا هر درخواستی که با مسیریابی default تطابق نداشت، توسط الگوی عمومی {url*} پردازش می‌شود و این پردازش در نهایت سبب راه اندازی برنامه‌ی SPA می‌گردد. اما مشکل اینجا است که برای فایل‌های استاتیک غیرموجود مانند تصاویر، فایل‌های js و css نیز خروجی HTML ایی خواهیم داشت؛ بجای خروجی 404 و یافت نشد.
برای حل این مشکل مایکروسافت بسته‌ای را به نام Microsoft.AspNetCore.SpaServices ارائه داده است.
برای افزودن آن بر روی گره references کلیک راست کرده و گزینه‌ی manage nuget packages را انتخاب کنید. سپس در برگه‌ی browse آن Microsoft.AspNetCore.SpaServices را جستجو کرده و نصب نمائید:


انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{
    "dependencies": {
       //same as before  
       "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000007"
 },
پس از بازیابی و نصب آن، اکنون catch all را حذف کرده و با یک سطر routes.MapSpaFallbackRoute ذیل جایگزین کنید:
app.UseMvc(routes =>
{
  routes.MapRoute(
   name: "default",
   template: "{controller=Home}/{action=Index}/{id?}");
 
  routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});
و برای یادآوری مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایل‌های استاتیک» در AngularJS 2.0، علاوه بر عمومی کردن پوشه‌ی wwwroot توسط UseFileServer نیاز است پوشه‌ی node_modules را هم با تنظیمات ذیل عمومی کرد و در معرض دید عموم قرار داد (جایی که بسته‌های node.js نصب می‌شوند):
// Serve wwwroot as root
app.UseFileServer();
 
// Serve /node_modules as a separate root (for packages that use other npm modules client side)
app.UseFileServer(new FileServerOptions
{
  // Set root of file server
  FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "node_modules")),
  // Only react to requests that match this path
  RequestPath = "/node_modules",
  // Don't expose file system
  EnableDirectoryBrowsing = false
});
مطالب
استفاده از لوسین برای برجسته سازی عبارت جستجو شده در نتایج حاصل
قسمت جستجوی سایت جاری رو با استفاده از لوسین بازنویسی کردم. خلاصه‌ای از نحوه انجام این‌کار رو در ادامه ملاحظه خواهید کرد:

1) دریافت کتابخانه‌های لازم
نیاز به کتابخانه‌های Lucene.NET و همچنین Lucene.Net Contrib است که هر دو مورد را به سادگی توسط NuGet می‌توانید دریافت و نصب کنید.
Highlighter استفاده شده، در کتابخانه Lucene.Net Contrib قرار دارد. به همین جهت این مورد را نیز باید جداگانه دریافت کرد.


2) تهیه منبع داده
در اینجا جهت سادگی کار فرض کنید که لیستی از مطالب را به فرمت زیر دراختیار داریم:
public class Post
{
    public int Id { set; get; }
    public string Title { set; get; }
    public string Body { set; get; }
}
تفاوتی نمی‌کند که از چه منبع داده‌ای استفاده می‌کنید. آیا قرار است یک سری فایل متنی ساده موجود در یک پوشه را ایندکس کنید یا تعدادی رکورد بانک اطلاعاتی؛ از NHibernate استفاده می‌کنید یا از Entity framework و یا از ADO.NET. کتابخانه Lucene مستقل است از منبع داده مورد استفاده و تنها اطلاعاتی با فرمت شیء Document معرفی شده به آن‌را می‌شناسد.


3) تبدیل اطلاعات به فرمت Lucene.NET
همانطور که عنوان شد نیاز است هر رکورد از اطلاعات خود را به شیء Document نگاشت کنیم. نمونه‌ای از اینکار را در متد ذیل مشاهده می‌نمائید:
static Document MapPostToDocument(Post post)
{
    var postDocument = new Document();
    postDocument.Add(new Field("Id", post.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));
    postDocument.Add(new Field("Title", post.Title, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
    postDocument.Add(new Field("Body", post.Body, Field.Store.YES, Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));
    return postDocument;
}
این متد وهله‌ای از شیء Post را دریافت کرده و آن‌را تبدیل به یک سند Lucene می‌کند.
کار با ایجاد یک وهله از شیء Document شروع شده و سپس اطلاعات به صوت فیلدهایی به این سند اضافه می‌شوند.

توضیحات آرگومان‌های مختلف سازنده کلاس Field:
- در ابتدا نام فیلد مورد نظر ذکر می‌گردد.
- سپس مقدار متناظر با آن فیلد، به صورت رشته باید معرفی شود.
- آرگومان سوم آن مشخص می‌کند که اصل اطلاعات نیز علاوه بر ایندکس شدن باید در فایل‌های Lucene ذخیره شوند یا خیر. توسط Field.Store.YES مشخص می‌کنیم که بله؛ علاقمندیم تا اصل اطلاعات نیز از طریق Lucene قابل بازیابی باشند. این مورد جهت نمایش سریع نتایج جستجوها می‌تواند مفید باشد. اگر قرار نیست اطلاعاتی را از این فیلد خاص به کاربر نمایش دهید می‌توانید از گزینه Field.Store.NO استفاده کنید. همچنین امکان فشرده سازی اطلاعات ذخیره شده با انتخاب گزینه Field.Store.COMPRESS نیز میسر است.
- توسط آرگومان چهارم آن تعیین خواهیم کرد که اطلاعات فیلد مورد نظر ایندکس شوند یا خیر. مقدار Field.Index.NOT_ANALYZED سبب عدم ایندکس شدن فیلد Id می‌شوند (چون قرار نیست روی id در قسمت جستجوی عمومی سایت، جستجویی صورت گیرد). به کمک مقدار Field.Index.ANALYZED، مقدار معرفی شده، ایندکس خواهد شد.
- پارامتر پنجم آن‌را جهت سرعت عمل در نمایان سازی/برجسته کردن و highlighting عبارات جستجو شده در متن‌های یافت شده معرفی کرده‌ایم. الگوریتم‌های متناظر با این روش در فایل‌های Lucene.Net Contrib قرار دارند.


یک نکته
اگر اطلاعاتی را که قرار است ایندکس کنید از نوع HTML می‌باشند، بهتر است تمام تگ‌های آن‌را پیش از افزودن به لوسین حذف کنید. به این ترتیب نتایج جستجوی دقیق‌تری را می‌توان شاهد بود. برای این منظور می‌توان از متد ذیل کمک گرفت:
public static string RemoveHtmlTags(string text)
{
    return string.IsNullOrEmpty(text) ? string.Empty : Regex.Replace(text, @"<(.|\n)*?>", string.Empty);
}


4) تهیه Full text index به کمک Lucene.NET
تا اینجا توانستیم اطلاعات خود را به فرمت اسناد لوسین تبدیل کنیم. اکنون ثبت و تبدیل آن‌ها به فایل‌های Full text search لوسین به سادگی زیر است:
static readonly Lucene.Net.Util.Version _version = Lucene.Net.Util.Version.LUCENE_29;
public static void CreateIdx(IEnumerable<Post> dataList)
{
    var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex"));
    var analyzer = new StandardAnalyzer(_version);
    using (var writer = new IndexWriter(directory, analyzer, create: true, mfl: IndexWriter.MaxFieldLength.UNLIMITED))
    {
         foreach (var post in dataList)
        {
            writer.AddDocument(MapPostToDocument(post));
        }

        writer.Optimize();
        writer.Commit();
        writer.Close();
        directory.Close();
    }
}
ابتدا محل ذخیره سازی فایل‌های full text search مشخص می‌شوند. سپس آنالیز کننده اطلاعات باید معرفی شود. در ادامه به کمک این اطلاعات، شیء IndexWriter ایجاد و مستندات لوسین به آن اضافه می‌شوند. در آخر، این اطلاعات بهینه سازی شده و ثبت نهایی صورت خواهد گرفت.
ذکر version در اینجا ضروری است؛ از این جهت که اگر ایندکسی با فرمت مثلا LUCENE_29 تهیه شود ممکن است با نگارش بعدی این کتابخانه سازگار نباشد و در صورت ارتقاء، نتایج جستجوی انجام شده، کاملا بی‌ربط نمایش داده شوند. با ذکر صریح نگارش، دیگر این اتفاق رخ نخواهد داد.


نکته
StandardAnalyzer توکار لوسین، امکان دریافت لیستی از واژه‌هایی که نباید ایندکس شوند را نیز دارا است. اطلاعات بیشتر در اینجا.


5) به روز رسانی ایندکس‌ها
به کمک سه متد ذیل می‌توان اطلاعات ایندکس‌های موجود را به روز یا حذف کرد:
public static void UpdateIndex(Post post)
{
        var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex"));
        var analyzer = new StandardAnalyzer(_version);
        using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED))
        {
            var newDoc = MapPostToDocument(post);

             indexWriter.UpdateDocument(new Term("Id", post.Id.ToString()), newDoc);
             indexWriter.Commit();
             indexWriter.Close();
             directory.Close();
         }
}

public static void DeleteIndex(Post post)
{
         var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex"));
         var analyzer = new StandardAnalyzer(_version);
         using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED))
         {
             indexWriter.DeleteDocuments(new Term("Id", post.Id.ToString()));
             indexWriter.Commit();
             indexWriter.Close();
             directory.Close();
          }
}

public static void AddIndex(Post post)
{
      var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex"));
      var analyzer = new StandardAnalyzer(_version, getStopWords());
      using (var indexWriter = new IndexWriter(directory, analyzer, create: false, mfl: IndexWriter.MaxFieldLength.UNLIMITED))
      {
           var searchQuery = new TermQuery(new Term("Id", post.Id.ToString()));
           indexWriter.DeleteDocuments(searchQuery);

            var newDoc = MapPostToDocument(post);
            indexWriter.AddDocument(newDoc);
            indexWriter.Commit();
            indexWriter.Close();
            directory.Close();
        }
}
تنها نکته مهم این متدها، استفاده از متد IndexWriter با پارامتر create مساوی false است. به این ترتیب فایل‌های موجود بجای از نو ساخته شدن، به روز خواهند شد.
محل فراخوانی این متدها هم می‌تواند در کنار متدهای به روز رسانی اطلاعات اصلی در بانک اطلاعاتی برنامه باشند. اگر رکوردی اضافه یا حذف شده، ایندکس متناظر نیز باید به روز شود.


6) جستجو در اطلاعات ایندکس شده و نمایش آن‌ها به همراه نمایان/برجسته سازی عبارات جستجو شده
قسمت نهایی کار با لوسین و اطلاعات ایندکس‌های تهیه شده، کوئری گرفتن از آن‌ها است. متدهای کامل مورد نیاز را در ذیل مشاهده می‌کنید:

public static void Query(string term)
{
     var directory = FSDirectory.Open(new DirectoryInfo(Environment.CurrentDirectory + "\\LuceneIndex"));
     using (var searcher = new IndexSearcher(directory, readOnly: true))
     {
          var analyzer = new StandardAnalyzer(_version);
          var parser = new MultiFieldQueryParser(_version, new[] { "Body", "Title" }, analyzer);
          var query = parseQuery(term, parser);
          var hits = searcher.Search(query, 10).ScoreDocs;

          if (hits.Length == 0)
          {
               term = searchByPartialWords(term);
               query = parseQuery(term, parser);
               hits = searcher.Search(query, 10).ScoreDocs;
           }

           FastVectorHighlighter fvHighlighter = new FastVectorHighlighter(true, true);
           foreach (var scoreDoc in hits)
           {
               var doc = searcher.Doc(scoreDoc.doc);
               string bestfragment = fvHighlighter.GetBestFragment(
                                fvHighlighter.GetFieldQuery(query),
                                searcher.GetIndexReader(),
                                docId: scoreDoc.doc,
                                fieldName: "Body",
                                fragCharSize: 400);
                var id = doc.Get("Id");
                var title = doc.Get("Title");
                var score = scoreDoc.score;
                Console.WriteLine(bestfragment);
            }

            searcher.Close();
            directory.Close();
      }
   }

   private static Query parseQuery(string searchQuery, QueryParser parser)
   {
       Query query;
        try
        {
            query = parser.Parse(searchQuery.Trim());
        }
        catch (ParseException)
        {
            query = parser.Parse(QueryParser.Escape(searchQuery.Trim()));
        }
        return query;
   }

   private static string searchByPartialWords(string bodyTerm)
   {
       bodyTerm = bodyTerm.Replace("*", "").Replace("?", "");
       var terms = bodyTerm.Trim().Replace("-", " ").Split(' ')
                                .Where(x => !string.IsNullOrEmpty(x))
                                .Select(x => x.Trim() + "*");
       bodyTerm = string.Join(" ", terms);
       return bodyTerm;
   }
توضیحات:
اکثر سایت‌ها را که بررسی کنید، جستجوی بر روی یک فیلد را توضیح داده‌اند. در اینجا نحوه جستجو بر روی چند فیلد را به کمک MultiFieldQueryParser ملاحظه می‌کنید.
نکته‌ی مهمی را هم که در اینجا باید به آن دقت داشت، حساس بودن لوسین به کوچکی و بزرگی نام فیلدهای معرفی شده است و در صورت عدم رعایت این مساله، جستجوی شما نتیجه‌ای را دربر نخواهد داشت.
در ادامه برای parse اطلاعات، از متد کمکی parseQuery استفاده شده است. ممکن است به ParseException بخاطر یک سری حروف خاص بکارگرفته شده در عبارات مورد جستجو برسیم. در اینجا می‌توان توسط متد QueryParser.Escape، اطلاعات دریافتی را اصلاح کرد.
سپس نحوه استفاده از کوئری تهیه شده و متد Search را ملاحظه می‌کنید. در اینجا بهتر است تعداد رکوردهای بازگشت داده شده را تعیین کرد (به کمک آرگومان دوم متد جستجو) تا بی‌جهت سرعت عملیات را پایین نیاورده و همچنین مصرف حافظه سیستم را نیز بالا نبریم.
ممکن است تعداد hits یا نتایج حاصل صفر باشد؛ بنابراین بد نیست خودمان دست به کار شده و به کمک متد searchByPartialWords، ورودی کاربر را بر اساس زبان جستجوی ویژه لوسین اندکی بهینه کنیم تا بتوان به نتایج بهتری دست یافت.
در آخر نحوه کار با  ScoreDocs یافت شده را ملاحظه می‌کنید. اگر محتوای فیلد را در حین ایندکس سازی ذخیره کرده باشیم، به کمک متد doc.Get می‌توان به اطلاعات کامل آن نیز دست یافت.
همچنین نکته دیگری را که در اینجا می‌توان ملاحظه کرد استفاده از FastVectorHighlighter می‌باشد. به کمک این Highlighter ویژه می‌توان نتایج جستجو را شبیه به نتایج نمایش داده شده توسط موتور جستجوی گوگل درآورد. برای مثال اگر شخصی ef code first را جستجو کرد، توسط متد GetBestFragment، بهترین جزئی که شامل بیشترین تعداد حروف جستجو شده است، یافت گردیده و همچنین به کمک تگ‌های B، ضخیم نمایش داده خواهند شد.

 
نظرات مطالب
TwitterBootstrapMVC
- نسخه سایت خودش سی روزه هست. باید یک فایل مجوز هم براش دریافت کنی، اینجا: https://www.twitterbootstrapmvc.com/Download یا نسخه پیوست شده از متن رو دریافت و استفاده کن.
- برای MVC5 در اینجا: http://www.nuget.org/packages/TwitterBootstrapMVC5/ 
- برای MVC4 در اینجا: http://www.nuget.org/packages/TwitterBootstrapMVC/ 
سایر موارد مرتبط
نظرات مطالب
هزینه استفاده از دات نت فریم ورک چقدر است؟
مجوز رو می‌تونید اینجا مطالعه کنید:
http://www.opensource.org/licenses/ms-pl.html
و زمانیکه در این سایت مطرح شده یعنی پذیرفته شده و معتبر است.
توضیحات بیشتر هم در این سؤال و جواب:
http://programmers.stackexchange.com/questions/85301/understanding-the-microsoft-public-license-ms-pl