This commit is contained in:
Jonathan Cook
2019-10-23 15:01:44 +02:00
parent db85c8f275
commit 684ec0d2e3
20486 changed files with 1642483 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/index/
+9
View File
@@ -0,0 +1,9 @@
## Apache Lucene
This module contains articles about Apache Lucene.
### Relevant Articles:
- [Introduction to Apache Lucene](https://www.baeldung.com/lucene)
- [A Simple File Search with Lucene](https://www.baeldung.com/lucene-file-search)
- [Guide to Lucene Analyzers](https://www.baeldung.com/lucene-analyzers)
+42
View File
@@ -0,0 +1,42 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>lucene</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>lucene</name>
<description>An Apache Lucene demo application</description>
<parent>
<groupId>com.baeldung</groupId>
<artifactId>parent-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencies>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>${lucene.version}</version>
</dependency>
</dependencies>
<properties>
<!-- various -->
<hibernate-jpa-2.1-api.version>1.0.0.Final</hibernate-jpa-2.1-api.version>
<!-- delombok maven plugin -->
<delombok-maven-plugin.version>1.16.10.0</delombok-maven-plugin.version>
<lucene.version>7.4.0</lucene.version>
</properties>
</project>
@@ -0,0 +1,128 @@
package com.baeldung.lucene;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.SortedDocValuesField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.util.BytesRef;
public class InMemoryLuceneIndex {
private Directory memoryIndex;
private Analyzer analyzer;
public InMemoryLuceneIndex(Directory memoryIndex, Analyzer analyzer) {
super();
this.memoryIndex = memoryIndex;
this.analyzer = analyzer;
}
/**
*
* @param title
* @param body
*/
public void indexDocument(String title, String body) {
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
try {
IndexWriter writter = new IndexWriter(memoryIndex, indexWriterConfig);
Document document = new Document();
document.add(new TextField("title", title, Field.Store.YES));
document.add(new TextField("body", body, Field.Store.YES));
document.add(new SortedDocValuesField("title", new BytesRef(title)));
writter.addDocument(document);
writter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public List<Document> searchIndex(String inField, String queryString) {
try {
Query query = new QueryParser(inField, analyzer).parse(queryString);
IndexReader indexReader = DirectoryReader.open(memoryIndex);
IndexSearcher searcher = new IndexSearcher(indexReader);
TopDocs topDocs = searcher.search(query, 10);
List<Document> documents = new ArrayList<>();
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
documents.add(searcher.doc(scoreDoc.doc));
}
return documents;
} catch (IOException | ParseException e) {
e.printStackTrace();
}
return null;
}
public void deleteDocument(Term term) {
try {
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
IndexWriter writter = new IndexWriter(memoryIndex, indexWriterConfig);
writter.deleteDocuments(term);
writter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public List<Document> searchIndex(Query query) {
try {
IndexReader indexReader = DirectoryReader.open(memoryIndex);
IndexSearcher searcher = new IndexSearcher(indexReader);
TopDocs topDocs = searcher.search(query, 10);
List<Document> documents = new ArrayList<>();
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
documents.add(searcher.doc(scoreDoc.doc));
}
return documents;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public List<Document> searchIndex(Query query, Sort sort) {
try {
IndexReader indexReader = DirectoryReader.open(memoryIndex);
IndexSearcher searcher = new IndexSearcher(indexReader);
TopDocs topDocs = searcher.search(query, 10, sort);
List<Document> documents = new ArrayList<>();
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
documents.add(searcher.doc(scoreDoc.doc));
}
return documents;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,80 @@
package com.baeldung.lucene;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
public class LuceneFileSearch {
private Directory indexDirectory;
private StandardAnalyzer analyzer;
public LuceneFileSearch(Directory fsDirectory, StandardAnalyzer analyzer) {
super();
this.indexDirectory = fsDirectory;
this.analyzer = analyzer;
}
public void addFileToIndex(String filepath) throws IOException, URISyntaxException {
Path path = Paths.get(getClass().getClassLoader().getResource(filepath).toURI());
File file = path.toFile();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
IndexWriter indexWriter = new IndexWriter(indexDirectory, indexWriterConfig);
Document document = new Document();
FileReader fileReader = new FileReader(file);
document.add(new TextField("contents", fileReader));
document.add(new StringField("path", file.getPath(), Field.Store.YES));
document.add(new StringField("filename", file.getName(), Field.Store.YES));
indexWriter.addDocument(document);
indexWriter.close();
}
public List<Document> searchFiles(String inField, String queryString) {
try {
Query query = new QueryParser(inField, analyzer).parse(queryString);
IndexReader indexReader = DirectoryReader.open(indexDirectory);
IndexSearcher searcher = new IndexSearcher(indexReader);
TopDocs topDocs = searcher.search(query, 10);
List<Document> documents = new ArrayList<>();
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
documents.add(searcher.doc(scoreDoc.doc));
}
return documents;
} catch (IOException | ParseException e) {
e.printStackTrace();
}
return null;
}
}
@@ -0,0 +1,26 @@
package com.baeldung.lucene;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.LowerCaseFilter;
import org.apache.lucene.analysis.StopFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.en.PorterStemFilter;
import org.apache.lucene.analysis.miscellaneous.CapitalizationFilter;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.standard.StandardFilter;
import org.apache.lucene.analysis.standard.StandardTokenizer;
public class MyCustomAnalyzer extends Analyzer{
@Override
protected TokenStreamComponents createComponents(String fieldName) {
final StandardTokenizer src = new StandardTokenizer();
TokenStream result = new StandardFilter(src);
result = new LowerCaseFilter(result);
result = new StopFilter(result, StandardAnalyzer.STOP_WORDS_SET);
result = new PorterStemFilter(result);
result = new CapitalizationFilter(result);
return new TokenStreamComponents(src, result);
}
}
+13
View File
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
@@ -0,0 +1,147 @@
package com.baeldung.lucene;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.core.KeywordAnalyzer;
import org.apache.lucene.analysis.core.SimpleAnalyzer;
import org.apache.lucene.analysis.core.StopAnalyzer;
import org.apache.lucene.analysis.core.WhitespaceAnalyzer;
import org.apache.lucene.analysis.custom.CustomAnalyzer;
import org.apache.lucene.analysis.en.EnglishAnalyzer;
import org.apache.lucene.analysis.miscellaneous.PerFieldAnalyzerWrapper;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.RAMDirectory;
import org.junit.Test;
public class LuceneAnalyzerIntegrationTest {
private static final String SAMPLE_TEXT = "This is baeldung.com Lucene Analyzers test";
private static final String FIELD_NAME = "sampleName";
@Test
public void whenUseStandardAnalyzer_thenAnalyzed() throws IOException {
List<String> result = analyze(SAMPLE_TEXT, new StandardAnalyzer());
assertThat(result, contains("baeldung.com", "lucene", "analyzers", "test"));
}
@Test
public void whenUseStopAnalyzer_thenAnalyzed() throws IOException {
List<String> result = analyze(SAMPLE_TEXT, new StopAnalyzer());
assertThat(result, contains("baeldung", "com", "lucene", "analyzers", "test"));
}
@Test
public void whenUseSimpleAnalyzer_thenAnalyzed() throws IOException {
List<String> result = analyze(SAMPLE_TEXT, new SimpleAnalyzer());
assertThat(result, contains("this", "is", "baeldung", "com", "lucene", "analyzers", "test"));
}
@Test
public void whenUseWhiteSpaceAnalyzer_thenAnalyzed() throws IOException {
List<String> result = analyze(SAMPLE_TEXT, new WhitespaceAnalyzer());
assertThat(result, contains("This", "is", "baeldung.com", "Lucene", "Analyzers", "test"));
}
@Test
public void whenUseKeywordAnalyzer_thenAnalyzed() throws IOException {
List<String> result = analyze(SAMPLE_TEXT, new KeywordAnalyzer());
assertThat(result, contains("This is baeldung.com Lucene Analyzers test"));
}
@Test
public void whenUseEnglishAnalyzer_thenAnalyzed() throws IOException {
List<String> result = analyze(SAMPLE_TEXT, new EnglishAnalyzer());
assertThat(result, contains("baeldung.com", "lucen", "analyz", "test"));
}
@Test
public void whenUseCustomAnalyzerBuilder_thenAnalyzed() throws IOException {
Analyzer analyzer = CustomAnalyzer.builder()
.withTokenizer("standard")
.addTokenFilter("lowercase")
.addTokenFilter("stop")
.addTokenFilter("porterstem")
.addTokenFilter("capitalization")
.build();
List<String> result = analyze(SAMPLE_TEXT, analyzer);
assertThat(result, contains("Baeldung.com", "Lucen", "Analyz", "Test"));
}
@Test
public void whenUseCustomAnalyzer_thenAnalyzed() throws IOException {
List<String> result = analyze(SAMPLE_TEXT, new MyCustomAnalyzer());
assertThat(result, contains("Baeldung.com", "Lucen", "Analyz", "Test"));
}
// ================= usage example
@Test
public void givenTermQuery_whenUseCustomAnalyzer_thenCorrect() {
InMemoryLuceneIndex luceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new MyCustomAnalyzer());
luceneIndex.indexDocument("introduction", "introduction to lucene");
luceneIndex.indexDocument("analyzers", "guide to lucene analyzers");
Query query = new TermQuery(new Term("body", "Introduct"));
List<Document> documents = luceneIndex.searchIndex(query);
assertEquals(1, documents.size());
}
@Test
public void givenTermQuery_whenUsePerFieldAnalyzerWrapper_thenCorrect() {
Map<String,Analyzer> analyzerMap = new HashMap<>();
analyzerMap.put("title", new MyCustomAnalyzer());
analyzerMap.put("body", new EnglishAnalyzer());
PerFieldAnalyzerWrapper wrapper =
new PerFieldAnalyzerWrapper(new StandardAnalyzer(), analyzerMap);
InMemoryLuceneIndex luceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), wrapper);
luceneIndex.indexDocument("introduction", "introduction to lucene");
luceneIndex.indexDocument("analyzers", "guide to lucene analyzers");
Query query = new TermQuery(new Term("body", "introduct"));
List<Document> documents = luceneIndex.searchIndex(query);
assertEquals(1, documents.size());
query = new TermQuery(new Term("title", "Introduct"));
documents = luceneIndex.searchIndex(query);
assertEquals(1, documents.size());
}
// ===================================================================
public List<String> analyze(String text, Analyzer analyzer) throws IOException {
List<String> result = new ArrayList<String>();
TokenStream tokenStream = analyzer.tokenStream(FIELD_NAME, text);
CharTermAttribute attr = tokenStream.addAttribute(CharTermAttribute.class);
tokenStream.reset();
while (tokenStream.incrementToken()) {
result.add(attr.toString());
}
return result;
}
}
@@ -0,0 +1,32 @@
package com.baeldung.lucene;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Paths;
import java.util.List;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Assert;
import org.junit.Test;
public class LuceneFileSearchIntegrationTest {
@Test
public void givenSearchQueryWhenFetchedFileNamehenCorrect() throws IOException, URISyntaxException {
String indexPath = "index";
String dataPath = "data/file1.txt";
Directory directory = FSDirectory.open(Paths.get(indexPath));
LuceneFileSearch luceneFileSearch = new LuceneFileSearch(directory, new StandardAnalyzer());
luceneFileSearch.addFileToIndex(dataPath);
List<Document> docs = luceneFileSearch.searchFiles("contents", "consectetur");
Assert.assertEquals("file1.txt", docs.get(0).get("filename"));
}
}
@@ -0,0 +1,152 @@
package com.baeldung.lucene;
import java.util.List;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.FuzzyQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.RAMDirectory;
import org.apache.lucene.util.BytesRef;
import org.junit.Assert;
import org.junit.Test;
public class LuceneInMemorySearchIntegrationTest {
@Test
public void givenSearchQueryWhenFetchedDocumentThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("Hello world", "Some hello world ");
List<Document> documents = inMemoryLuceneIndex.searchIndex("body", "world");
Assert.assertEquals("Hello world", documents.get(0).get("title"));
}
@Test
public void givenTermQueryWhenFetchedDocumentThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("activity", "running in track");
inMemoryLuceneIndex.indexDocument("activity", "Cars are running on road");
Term term = new Term("body", "running");
Query query = new TermQuery(term);
List<Document> documents = inMemoryLuceneIndex.searchIndex(query);
Assert.assertEquals(2, documents.size());
}
@Test
public void givenPrefixQueryWhenFetchedDocumentThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("article", "Lucene introduction");
inMemoryLuceneIndex.indexDocument("article", "Introduction to Lucene");
Term term = new Term("body", "intro");
Query query = new PrefixQuery(term);
List<Document> documents = inMemoryLuceneIndex.searchIndex(query);
Assert.assertEquals(2, documents.size());
}
@Test
public void givenBooleanQueryWhenFetchedDocumentThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("Destination", "Las Vegas singapore car");
inMemoryLuceneIndex.indexDocument("Commutes in singapore", "Bus Car Bikes");
Term term1 = new Term("body", "singapore");
Term term2 = new Term("body", "car");
TermQuery query1 = new TermQuery(term1);
TermQuery query2 = new TermQuery(term2);
BooleanQuery booleanQuery = new BooleanQuery.Builder().add(query1, BooleanClause.Occur.MUST)
.add(query2, BooleanClause.Occur.MUST).build();
List<Document> documents = inMemoryLuceneIndex.searchIndex(booleanQuery);
Assert.assertEquals(1, documents.size());
}
@Test
public void givenPhraseQueryWhenFetchedDocumentThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("quotes", "A rose by any other name would smell as sweet.");
Query query = new PhraseQuery(1, "body", new BytesRef("smell"), new BytesRef("sweet"));
List<Document> documents = inMemoryLuceneIndex.searchIndex(query);
Assert.assertEquals(1, documents.size());
}
@Test
public void givenFuzzyQueryWhenFetchedDocumentThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("article", "Halloween Festival");
inMemoryLuceneIndex.indexDocument("decoration", "Decorations for Halloween");
Term term = new Term("body", "hallowen");
Query query = new FuzzyQuery(term);
List<Document> documents = inMemoryLuceneIndex.searchIndex(query);
Assert.assertEquals(2, documents.size());
}
@Test
public void givenWildCardQueryWhenFetchedDocumentThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("article", "Lucene introduction");
inMemoryLuceneIndex.indexDocument("article", "Introducing Lucene with Spring");
Term term = new Term("body", "intro*");
Query query = new WildcardQuery(term);
List<Document> documents = inMemoryLuceneIndex.searchIndex(query);
Assert.assertEquals(2, documents.size());
}
@Test
public void givenSortFieldWhenSortedThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("Ganges", "River in India");
inMemoryLuceneIndex.indexDocument("Mekong", "This river flows in south Asia");
inMemoryLuceneIndex.indexDocument("Amazon", "Rain forest river");
inMemoryLuceneIndex.indexDocument("Rhine", "Belongs to Europe");
inMemoryLuceneIndex.indexDocument("Nile", "Longest River");
Term term = new Term("body", "river");
Query query = new WildcardQuery(term);
SortField sortField = new SortField("title", SortField.Type.STRING_VAL, false);
Sort sortByTitle = new Sort(sortField);
List<Document> documents = inMemoryLuceneIndex.searchIndex(query, sortByTitle);
Assert.assertEquals(4, documents.size());
Assert.assertEquals("Amazon", documents.get(0).getField("title").stringValue());
}
@Test
public void whenDocumentDeletedThenCorrect() {
InMemoryLuceneIndex inMemoryLuceneIndex = new InMemoryLuceneIndex(new RAMDirectory(), new StandardAnalyzer());
inMemoryLuceneIndex.indexDocument("Ganges", "River in India");
inMemoryLuceneIndex.indexDocument("Mekong", "This river flows in south Asia");
Term term = new Term("title", "ganges");
inMemoryLuceneIndex.deleteDocument(term);
Query query = new TermQuery(term);
List<Document> documents = inMemoryLuceneIndex.searchIndex(query);
Assert.assertEquals(0, documents.size());
}
}
+3
View File
@@ -0,0 +1,3 @@
Cras auctor viverra arcu, id consequat diam posuere id. Pellentesque hendrerit felis tortor, et ornare nibh ullamcorper sed. Aenean sed mauris vitae purus auctor gravida. Nam aliquam egestas orci, sit amet imperdiet leo porttitor quis. Integer commodo sodales orci, ultrices vulputate arcu vestibulum non. Nunc at tellus id urna tristique ultrices in in massa. Vestibulum laoreet ullamcorper nulla vel porttitor. Duis blandit commodo elit at consequat. Vestibulum faucibus lectus eget mi tincidunt, quis molestie lacus mollis. Duis elementum urna eros, non iaculis est facilisis in. Praesent et neque vel ipsum viverra euismod ac ac metus. Ut vitae libero ex.
Proin consectetur, neque nec feugiat facilisis, metus libero mollis arcu, id pharetra nibh sapien in elit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam pulvinar fringilla orci in posuere. Duis ut turpis dignissim nisl eleifend posuere nec a massa. Cras fringilla iaculis ipsum a aliquet. Nunc ultrices nisl ipsum, vitae consectetur tellus vehicula in. Aliquam lacinia elit nec scelerisque dapibus. Duis pharetra mauris vitae quam tincidunt, viverra iaculis orci iaculis. Nunc gravida sem arcu, et mollis leo porttitor nec. Ut dictum tempor est, at fringilla ex feugiat sed. Nullam purus mi, aliquet eu libero ut, finibus efficitur metus.