Solr搜索基础
本例我们使用类库和代码均来自:
http://www.cnblogs.com/TerryLiang/archive/2011/04/17/2018962.html
使用C#来模拟搜索、索引建立、删除、更新过程,Demo截图如下:
一、准备工作:
先准备一个实体类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快速查询,如下图:
创建索引实际上就是用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管理界面查询:
注意:每次使用管理器搜索时,右上角都会显示搜索使用的URL:
http://localhost:8080/solr/collection1/select?q=*%3A*&wt=json&indent=true
这些参数的含义较为简单可以查询一些文档获取信息。
2. 创建查询
查询其实就是提交一个请求给服务器,等待服务器将结果返回的过程,可以使用任何语言只要能发起请求并接受结果即可,这里我们使用客户端。
先创建一个ISolrQuery对象,传入搜索关键字,关键字的构建方法可以从Solr管理界面推理出来:
假如我们要查询name中带“苹果”的信息,我们需要在管理界面输入:
如果想知道Solr是如何构建查询的话可以勾选DebugQuery选项,得到调试信息:
意思是只在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);
这样就完成了一个最基本的创建索引,更新删除索引和查询的过程,本例查询速度并没有直接操作管理界面那么快,原因在于序列化和反序列化,延续上述提到的:任何语言只要能发起请求和接收响应即可以查询,可以避免这个过程,提高查询效率。