Monday, May 23, 2016

Important Apache Solr 6+ Commands for Windows

Sometimes what we really need is a quick reference to common commands we use on a somewhat-daily basis.
We are a Windows shop and sometimes Windows doesn't receive the same love as the *nix world in Solr.

Note that most of these commands are to be executed from the root directory of your Apache Solr installation (or solr-src\solr directory if you compiled from source).  This article will be updated as we get more involved with Apache Solr.  Not all of these commands are Windows-specific, but many are.

ZooKeeper

Solr Cloud

One thing that's unique about Solr Cloud is that everything is in a "cloud," so instead of editing a file on the filesystem, you are expected to use the Config API or download and upload the config file to be distributed throughout the cloud.  That process took a while to discover.  The procedure is to download the working version of the solr cloud solrconfig.xml, put it in source control (optional, but recommended), make your changes, then push those changes back to the cloud by uploading the file to ZooKeeper and check back into source control.


Download the solrconfig.xml for the gettingstarted collection
server\scripts\cloud-scripts\zkcli.bat -cmd getfile /configs/gettingstarted/solrconfig.xml solrconfiglocal.xml -zkhost localhost:9983


Upload the solrconfig.xml for the gettingstarted collection
server\scripts\cloud-scripts\zkcli.bat -cmd putfile /configs/gettingstarted/solrconfig.xml solrconfiglocal.xml -zkhost localhost:9983

Apache Solr

Management

Start
Start Solr for the first time to create a cloud collection (called gettingstarted [default name]).  Also used to start it after the first time (which created it).
(from solr dir)
bin\solr.cmd start -e cloud -noprompt

Stop
bin\solr.cmd stop -all

Index documents (from the file system)
java -Dc=gettingstarted -Dauto=yes -Drecursive=yes -jar example\exampledocs\post.jar example\exampledocs

Search

Use a different query parser
Let's say you want to use the surround query parser, which comes with Solr.
q={!surround}test

Wednesday, May 18, 2016

How to write a Custom Solr Query Parser for Solr 6

Introduction

Solr comes pre-installed with a bunch of great query parsers, so if you're starting out, there's a push to learn and use that syntax.  However, many times we are not starting out without a historical query language--and converting to a new query language is not an option.  This article is meant to assist those embarking on this voyage.

Solr advertises the fact that it supports extending its base functionality through plugins, but there are not many examples out there of a query parser from start to finish.  With this, my goal is to get the plumbing out of the way so that you can focus on implementing your particular parsing algorithm.

Overview

Here's the bird's-eye view of what we need to do.

  • Download and compile Solr 6 in Eclipse
  • Create a separate project for your plugin
  • Export your parser as a JAR file
  • Install the JAR file in Solr
  • Configure Solr to use the JAR
  • Use the custom Query Parser


Create a separate project for your plugin

It is assumed that you followed these instructions on how to download and compile Solr 6 in Eclipse.
At this point, you should have Eclipse happy with the solr code base (no red marks--errors).
  1. Collapse the solr root folder in Package Explorer
  2. Right-click in the whitepsace in Package Explorer
  3. New > Java Project
  4. Project Name: HelloWorldParser
  5. My execution Environment JRE happened to be JavaSE-1.8
  6. Next
  7. Click on the Projects tab
  8. Add...
  9. Check the solr source code project name and press OK
  10. Click Finish
  11. Right-click HelloWorldParser's src folder > New > Package
  12. Name: org.mycompany.lucene.search
  13. Click Finish
  14. Right-Click the new package created > New > Class
  15. Name: HelloWorldQParserPlugin
  16. Click Finish
  17. Here's the code for our simple HelloWorldQParserPlugin.java file
package org.mycompany.lucene.search;

import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.search.QParser;
import org.apache.solr.search.QParserPlugin;
import org.apache.solr.search.QueryParsing;

public class HelloWorldQParserPlugin extends QParserPlugin {
  public static final String NAME = "helloWorld";
      
