一步一步跟我学习lucene(17)---lucene搜索之expressions表达式处理

有时候我们在做lucene的结果展示的时候可能需要对多个列的内容进行计算,根据多个field对应的值做数值方面的运算。

lucene自4.6版本起,提供了用于运算的expression模块;

expression分为两部分:

  • org.apache.lucene.expressions:提供了字段绑定和相关的表达式参数传递的功能;
  • org.apache.lucene.expressions.js:提供了表达式定义的功能。

Expression类使用示例

Expression是提供document的运算的支持类;

我们的运算表达式和其绑定内容通常类似于如下:

   // compile an expression:
   Expression expr = JavascriptCompiler.compile("sqrt(_score) + ln(popularity)");

   // SimpleBindings just maps variables to SortField instances
   SimpleBindings bindings = new SimpleBindings();
   bindings.add(new SortField("_score", SortField.Type.SCORE));
   bindings.add(new SortField("popularity", SortField.Type.INT));

   // create a sort field and sort by it (reverse order)
   Sort sort = new Sort(expr.getSortField(bindings, true));
   Query query = new TermQuery(new Term("body", "contents"));
   searcher.search(query, null, 10, sort);

如上所示,我们对document中的_score和popularity两个字段进行值的运算,这里是对_score开平方之后和popularity的对数运算求和,运算方式的定义在第一行;

下边有定义了SimpleBindings,binding主要是对运算的数据进行数据绑定;

最终的查询结果是根据以上的运算结果采取倒排序的方式

表达式说明

表达式的构造可以采用如下的几种来进行组合:

  • 数值型的
  • + - * / %等运算符
  • 移位运算符:| & ^ ~ << >> >>>
  • 布尔运算符(包括三目运算符):    
    && || ! ?:
  • 比较运算符:< <= == >= >
  • 数学运算函数:abs ceil exp floor ln log10 logn max min sqrt pow
  • 三角运算函数:acosh acos asinh asin atanh atan atan2 cosh cos sinh sin tanh tan
  • haversin公式
  • min,max函数

代码示例

我写了一个测试程序,模拟长方形的运算并排序;

面积倒序排序;

当面积相同时,按宽度倒序,长度倒序;

周长倒序排序;

周长相同时,按宽度倒序,长度倒序;

示例程序如下:

package com.lucene.expression;

import java.io.IOException;
import java.nio.file.Paths;
import java.text.ParseException;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.IntField;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.expressions.SimpleBindings;
import org.apache.lucene.expressions.js.JavascriptCompiler;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;

