Setting up development environment for Google App Engine and Python

TechnologyTuesday-23-August.png

Google App Engine is a PAAS offering from Google Cloud Platform, which enables you to build complex web solutions with significant ease without worrying too much about the scalability or infrastructure management. If you want to develop GAE applications using python and looking for a way to setup your development environment then this post is for you. Continue reading

Build a Custom Solr Filter to Handle Unit Conversions

Recently, I came across a use case where it was required to handle units of weight in the index. For instance, 2kg and 2000g, when searched should return the same set of results.

So, for achieving the above, I wrote a custom Solr filter that will work along with KeywordTokenizer to convert all units of weight in the incoming request to a single unit (g) and hence every measurement will be saved in the form of a number; at the same time, it will also keep units like kg/g/mg intact while returning the docs.

Firstly, we need to write custom tokenfilter and tokenfilterfactory .

UnitConversionFilter.java


package com.solr.custom.filter.test;
import java.io.IOException;

import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;

/**
 * @author SumeetS
 *
 */
public class UnitConversionFilter extends TokenFilter{

private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);

/**
 * @param input
 */
 public UnitConversionFilter(TokenStream input) {
 super(input);
 }

/* (non-Javadoc)
 * @see org.apache.lucene.analysis.TokenStream#incrementToken()
 */
 @Override
 public boolean incrementToken() throws IOException {
 if (input.incrementToken()) {
// charUtils.toLowerCase(termAtt.buffer(), 0, termAtt.length());
 int length = termAtt.length();
 String inputWt = termAtt.toString(); //assuming format to be 1kg/mg
 float valInGrams = convertUnit(inputWt);
 String storeFormat = valInGrams+"";
 termAtt.setEmpty();
 termAtt.copyBuffer(storeFormat.toCharArray(), 0, storeFormat.length());
 return true;
 } else
 return false;
 }

 private float convertUnit(String field){
 String [] tmp = field.split("(k|m)?g");
 float weight = Integer.parseInt(tmp[0]);
 String[] tmp2 = field.split(tmp[0]);
 String unit = tmp2[1];
 float convWt = 0;
 switch(unit) {
 case "kg":
 convWt = weight * 1000;
 break;
 case "mg":
 convWt = weight /1000;
 break;
 case "g":
 convWt = weight;
 break;
 }
 return convWt; 
 }
}

UnitConversionTokenFilterFactory.java


package com.solr.custom.filter.test; 
import java.util.Map;

import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.util.TokenFilterFactory;

/**
 * @author SumeetS
 *
 */
public class UnitConversionTokenFilterFactory extends TokenFilterFactory {

/**
 * @param args
 */
 public UnitConversionTokenFilterFactory(Map<String, String> args) {
 super(args);
 if (!args.isEmpty()) {
 throw new IllegalArgumentException("Unknown parameters: " + args);
 }
 }

/* (non-Javadoc)
 * @see org.apache.lucene.analysis.util.TokenFilterFactory#create(org.apache.lucene.analysis.TokenStream)
 */
 @Override
 public TokenStream create(TokenStream input) {
 return new UnitConversionFilter(input);
 }

}

NOTE: When you override the TokenFilter and TokenFilterFactory, make sure to edit the protected constructors to public, otherwise it will throw NoSuchMethodException during plugin init.

Now, compile and export your above classes into a jar say customUnitConversionFilterFactory.jar

Steps to Deploy Your Jar Into Solr

1. Place your jar file under <solr installation>/lib

2. Make an entry in solrConfig.xml file to help it identify your custom jar.


	<lib dir="../../../lib/" regex=".*\.jar" />

3. Add custom fieldType and field in your schema.xml

 

<field name="unitConversion" type="unitConversion" indexed="true" stored="true"/>
<fieldType name="unitConversion" class="solr.TextField" positionIncrementGap="100">
<analyzer>
<tokenizer class="solr.KeywordTokenizerFactory"/>
<filter class="com.solr.custom.filter.test.UnitConversionTokenFilterFactory" />
</analyzer>
</fieldType>

4. Now restart Solr and browse to the Solr console/<core>/documents

5. Add documents in your index like below:

{"id":"tmp1","unitConversion":"1000g"}
{"id":"tmp2","unitConversion":"2kg"}
{"id":"tmp3","unitConversion":"1kg"}

6. Query your index.

Query1 : querying for documents with 1kg

http://localhost:8983/solr/core1/select?q=*%3A*&fq=unitConversion%3A1kg&wt=json&indent=true

Result:

{
 "responseHeader":{
 "status":0,
 "QTime":0,
 "params":{
 "q":"*:*",
 "indent":"true",
 "fq":"unitConversion:1kg",
 "wt":"json"}},
 "response":{"numFound":2,"start":0,"docs":[
 {
 "id":"tmp1",
 "unitConversion":"1000g",
 "_version_":1524411029806645248},
 {
 "id":"tmp3",
 "unitConversion":"1kg",
 "_version_":1524411081738420224}]
 }}

Query2: querying for documents with 2kg

http://localhost:8983/solr/core1/select?q=*%3A*&fq=unitConversion%3A2kg&wt=json&indent=true

Result:

{
 "responseHeader":{
 "status":0,
 "QTime":0,
 "params":{
 "q":"*:*",
 "indent":"true",
 "fq":"unitConversion:2kg",
 "wt":"json"}},
 "response":{"numFound":1,"start":0,"docs":[
 {
 "id":"tmp2",
 "unitConversion":"2kg",
 "_version_":1524411089834475520}]
 }}

Query3: let’s try faceting

http://localhost:8983/solr/core1/select?q=*%3A*&rows=0&wt=json&indent=true&facet=true&facet.field=unitConversion

{
 "responseHeader":{
 "status":0,
 "QTime":1,
 "params":{
 "q":"*:*",
 "facet.field":"unitConversion",
 "indent":"true",
 "rows":"0",
 "wt":"json",
 "facet":"true"}},
 "response":{"numFound":335,"start":0,"docs":[]
 },
 "facet_counts":{
 "facet_queries":{},
 "facet_fields":{
 "unitConversion":[
 "1000.0",2,
 "2000.0",1]},
 "facet_dates":{},
 "facet_ranges":{},
 "facet_intervals":{},
 "facet_heatmaps":{}}}

This is just a basic implementation. One can add additional fields to identify the type of unit and then based on that decide the conversion.

Further improvements include handling of range queries along with the units.

Flexible Data Extraction from Multiple Sources for Analytics

Analytics systems are a huge demand with organizations that deal with massive data on a daily basis. Out of the many requirements that such organizations have with regards to analytics extraction, one in great demand is extraction of data having the same source but with some additional information (Columns) from it or a completely new source (Table).Here’s a case study of the process of building an analytics system for one of our clients who wanted to support analytics extraction based on the above requirement.

Continue reading