You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jena.apache.org by an...@apache.org on 2014/03/26 11:18:41 UTC

svn commit: r1581780 - in /jena/trunk/jena-text/src: main/java/org/apache/jena/query/text/ main/java/org/apache/jena/query/text/assembler/ test/java/org/apache/jena/query/text/ test/java/org/apache/jena/query/text/assembler/

Author: andy
Date: Wed Mar 26 10:18:40 2014
New Revision: 1581780

URL: http://svn.apache.org/r1581780
Log:
JENA-654 : Configurable Lucene Analyzer for index

Modified:
    jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/EntityDefinition.java
    jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/TextIndexLucene.java
    jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/EntityDefinitionAssembler.java
    jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextAssembler.java
    jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextVocab.java
    jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java
    jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/TS_Text.java
    jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/assembler/TestEntityMapAssembler.java

Modified: jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/EntityDefinition.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/EntityDefinition.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/EntityDefinition.java (original)
+++ jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/EntityDefinition.java Wed Mar 26 10:18:40 2014
@@ -24,6 +24,7 @@ import java.util.HashMap ;
 import java.util.Map ;
 
 import org.apache.jena.atlas.lib.MultiMap ;
+import org.apache.lucene.analysis.Analyzer ;
 
 import com.hp.hpl.jena.graph.Node ;
 import com.hp.hpl.jena.rdf.model.Resource ;
