Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.clerezza.rdf.cris; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import org.apache.clerezza.rdf.core.NonLiteral; import org.apache.clerezza.rdf.core.Resource; import org.apache.clerezza.rdf.core.Triple; import org.apache.clerezza.rdf.core.TripleCollection; import org.apache.clerezza.rdf.core.UriRef; import org.apache.clerezza.rdf.core.access.LockableMGraph; import org.apache.clerezza.rdf.core.event.FilterTriple; import org.apache.clerezza.rdf.core.event.GraphEvent; import org.apache.clerezza.rdf.core.event.GraphListener; import org.apache.clerezza.rdf.cris.ontologies.CRIS; import org.apache.clerezza.rdf.ontologies.RDF; import org.apache.clerezza.rdf.utils.GraphNode; import org.apache.clerezza.rdf.utils.RdfList; 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.index.IndexWriter; import org.apache.lucene.index.Term; import org.apache.lucene.queryparser.classic.ParseException; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.Sort; import org.apache.lucene.search.TopFieldDocs; import org.apache.lucene.search.TopScoreDocCollector; import org.apache.lucene.store.Directory; import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.util.Version; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Creates an index of RDF resources and provides an interface to search for * indexed resources. * * @author reto, tio, daniel */ public class GraphIndexer extends ResourceFinder { /** * Default value for {@code maxhits}. */ public static final int DEFAULT_MAXHITS = 100000; private final Logger logger = LoggerFactory.getLogger(getClass()); /** * Prefix for stored Lucene fields. */ static final String SORT_PREFIX = "_STORED_"; /** * Field name for the resource field in Lucene. */ static final String URI_FIELD_NAME = "resource-uri"; /** * Handler for asynchronous indexing. */ ReindexThread reindexer; private TripleCollection definitionGraph; private TripleCollection baseGraph; private int maxHits; private Analyzer analyzer; private LuceneTools luceneTools; private Map<UriRef, Set<VirtualProperty>> type2IndexedProperties = null; private Map<VirtualProperty, Set<UriRef>> property2TypeMap = new HashMap<VirtualProperty, Set<UriRef>>(); private Map<UriRef, Set<VirtualProperty>> property2IncludingVProperty = new HashMap<UriRef, Set<VirtualProperty>>(); private final GraphListener typeChangeListener; private final GraphListener indexedPropertyChangeListener; private Map<SortFieldArrayWrapper, Sort> sortCache = new HashMap<SortFieldArrayWrapper, Sort>(); private Timer timer = new Timer(); private final OptimizationTask optimizationTask = new OptimizationTask(); /** * Allows to schedule optimizations using a Timer. * * NOTE: not for public access as this functionality is likely to be moved * into a stand-alone service. */ private class OptimizationTask extends TimerTask { @Override public void run() { optimizeIndex(); } } /** * When resources are (re)-indexed, this thread updates the Lucene index * asynchronously. */ class ReindexThread extends Thread { private final long resourceCacheCapacity; private final long stableThreshold; private final String name; private final Set<Resource> resourcesToProcess; private final Lock lock = new ReentrantLock(true); private final java.util.concurrent.locks.Condition indexResources = lock.newCondition(); private long counter; private boolean stop; private boolean resourcesClean; /** * Constructs a new thread with specified name and indexing threshold. * Setting the name may be useful for distinguishing logging output when * multiple instances of GraphIndexer are running. * * {@code stableThreshold} specifies a waiting period before the * indexing starts. The timer is restarted if more resources are added * within {@code stableThreshold} nanoseconds. A high value means the * thread will wait a long time before indexing resources added using * {@link addResource(Resource resource)}. A short value means new * resources are added to the index quickly. Configure this value such * that when adding many new resources in a short time these are * gathered and indexed at once. * * @param name the thread name (used in logging output). * @param stableThreshold If no new resource has been added for * {@code stableThreshold} nanoseconds and there are cached unindexed * resources, then indexing starts. * @param resourceCacheCapacity How many resources will be cached * maximally before indexing. A negative number means infinite. */ ReindexThread(String name, long stableThreshold, long resourceCacheCapacity) { this.resourceCacheCapacity = resourceCacheCapacity; this.stableThreshold = stableThreshold; this.name = name; this.resourcesToProcess = new HashSet<Resource>(); this.resourcesClean = true; } /** * Constructs a new thread with specified indexing threshold. * * @code stableThreshold} specifies a waiting period before the indexing * starts. The timer is restarted if more resources are added within * {@code stableThreshold} nanoseconds. A high value means the thread * will wait a long time before indexing resources added using * {@link addResource(Resource resource)}. A short value means new * resources are added to the index quickly. Configure this value such * that when adding many new resources in a short time these are * gathered and indexed at once. * * @param stableThreshold If no new resource has been added for * {@code stableThreshold} nanoseconds and there are cached unindexed * resources, then indexing starts. * @param resourceCacheCapacity How many resources will be cached * maximally before indexing. A negative number means infinite. */ ReindexThread(long stableThreshold, long resourceCacheCapacity) { this(null, stableThreshold, resourceCacheCapacity); } /** * Request the termination of this thread. The thread will finish its * current operations before it terminates. */ void stopThread() { stop = true; lock.lock(); try { indexResources.signal(); } finally { lock.unlock(); } } @Override public void run() { if (name == null) { setName("CRIS Reindex Thread[" + getId() + "]"); } else { setName(name); } stop = false; counter = 0; Set<Resource> set; logger.info("{} started.", getName()); while (true) { try { lock.lock(); try { waitForDirty(); if (stop) { break; } logger.debug("{}: registered write - waiting for more writes to follow.", getName()); waitUntilStable(); set = new HashSet<Resource>(resourcesToProcess); resourcesToProcess.clear(); counter = 0; resourcesClean = true; } finally { lock.unlock(); } logger.info("{}: cache full or writes have ceased. Indexing...", getName()); for (Resource resource : set) { indexResource(resource, luceneTools.getIndexWriter()); } luceneTools.commitChanges(); } catch (InterruptedException ex) { logger.warn("{}: interrupted: {}.", getName(), ex); } } logger.info("{} stopped.", getName()); } private void waitUntilStable() throws InterruptedException { while (!resourcesClean) { resourcesClean = true; indexResources.awaitNanos(stableThreshold); if (resourceCacheCapacity >= 0 && ++counter > resourceCacheCapacity) { break; } } } private void waitForDirty() throws InterruptedException { while (resourcesClean && !stop) { indexResources.await(); } } /** * Add a new resource for indexing. * * @param resource the resource. */ public void addResource(Resource resource) { lock.lock(); try { resourcesToProcess.add(resource); resourcesClean = false; indexResources.signal(); } finally { lock.unlock(); } } } /** * Creates a new index. * * The {@code GraphIndexer} looks for specifications of what properties on * what resources to index in the {@code definitionGraph}. * * The {@code baseGraph} specifies the graph on which the index is built. * * <p> * Notes: * * <p> * This is an expensive operation and it is advisable to call * {@link #closeLuceneIndex()} when this instance is no longer needed. * </p><p> * The GraphIndexer must have write-access to the index directory specified. * </p> * * @param definitionGraph where index definitions are stored * @param baseGraph where the resources to index are stored * @param indexDirectory The directory where the index is stored. * @param createNewIndex Whether to create a new index or reuse an existing * one. The constructor does not check if there is a valid exiting index. * The user is responsible for setting this value correctly. * @param maxHits How many results the indexer returns. All entries in the * index are searched, but only @code{maxHits} resources are resolved and * returned in the result. * * @see IndexDefinitionManager */ public GraphIndexer(TripleCollection definitionGraph, TripleCollection baseGraph, Directory indexDirectory, boolean createNewIndex, int maxHits) { this.definitionGraph = definitionGraph; this.baseGraph = baseGraph; this.maxHits = maxHits; analyzer = new StandardAnalyzer(Version.LUCENE_41); luceneTools = new LuceneTools(indexDirectory, analyzer); processDefinitions(); this.reindexer = new ReindexThread(100000000L, 500000L); typeChangeListener = new GraphListener() { @Override public void graphChanged(List<GraphEvent> events) { for (GraphEvent e : events) { Triple triple = e.getTriple(); logger.debug("processing addition of type " + triple.getObject()); if (type2IndexedProperties.containsKey((UriRef) triple.getObject())) { scheduleForReindex(triple.getSubject()); } } } }; baseGraph.addGraphListener(typeChangeListener, new FilterTriple(null, RDF.type, null)); indexedPropertyChangeListener = new GraphListener() { @Override public void graphChanged(List<GraphEvent> events) { for (GraphEvent e : events) { logger.debug("Triple: " + e.getTriple()); Triple triple = e.getTriple(); UriRef predicate = triple.getPredicate(); Set<VirtualProperty> vProperties = property2IncludingVProperty.get(predicate); List<Resource> indexedResources = new ArrayList<Resource>(); logger.debug("Predicate: " + predicate); for (VirtualProperty vProperty : vProperties) { logger.debug("Subject: " + " " + triple.getSubject()); followInversePaths(triple.getSubject(), vProperty.pathToIndexedResource(predicate), indexedResources); } for (Resource resource : indexedResources) { GraphNode node = new GraphNode(resource, e.getGraph()); List<UriRef> types = new ArrayList<UriRef>(); Lock lock = node.readLock(); lock.lock(); try { Iterator<Resource> resources = node.getObjects(RDF.type); while (resources.hasNext()) { Resource res = resources.next(); if (res instanceof UriRef) { types.add((UriRef) res); } } } finally { lock.unlock(); } for (UriRef type : types) { if (type2IndexedProperties.containsKey(type)) { scheduleForReindex(resource); } } } } } }; baseGraph.addGraphListener(indexedPropertyChangeListener, new FilterTriple(null, null, null) { @Override public boolean match(Triple triple) { UriRef predicate = triple.getPredicate(); //check indirectly involved properties Set<VirtualProperty> vProperties = property2IncludingVProperty.get(predicate); if (vProperties != null) { for (VirtualProperty vProperty : vProperties) { if (property2TypeMap.containsKey(vProperty)) { return true; } } } return false; } }); reindexer.start(); if (createNewIndex) { reCreateIndex(); } } /** * Creates a new index with default {@code maxHits}. * * The {@code GraphIndexer} looks for specifications of what properties on * what resources to index in the {@code definitionGraph}. * * The {@code baseGraph} specifies the graph on which the index is built. * * <p> * Notes: * * <p> * This is an expensive operation and it is advisable to call * {@link #closeLuceneIndex()} when this instance is no longer needed. * </p><p> * The GraphIndexer must have write-access to the index directory specified. * </p> * * @param definitionGraph where index definitions are stored * @param baseGraph where the resources to index are stored * @param indexDirectory The directory where the index is stored. * @param createNewIndex Whether to create a new index or reuse an existing * one. The constructor does not check if there is a valid exiting index. * The user is responsible for setting this value correctly. */ public GraphIndexer(TripleCollection definitionGraph, TripleCollection baseGraph, Directory indexDirectory, boolean createNewIndex) { this(definitionGraph, baseGraph, indexDirectory, createNewIndex, DEFAULT_MAXHITS); } /** * Creates a new in-memory index with default {@code maxHits}. * * The {@code GraphIndexer} looks for specifications of what properties on * what resources to index in the {@code definitionGraph}. * * The {@code baseGraph} specifies the graph on which the index is built. * * <p> * Notes: * * <p> * This is an expensive operation and it is advisable to call * {@link #closeLuceneIndex()} when this instance is no longer needed. * </p><p> * The GraphIndexer must have write-access to the index directory specified. * </p> * * @param definitionGraph where index definitions are stored * @param baseGraph where the resources to index are stored */ public GraphIndexer(TripleCollection definitionGraph, TripleCollection baseGraph) { this(definitionGraph, baseGraph, new RAMDirectory(), true); } /** * Releases resources held by GraphIndexer. After the call to this method, * this GraphIndexer instance must not be used anymore. */ public void closeLuceneIndex() { this.baseGraph.removeGraphListener(typeChangeListener); this.baseGraph.removeGraphListener(indexedPropertyChangeListener); this.reindexer.stopThread(); this.luceneTools.closeIndexSearcher(); this.luceneTools.closeIndexWriter(); this.sortCache.clear(); } /** * Returns the Analyzer used by this GraphIndexer instance. * * @return the Analyzer */ public Analyzer getAnalyzer() { return analyzer; } /** * Returns the graph that this GraphIndexer builds an index on. * * @return The graph containing the indexed resources. */ public TripleCollection getBaseGraph() { return baseGraph; } /** * Returns the graph where the index definitions are stored. * * @return The graph with the index definitions. */ public TripleCollection getDefinitionGraph() { return definitionGraph; } /** * How many results a search on the index returns maximally. * * @return the maximum number of results. */ public int getMaxHits() { return maxHits; } /** * Set how many results a search on the index returns maximally. * * @param maxHits the maximum number of results. */ public void setMaxHits(int maxHits) { this.maxHits = maxHits; } @Override public void optimizeIndex() { luceneTools.optimizeIndex(); } /** * Schedule optimizations for repeated executions. * * @param delay The delay before the first execution in milliseconds. * @param period Time between successive executions (execution rate) in * milliseconds. */ public void scheduleIndexOptimizations(long delay, long period) { if (timer != null) { timer.cancel(); } timer = new Timer(); timer.scheduleAtFixedRate(optimizationTask, delay, period); } /** * Cancel scheduled optimizations. This call does not have any effect on * optimizations that are being executed while the method is called. */ public void terminateIndexOptimizationSchedule() { timer.cancel(); timer = null; } @Override public void reCreateIndex() { processDefinitions(); List<NonLiteral> instances = new ArrayList<NonLiteral>(); for (UriRef indexedType : type2IndexedProperties.keySet()) { //lock necessary? Lock lock = new GraphNode(indexedType, this.baseGraph).readLock(); lock.lock(); try { Iterator<Triple> iter = this.baseGraph.filter(null, RDF.type, indexedType); while (iter.hasNext()) { instances.add(iter.next().getSubject()); } } finally { lock.unlock(); } } logger.debug("instances " + instances.size()); IndexWriter writer = luceneTools.getIndexWriter(true); try { writer.deleteAll(); } catch (IOException ex) { throw new RuntimeException(ex); } for (NonLiteral instance : instances) { indexResource(instance, writer); } luceneTools.commitChanges(); } @Override public List<NonLiteral> findResources(List<? extends Condition> conditions, SortSpecification sortSpecification, FacetCollector... facetCollectors) throws ParseException { return findResources(conditions, sortSpecification, Arrays.asList(facetCollectors), 0, maxHits + 1); } /** * Find resources using conditions and collect facets and specify a sort * order. * * This method allows to specify the indices of the query results to return * (e.g. for pagination). * * @param conditions a list of conditions to construct a query from. * @param facetCollectors Facet collectors to apply to the query result. Can * be {@link Collections#EMPTY_LIST}, if not used. * @param sortSpecification Specifies the sort order. Can be null, if not * used. * @param from return results starting from this index (inclusive). * @param to return results until this index (exclusive). * @return a list of resources that match the query. * * @throws ParseException when the resulting query is illegal. */ public List<NonLiteral> findResources(List<? extends Condition> conditions, SortSpecification sortSpecification, List<FacetCollector> facetCollectors, int from, int to) throws ParseException { if (from < 0) { from = 0; } if (to < from) { to = from + 1; } if (facetCollectors == null) { facetCollectors = Collections.EMPTY_LIST; } BooleanQuery booleanQuery = new BooleanQuery(); for (Condition c : conditions) { booleanQuery.add(c.query(), BooleanClause.Occur.MUST); } IndexSearcher searcher = luceneTools.getIndexSearcher(); ScoreDoc[] hits = null; try { if (sortSpecification != null) { SortFieldArrayWrapper fieldKey = new SortFieldArrayWrapper(sortSpecification.getSortFields()); Sort sort = sortCache.get(fieldKey); if (sort == null) { sort = new Sort(sortSpecification.getSortFields()); sortCache.put(fieldKey, sort); } //searcher.setDefaultFieldSortScoring(true, true); TopFieldDocs topFieldDocs = searcher.search(booleanQuery, null, to, sort); hits = topFieldDocs.scoreDocs; } else { TopScoreDocCollector collector = TopScoreDocCollector.create(to, true); searcher.search(booleanQuery, collector); hits = collector.topDocs().scoreDocs; } } catch (IOException ex) { throw new RuntimeException(ex); } List<NonLiteral> result = new ArrayList<NonLiteral>(); for (int i = from; i < hits.length; ++i) { int docId = hits[i].doc; Document d; try { d = searcher.doc(docId); collectFacets(facetCollectors, d); result.add(getResource(d)); } catch (IOException ex) { logger.error("CRIS Error: ", ex); } } for (FacetCollector facetCollector : facetCollectors) { facetCollector.postProcess(); } return result; } @Override public void finalize() throws Throwable { super.finalize(); closeLuceneIndex(); } /** * Schedule an update or creation of an index for a resource. * * @param resource the resource to index. */ protected void scheduleForReindex(Resource resource) { logger.debug("Scheduling for reindex: " + resource); reindexer.addResource(resource); } /** * Read the index definitions and initialize the GraphIndexer with them. */ protected void processDefinitions() { Lock l; if (definitionGraph instanceof LockableMGraph) { l = ((LockableMGraph) definitionGraph).getLock().readLock(); } else { l = new ReentrantLock(); } Map<UriRef, Set<VirtualProperty>> type2IndexedPropertiesTuples = new HashMap<UriRef, Set<VirtualProperty>>(); l.lock(); try { Iterator<Triple> indexDefinitionResources = this.definitionGraph.filter(null, RDF.type, CRIS.IndexDefinition); while (indexDefinitionResources.hasNext()) { GraphNode node = new GraphNode(indexDefinitionResources.next().getSubject(), this.definitionGraph); Iterator<GraphNode> types = node.getObjectNodes(CRIS.indexedType); while (types.hasNext()) { UriRef tUri = (UriRef) types.next().getNode(); Iterator<GraphNode> properties = node.getObjectNodes(CRIS.indexedProperty); Set<VirtualProperty> props = new HashSet<VirtualProperty>(); while (properties.hasNext()) { VirtualProperty vProp = asVirtualProperty(properties.next(), null); if (property2TypeMap.containsKey(vProp)) { property2TypeMap.get(vProp).add(tUri); } else { Set<UriRef> set = new HashSet<UriRef>(); set.add(tUri); property2TypeMap.put(vProp, set); } for (UriRef baseProperty : vProp.baseProperties) { if (property2IncludingVProperty.containsKey(baseProperty)) { property2IncludingVProperty.get(baseProperty).add(vProp); } else { Set<VirtualProperty> set = new HashSet<VirtualProperty>(); set.add(vProp); property2IncludingVProperty.put(baseProperty, set); } } props.add(vProp); } type2IndexedPropertiesTuples.put(tUri, props); } } } finally { l.unlock(); } type2IndexedProperties = new HashMap(type2IndexedPropertiesTuples); } /** * Index a resource. * * @param resource the resource to index. * @param writer the index writer. */ protected void indexResource(Resource resource, IndexWriter writer) { if (resource instanceof UriRef) { try { indexNamedResource((UriRef) resource, writer); } catch (IOException ex) { throw new RuntimeException(ex); } } else { indexAnonymousResource(resource); } } private NonLiteral getResource(Document d) { return new UriRef(d.get(URI_FIELD_NAME)); } private void followInversePaths(Resource resource, List<UriRef> pathToIndexedResource, List<Resource> list) { if (pathToIndexedResource.isEmpty()) { list.add(resource); } else { GraphNode node = new GraphNode(resource, this.baseGraph); Lock lock = node.readLock(); lock.lock(); try { Iterator<NonLiteral> predecessors = node.getSubjects(pathToIndexedResource.get(0)); List<UriRef> tail = pathToIndexedResource.subList(1, pathToIndexedResource.size()); while (predecessors.hasNext()) { followInversePaths(predecessors.next(), tail, list); } } finally { lock.unlock(); } //throw new RuntimeException("modification of indirect properties not yet supported") } } private VirtualProperty asVirtualProperty(GraphNode r, List<VirtualProperty> vPropertyList) { if (r.hasProperty(RDF.type, CRIS.JoinVirtualProperty)) { if (vPropertyList == null) { vPropertyList = new ArrayList<VirtualProperty>(); } return new JoinVirtualProperty(getVirtualPropertyList(r, vPropertyList), r.hasProperty(RDF.type, CRIS.FacetProperty)); } else { if (r.hasProperty(RDF.type, CRIS.PathVirtualProperty)) { return new PathVirtualProperty(getUriPropertyList(r), r.hasProperty(RDF.type, CRIS.FacetProperty)); } else { if ((r.getNode()) instanceof UriRef) { return new PropertyHolder((UriRef) r.getNode(), r.hasProperty(RDF.type, CRIS.FacetProperty)); } else { throw new RuntimeException( r + " is not of a knows VirtualProperty type and its not a UriRef (it's a " + (r.getNode()).getClass() + ")"); } } } } private List<VirtualProperty> getVirtualPropertyList(GraphNode r, List<VirtualProperty> vPropertyList) { List<Resource> rdfList = getPropertyList(r); for (Resource childPropertyResource : rdfList) { vPropertyList.add(asVirtualProperty(new GraphNode(childPropertyResource, r.getGraph()), vPropertyList)); } return vPropertyList; } private List<UriRef> getUriPropertyList(GraphNode r) { List<UriRef> uriPropertyList = new ArrayList<UriRef>(); List<Resource> rdfList = getPropertyList(r); for (Resource childPropertyResource : rdfList) { uriPropertyList.add((UriRef) childPropertyResource); } return uriPropertyList; } private List<Resource> getPropertyList(GraphNode r) { Iterator<GraphNode> propertyLists = r.getObjectNodes(CRIS.propertyList); if (propertyLists.hasNext()) { List<Resource> rdfList = new RdfList(propertyLists.next()); return rdfList; } throw new RuntimeException("There is no propertyList on this definition."); } private void collectFacets(List<FacetCollector> facetCollectors, Document d) { if (facetCollectors.size() > 0) { for (FacetCollector facetCollector : facetCollectors) { Map<VirtualProperty, Map<String, Object>> facetMap = facetCollector.getFacetMap(); for (VirtualProperty property : facetMap.keySet()) { String[] values = d.getValues(SORT_PREFIX + property.getStringKey()); if (values != null) { for (String value : values) { facetCollector.addFacetValue(property, value); } } } } } } private void indexNamedResource(UriRef uriRef, IndexWriter writer) throws IOException { Term term = new Term(URI_FIELD_NAME, uriRef.getUnicodeString()); writer.deleteDocuments(term); //the reindexing might be caused by the removal of a type statement GraphNode node = new GraphNode(uriRef, this.baseGraph); List<UriRef> types = new ArrayList<UriRef>(); Lock lock = node.readLock(); lock.lock(); try { Iterator<Resource> resources = node.getObjects(RDF.type); while (resources.hasNext()) { Resource res = resources.next(); if (res instanceof UriRef) { types.add((UriRef) res); } } } finally { lock.unlock(); } for (UriRef type : types) { if (type2IndexedProperties.containsKey(type)) { Document doc = resourceToDocument(uriRef, type); doc.add(new Field(URI_FIELD_NAME, uriRef.getUnicodeString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); writer.addDocument(doc); } } } private Document resourceToDocument(UriRef resource, UriRef resourceType) { Document doc = new Document(); Set<VirtualProperty> indexedProperties = type2IndexedProperties.get(resourceType); logger.info("indexing " + resource + " considering " + indexedProperties.size() + " properties (" + indexedProperties + ")"); for (VirtualProperty vProperty : indexedProperties) { logger.info("indexing " + vProperty + " with values " + (vProperty.value(new GraphNode(resource, this.baseGraph))).size()); for (String propertyValue : vProperty.value(new GraphNode(resource, this.baseGraph))) { logger.info( "indexing " + vProperty + "(" + vProperty.stringKey + ") with value " + (propertyValue)); //for sorting doc.add(new Field(SORT_PREFIX + vProperty.stringKey, propertyValue, Field.Store.YES, Field.Index.NOT_ANALYZED_NO_NORMS)); //for searching (the extra field doesn't cost much time) doc.add(new Field(vProperty.stringKey, propertyValue, Field.Store.NO, Field.Index.NOT_ANALYZED)); doc.add(new Field(vProperty.stringKey, propertyValue, Field.Store.NO, Field.Index.ANALYZED)); } } return doc; } private void indexAnonymousResource(Resource resource) { logger.warn("Currently only indexing named resources is supported"); /*val doc = resourceToDocument(resource) doc.add(new Field(URI_FIELD_NAME, getIdentifier(resource), Field.Store.YES, Field.Index.ANALYZED)) writer.addDocument(doc)*/ } }