  @Override
  public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
    return new QParser(qstr, localParams, params, req) {
      @Override
      public Query parse() {
        final IndexSchema schema = req.getSchema();
        final String defaultField = QueryParsing.getDefaultField(schema, getParam(CommonParams.DF));
        // When you implement your Query Parser, you may want to read up on the commented line items.
        // I left them here to give you a jump-off point, but we don't need them in this example.
        //final Analyzer analyzer = schema.getQueryAnalyzer();
        //final SolrCoreParser solrParser = new SolrCoreParser(defaultField, analyzer, req);
               
        // Yes, at the end of the day, this HelloWorldQParserPlugin is nothing more than a wrapper for a TermQuery
        // I wanted to allow some functionality, but not get too crazy because you're likely to replace it, anyway :D
        TermQuery tq = new TermQuery(new Term(defaultField,qstr));
        return tq;
      }
    };
  }
}

Eclipse generally compiles your code as soon as you save it--let's make sure.  Open a Windows Explorer window (Windows Key + E) and navigate to your code for this plugin.  Then click through bin\org\mycompany\lucene\search.  Verify that you see two class files there: HelloWorldQParserPlugin$1.class and HelloWorldQParserPlugin.class.
If they're there, then we're set to export this to a JAR file.

Export your parser as a JAR file

  1. Go back to Eclipse
  2. Right-click on the HelloWorldParser project in Package Explorer > Export...
  3. Expand the Java folder and select JAR file and click Next
  4. I left everything as default (just my HelloWorldParser's src folder was checked
  5. JAR file: Choose where you want to export your JAR file to.  I'll choose a directory where I keep all of them in a backup.  I like to put a version number at the end so I know I'm working with the latest version in Solr, but that's entirely up to you.  I named my JAR HelloWorldParser-0.0.JAR.
  6. Click Finish
  7. Verify that it generated your JAR file.

Install the JAR file in Solr

It is assumed that you have compiled Solr from the source, so that you know that your plugin will work with the version of Solr you have installed.  The idea is that You've compiled Solr 6.0.0 in this case and you have your HelloWorldParser referencing that very version of Solr--so you shouldn't have to worry about your parser code being out-of-date with solr--which can and has happened to me.  Let's save you that frustration. :)
If you haven't done it yet, follow these instructions--specifically, Using the build.xml files in the Project.  In there it tells how to use the Apache Ant build.xml file to build the server (make sure you get the correct build.xml file located under the solr subfolder--not the one at the top level).

  1. Open a Command window and navigate to the root of your Solr source
  2. cd solr
  3. bin\solr.cmd start -e cloud -noprompt
  4. Navigate to the URL provided (i.e. http://localhost:8983/solr)
  5. This sets us up with a gettingstarted collection as a Solr Cloud
  6. Now, let's index some documents.
    java -Dc=gettingstarted -Dauto=yes -Drecursive=yes -jar example\exampledocs\post.jar example\exampledocs
  7. Now we need to shut it down and install the JAR file
    bin\solr.cmd stop -all

Okay--NOW you should be at a point where you can install the JAR file.
Since this is Solr Cloud, there are better ways of installing your JAR, but we just want to get it loaded and test it.  Please keep this in mind for later, as ZooKeeper has a way of distributing your JAR files through it's Blob Store API (see here and here).
But for now, we're not going to pay attention to "best practices" and just get it loaded.
  1. Go to Windows Explorer (Windows Key + E) and navigate to <your solr source root>\solr\example\cloud\node1\solr
  2. Create a new folder called lib
  3. Paste your JAR file in there
  4. For each of the remaining cores, copy the lib folder you just made to nodeN\solr
  5. Go back to the command prompt and start solr back up
    bin\solr.cmd start -e cloud -noprompt
  6. Check the log to make sure it loaded our JAR file--important!
    1. The log file we're looking for is located:
      <solr source>\solr\example\cloud\node1\logs\solr.log
    2. Search for HelloWorld
      You should see something like:
      Adding 'file:/D:/solr-6.0.0/solr/example/cloud/node1/solr/lib/HelloWorldParser-0.0.jar' to classloaderNOTE: For some reason, it didn't load it the first time I tried. I renamed the JAR file from a .JAR extension to a .jar extension (it shouldn't make a difference, but it loaded it the second time. So, if you're having a problem, maybe that's it?)

Configure Solr to use the JAR

Awesome, so our JAR file is loaded--now we need to hook into it and use the parser.  Since we're using Solr Cloud, we will need to use the ZooKeeper API to play with the configs.
  1. Go to your command prompt (you should still be at the <solr-src>\solr subdirectory
  2. Get the solrconfig.xml (rename it so we know it's our local version):
    server\scripts\cloud-scripts\zkcli.bat -cmd getfile /configs/gettingstarted/solrconfig.xml solrconfiglocal.xml -zkhost localhost:9983
  3. Open it up:
    notepad solrconfiglocal.xml
  4. Notepad doesn't do a good job of formatting this file, so be careful.  You may want to use a better text editor, but notepad will work
  5. Search for <queryParser
  6. Copy that example and paste it just below the comment it's contained within and make the following changes:
    <queryParser name="helloWorld" class="org.mycompany.lucene.search.HelloWorldQParserPlugin"/>
    The name attribute will be used when we specify which parser we want.  The class attribute is the class that specifically points to our QParserPlugin.  Note that you don't specify the path to the JAR file--it's already loaded by the class loader and it should be found by the class name.
  7. Save the file and exit Notepad
  8. Now, push it back to ZooKeeper:
    server\scripts\cloud-scripts\zkcli.bat -cmd putfile /configs/gettingstarted/solrconfig.xml solrconfiglocal.xml -zkhost localhost:9983
  9. We need to reload the core in the Amin UI interface, so hop on your web browser
  10. From the left-hand side, select Collections, then select gettingstarted
  11. Click the Reload button
  12. You should see the Reload button turn green

Use the custom Query Parser

  1. Now select gettingstarted from the core selector from the left drop-down
  2. Select Query
  3. Change the q field to:
    {!helloWorld}hello
  4. Click Execute Query
  5. I got one result, so if you didn't get any, change the q field to {!helloWorld}test
  6. Now, let's try to specify a field to query:
    1. Set the df field to id
    2. Copy the id field value from one of your search results and paste it over the q field like this:
      {!helloWorld}UTF8TEST
    3. Click the Execute Query button and notice that it correctly identifies that document!
Hallelujah!  It works!

Conclusion

Wow, what an adventure that was, right?  The exciting part is that we now have a base from which to develop our very own query parser, which is quite an adventure in and of itself.


Be blessed.

How to Download and Compile Solr 6 in Eclipse

Overview

  • Download and install the Oracle Java SE JDK
  • Download and Install Eclipse
  • Download and Install Apache Ant
  • Download Solr source code
  • Prepare Solr source code for Eclipse environment
  • Importing and compiling Solr source in Eclipse
  • Using the Build.xml files in the Project


Download and install the Oracle Java SE JDK

  1. For Solr 6.0.0, you need at least Java SE 8
  2. I chose to install the Windows x64 product
  3. Make sure that java is accessible from the command line
    1. Open a new command window
    2. type: java -version
You should see something like:
java version "1.8.0_91"
Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)


The JDK automatically installs the JRE, so you don't need to install them separately.

Download and Install Eclipse

  1. Go to the Eclipse download page and download the version that's right for you (I chose 64-bit Eclipse IDE for Java Developers)
    NOTE: If you installed the 64-bit JDK, you'll need to install the 64-bit version of Eclipse.  If you installed the 32-bit version, you'll need to download the 32-bit version of Eclipse.
  2. All you need to do in order to install Eclipse is to extract it to where you want it to live--I chose D:\eclipse64
  3. Open that folder and double-click eclipse.exe
  4. When asked to set the workspace, I chose D:\eclipse_workspace

Download and Install Apache Ant

  1. Download page
  2. I downloaded the .zip archive
  3. I chose to extract it to C:\Apache\ant-1.9.7
  4. Add Apache Ant's bin directory to your PATH environment variable (C:\Apache\ant-1.9.7\bin)

Download Solr source code

  1. I used this link to download the tgz source file (they didn't have a ZIP version of the source at this time).  If that link isn't available, then go to the Solr latest redirect page.
  2. Since I'm on Windows, I needed to download 7-Zip (I used the 64-bit version)
  3. Extract the source (I will use D:\solr, which should generate a D:\solr\solr-6.0.0 folder)

Prepare Solr source code for Eclipse environment

  1. Open this folder in Windows Explorer
  2. Shift + Right-Click in the whitespace inside the folder and click Open command window here (or open a command window and cd to your new solr-6.0.0 folder)
  3. ant eclipse
  4. At this point, Solr should be preparing the source for use in Eclipse.  You should see something like this:
    BUILD SUCCESSFUL
    Total time: 
    22 seconds

Importing and compiling Solr source in Eclipse

  1. Go back to Eclipse
  2. File > Import...
  3. Expand General and select Existing Projects into Workspace
  4. Click Browse... next to Select root directory and select the solr root directory (where you extracted the source tgz file)
  5. It should find the solr project and list it in the Projects list below.  Make sure it's selected and click Finish
  6. Eclipse should automatically start building the project (you should see an indicator in the bottom-right corner)
  7. While it's building, click on the Workbench icon at the top-right corner
  8. You should see the Package Explorer on the right-hand side
  9. At the bottom, middle, you should see a Problems tab.  Click on it if it's not the one in focus.
    As it was compiling, I saw a few items get put in there, but they went away by the time it stopped compiling and I was left with 11,156 warnings and 0 errors.
Eclipse should be happy with the compile results, but you will want to do a bit more than Eclipse does when it first imports the code.  Solr makes a lot of use out of Apache Ant, which operates on the build.xml file.

Using the build.xml files in the Project

  1. Expand the solr root folder in the Package Explorer and select the build.xml file
  2. Right-click the build.xml file > Run As > Ant Build...
  3. Uncheck the -projecthelp [default] item at the top of the list
  4. This is how you can trigger the ant targets in Eclipse.  Let's trigger the real compile (as far as solr is concerned).
  5. Check the compile target and click Run
  6. It will probably take a few minutes before you see the BUILD SUCCESSFUL message
  7. Now let's use build.xml in Eclipse to build the server
  8. Expand the solr subfolder and right-click that build.xml (note that this is a subfolder off of the root source folder) > Run As > Ant Build...
  9. Uncheck the usage [default] target
  10. Check server and click Run
  11. That should build the server, which you can then run and index files.
    Check out this article I wrote about this if you want to learn more (you can skip the first part, since you've essentially set up your environment).

Conclusion

Now you're in a good position to get started in development in the Solr community, although you will want to use the git repository to get the latest version.  Getting started can be a bit tough, so starting with a known good source is important, as you can scratch your head for days not knowing why the code doesn't compile--when it just so happened to be a bad check-in.  That's one of the reasons I started by downloading the 6.0.0 source, because it's quite likely that that code compiles.

I owe a huge thank-you to @elyograg from the #solr chat room, who walked me through my Eclipse woes!  It seems as though I was doing everything wrong and this guy set me straight.

Compiling and Running Apache Solr 6 from Source on Windows

To get started, I'm going to quickly outline a bunch of little, specific steps to get us where we want to be.
  1. Download and install the Oracle Java SE JDK
    1. For Solr 6.0.0, you need at least Java SE 8
    2. I chose to install the Windows x64 product
    3. Make sure that java is accessible from the command line
      1. Open a new command window
      2. type: java -version
      3. You should see something like:
        java version "1.8.0_91"
        Java(TM) SE Runtime Environment (build 1.8.0_91-b14)
        Java HotSpot(TM) 64-Bit Server VM (build 25.91-b14, mixed mode)
  2. Download and install Apache Ant
    1. Download page
    2. I downloaded the .zip archive
    3. I chose to extract it to C:\Apache\ant-1.9.7
    4. Add Apache Ant's bin directory to your PATH environment variable (C:\Apache\ant-1.9.7\bin)
  3. Download Apache Solr source (6.0.0 in this case)
    I used this link to download the tgz source file (they didn't have a ZIP version of the source at this time)
  4. Since I'm on Windows, I needed to download 7-Zip (I used the 64-bit version)
  5. Extract the source (I will use D:\solr, which should generate a D:\solr\solr-6.0.0 folder)
    1. Open this folder in Windows Explorer
    2. Shift + Right-Click in the whitespace inside the folder and click Open command window here (or open a command window and cd to your new solr-6.0.0 folder)
    3. cd solr
    4. ant server
      At this point, Solr should be compiling everything that's necessary to run the server.
      You should see something like this:
      BUILD SUCCESSFUL
      Total time: 4 minutes 3 seconds

Now that we have something to work with, let's talk a bit about it.
We now have something that we can run--let's do that now.

Solr Cloud

Since most people are going crazy over Solr Cloud, let's use that example.
From the same command prompt you used to compile Solr:
bin\solr.cmd start -e cloud -noprompt
You should see something like:

Welcome to the SolrCloud example!
Starting up 2 Solr nodes for your example SolrCloud cluster.
Solr home directory D:\solr\solr-6.0.0\solr\example\cloud\node1\solr already exists.D:\solr\solr-6.0.0\solr\example\cloud\node2 already exists.
Starting up Solr on port 8983 using command:D:\solr\solr-6.0.0\solr\bin\solr.cmd start -cloud -p 8983 -s "D:\solr\solr-6.0.0\solr\example\cloud\node1\solr"
Waiting up to 30 to see Solr running on port 8983Started Solr server on port 8983. Happy searching!
Starting up Solr on port 7574 using command:D:\solr\solr-6.0.0\solr\bin\solr.cmd start -cloud -p 7574 -s "D:\solr\solr-6.0.0\solr\example\cloud\node2\solr" -z localhost:9983
Waiting up to 30 to see Solr running on port 7574Started Solr server on port 7574. Happy searching!
Connecting to ZooKeeper at localhost:9983 ...Uploading D:\solr\solr-6.0.0\solr\server\solr\configsets\data_driven_schema_configs\conf for config gettingstarted to ZooKeeper at localhost:9983
Creating new collection 'gettingstarted' using command:http://localhost:8983/solr/admin/collections?action=CREATE&name=gettingstarted&numShards=2&replicationFactor=2&maxShardsPerNode=2&collection.configName=gettingstarted
{  "responseHeader":{    "status":0,    "QTime":14712},  "success":{    "192.168.28.56:7574_solr":{      "responseHeader":{        "status":0,        "QTime":4758},      "core":"gettingstarted_shard1_replica1"},    "192.168.28.56:8983_solr":{      "responseHeader":{        "status":0,        "QTime":5443},      "core":"gettingstarted_shard1_replica2"}}}
Enabling auto soft-commits with maxTime 3 secs using the Config API
POSTing request to Config API: http://localhost:8983/solr/gettingstarted/config{"set-property":{"updateHandler.autoSoftCommit.maxTime":"3000"}}Successfully set-property updateHandler.autoSoftCommit.maxTime to 3000

SolrCloud example running, please visit: http://localhost:8983/solr
Hallelujah!  Let's check it out!  Copy the URL from the last line in the output and paste it in the browser.

Beautiful!

Now, we need to import some content--and for that, let's go back to our command prompt.
Solr ships with a bunch of test documents, so let's throw those guys at the server and see what happens.

java -Dc=gettingstarted -Dauto=yes -Drecursive=yes -jar example\exampledocs\post.jar example\exampledocs

You should see something like:
SimplePostTool version 5.0.0Posting files to [base] url http://localhost:8983/solr/gettingstarted/update...Entering auto mode. File endings considered are xml,json,jsonl,csv,pdf,doc,docx,ppt,pptx,xls,xlsx,odt,odp,ods,ott,otp,ots,rtf,htm,html,txt,logEntering recursive mode, max depth=999, delay=0sIndexing directory example\exampledocs (19 files, depth=0)POSTing file books.csv (text/csv) to [base]POSTing file books.json (application/json) to [base]/json/docsPOSTing file gb18030-example.xml (application/xml) to [base]POSTing file hd.xml (application/xml) to [base]POSTing file ipod_other.xml (application/xml) to [base]POSTing file ipod_video.xml (application/xml) to [base]POSTing file manufacturers.xml (application/xml) to [base]POSTing file mem.xml (application/xml) to [base]POSTing file money.xml (application/xml) to [base]POSTing file monitor.xml (application/xml) to [base]POSTing file monitor2.xml (application/xml) to [base]POSTing file more_books.jsonl (application/json) to [base]/json/docsPOSTing file mp500.xml (application/xml) to [base]POSTing file sample.html (text/html) to [base]/extractPOSTing file sd500.xml (application/xml) to [base]POSTing file solr-word.pdf (application/pdf) to [base]/extractPOSTing file solr.xml (application/xml) to [base]POSTing file utf8-example.xml (application/xml) to [base]POSTing file vidcard.xml (application/xml) to [base]19 files indexed.COMMITting Solr index changes to http://localhost:8983/solr/gettingstarted/update...Time spent: 0:00:10.135
Now that we have something to search for, let's search!
Go back to the Admin UI (BTW: the UI was redone for 6.0) in the web browser.
Click on the Collection Selector and select gettingstarted.
Now click on the Query tab.
There is a "match all" query already written out for you, so all you need to do is click "Execute Query" at the bottom and notice the search results come back.
Let's change the query a bit.
Change *:* to inStock:false, press the tab key and press enter (This is the short-cut so you don't have to scroll down and click "Execute Query")

Conclusion

There you have it!  From source to server in probably less than ten minutes.  I know for myself, this was a long path to success as I had to figure out how to do things a bit differently on Windows (since most of the time they assume you're using Linux).

Most Importantly

I know for myself, I went through life with people not sharing the most important truth of life and I don't want that to happen to you.  As I'm sure you know, we as people are not good--we do terrible, immoral things--all of us.  God, the Creator of the universe, is going to judge us and we are going to be found deserving punishment--in the Lake of Fire.  The Good News is that He knew we would be depraved and he loved us so much that he sent his Son, Jesus Christ (Y'shua in Hebrew), to provide a way that God can be just and loving.  His Son, Jesus, was sent from heaven (where God lives), was born as a baby, grew up and didn't sin (break God's laws) even once, told people the truth about their condition, was brutally beaten, mocked, and murdered in the most humiliating and torturous way.  This was all planned from the beginning--before God created the world.  He knew going into it all that his Son was going to go on this rescue mission and die at the very hands of those he sought to save.  This is the first story I've heard where the hero dies for the villain.  However, three days after Jesus was murdered, God raised him from the dead, validating everything Jesus spoke.
Here's the overview:  You've broken God's law and the penalty for that is being thrown into the Lake of Fire (Revelation 20).  It doesn't have to be that way, but it will be if you don't obey what Jesus said:
“For this is how God loved the world: He gave his one and only Son, so that everyone who believes in him will not perish but have eternal life. God sent his Son into the world not to judge the world, but to save the world through him.
“There is no judgment against anyone who believes in him. But anyone who does not believe in him has already been judged for not believing in God’s one and only Son. And the judgment is based on this fact: God’s light came into the world, but people loved the darkness more than the light, for their actions were evil.  All who do evil hate the light and refuse to go near it for fear their sins will be exposed. But those who do what is right come to the light so others can see that they are doing what God wants.” 
John 3:16-21 NLT
Here's how Paul puts it:
If you openly declare that Jesus is Lord and believe in your heart that God raised him from the dead, you will be saved.  For it is by believing in your heart that you are made right with God, and it is by openly declaring your faith that you are saved. As the Scriptures tell us, “Anyone who trusts in him will never be disgraced.” Jew and Gentile are the same in this respect. They have the same Lord, who gives generously to all who call on him. For “Everyone who calls on the name of the Lord will be saved.”
Romans 10:9-13 NLT
How about you? You can change your mind about your sins, confess them to God, and trust in Jesus to save you from the wrath of God right now.  The only way we can know anything for sure is because of God's Word, so the more you study and apply what it teaches, the more spiritually blessed you will be.  I recommend starting out reading in the Gospel of John, whose purpose in writing it was to present the facts so that you can believe and be saved.  Learn more.

While you're reading along in the Gospel of John, watch this free movie called The Gospel of John on YouTube that follows along quite nicely.

May the Lord Jesus Christ bless you with grace and truth and life and life in abundance!

Monday, May 9, 2016

Replacing dtSearch with Solr: Using Solr's XMLQueryParser to process Search Queries

The Problem

Sometimes you need a query that supports nesting--and when you do, it can be quite frustrating in Solr.  The company I work for must support deep-nesting proximity queries--some of which have five or six or more levels of nesting. Try doing that with the standard Lucene syntax of "word1 word2"~3!  Yeah, you can't.  Not only that, but it doesn't support "word1 must occur within three words, before, word2," let alone nesting.  Don't get me wrong--Solr supports this kind of query under the hood, but good luck finding a query parser that allows for it.

While dtSearch is an implementation detail, it is an important one for us because dtSearch has a richer search syntax than Solr.  You may not need to support dtSearch, but you may very well want to support something that the default query parser doesn't support.

I've tried various other out-of-the-box query parsers that come with Solr, however each of them were lacking something when looking into them.

The problem I ran into is that Solr's default search syntax does not support complex, deeply-nested proximity queries.  I've been looking and looking for ways of overcoming this handicap and I think I finally found a solution.

XmlQueryParser

Since Solr 5.5, XmlQueryParser has been available for use.  Good luck finding any documentation beyond the only example given in an obscure location in the documentation.

I've done some code spelunking and have found some more complex examples not included in the documentation.

Let's start with a simple, nested example:
dtSearch Syntax: new pre/3 (daybook or (employee w/3 onboarding))

dtSearch syntax:
  • X pre/N Y - X must occur within N words before Y
  • X w/N Y - X and Y must occur within N words of each other (i.e. X within N words, before or after, Y)
So, in the above example, we must match the word new, followed by either daybook or employee and onboarding within three words of each other.

Here's what the query looks like for the XmlQueryParser:

{!xmlparser}
<BooleanQuery>
    <Clause occurs="must">
        <SpanNear fieldName="headline" slop="3" inOrder="true">
            <SpanTerm>new</SpanTerm>
            <SpanOr>
                <SpanTerm>daybook</SpanTerm>
                <SpanNear slop="3" inOrder="false">
                    <SpanTerm>employee</SpanTerm>
                    <SpanTerm>onboarding</SpanTerm>
                </SpanNear>
            </SpanOr>
        </SpanNear>
    </Clause>
</BooleanQuery>

Here's the response:
{ "responseHeader":{ "status":0, "QTime":48, "params":{ "q":"{!xmlparser}\n<BooleanQuery>\n <Clause occurs=\"must\">\n <SpanNear fieldName=\"headline\" slop=\"3\" inOrder=\"true\">\n\t<SpanTerm>new</SpanTerm>\n\t<SpanOr>\n\t <SpanTerm>daybook</SpanTerm>\n\t <SpanNear slop=\"3\" inOrder=\"false\">\n\t\t<SpanTerm>employee</SpanTerm>\n\t\t<SpanTerm>onboarding</SpanTerm>\n\t </SpanNear>\n\t</SpanOr>\n </SpanNear>\n </Clause>\n</BooleanQuery>", "indent":"on", "fl":"headline", "rows":"1000", "wt":"json", "_":"1462805933013"}}, "response":{"numFound":14,"start":0,"maxScore":12.657374,"docs":[ {"headline":"Upstate New York Daybook"}, {"headline":"New Hampshire Daybook"}, {"headline":"New Jersey Daybook"}, {"headline":"New Mexico Daybook"}, {"headline":"New Mexico Daybook"}, {"headline":"Upstate New York Daybook"}, {"headline":"New Hampshire Daybook"}, {"headline":"New Jersey Daybook"}, {"headline":"Upstate New York Daybook"}, {"headline":"New Mexico Daybook"}, {"headline":"New Jersey Daybook"}, {"headline":"New Hampshire Daybook"}, {"headline":"New Employee Onboarding Center combines services for new employees"}, {"headline":"New Employee Onboarding Center combines services for new employees"}] }}

The {!xmlparser} at the beginning of the q= parameter is very important--it tells Solr to use the XmlQueryParser for this search request.

I'm doing more research and will likely follow up with more articles about the various types of subqueries.

Friday, November 20, 2015

jQuery: How to select DOM elements with multiple classes, etc. using the intersection set operator

Today I needed a way to select items that had a combination of classes employing a simple intersection set operation.  I think it took entirely too long to find it, so I'm writing this blog post to provide easier access to this answer.  I couldn't even find it on w3schools.com's CSS Selector list or jQuery's Category Selectors page.
Thank you, Stack Overflow!  Here's the link so that credit goes to where credit is due: How can I select an element with multiple classes?

The Problem

I have a bunch of elements scattered all over the web page and I want only those that are decorated with classes PetAvailableToBeSelected and ItemIsSelected.  Let's assume that looking for ItemIsSelected isn't enough by itself--there are other lists out there that can also be selected and you only want selected pets.

The Example

HTML Snippet:
<ul>
  <li class="CityAvailableToBeSelected">Austin, TX</li>
  <li class="CityAvailableToBeSelected ItemIsSelected">Round Rock, TX</li>
  <li class="CityAvailableToBeSelected">Georgetown, TX</li>
  <li class="CityAvailableToBeSelected ItemIsSelected">San Antonio, TX</li>
</ul>
<ul>
  <li class="PetAvailableToBeSelected">Rocky, the pet rock</li>
  <li class="PetAvailableToBeSelected ItemIsSelected">Tripod, the not-so-lucky dog</li>
  <li class="PetAvailableToBeSelected">Mr. Shakes, the Chihuahua</li>
  <li class="PetAvailableToBeSelected ItemIsSelected">Mr. Meowgi, the Persian kitten</li>
</ul>



The Answer 

$(".PetAvailableToBeSelected.ItemIsSelected");



Also, in case you want to only select list items with that, you can do this:
$("li.PetAvailableToBeSelected.ItemIsSelected");

Which, in this example, gives you the same result.

Happy developing and God bless!

Monday, May 26, 2014

Today's Noah's Ark

I invite everyone here to receive the Gospel of Jesus Christ.
The whole Bible makes Jesus look GORGEOUS! Why do I say this? The biggest thing by far that is against us is the wrath of God. Yes, there's the curse that creation received when Adam sinned in the garden, but that is NOTHING compared to the infinite, awesome wrath of God! We were given instructions as to what sin is so that when we do sin, we know that we've broken the laws set by the infinite, just God of the universe. He didn't just randomly come up with rules and expect us to follow them. We cause real harm when we choose to sin.
What does it mean to be on the wrong side of the infinite, just, wrathful God of the universe? Things will not go well for us at ALL if we don't have a way of escape! Even though God was angry at our sin, He made a way of salvation for all who would choose it. He poured out His love for us when He sent His Son to drink in our sins, whereby His Son also received His wrath! If God was so angry, yet loved us so much to send His Son to die for us, how much more angry will He be when we spit at and reject His free gift of salvation? There is no middle ground here. If you don't receive it, you're rejecting it--even if you think you'll open the gift later.
Do you think the Father just doesn't care about His Son? Far from it! The only reason He sent His Son is because that is the ONLY way for us to get right with the infinite, just, loving God of the universe. Jesus prayed that there could be another way, but there wasn't. And because of this, Jesus marched forward and received the outrageous punishment that WE deserved so that WE could be reconciled to Him.
What did Jesus do for us? God came down from Heaven and was born of a virgin. He lived a mundane life in a really messed up place where people had the impression that nothing good could ever come from there. He fulfilled the fingerprint of the Messiah prophesied in the Old Testament (declared hundreds of years before He was even born). He lived a completely sinless life. He laid His life down and was slaughtered by the ones He was promised to. They lied about what He said and unjustly condemned Him to die. He was beaten and crucified so that we may be healed. He died on the cross. He was buried. On the third day, he was raised to life by the resurrection power of the Holy Spirit, thus validating Him, His message, and the validity of the Old Testament which promised Him. He ascended into Heaven and is now sitting at the right hand of the Father where He received all authority and power. There is no place where His reign does not extend and it is eternal, never-ending.
If you believe this and repent (turn from your sinful ways), you will receive salvation from the wrath of God. Not only that, but you will be with God for ever and ever! The world will hate you because you will now be God's, but God is greater than the world.
This Gospel is the new Noah's Ark. All who are found outside will receive the full wrath of God. No amount of beating on the door will make it open once the door is shut. Think of how the people felt when the wrath of God was pouring down on them and they realized that they just missed the boat. If only they had gotten in the boat BEFORE the rain! The opportunity was still available MINUTES before! Now it's too late! How helpless they must have felt! Noah preached to them for at least 90 years, warning them of God's wrath, but they hardened their hearts. They scoffed at it, rejected it. Oh, human! Before you is a blessing and a curse. Choose life!
I am extending my hand out to you, begging you to enter this boat!
When we truly receive the free gift of God, it powerfully saves us! We are converted and we become a new creature! The old creature is dead and the new creature has life everlasting and a new appetite. This appetite is the Word of God and to please God. When you look at the life of someone who is transformed, their life glorifies God! The neck of the sins that formerly had them in bondage is broken and they are free to live a life which sings praises to God, unencumbered by sin. They are set free and their hearts cry praises to God and eternal thankfulness for Jesus!
If you think you've received the free gift of salvation through Jesus, but your life isn't changed, you've been deceived. It is impossible to receive the Gospel and your life to not completely change. Did you leave the gift unopened? If you open it, what will you find? God! When you have God, who needs ANYTHING else? People all over the world are in dire places with everything against them, but they sing praises to God and are 100% content. Only God can do that! Only the Gospel can do that! It is only attainable through the Gospel of Jesus Christ!
Oh, God, thank you for giving us YOU!
Did you receive God's free gift of Salvation?
If so, I would like to hear from you and welcome you into God's family!
If not, I sincerely urge you to carefully and prayerfully consider the claims of the Bible. The book of Romans does an amazing job presenting the Gospel! I highly recommend reading it many times!
When you believe what the Bible affirms, Jesus will look absolutely GORGEOUS!
Peace and God bless!