@@ -33,6 +34,7 @@ import com.hp.hpl.jena.rdf.model.Resourc
  */
 public class EntityDefinition {
     private final Map<Node, String>      predicateToField = new HashMap<Node, String>() ;
+    private final Map<String, Analyzer>    fieldToAnalyzer  = new HashMap<String, Analyzer>();
     private final MultiMap<String, Node> fieldToPredicate = MultiMap.createMapList() ;
     private final Collection<String>     fields           = Collections.unmodifiableCollection(fieldToPredicate.keys()) ;
     // private final Collection<String> fields =
@@ -125,6 +127,14 @@ public class EntityDefinition {
     public String getField(Node predicate) {
         return predicateToField.get(predicate) ;
     }
+    
+    public void setAnalyzer(String field, Analyzer analyzer) {
+    	fieldToAnalyzer.put(field, analyzer);
+    }
+    
+    public Analyzer getAnalyzer(String field) {
+    	return fieldToAnalyzer.get(field);
+    }
 
     public String getPrimaryField() {
         return primaryField ;

Modified: jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/TextIndexLucene.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/TextIndexLucene.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/TextIndexLucene.java (original)
+++ jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/TextIndexLucene.java Wed Mar 26 10:18:40 2014
@@ -83,6 +83,14 @@ public class TextIndexLucene implements 
         analyzerPerField.put(def.getEntityField(), new KeywordAnalyzer()) ;
         if ( def.getGraphField() != null )
             analyzerPerField.put(def.getGraphField(), new KeywordAnalyzer()) ;
+        
+        for (String field : def.fields()) {
+        	Analyzer analyzer = def.getAnalyzer(field);
+        	if (analyzer != null) {
+        		analyzerPerField.put(field, analyzer);
+        	}
+        }
+        
         this.analyzer = new PerFieldAnalyzerWrapper(new StandardAnalyzer(VER), analyzerPerField) ;
 
         // force creation of the index if it don't exist

Modified: jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/EntityDefinitionAssembler.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/EntityDefinitionAssembler.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/EntityDefinitionAssembler.java (original)
+++ jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/EntityDefinitionAssembler.java Wed Mar 26 10:18:40 2014
@@ -21,21 +21,28 @@ package org.apache.jena.query.text.assem
 import static org.apache.jena.query.text.assembler.TextVocab.NS ;
 
 import java.util.Collection ;
+import java.util.HashMap;
 import java.util.List ;
+import java.util.Map;
 
 import org.apache.jena.atlas.lib.MultiMap ;
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.apache.jena.atlas.logging.Log ;
 import org.apache.jena.query.text.EntityDefinition ;
 import org.apache.jena.query.text.TextIndexException ;
+import org.apache.lucene.analysis.Analyzer;
 
 import com.hp.hpl.jena.assembler.Assembler ;
 import com.hp.hpl.jena.assembler.Mode ;
 import com.hp.hpl.jena.assembler.assemblers.AssemblerBase ;
 import com.hp.hpl.jena.graph.Node ;
 import com.hp.hpl.jena.query.* ;
+import com.hp.hpl.jena.rdf.model.Literal;
 import com.hp.hpl.jena.rdf.model.Model ;
+import com.hp.hpl.jena.rdf.model.RDFNode;
 import com.hp.hpl.jena.rdf.model.Resource ;
+import com.hp.hpl.jena.rdf.model.Statement;
+import com.hp.hpl.jena.vocabulary.RDF;
 
 public class EntityDefinitionAssembler extends AssemblerBase implements Assembler
 {
@@ -85,20 +92,67 @@ public class EntityDefinitionAssembler e
         String entityField = qsol1.getLiteral("entityField").getLexicalForm() ;
         String graphField = qsol1.contains("graphField") ? qsol1.getLiteral("graphField").getLexicalForm() : null;
         String defaultField = qsol1.contains("dftField") ? qsol1.getLiteral("dftField").getLexicalForm() : null ;
-        
-        String qs2 = StrUtils.strjoinNL("SELECT * { ?map list:member [ :field ?field ; :predicate ?predicate ] }") ;
-        Query query2 = QueryFactory.create(prologue+" "+qs2) ;
-        QueryExecution qexec2 = QueryExecutionFactory.create(query2, model, qsol1) ;
-        ResultSet rs2 = qexec2.execSelect() ;
-        List<QuerySolution> mapEntries = ResultSetFormatter.toList(rs2) ;
-        
+              
         MultiMap<String, Node> mapDefs = MultiMap.createMapList() ; 
-        for ( QuerySolution qsol : mapEntries ) {
-            String field =  qsol.getLiteral("field").getLexicalForm() ;
-            Resource p = qsol.getResource("predicate") ;
-            mapDefs.put(field, p.asNode()) ;
-        }
+        Map<String, Analyzer> analyzerDefs = new HashMap<String, Analyzer>();
         
+        Statement listStmt = root.getProperty(TextVocab.pMap);
+        while (listStmt != null) {
+        	RDFNode n = listStmt.getObject();
+        	if (! n.isResource()) {
+        		throw new TextIndexException("Text list node is not a resource : " + n);
+        	}
+        	Resource listResource = (Resource) n;
+        	if (listResource.equals(RDF.nil)) {
+        		break;  // end of the list
+        	}
+        	
+        	Statement listEntryStmt = listResource.getProperty(RDF.first);
+        	if (listEntryStmt == null) {
+        		throw new TextIndexException("Text map list is not well formed.  No rdf:first property");
+        	}
+        	n = listEntryStmt.getObject();
+        	if (! n.isResource()) {
+        		throw new TextIndexException("Text map list entry is not a resource : " + n);
+        	}
+        	Resource listEntry = (Resource) n;
+        	
+        	Statement fieldStatement = listEntry.getProperty(TextVocab.pField);
+        	if (fieldStatement == null) {
+        		throw new TextIndexException("Text map entry has no field property");
+        	}
+        	n = fieldStatement.getObject();
+        	if (! n.isLiteral()) {
+        		throw new TextIndexException("Text map entry field property has no literal value : " + n);
+        	}
+        	String field = ((Literal)n).getLexicalForm();
+        	
+        	Statement predicateStatement = listEntry.getProperty(TextVocab.pPredicate);
+        	if (predicateStatement == null) {
+        		throw new TextIndexException("Text map entry has no predicate property");
+        	}
+        	n = predicateStatement.getObject();
+        	if (! n.isURIResource()) {
+        		throw new TextIndexException("Text map entry predicate property has non resource value : " + n);
+        	}
+        	Resource predicate = (Resource) n;
+        	mapDefs.put(field, predicate.asNode()) ;
+        	
+        	Statement analyzerStatement = listEntry.getProperty(TextVocab.pAnalyzer);
+        	if (analyzerStatement != null) {
+        		n = analyzerStatement.getObject();
+        		if (! n.isResource()) {
+        			throw new TextIndexException("Text map entry analyzer property is not a resource : " + n);
+        		}
+        		Resource analyzerResource = (Resource) n;
+        		Analyzer analyzer = (Analyzer) a.open(analyzerResource);
+        		analyzerDefs.put(field, analyzer);
+        	}
+        	
+        	// move on to the next element in the list
+        	listStmt = listResource.getProperty(RDF.rest);
+        }
+
         // Primary field/predicate
         if ( defaultField != null ) {
             Collection<Node> c = mapDefs.get(defaultField) ;
@@ -106,12 +160,14 @@ public class EntityDefinitionAssembler e
                 throw new TextIndexException("No definition of primary field '"+defaultField+"'") ;
         }
         
-        
         EntityDefinition docDef = new EntityDefinition(entityField, defaultField, graphField) ;
         for ( String f : mapDefs.keys() ) {
             for ( Node p : mapDefs.get(f)) 
                 docDef.set(f, p) ;
         }
+        for (String f : analyzerDefs.keySet()) {
+        	docDef.setAnalyzer(f, analyzerDefs.get(f));
+        }
         return docDef ;
     }
 }

Modified: jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextAssembler.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextAssembler.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextAssembler.java (original)
+++ jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextAssembler.java Wed Mar 26 10:18:40 2014
@@ -30,6 +30,9 @@ public class TextAssembler
         Assembler.general.implementWith(TextVocab.entityMap,        new EntityDefinitionAssembler()) ;
         Assembler.general.implementWith(TextVocab.textIndexSolr,    new TextIndexSolrAssembler()) ; 
         Assembler.general.implementWith(TextVocab.textIndexLucene,  new TextIndexLuceneAssembler()) ;
+        Assembler.general.implementWith(TextVocab.standardAnalyzer, new StandardAnalyzerAssembler()) ;
+        Assembler.general.implementWith(TextVocab.simpleAnalyzer,	new SimpleAnalyzerAssembler()) ;
+        Assembler.general.implementWith(TextVocab.keywordAnalyzer,	new KeywordAnalyzerAssembler()) ;
     }
 }
 

