Solr搜尋基礎

石曼迪發表於2014-11-30

 

本例我們使用類庫和程式碼均來自:

http://www.cnblogs.com/TerryLiang/archive/2011/04/17/2018962.html

使用C#來模擬搜尋、索引建立、刪除、更新過程,Demo截圖如下:

wps6401.tmp

image

一、準備工作:


先準備一個實體類Product:

  public  class Product
    {
      public string ID { get; set; }
      public string Name { get; set; }
      public String[] Features { get; set; }
      public float Price { get; set; }
      public int Popularity { get; set; }
      public bool InStock { get; set; }
      public DateTime Incubationdate_dt { get; set; }
    }

再為這個實體類建立一個反序列化類ProductDeserializer:

  class ProductDeserializer : IObjectDeserializer<Product>
  {
      public IEnumerable<Product> Deserialize(SolrDocumentList result)
      {
          foreach (SolrDocument doc in result)
          {
              yield return new Product()
              {
                  ID = doc["id"].ToString(),
                  Name = doc["name"].ToString(),
                  Features = (string[])((ArrayList)doc["features"]).ToArray(typeof(string)),
                  Price = (float)doc["price"],
                  Popularity = (int)doc["popularity"],
                  InStock = (bool)doc["inStock"],
                  Incubationdate_dt = (DateTime)doc["incubationdate_dt"]
              };
          }
      }
  }

為專案引入EasyNet.Solr.dll。

二、建立搜尋:


執行Solr客戶端初始化操作:

        #region 初始化
       static List<SolrInputDocument> docs = new List<SolrInputDocument>();
        static OptimizeOptions optimizeOptions = new OptimizeOptions();
        static ISolrResponseParser<NamedList, ResponseHeader> binaryResponseHeaderParser = new BinaryResponseHeaderParser();
        static IUpdateParametersConvert<NamedList> updateParametersConvert = new BinaryUpdateParametersConvert();
        static ISolrUpdateConnection<NamedList, NamedList> solrUpdateConnection = new SolrUpdateConnection<NamedList, NamedList>() { ServerUrl = "http://localhost:8080/solr/" };
        static ISolrUpdateOperations<NamedList> updateOperations = new SolrUpdateOperations<NamedList, NamedList>(solrUpdateConnection, updateParametersConvert) { ResponseWriter = "javabin" };

        static ISolrQueryConnection<NamedList> connection = new SolrQueryConnection<NamedList>() { ServerUrl = "http://localhost:8080/solr/" };
        static ISolrQueryOperations<NamedList> operations = new SolrQueryOperations<NamedList>(connection) { ResponseWriter = "javabin" };

        static IObjectDeserializer<Product> exampleDeserializer = new ProductDeserializer();
        static ISolrResponseParser<NamedList, QueryResults<Product>> binaryQueryResultsParser = new BinaryQueryResultsParser<Product>(exampleDeserializer);
        #endregion

我們先模擬一個資料來源,這裡內建一些資料作為示例:

            List<Product> products = new List<Product>();
            Product juzi = new Product
            {
                ID = "SOLR1000",
                Name = "浙江桔子",
                Features = new String[] { 
                    "色香味兼優", 
                    "既可鮮食,又可加工成以果汁",
                    "果實營養豐富"},
                Price = 2.0f,
                Popularity = 100,
                InStock = true,
                Incubationdate_dt = new DateTime(2006, 1, 17, 0, 0, 0, DateTimeKind.Utc)
            };
            products.Add(juzi);

            var doc = new SolrInputDocument();
            doc.Add("id", new SolrInputField("id", juzi.ID));
            doc.Add("name", new SolrInputField("name", juzi.Name));
            doc.Add("features", new SolrInputField("features", juzi.Features));
            doc.Add("price", new SolrInputField("price", juzi.Price));
            doc.Add("popularity", new SolrInputField("popularity", juzi.Popularity));
            doc.Add("inStock", new SolrInputField("inStock", juzi.InStock));
            doc.Add("incubationdate_dt", new SolrInputField("incubationdate_dt", juzi.Incubationdate_dt));

            docs.Add(doc);

            Product pingguo = new Product
            {
                ID = "SOLR1002",
                Name = "陝西蘋果",
                Features = new String[] { 
                "味道甜美",
                "光澤鮮豔", 
                "營養豐富"
            },
                Price = 1.7f,
                Popularity = 50,
                InStock = true,
                Incubationdate_dt = new DateTime(2010, 1, 17, 0, 0, 0, DateTimeKind.Utc)
            };
            products.Add(pingguo);
            var doc2 = new SolrInputDocument();
            doc2.Add("id", new SolrInputField("id", pingguo.ID));
            doc2.Add("name", new SolrInputField("name", pingguo.Name));
            doc2.Add("features", new SolrInputField("features", pingguo.Features));
            doc2.Add("price", new SolrInputField("price", pingguo.Price));
            doc2.Add("popularity", new SolrInputField("popularity", pingguo.Popularity));
            doc2.Add("inStock", new SolrInputField("inStock", pingguo.InStock));
            doc2.Add("incubationdate_dt", new SolrInputField("incubationdate_dt", pingguo.Incubationdate_dt));

            docs.Add(doc2);

            dataGridView1.DataSource = products;

同時將這些資料新增到List<SolrInputDocument>中,SolrInputDocument是TerryLiang編寫的文件交換實體,可以在他提供的原始碼中看到。