public class LuceneExpressionTest{
	@Test
	public void index(){
		try {
			Directory directory = FSDirectory.open(Paths.get("index"));
			Analyzer analyzer = new StandardAnalyzer();
			IndexWriterConfig config = new IndexWriterConfig(analyzer);
			config.setOpenMode(OpenMode.CREATE_OR_APPEND);
			IndexWriter writer = new IndexWriter(directory, config);

			Document doc = new Document();
			//模拟长方形
			doc.add(new IntField("width", 3,Field.Store.YES));
			doc.add(new IntField("longth", 4,Field.Store.YES));
			doc.add(new NumericDocValuesField("width", 3));
			doc.add(new NumericDocValuesField("longth", 4));
			writer.addDocument(doc);

			Document doc1 = new Document();
			doc1.add(new IntField("width", 2,Field.Store.YES));
			doc1.add(new IntField("longth", 5,Field.Store.YES));
			doc1.add(new NumericDocValuesField("width", 2));
			doc1.add(new NumericDocValuesField("longth", 5));
			writer.addDocument(doc1);

			Document doc2 = new Document();
			doc2.add(new IntField("width", 2,Field.Store.YES));
			doc2.add(new IntField("longth", 6,Field.Store.YES));
			doc2.add(new NumericDocValuesField("width", 2));
			doc2.add(new NumericDocValuesField("longth", 6));
			writer.addDocument(doc2);

			writer.commit();
			writer.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * 面积比较
	 */
	@Test
	public void testAcreage(){
		try {
			Expression expr = JavascriptCompiler.compile("width*longth");

			SimpleBindings bindings = new SimpleBindings();
			bindings.add(new SortField("width", SortField.Type.INT));
			bindings.add(new SortField("longth", SortField.Type.INT));

			Sort sort = new Sort(expr.getSortField(bindings, true));
			Query query = new MatchAllDocsQuery();

			Directory directory = FSDirectory.open(Paths.get("index"));
			IndexReader reader = DirectoryReader.open(directory);
			IndexSearcher searcher = new IndexSearcher(reader);
			TopDocs docs = searcher.search(query, 10, sort);
			for (ScoreDoc scoreDoc : docs.scoreDocs) {
				System.out.println(searcher.doc(scoreDoc.doc));
			}
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	/**
	 * 周长比较
	 */
	@Test
	public void testCircum(){
		try {
			Expression expr = JavascriptCompiler.compile("width+longth+sqrt(pow(width,2)+pow(longth,2))");

			SimpleBindings bindings = new SimpleBindings();
			bindings.add(new SortField("width", SortField.Type.INT));
			bindings.add(new SortField("longth", SortField.Type.INT));

			Sort sort = new Sort(expr.getSortField(bindings, true));
			Query query = new MatchAllDocsQuery();

			Directory directory = FSDirectory.open(Paths.get("index"));
			IndexReader reader = DirectoryReader.open(directory);
			IndexSearcher searcher = new IndexSearcher(reader);
			TopDocs docs = searcher.search(query, 10, sort);
			for (ScoreDoc scoreDoc : docs.scoreDocs) {
				System.out.println(searcher.doc(scoreDoc.doc));
			}
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

面积的比较运行结果为:

Document<stored<width:3> stored<longth:4>>
Document<stored<width:2> stored<longth:6>>
Document<stored<width:2> stored<longth:5>>

同样的,周长比较的运行结果为:

Document<stored<width:2> stored<longth:6>>
Document<stored<width:2> stored<longth:5>>
Document<stored<width:3> stored<longth:4>>

以上是lucene的expression的应用,源代码会在明天放出;

一步一步跟我学习lucene是对近期做lucene索引的总结,大家有问题的话联系本人的Q-Q:  891922381,同时本人新建Q-Q群:106570134(lucene,solr,netty,hadoop),如蒙加入,不胜感激,大家共同探讨,本人争取每日一博,希望大家持续关注,会带给大家惊喜的



时间: 2024-10-10 02:13:33

一步一步跟我学习lucene(17)---lucene搜索之expressions表达式处理的相关文章

SQL反模式学习笔记17 全文搜索

2014-10-16 09:56:37 目标:全文搜索 使用SQL搜索关键字,同时保证快速和精确,依旧是相当地困难. SQL的一个基本原来(以及SQL所继承的关系原理)就是一列中的单个数据是原子性的. 反模式:模式匹配 使用Like 或者正则表达式. 缺点:(1)无法使用索引,进行全表遍历,非常耗时,性能极低. (2)有时候会返回医疗之外的结果.select * from bugs where description like '%one%', 返回结果可能是money.prone.lonely

一步一步跟我学习lucene(19)---lucene增量更新和NRT(near-real-time)Query近实时查询

这两天加班,不能兼顾博客的更新,请大家见谅. 有时候我们创建完索引之后,数据源可能有更新的内容,而我们又想像数据库那样能直接体现在查询中,这里就是我们所说的增量索引.对于这样的需求我们怎么来实现呢?lucene内部是没有提供这种增量索引的实现的: 这里我们一般可能会想到,将之前的索引全部删除,然后进行索引的重建.对于这种做法,如果数据源的条数不是特别大的情况下倒还可以,如果数据源的条数特别大的话,势必会造成查询数据耗时,同时索引的构建也是比较耗时的,几相叠加,势必可能造成查询的时候数据缺失的情况

一步一步跟我学习lucene(9)---lucene搜索之拼写检查和相似度查询提示(spellcheck)

suggest应用场景 用户的输入行为是不确定的,而我们在写程序的时候总是想让用户按照指定的内容或指定格式的内容进行搜索,这里就要进行人工干预用户输入的搜索条件了:我们在用百度谷歌等搜索引擎的时候经常会看到按键放下的时候直接会提示用户是否想搜索某些相关的内容,恰好lucene在开发的时候想到了这一点,lucene提供的suggest包正是用来解决上述问题的. suggest包联想词相关介绍 suggest包提供了lucene的自动补全或者拼写检查的支持: 拼写检查相关的类在org.apache.

一步一步跟我学习lucene(13)---lucene搜索之自定义排序的实现原理和编写自己的自定义排序工具

自定义排序说明 我们在做lucene搜索的时候,可能会需要排序功能,虽然lucene内置了多种类型的排序,但是如果在需要先进行某些值的运算然后在排序的时候就有点显得无能为力了: 要做自定义查询,我们就要研究lucene已经实现的排序功能,lucene的所有排序都是要继承FieldComparator,然后重写内部实现,这里以IntComparator为例子来查看其实现: IntComparator相关实现 其类的声明为 public static class IntComparator exte

一步一步跟我学习lucene(16)---lucene搜索之facet查询查询示例(2)

本篇是接一步一步跟我学习lucene(14)---lucene搜索之facet索引原理和facet查询实例(http://blog.csdn.net/wuyinggui10000/article/details/45973769),上篇主要是统计facet的dim和每个种类对应的数量,个人感觉这个跟lucene的group不同的在于facet的存储类似于hash(key-field-value)形式的,而group则是单一的map(key-value)形式的,虽然都可以统计某一品类的数量,显然f

一步一步跟我学习lucene(14)---lucene搜索之facet查询原理和facet查询实例

Facet说明 我们在浏览网站的时候,经常会遇到按某一类条件查询的情况,这种情况尤以电商网站最多,以天猫商城为例,我们选择某一个品牌,系统会将该品牌对应的商品展示出来,效果图如下: 如上图,我们关注的是品牌,选购热点等方面,对于类似的功能我们用lucene的term查询当然可以,但是在数据量特别大的情况下还用普通查询来实现显然会因为FSDirectory.open等耗时的操作造成查询效率的低下,同时普通查询是全部document都扫描一遍,这样显然造成了查询效率低: lucene提供了facet

一步一步跟我学习lucene(12)---lucene搜索之分组处理group查询

grouping介绍 我们在做lucene搜索的时候,可能会用到对某个条件的数据进行统计,比如统计有多少个省份,在sql查询中我们可以用distinct来完成类似的功能,也可以用group by来对查询的列进行分组查询.在lucene中我们实现类似的功能怎么做呢,比较费时的做法时我们查询出所有的结果,然后对结果里边的省份对应的field查询出来,往set里边放,显然这种做法效率低,不可取:lucene为了解决上述问题,提供了用于分组操作的模块group,group主要用户处理不同lucene中含

一步一步跟我学习lucene(11)---lucene搜索之高亮显示highlighter

highlighter介绍 这几天一直加班,博客有三天没有更新了,望见谅:我们在做查询的时候,希望对我们自己的搜索结果与搜索内容相近的地方进行着重显示,就如下面的效果 这里我们搜索的内容是"一步一步跟我学习lucene",搜索引擎展示的结果中对用户的输入信息进行了配色方面的处理,这种区分正常文本和输入内容的效果即是高亮显示: 这样做的好处: 视觉上让人便于查找有搜索对应的文本块: 界面展示更友好: lucene提供了highlighter插件来体现类似的效果: highlighter对

一步一步跟我学习lucene(10)---lucene搜索之联想词提示之suggest原理和应用

昨天了解了suggest包中的spell相关的内容,主要是拼写检查和相似度查询提示: 今天准备了解下关于联想词的内容,lucene的联想词是在org.apache.lucene.search.suggest包下边,提供了自动补全或者联想提示功能的支持: InputIterator说明 InputIterator是一个支持枚举term,weight,payload三元组的供suggester使用的接口,目前仅支持AnalyzingSuggester,FuzzySuggester andAnalyz