Modified: jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextVocab.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextVocab.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextVocab.java (original)
+++ jena/trunk/jena-text/src/main/java/org/apache/jena/query/text/assembler/TextVocab.java Wed Mar 26 10:18:40 2014
@@ -47,6 +47,13 @@ public class TextVocab
     public static final Property pField             = Vocab.property(NS, "field") ;
     public static final Property pPredicate         = Vocab.property(NS, "predicate") ;
     public static final Property pOptional          = Vocab.property(NS, "optional") ;
+    
+    // Analyzers
+    public static final Property pAnalyzer          = Vocab.property(NS, "analyzer");
+    public static final Resource standardAnalyzer   = Vocab.resource(NS, "StandardAnalyzer");
+    public static final Property pStopWords         = Vocab.property(NS, "stopWords");
+    public static final Resource simpleAnalyzer     = Vocab.resource(NS, "SimpleAnalyzer");
+    public static final Resource keywordAnalyzer    = Vocab.resource(NS, "KeywordAnalyzer");
 
 }
 

Modified: jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java (original)
+++ jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/AbstractTestDatasetWithTextIndex.java Wed Mar 26 10:18:40 2014
@@ -18,11 +18,6 @@
 
 package org.apache.jena.query.text;
 
-import static org.junit.Assert.assertEquals ;
-import static org.junit.Assert.assertTrue ;
-
-import java.io.Reader ;
-import java.io.StringReader ;
 import java.util.Arrays ;
 import java.util.HashSet ;
 import java.util.Set ;
@@ -30,29 +25,13 @@ import java.util.Set ;
 import org.apache.jena.atlas.lib.StrUtils ;
 import org.junit.Test ;
 
-import com.hp.hpl.jena.query.* ;
-import com.hp.hpl.jena.rdf.model.Model ;
-
 /*
  * This abstract class defines a collection of test methods for testing
  * test searches.  Its subclasses create a dataset using the index to 
  * to be tested and then call the test methods in this class to run
  * the actual tests.
  */
-public abstract class AbstractTestDatasetWithTextIndex {
-	protected static final String RESOURCE_BASE = "http://example.org/data/resource/";
-	protected static Dataset dataset;
-	protected static final String QUERY_PROLOG = 
-			StrUtils.strjoinNL(
-				"PREFIX text: <http://jena.apache.org/text#>",
-				"PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>"
-				);
-	
-	protected static final String TURTLE_PROLOG = 
-				StrUtils.strjoinNL(
-						"@prefix text: <http://jena.apache.org/text#> .",
-						"@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> ."
-						);
+public abstract class AbstractTestDatasetWithTextIndex extends AbstractTestDatasetWithTextIndexBase {
 	
 	@Test
 	public void testOneSimpleResult() {
@@ -429,69 +408,4 @@ public abstract class AbstractTestDatase
 		    ));
 		doTestSearch("default field:", turtle, queryString, expectedURIs, 3 );
 	}