1. 建立索引:

  建立索引是指將原始資料傳遞給Solr,然後在Solr目錄下建立指定格式檔案,這些檔案能夠被Solr快速查詢,如下圖:

wps6412.tmp

建立索引實際上就是用Update將資料POST給collection1,程式碼如下:

            var result = updateOperations.Update("collection1", "/update", new UpdateOptions() { OptimizeOptions = optimizeOptions, Docs = docs });
            var header = binaryResponseHeaderParser.Parse(result);

            lbl_info.Text= string.Format("Update Status:{0} QTime:{1}", header.Status, header.QTime);

索引成功後我們可以在Solr管理介面查詢:

wps6422.tmp

注意:每次使用管理器搜尋時,右上角都會顯示搜尋使用的URL:

http://localhost:8080/solr/collection1/select?q=*%3A*&wt=json&indent=true

這些引數的含義較為簡單可以查詢一些文件獲取資訊。

2. 建立查詢

  查詢其實就是提交一個請求給伺服器,等待伺服器將結果返回的過程,可以使用任何語言只要能發起請求並接受結果即可,這裡我們使用客戶端。

先建立一個ISolrQuery物件,傳入搜尋關鍵字,關鍵字的構建方法可以從Solr管理介面推理出來:

假如我們要查詢name中帶“蘋果”的資訊,我們需要在管理介面輸入:

wps6443.tmp

如果想知道Solr是如何構建查詢的話可以勾選DebugQuery選項,得到除錯資訊:

wps6444.tmp

意思是隻在Name這個列中檢索。

所以我們程式碼中需要這麼寫:

ISolrQuery query = new SolrQuery("name:"+keyWord);

安全問題自行考慮。

但是如果要查詢全部就簡單多了:

ISolrQuery query = SolrQuery.All;

將查詢條件傳送給伺服器之後再把伺服器返回的資料還原成物件顯示出來即完成了一次查詢操作,具體操作程式碼如下:

            ISolrQuery query = SolrQuery.All;
            if (!string.IsNullOrWhiteSpace(keyWord))
            {
                query = new SolrQuery("name:"+keyWord);
            }
            var result = operations.Query("collection1", "/select", query, null);
            var header = binaryResponseHeaderParser.Parse(result);

            var examples = binaryQueryResultsParser.Parse(result);

            lbl_info.Text= string.Format("Query Status:{0} QTime:{1} Total:{2}", header.Status, header.QTime, examples.NumFound);
            dataGridView1.DataSource = examples.ToList();

3. 增量索引

   實際上經常會有資料是新增或者改變的,那麼我們就需要及時更新索引便於查詢出新資料,就需要增量索引。這和初次索引一樣,如果你想更新原有資料,那麼將新資料再次提交一次即可,如果想增加提交不同資料即可。資料判斷標準為id,這是個配置項,可以在中D:\apache-tomcat-7.0.57\webapps\solr\solr_home\collection1\conf\schema.xml找到:

<field name="id" type="string" indexed="true" stored="true" required="true" multiValued="false" />

可以理解為主鍵。

程式碼如下:

             var docs = new List<SolrInputDocument>();
             Product hetao = new Product
             {
                 ID = "SOLR1003",
                 Name = "陝西山核桃",
                 Features = new String[] { 
                "營養好吃",
                "微量元素豐富", 
                "補腦"
            },
                 Price = 1.7f,
                 Popularity = 50,
                 InStock = true,
                 Incubationdate_dt = new DateTime(2010, 1, 17, 0, 0, 0, DateTimeKind.Utc)
             };
             var doc2 = new SolrInputDocument();
             doc2.Add("id", new SolrInputField("id", hetao.ID));
             doc2.Add("name", new SolrInputField("name", hetao.Name));
             doc2.Add("features", new SolrInputField("features", hetao.Features));
             doc2.Add("price", new SolrInputField("price", hetao.Price));
             doc2.Add("popularity", new SolrInputField("popularity", hetao.Popularity));
             doc2.Add("inStock", new SolrInputField("inStock", hetao.InStock));
             doc2.Add("incubationdate_dt", new SolrInputField("incubationdate_dt", hetao.Incubationdate_dt));
             docs.Clear();
             docs.Add(doc2);

             var result = updateOperations.Update("collection1", "/update", new UpdateOptions() { OptimizeOptions = optimizeOptions, Docs = docs });
             var header = binaryResponseHeaderParser.Parse(result);

             lbl_info.Text= string.Format("Update Status:{0} QTime:{1}", header.Status, header.QTime);

4. 刪除索引

   和資料庫刪除一樣,當然按照主鍵進行刪除。傳入刪除Option同時帶入主鍵名和主鍵值傳送給伺服器即可。

具體操作程式碼如下:

              var result = updateOperations.Update("collection1", "/update", new UpdateOptions() { OptimizeOptions = optimizeOptions, DelById = new string[] { id } });
              var header = binaryResponseHeaderParser.Parse(result);

              lbl_info.Text=string.Format("Update Status:{0} QTime:{1}", header.Status, header.QTime);

這樣就完成了一個最基本的建立索引,更新刪除索引和查詢的過程,本例查詢速度並沒有直接操作管理介面那麼快,原因在於序列化和反序列化,延續上述提到的:任何語言只要能發起請求和接收響應即可以查詢,可以避免這個過程,提高查詢效率。

程式碼下載

 

相關文章