-	
-	protected void doTestSearch(String turtle, String queryString, Set<String> expectedEntityURIs) {
-		doTestSearch("", turtle, queryString, expectedEntityURIs);
-	}
-	
-	private void doTestSearch(String label, String turtle, String queryString, Set<String> expectedEntityURIs) {
-		doTestSearch(label, turtle, queryString, expectedEntityURIs, expectedEntityURIs.size());
-	}
-	
-	private void doTestSearch(String label, String turtle, String queryString, Set<String> expectedEntityURIs, int expectedNumResults) {
-		Model model = dataset.getDefaultModel();
-		Reader reader = new StringReader(turtle);
-		dataset.begin(ReadWrite.WRITE);
-		model.read(reader, "", "TURTLE");
-		dataset.commit();
-		doTestQuery(dataset, label, queryString, expectedEntityURIs, expectedNumResults);
-	}
-	
-	public static void doTestQuery(Dataset dataset, String label, String queryString, Set<String> expectedEntityURIs, int expectedNumResults) {
-		Query query = QueryFactory.create(queryString) ;
-		QueryExecution qexec = QueryExecutionFactory.create(query, dataset) ;
-		try {
-			dataset.begin(ReadWrite.READ);
-		    ResultSet results = qexec.execSelect() ;
-		    
-		    boolean b = ( (expectedNumResults > 0) == results.hasNext() ) ;
-		    if ( !b ) {
-		        System.out.println(queryString) ;
-		        //System.out.println(expectedNumResults) ;
-		    }
-		    assertEquals(label, expectedNumResults > 0, results.hasNext());
-		    int count;
-		    for (count=0; results.hasNext(); count++) {
-		    	String entityURI = results.next().getResource("s").getURI();
-		        assertTrue(label + ": unexpected result: " + entityURI, expectedEntityURIs.contains(entityURI));
-		    }
-		    assertEquals(label, expectedNumResults, count);
-		} finally { qexec.close() ; dataset.end() ; }		
-	}
-	
-//	private void dumpSolrTextIndex(TextIndexSolr index) {
-//		System.out.println("Index:");
-//		SolrServer solrServer = index.getServer();
-//        SolrQuery sq = new SolrQuery("*:*");
-//        try {
-//            QueryResponse rsp = solrServer.query( sq ) ;
-//            SolrDocumentList docs = rsp.getResults();
-//            Iterator<SolrDocument> iter = docs.iterator();
-//            while (iter.hasNext()) {
-//            	SolrDocument doc = iter.next();
-//            	Iterator<String> iterFieldNames = doc.getFieldNames().iterator();
-//            	while (iterFieldNames.hasNext()) {
-//            		System.out.println("  --");
-//            		String fieldName = iterFieldNames.next();
-//            		Object fieldValue = doc.getFieldValue(fieldName);
-//            		System.out.println("    " + fieldName + " : " + fieldValue);
-//            		System.out.println("  --");
-//            	}
-//            	
-//            }
-//        } catch (SolrServerException e) { 
-//        	e.printStackTrace();
-//        }
-//        System.out.println("**");		
-//	}
-}
+}
\ No newline at end of file

Modified: jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/TS_Text.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/TS_Text.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/TS_Text.java (original)
+++ jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/TS_Text.java Wed Mar 26 10:18:40 2014
@@ -41,6 +41,9 @@ import org.junit.runners.Suite.SuiteClas
     , TestTextIndexLuceneAssembler.class
     , TestTextIndexSolrAssembler.class
     , TestTextTDB.class
+    , TestDatasetWithSimpleAnalyzer.class
+    , TestDatasetWithStandardAnalyzer.class
+    , TestDatasetWithKeywordAnalyzer.class
 })
 
 public class TS_Text

Modified: jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/assembler/TestEntityMapAssembler.java
URL: http://svn.apache.org/viewvc/jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/assembler/TestEntityMapAssembler.java?rev=1581780&r1=1581779&r2=1581780&view=diff
==============================================================================
--- jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/assembler/TestEntityMapAssembler.java (original)
+++ jena/trunk/jena-text/src/test/java/org/apache/jena/query/text/assembler/TestEntityMapAssembler.java Wed Mar 26 10:18:40 2014
@@ -24,22 +24,37 @@ import static org.junit.Assert.assertTru
 import java.util.Collection ;
 
 import org.apache.jena.atlas.lib.InternalErrorException ;
+import org.apache.jena.atlas.logging.LogCtl ;
 import org.apache.jena.query.text.EntityDefinition ;
 import org.apache.jena.query.text.TextIndexException ;
+import org.apache.lucene.analysis.core.KeywordAnalyzer ;
+import org.apache.lucene.analysis.core.SimpleAnalyzer ;
+import org.apache.lucene.analysis.standard.StandardAnalyzer ;
+import org.junit.AfterClass ;
+import org.junit.BeforeClass ;
 import org.junit.Test ;
 
+import com.hp.hpl.jena.assembler.Assembler ;
 import com.hp.hpl.jena.graph.Node ;
 import com.hp.hpl.jena.rdf.model.* ;
+import com.hp.hpl.jena.vocabulary.RDF ;
 import com.hp.hpl.jena.vocabulary.RDFS ;
 
 /**
  * Test assembler for EntityMap
  */
 public class TestEntityMapAssembler {
+    
+    // Suppress warnings
+    @BeforeClass public static void beforeClass() { LogCtl.setError(EntityDefinitionAssembler.class); }
+    @AfterClass  public static void afterClass()  { LogCtl.setInfo(EntityDefinitionAssembler.class); }
 	
 	private static final String TESTBASE = "http://example.org/test/";
 	private static final Resource spec1;
 	private static final Resource spec2;
+	private static final Resource spec3;
+	private static final Resource spec4;
+	private static final Resource spec5;
 	private static final Resource specNoEntityField;
 	private static final Resource specNoDefaultField;
 	private static final Resource specNoMapProperty;
@@ -78,6 +93,24 @@ public class TestEntityMapAssembler {
 		assertEquals(SPEC2_PREDICATE1.asNode(), getOne(entityDef,SPEC2_DEFAULT_FIELD));
 		assertEquals(SPEC2_PREDICATE2.asNode(), getOne(entityDef, SPEC2_FIELD2));
 	}
+    
+    @Test public void EntityHasMapEntryWithSimpleAnalyzer() {
+    	EntityDefinitionAssembler entDefAssem = new EntityDefinitionAssembler();
+    	EntityDefinition entityDef = entDefAssem.open(Assembler.general, spec3,  null);
+    	assertEquals(SimpleAnalyzer.class, entityDef.getAnalyzer(SPEC1_DEFAULT_FIELD).getClass());
+    }
+    
+    @Test public void EntityHasMapEntryWithStandardAnalyzerAndStopWords() {
+    	EntityDefinitionAssembler entDefAssem = new EntityDefinitionAssembler();
+    	EntityDefinition entityDef = entDefAssem.open(Assembler.general, spec4,  null);
+    	assertEquals(StandardAnalyzer.class, entityDef.getAnalyzer(SPEC1_DEFAULT_FIELD).getClass());
+    }
+    
+    @Test public void EntityHasMapEntryWithKeywordAnalyzer() {
+    	EntityDefinitionAssembler entDefAssem = new EntityDefinitionAssembler();
+    	EntityDefinition entityDef = entDefAssem.open(Assembler.general, spec5,  null);
+    	assertEquals(KeywordAnalyzer.class, entityDef.getAnalyzer(SPEC1_DEFAULT_FIELD).getClass());
+    }    
 	
 	@Test(expected=TextIndexException.class) public void errorOnNoEntityField() {
 		EntityDefinitionAssembler entDefAssem = new EntityDefinitionAssembler();
@@ -115,6 +148,7 @@ public class TestEntityMapAssembler {
 	private static final Property SPEC2_PREDICATE1 = RDFS.label;
 	private static final Property SPEC2_PREDICATE2 = RDFS.comment;
 	static {
+		TextAssembler.init();
 		Model model = ModelFactory.createDefaultModel();
 		
 		// create a simple entity map specification
@@ -148,7 +182,57 @@ public class TestEntityMapAssembler {
 				    		    				    
 				    		    		  }))
 				     ;
+		
+		// create a simple entity map specification using a keyword analyzer
+		
+				spec3 = model.createResource(TESTBASE + "spec3")
+						     .addProperty(TextVocab.pEntityField, SPEC1_ENTITY_FIELD)
+						     .addProperty(TextVocab.pDefaultField, SPEC1_DEFAULT_FIELD)
+						     .addProperty(TextVocab.pMap,
+						    		      model.createList(
+						    		    		  new RDFNode[] {
+						    		    				model.createResource()
+						    		    				     .addProperty(TextVocab.pField, SPEC1_DEFAULT_FIELD)
+						    		    				     .addProperty(TextVocab.pPredicate, SPEC1_PREDICATE)
+						    		    				     .addProperty(TextVocab.pAnalyzer, 
+						    		    				    		      model.createResource()
+						    		    				    		           .addProperty(RDF.type, TextVocab.simpleAnalyzer))
+						    		    		  }))
+						     ;
+				// create a simple entity map specification using a standard analyzer with stop words
+				
+				spec4 = model.createResource(TESTBASE + "spec4")
+						     .addProperty(TextVocab.pEntityField, SPEC1_ENTITY_FIELD)
+						     .addProperty(TextVocab.pDefaultField, SPEC1_DEFAULT_FIELD)
+						     .addProperty(TextVocab.pMap,
+						    		      model.createList(
+						    		    		  new RDFNode[] {
+						    		    				model.createResource()
+						    		    				     .addProperty(TextVocab.pField, SPEC1_DEFAULT_FIELD)
+						    		    				     .addProperty(TextVocab.pPredicate, SPEC1_PREDICATE)
+						    		    				     .addProperty(TextVocab.pAnalyzer, 
+						    		    				    		      model.createResource()
+						    		    				    		           .addProperty(RDF.type, TextVocab.standardAnalyzer))
+						    		    				    		           .addProperty(TextVocab.pStopWords, toList(model, "and the but"))
+						    		    		  }));		
+		// create a simple entity map specification using a keyword analyzer
+		
+				spec5 = model.createResource(TESTBASE + "spec5")
+						     .addProperty(TextVocab.pEntityField, SPEC1_ENTITY_FIELD)
+						     .addProperty(TextVocab.pDefaultField, SPEC1_DEFAULT_FIELD)
+						     .addProperty(TextVocab.pMap,
+						    		      model.createList(
+						    		    		  new RDFNode[] {
+						    		    				model.createResource()
+						    		    				     .addProperty(TextVocab.pField, SPEC1_DEFAULT_FIELD)
+						    		    				     .addProperty(TextVocab.pPredicate, SPEC1_PREDICATE)
+						    		    				     .addProperty(TextVocab.pAnalyzer, 
+						    		    				    		      model.createResource()
+						    		    				    		           .addProperty(RDF.type, TextVocab.keywordAnalyzer))
+						    		    		  }));
+				
 		// bad assembler spec
+				
 		specNoEntityField = 
 				model.createResource(TESTBASE + "specNoEntityField")
 				     .addProperty(TextVocab.pDefaultField, SPEC1_DEFAULT_FIELD)
@@ -160,7 +244,9 @@ public class TestEntityMapAssembler {
 				    		    				     .addProperty(TextVocab.pPredicate, SPEC1_PREDICATE)
 				    		    		  }))
 				     ;
+		
 		// bad assembler spec
+		
 		specNoDefaultField = 
 				model.createResource(TESTBASE + "specNoDefaultField")
 				     .addProperty(TextVocab.pDefaultField, SPEC1_DEFAULT_FIELD)
@@ -193,4 +279,16 @@ public class TestEntityMapAssembler {
 				    		    		  }))
 				     ;
 	}
+	
+	private static Resource toList(Model model, String string) {
+		String[] members = string.split("\\s");
+		Resource current = RDF.nil;
+		for (int i = members.length-1; i>=0; i--) {
+			Resource previous = current;
+			current = model.createResource();
+			current.addProperty(RDF.rest, previous);
+			current.addProperty(RDF.first, members[i]);			
+		}
+		return current;	
+	}
 }