SolrNet Documentation
Short Description
SolrNet Documentation...
Description
1.
Overview and basic usage First, we have to map the Solr document to a class. Let's use a subset of the default schema that comes with the Solr distribution:
public class Product { [SolrUniqueKey("id")] public string Id { get; set; }
[SolrField("manu_exact")] public string Manufacturer { get; set; }
[SolrField("cat")] public ICollection Categories { get; set; }
[SolrField("price")] public decimal Price { get; set; }
[SolrField("inStock")] public bool InStock { get; set; } }
It's just a POCO with some attributes: SolrField maps the attribute to a Solr field and SolrUniqueKey (optional but recommended) maps an attribute to a Solr unique key field. Now we'll write some tests using this mapped class. Let's initialize the library:
[TestFixtureSetUp] public void FixtureSetup() { Startup.Init("http://localhost:8983/solr"); }
Let's add a document (make sure you have a running Solr instance before running this test): [Test] public void Add() { var p = new Product { Id = "SP2514N", Manufacturer = "Samsung Electronics Co. Ltd.", Categories = new[] { "electronics", "hard drive", }, Price = 92, InStock = true, };
var solr = ServiceLocator.Current.GetInstance(); solr.Add(p); solr.Commit(); }
Let's see if the document is where we left it: [Test] public void Query() { var solr = ServiceLocator.Current.GetInstance(); var results = solr.Query(new SolrQueryByField("id", "SP2514N")); Assert.AreEqual(1, results.Count); Console.WriteLine(results[0].Price); }
2.
Mapping Solr fields defined in your schema.xml must be mapped to properties in a .NET class. There are currently three built-in ways to map fields:
Attributes (default) With this method you decorate the properties you want to map with the
SolrField
and
SolrUniqueKey
attributes. The attribute parameter indicates the
corresponding Solr field name. Example: public class Product { [SolrUniqueKey("id")] public string Id { get; set; }
[SolrField("manu_exact")] public string Manufacturer { get; set; }
[SolrField("cat")] // cat is a multiValued field public ICollection Categories { get; set; }
[SolrField("price")] public decimal Price { get; set; }
[SolrField("inStock")] public bool InStock { get; set; }
[SolrField("timestamp")] public DateTime Timestamp { get; set; }
[SolrField("weight")] public double? Weight { get; set;} // nullable property, it might not be defined on all documents. }
This way of mapping is implemented by the
AttributesMappingManager
class.
Index-time field boosting You can also use the mapping attribute to apply a boost to a specific field at indextime. [SolrField("inStock", Boost = 10.5)] public bool InStock { get; set; }
.. this will add a boost of 10.5 to the InStock field each time the document is indexed.
All-properties This maps each property of the class to a field of the exact same name as the property (note that Solr field names are case-sensitive). It's implemented by the
AllPropertiesMappingManager
class. Note that unique keys cannot be inferred,
and therefore have to be explicitly mapped. The same mapping as above could be accomplished like this: public class Product { public string id { get; set; } public string manu_exact { get; set; } public ICollection cat { get; set; } public decimal price { get; set; } public bool inStock { get; set; } public DateTime timestamp { get; set; } public double? weight { get; set; } }
Then to add the unique key: var mapper = new AllPropertiesMappingManager(); mapper.SetUniqueKey(typeof(Product).GetProperty("id"));
Manual mapping This allows you to programmatically define the field for each property: public class Product {
public string Id { get; set; } public string Manufacturer { get; set; } public ICollection Categories { get; set; } public decimal Price { get; set; } public bool InStock { get; set; } public DateTime Timestamp { get; set; } public double? Weight { get; set; } } var mgr = new MappingManager(); var property = typeof (Product).GetProperty("Id"); mgr.Add(property, "id"); mgr.SetUniqueKey(property); mgr.Add(typeof(Product).GetProperty("Manufacturer"), "manu_exact"); mgr.Add(typeof(Product).GetProperty("Categories"), "cat_exact"); mgr.Add(typeof(Product).GetProperty("Price"), "price"); mgr.Add(typeof(Product).GetProperty("InStock"), "inStock"); mgr.Add(typeof(Product).GetProperty("Timestamp"), "timestamp"); mgr.Add(typeof(Product).GetProperty("Weight"), "weight");
Dictionary mappings and dynamic fields Solr dynamicFields can be mapped differently depending on the use case. They can be mapped "statically", e.g, given:
type="integer"
indexed="true"
a particular dynamicField instance can be mapped as:
[SolrField("price_i")] public decimal? Price {get;set;}
However, it's often necessary to have more flexibility. You can also map dynamicFields as a dictionary, with a field name prefix: [SolrField("price_")] public IDictionary Price {get;set;}
In this case, price_ is used as a prefix to the actual Solr field name, e.g. with this mapping,
Price["regular"]
maps to a Solr field named price_regular.
Another, even more flexible mapping: [SolrField("*")] public IDictionary OtherFields {get;set;}
This acts as a catch-all container for any fields that are otherwise unmapped. E.g.
OtherFields["price_i"]
maps to a Solr field named
price_i .
Fully loose mapping An even more "dynamic" mapping can be achieved by using a
Dictionary
as document type. In this document type, the dictionary
key corresponds to the Solr field name and the value to the Solr field value. Statically typing the fields is obviously lost in this case, though. When adding documents as
Dictionary
SolrNet will recognize field
value types as usual, e.g. you can use strings, int, collections, arrays, etc. Example: Startup.Init(serverUrl); var solr = ServiceLocator.Current.GetInstance();
solr.Add(new Dictionary { {"field1", 1}, {"field2", "something else"}, {"field3", new DateTime(2010, 5, 5, 12, 23, 34)}, {"field4", new[] {1,2,3}}, });
When fetching documents as
Dictionary
SolrNet will automatically
map each field value to a .NET type, but it's up to you to downcast the field value to a properly typed variable. Example: ISolrOperations solr = ... ICollection results = solr.Query(SolrQuery.All); bool inStock = (bool) results[0]["inStock"];
Custom mapping You can code your own mapping mechanism by implementing the
IReadOnlyMappingManager
interface.
Overriding the default mapper By default SolrNet maps Solr fields using attributes. However you might want to use another mapper. Replacing the default mapper depends on how you set up the library:
Built-in container If you're using the default built-in container, you can replace it like this before calling
Startup.Init() :
var mapper = new MappingManager();
/* Here come your mappings */ var container = new Container(Startup.Container); container.RemoveAll(); container.Register(c => mapper); ServiceLocator.SetLocatorProvider(() => container); Startup.Init("http://localhost:8983/solr");
Windsor facility If you're using the Windsor facility, you can override the mapper like this: var mapper = new MappingManager(); /* Here come your mappings */ var solrFacility = new SolrNetFacility("http://localhost:8983/solr") {Mapper = mapper}; var container = new WindsorContainer(); container.AddFacility("solr", solrFacility);
Ninject module var mapper = new MappingManager(); /* Here come your mappings */ var c = new StandardKernel(); c.Load(new SolrNetModule("http://localhost:8983/solr") {Mapper = mapper});
3. Initialization Once you have created your document class, you have to initialize the library in order to operate against the Solr instance. This is usually done once at application startup:
Startup.Init("http://localhost:8983/solr");
Then you ask the service locator for the SolrNet service instance which allows you to issue any supported operation: var solr = ServiceLocator.Current.GetInstance(); solr.Delete(SolrQuery.All); solr.Add(p); solr.Commit(); var products = solr.Query(new SolrQueryByRange("price", 10m, 100m));
Castle Windsor Alternatively, if your app uses Castle Windsor you can set up SolrNet using the included facility: container.AddFacility("solr", new SolrNetFacility("http://localhost:8983/solr"));
Or using Windsor's xml config: http://localhost:8983/solr
Ninject If you're using Ninject, you can use the Ninject module:
kernel.Load(new SolrNetModule("http://localhost:8983/solr"));
StructureMap If you are using StructureMap, you can use the StructureMap module ( StructureMap.SolrNetIntegration ): ObjectFactory.Initialize( x => x.AddRegistry( new SolrNetRegistry("http://localhost:8893/solr") ) );
Autofac (SolrNet 0.4.0) var builder = new ContainerBuilder(); builder.RegisterModule(new SolrNetModule("http://localhost:8983/solr")); var container = builder.Build();
Unity (SolrNet 0.4.0) var solrServers = new SolrServers { new SolrServerElement { Id = "test", Url = "htp://localhost:8893", DocumentType = typeof (Entity).AssemblyQualifiedName, }
}; container = new UnityContainer(); new SolrNetContainerConfiguration().ConfigureContainer(solrServers, container);
Multi-core mapping If you need to map multiple Solr cores/instances, see this page.
4. CRUD operations Create/Update Create and update are the same operation in Solr (and SolrNet). It's mapped to the
Add()
method in the
ISolrOperations interface.
There are two overloads of other takes a these
Add()
IEnumerable
Add() :
one takes a single document as parameter, the
of documents. Keep in mind that one call to any of
methods will end up in one HTTP request to Solr. So for example this:
ISolrOperations solr = ... for (var i = 0; i < 100; i++) solr.Add(new MyDocument(i));
is not the same as this: var documents = Enumerable.Range(0, 100).Select(i => new MyDocument(i)); solr.Add(documents);
If you
Add()
a document that already exists in the Solr index, it's replaced (using
the unique key as equality) There's also
AddWithBoost()
(also with single-document and
IEnumerable
overloads)
that you can use to apply an index-time boosting to the added documents. Add()
supports the
commitWithin
more information about them.
Retrieve
and
overwrite
parameters. See the Solr wiki for
See Querying
Delete The ISolrOperations interface has several
Delete()
Delete(T doc) :
Delete(string id) :
Delete(IEnumerable docs) :
Delete(IEnumerable ids) :
Delete(ISolrQuery q) :
Delete(IEnumerable ids, ISolrQuery q) :
overloads:
deletes a single document using its unique key to identify it. deletes a single document with a unique key deletes a batch of documents in a single shot. deletes a batch of documents in a single shot.
deletes all documents that match a query. deletes a batch of documents and all
documents that match a query in a single shot.
Commit and Optimize After issuing any number of
Add()
or
Delete()
operations, be sure to call
Commit() :
solr.Add(myDocument); solr.Add(anotherDocument); solr.Delete(oldDocument); solr.Commit();
... this tells Solr to finalise the changes you have made and to start rebuilding indexes and related data. Obviously this has a performance penalty on the Solr instance as all query caches are cleared and repopulated due to the new index. Alternatively, you can let Solr manage commits, enabling the autoCommit options in the Solr configuration. The
Optimize()
method:
solr.Optimize();
... issues a command to tell Solr to begin optimizing its indexes. Again this is an expensive operation in terms of the Solr instance and shouldn't be called too frequently. Additional information on committing and optimization considerations can be found here
Rollback Rollback()
rollbacks all add/deletes made to the index since the last commit. Note
that this is nothing like the rollback of relational databases. See the Solr wiki for more information.
5. Querying Simple query This is the simplest 'query object' in SolrNet. Whatever you give it is passed straight to Solr's q parameter ISolrOperations solr = ... var products1 = solr.Query(new SolrQuery("lucene")); // search for "lucene" in the default field var products2 = solr.Query(new SolrQuery("name:solr")); // search for "solr" in the "name" field
Query by field This allows you to define field name and value separately: ISolrOperations solr = ... var products = solr.Query(new SolrQueryByField("name", "solr")); // search for "solr" in the "name" field
It also has the benefit that it handles special character escaping for you. (SolrNet 0.4.0) You can disable character escaping by setting
Quoted = false :
var q = new SolrQueryByField("name", "John*") { Quoted = false };
Query by range Creates a range query: ISolrOperations solr = ... var products = solr.Query(new SolrQueryByRange("price", 100m, 250.50m)); // search for price between 100 and 250.50
Query by list of values var q = new SolrQueryInList("name", "solr", "samsung", "maxtor");
is the same as
name:solr OR name:samsung OR name:maxtor
"Any value" query It's often convenient to see what documents have a field defined or not: var q = new SolrHasValueQuery("name");
is equivalent to the Solr query
name:[* TO *]
Query by distance (SolrNet 0.4.0) Creates a
geofilt
or
bbox
filter on a LatLonType field.
Examples: // default accuracy is CalculationAccuracy.Radius (higher accuracy) var q = new SolrQueryByDistance("store", pointLatitude = 45.15, pointLongitude = 93.85, distance = 5); var q = new SolrQueryByDistance("store", pointLatitude = 45.15, pointLongitude = 93.85, distance = 5, accuracy = CalculationAccuracy.BoundingBox);
See the Solr wiki for more information.
Query operators You can use the
&&
and
||
operators to connect queries, with the expected results:
var q = new SolrQuery("solr") && new SolrQuery("name:desc");
generates the query
solr AND name:desc
The plus (+) operator is also overloaded. It concatenates the queries and leaves the actual operator to the default as specified in Solr's configuration. var q = new SolrQuery("solr") + new SolrQuery("name:desc");
creates the query
solr name:desc
To negate a query, you can call
Not()
on it or just use the
!
operator:
var q = !new SolrQuery("solr");
creates the query
-solr
Finally, the minus (-) operator: var q = new SolrQuery("solr") - new SolrQuery("name:desc"); // solr - name:desc
which is equivalent to (and more intuitive than): var q = new SolrQuery("solr") + !new SolrQuery("name:desc"); // solr - name:desc
Alternatively, if you have a list of queries you want to aggregate you can use
SolrMultipleCriteriaQuery .
For example:
new SolrMultipleCriteriaQuery(new[] {new SolrQuery("1"), new SolrQuery("2")})
is the same as: new SolrQuery("1") + new SolrQuery("2")
You can also define what operators to use to join these queries, e.g: new SolrMultipleCriteriaQuery(new[] {new SolrQuery("1"), new SolrQuery("2")}, "AND")
Boosting You can boost particular queries by calling
Boost() ,
for example:
var q = new SolrQuery("name:desc").Boost(2); // (name:desc)^2
See the Lucene docs for more information about boosting.
DSL See the fluent API documentation for an alternative way of expressing queries.
Filter queries Filter queries can be used to specify a query that can be used to restrict the super set of documents that can be returned, without influencing score. ISolrOperations solr = ... var products = solr.Query(SolrQuery.All, new QueryOptions { FilterQueries = new ISolrQuery[] { new SolrQueryByField("manu", "apache"), new SolrQueryByRange("price", 100m, 200m), } });
More information in the Solr wiki.
Fields By default Solr returns all stored fields. You can retrieve only selected fields instead: ISolrOperations solr = ... var products = solr.Query(SolrQuery.All, new QueryOptions { Fields = new[] {"id", "manu"} });
Sorting
By default Solr returns search results ordered by "score desc". You can sort the results by any field(s): ISolrOperations solr = ... var products = solr.Query(SolrQuery.All, new QueryOptions { OrderBy = new[] {new SortOrder("manu", Order.DESC), SortOrder.Parse("id asc")} });
You can random sort using
RandomSortOrder :
solr.Query(SolrQuery.All, new QueryOptions { OrderBy = new[] {new RandomSortOrder("randomF")}, });
where
randomF
is a random sort field.
RandomSortOrder
has various constructors to
generate a random seed (as in the example above) or use a predefined seed.
Pagination In Solr you can't retrieve all your documents in single query. However, by default SolrNet will try to retrieve a large amount of documents, trying to mimic the behavior of a RDBMS without a TOP clause. It's not recommended to rely on this behavior. Instead, always define pagination parameters, for example: ISolrOperations solr = ... solr.Query("somequery", new QueryOptions{ Start = 10, Rows = 25 });
This will fetch at most 25 documents, starting from the 10th document in the total result set.
Additional parameters Solr has lots of features that aren't directly mapped in SolrNet, but you can enable and use most of them with the
ExtraParams dictionary.
Parameters defined
in
ExtraParams
are directly passed to the Solr querystring. For example you
can restrict the maximum time allowed for a query: ISolrOperations solr = ... var products = solr.Query(SolrQuery.All, new QueryOptions { ExtraParams = new Dictionary { {"timeAllowed", "100"} } });
Or enable DisMax instead of the standard request handler: ISolrOperations solr = ... var products = solr.Query(SolrQuery.All, new QueryOptions { ExtraParams = new Dictionary { {"qt", "dismax"} } });
LocalParams LocalParams provide a way to add certain metadata to a piece of query. It's used among other things to change the default operator type on the fly for a particular query. In SolrNet, LocalParams are represented by the basically a
Dictionary .
LocalParams
class, which is
LocalParams are attached to a query using
the "+" operator. Here's an example: solr.Query(new LocalParams {{"type", "dismax"},{"qf", "myfield"}} + new SolrQuery("solr rocks"));
This will generate:
q={!type=dismax qf=myfield}solr rocks
More information about LocalParams in the Solr wiki.
6. Faceting SolrNet supports faceted searching.
There are basically three kinds of facet queries: 1. querying by field 2. date facets 3. arbitrary facet queries Facet queries are issued through the the
QueryOptions
FacetQueries
property of
QueryOptions .
Then
instance is passed to the server instance.
Querying by field Querying by field is handled by the through the
FacetFields
SolrFacetFieldQuery
class. Results are available
property.
Example: print all categories sorted by popularity. ISolrOperations solr = ... var r = solr.Query(SolrQuery.All, new QueryOptions { Rows = 0, Facet = new FacetParameters { Queries = new[] {new SolrFacetFieldQuery("category")} } }); foreach (var facet in r.FacetFields["category"]) { Console.WriteLine("{0}: {1}", facet.Key, facet.Value); }
Date facets Date facet queries create facets from date ranges. Sample code: ISolrOperations solr = ...
var results = solr.Query(SolrQuery.All, new QueryOptions { Facet = new FacetParameters { Queries = new[] { new SolrFacetDateQuery("timestamp", new DateTime(2001, 1, 1).AddDays(-1) /* range start */, new DateTime(2001, 1, 1).AddMonths(1) /* range end */, "+1DAY" /* gap */) { HardEnd = true, Other = new[] {FacetDateOther.After, FacetDateOther.Before} }, } } }); DateFacetingResult dateFacetResult = results.FacetDates["timestamp"]; foreach (KeyValuePair dr in dateFacetResult.DateResults) { Console.WriteLine(dr.Key); Console.WriteLine(dr.Value); }
Arbitrary facet queries Arbitrary facet queries are handled by the available through the
FacetQueries
SolrFacetQuery
class. Results are
property.
Example: segregate items by price (less than $500 - more than $500) ISolrOperations solr = ... var lessThan500 = new SolrQueryByRange("price", 0m, 500m); var moreThan500 = new SolrQueryByRange("price", "500", "*");
var r = solr.Query(SolrQuery.All, new QueryOptions { Rows = 0, Facet = new FacetParameters { Queries = new[] {new SolrFacetQuery(lessThan500), new SolrFacetQuery(moreThan500)} } }); foreach (var facet in r.FacetQueries) { Console.WriteLine("{0}: {1}", facet.Key, facet.Value); }
Pivot faceting http://wiki.apache.org/solr/HierarchicalFaceting#Pivot_Facets http://wiki.apache.org/solr/SimpleFacetParameters#Pivot_.28ie_Decision_Tree.29 _Faceting (to be documented)
7. Highlighting This feature will "highlight" (typically with markup) the terms that matched the query, including a snippet of the text around the matched term. Sample code: var results = solr.Query(new SolrQueryByField("features", "noise"), new QueryOptions { Highlight = new HighlightingParameters { Fields = new[] {"features"}, }
}); foreach (var h in results.Highlights[results[0].Id]) { Console.WriteLine("{0}: {1}", h.Key, string.Join(", ", h.Value.ToArray())); }
would print for example: features: NoiseGuard, SilentSeek technology, Fluid Dynamic Bearing (FDB) motor
Solr reference documentation: http://wiki.apache.org/solr/HighlightingParameters
8. More-like-this This feature returns a list of similar documents for each document returned in the results of the original query. Parameters are defined through the
MoreLikeThis
property of the
QueryOptions .
Example: searching for "apache", for each document in the result search for similar documents in the "cat" (category) and "manu" (manufacturer) fields: ISolrBasicOperations solr = ... var results = solr.Query(new SolrQuery("apache"), new QueryOptions { MoreLikeThis = new MoreLikeThisParameters(new[] {"cat", "manu"}) { MinDocFreq = 1, // minimum document frequency MinTermFreq = 1, // minimum term frequency }, }); foreach (var r in results.SimilarResults) {
Console.WriteLine("Similar documents to {0}", r.Key.Id); foreach (var similar in r.Value) Console.WriteLine(similar.Id); Console.WriteLine(); }
All parameters defined in the Solr docs are supported.
9. Spell checking You'll need to install the
SpellCheckComponent
in the standard request handler in
order to use this. Next, a spellcheck dictionary must be provided. Normally a default dictionary is created by invoking
BuildSpellCheckDictionary()
at commit/optimize time (you can
also configure Solr to automatically rebuild spellchecking indices): ISolrOperations solr = ... solr.BuildSpellCheckDictionary();
Now you can start issuing spellchecking queries by defining the
SpellCheck
parameter in the
QueryOptions :
ISolrOperations solr = ... var results = solr.Query("ipo appl", new QueryOptions { SpellCheck = new SpellCheckingParameters {Collate = true} });
Then you get the suggestions from
results.SpellChecking ,
foreach (var sc in results.SpellChecking) { Console.WriteLine("Query: {0}", sc.Query);
i.e.:
foreach (var s in sc.Suggestions) { Console.WriteLine("Suggestion: {0}", s); } }
which would print something like: Query: ipo Suggestion: ipod Query: appl Suggestion: apple
All of the SpellCheckComponent parameters are supported, except for the
extendedResults
10.
option.
Stats
Property
Description
Min
The minimum value
Max
The maximum value
Sum
Sum of all values
Count
How many (non-null) values
Missing
How many null values
Property
Description
SumOfSquares
Sum of all values squared (useful for stddev)
Mean
The average (v1+v2...+vN)/N
Standard Deviation -- measuring how widely spread the
StdDev
values in a data set are.
Sample usage: ISolrOperations solr = ... var results = solr.Query(SolrQuery.All, new QueryOptions { Rows = 0, Stats = new StatsParameters { Facets = new[] { "inStock" }, FieldsWithFacets = new Dictionary { {"popularity", new List {"price"}} } } });
foreach (var kv in results.Stats) { Console.WriteLine("Field {0}: ", kv.Key); var s = kv.Value;
Console.WriteLine("Min: {0}", s.Min); Console.WriteLine("Max: {0}", s.Max); Console.WriteLine("Sum of squares: {0}", s.SumOfSquares); foreach (var f in s.FacetResults) { Console.WriteLine("Facet: {0}", f.Key); foreach (var fv in f.Value) { Console.WriteLine("Facet value: {0}", fv.Key); Console.WriteLine("Min: {0}", fv.Value.Min); Console.WriteLine("Max: {0}", fv.Value.Max); Console.WriteLine("Sum of squares: {0}", fv.Value.SumOfSquares); } } }
For more details see the Solr wiki.
11.
Field collapsing / grouping
This feature can be used to collapse - or group - documents by the unique values of a specified field. Included in the results are the number of records by document key and by field value.
For Solr 1.4 / 3.1 This feature is not included with the stock Solr 1.4 or 3.1. You need to apply a patch and recompile Solr. Sample code: ISolrOperations solr = ...
var results = solr.Query(new SolrQueryByField("features", "noise"), new QueryOptions { Collapse = new CollapseParameters { Field = "manu" } }); foreach (KeyValuePair collapsedDocument in results.Collapsing.DocResults) Console.WriteLine("Collapse count for document '{0}': {1}", collapsedDocument.Key, collapsedDocument.Value);
For more details see:
http://wiki.apache.org/solr/FieldCollapsing
https://issues.apache.org/jira/browse/SOLR-236
http://blog.jteam.nl/2009/10/20/result-grouping-field-collapsing-with-solr/
For Solr 3.3+ Use
Grouping
instead of
Collapse
in
QueryOptions
with SolrNet 0.4.0 alpha 1 or
later. Sample code
12.
Core admin
SolrNet offers some facilities to execute Solr core admin commands. See the Solr wiki for detailed explanations of these commands. First, build an instance of
ISolrCoreAdmin :
const string solrUrl = "http://localhost"8983/solr"; var headerParser = ServiceLocator.Current.GetInstance(); var statusParser = ServiceLocator.Current.GetInstance(); ISolrCoreAdmin solrCoreAdmin = new SolrCoreAdmin(new SolrConnection(solrUrl), headerParser, statusParser); ISolrCoreAdmin
can execute the following core admin commands:
Status /// Get the status of all registered cores: IList coreStatus = solrCoreAdmin.Status(); /// Get the status of a single core: var coreStatus = solrCoreAdmin.Status("core1");
Create solrCoreAdmin.Create(coreName: "items", instanceDir: "items");
Reload solrCoreAdmin.Reload("core1");
Rename solrCoreAdmin.Rename("core1", "newCoreName");
Swap solrCoreAdmin.Swap("core0", "core1");
Unload solrCoreAdmin.Swap("core0", UnloadCommand.Delete.Data);
Merge solrCoreAdmin.Merge("destinationCore", new MergeCommand.SrcCore("sourceCore0"), new MergeCommand.SrcCore("sourceCore1"));
Alias solrCoreAdmin.Alias("existingCore", "alias");
13.
Fluent API
Query-building Some examples: Query.Simple("name:solr"); // name:solr Query.Field("name").Is("solr"); // name:solr Query.Field("price").From(10).To(20); // price:[10 TO 20] Query.Field("price").In(10, 20, 30); // price:10 OR price:20 OR price:30 Query.Field("name").HasAnyValue(); // name:[* TO *]
These can be used anywhere where a
ISolrQuery
is accepted, e.g.:
ISolrOperations solr = ... solr.Query(Query.Field("name").HasAnyValue());
They can also be mixed with boolean operators: ISolrOperations solr = ... solr.Query(Query.Field("name").HasAnyValue() && Query.Field("price").Is(0));
Querying This API is deprecated. Please don't use it. If you're using it, be aware that it will be removed in a future release of SolrNet. Example: [SetUp] public void setup() { Solr.Connection = new SolrConnection("http://localhost:8983/solr"); }
[Test] public void QueryById() { ISolrQueryResults r = Solr.Query().By("id").Is("123456").Run(); }
[Test] public void QueryByRange() { ISolrQueryResults r = Solr.Query().By("id").Between(123).And(456).OrderBy("id", Order.ASC).Run(); }
[Test] public void DeleteByQuery() { Solr.Delete.ByQuery("id:123456");
}
Run()
is the explicit kicker method. There are some more examples in the tests.
14.
Overriding the default mapper
By default SolrNet maps Solr fields using attributes. However you might want to use another mapper. Replacing the default mapper depends on how you set up the library:
Built-in container If you're using the default built-in container, you can replace it like this before calling
Startup.Init() :
var mapper = new MappingManager(); /* Here come your mappings */ var container = new Container(Startup.Container); container.RemoveAll(); container.Register(c => mapper); ServiceLocator.SetLocatorProvider(() => container); Startup.Init("http://localhost:8983/solr");
Windsor facility If you're using the Windsor facility, you can override the mapper like this: var mapper = new MappingManager(); /* Here come your mappings */ var solrFacility = new SolrNetFacility("http://localhost:8983/solr") {Mapper = mapper}; var container = new WindsorContainer();
container.AddFacility("solr", solrFacility);
Ninject module var mapper = new MappingManager(); /* Here come your mappings */ var c = new StandardKernel(); c.Load(new SolrNetModule("http://localhost:8983/solr") {Mapper = mapper});
15.
NHibernate-SolrNet integration
A NHibernate-SolrNet module is included in SolrNet, with these features:
Automatic database -> Solr synchronization
Issue Solr queries from NHibernate's ISession This is intended to be used on entities that are similarly mapped on both NHibernate and SolrNet. This integration is deprecated. It is not generic enough to be truly reusable for most non-trivial cases. It will be removed in a future release of SolrNet.
Setup Configure SolrNet and NHibernate as usual. Then apply SolrNet's integration to NHibernate's configuration like this: NHibernate.Cfg.Configuration cfg = SetupNHibernate(); var cfgHelper = new NHibernate.SolrNet.CfgHelper(); cfgHelper.Configure(cfg, true); // true -> autocommit Solr after every operation (not really recommended)
If you're not using the default built-in container, you have to tell e.g.:
CfgHelper
to use it,
IWindsorContainer container = new WindsorContainer(); ... var cfgHelper = new NHibernate.SolrNet.CfgHelper(container); ...
Usage Whenever a NHibernate entity that is also mapped in SolrNet is created, updated, or deleted, it will be automatically updated on Solr. NHibernate transactions are honored: entities are updated on Solr only when the NHibernate transaction is committed. This synchronization goes only in the direction NHibernate -> SolrNet, not the other way around, i.e. operations issued directly through ISolrOperations will not be reflected on NHibernate. In order to issue Solr queries through NHibernate, the ISession needs to be wrapped. Sample: ISessionFactory sessionFactory = ... using (var session = cfgHelper.OpenSession(sessionFactory)) { ICollection entities = session.CreateSolrQuery("this is a fulltext query").SetMaxResults(10).List(); }
16.
Multi-core / multi-instance
This page describes how to configure SolrNet to access (read/write) multiple Solr cores or instances. It assumes you know what Solr cores are, how to configure and use them outside of SolrNet. This page doesn't cover CoreAdminHandler commands.
How to configure SolrNet for multicore depends on how it's integrated to your app, and if your cores map to different types or the same type.
With built-in container The built-in container ( Startup ) is currently limited to access multiple cores/instances with different mapped types. It's quite simple to configure: assuming you have a core that maps to class Product and another core mapping to class
Person :
Startup.Init("http://localhost:8983/solr/product"); Startup.Init("http://localhost:8983/solr/person");
ISolrOperations solrProduct = ServiceLocator.Current.GetInstance(); solrProduct.Add(new Product { Name = "Kodak EasyShare" }); solrProduct.Commit();
ISolrOperations solrPerson = ServiceLocator.Current.GetInstance(); solrPerson.Add(new Person { FirstName = "Joe", LastName = "Example" }); solrPerson.Commit();
With Windsor facility Code config: var solrFacility = new SolrNetFacility("http://localhost:8983/solr/defaultCore"); solrFacility.AddCore("core0-id", typeof(Product), "http://localhost:8983/solr/product");
solrFacility.AddCore("core1-id", typeof(Product), "http://localhost:8983/solr/product2"); solrFacility.AddCore(typeof(Person), "http://localhost:8983/solr/person"); // no need to set an explicit ID since it's the only core for Person container.AddFacility("solr", solrFacility);
ISolrOperations solrPerson = container.Resolve(); ISolrOperations solrProduct1 = container.Resolve("core0-id"); // use proper Windsor service overrides instead of resolving like this ISolrOperations solrProduct2 = container.Resolve("core1-id");
Equivalent XML config: http://localhost:8983/solr/defaultCore Namespace.To.Product, MyAssembly http://localhost:8983/solr/product Namespace.To.Product, MyAssembly http://localhost:8983/solr/product2 Namespace.To.Person, MyAssembly http://localhost:8983/solr/person
With StructureMap registry
var cores = new SolrServers { new SolrServerElement { Id = "core0-id", DocumentType = typeof(Product).AssemblyQualifiedName, Url = "http://localhost:8983/solr/product", }, new SolrServerElement { Id = "core1-id", DocumentType = typeof(Person).AssemblyQualifiedName, Url = "http://localhost:8983/solr/person", }, }; ObjectFactory.Initialize(c => c.IncludeRegistry(new SolrNetRegistry(cores))); var solr1 = ObjectFactory.GetInstance(); var solr2 = ObjectFactory.GetInstance();
Equivalent XML config (in App.config):
17.
Schema/mapping validation
SolrNet aims to be flexible to map a Solr schema in different ways depending on the project needs. A single schema may be mapped in several different ways even within a single project. However, there are invalid mappings that will end up in runtime errors, and it may not be evident from these errors that the problem lies in the mapping. SolrNet offers a facility to aid with the detection of mapping mismatches. Example: ISolrOperations solr = ... IList mismatches = solr.EnumerateValidationResults().ToList(); var errors = mismatches.OfType(); var warnings = mismatches.OfType(); foreach (var error in errors) Console.WriteLine("Mapping error: " + error.Message); foreach (var warning in warnings) Console.WriteLine("Mapping warning: " + warning.Message);
if (errors.Any()) throw new Exception("Mapping errors, aborting");
18.
Sample application
A sample web application is provided to demo some of the library's features. This sample application is not meant to be an absolute reference of SolrNet integration with your application. Real world applications are likely to need much more complex interactions with SolrNet. Screenshot:
Requirements:
.NET 3.5 SP1
ASP.NET MVC 2.0
Java 1.5 or better
After downloading and extracting the sample app, run
runsample.bat
to fire up the
included Solr and Cassini.
19. Frequently asked questions about SolrNet Is SolrNet a .NET port of Solr? No, SolrNet is a HTTP client so you can talk to Solr from your .NET application. If you want to run Solr on .NET, take a look at thisblog post.
What version of Solr do I need? Does SolrNet work with the latest release of Solr? It's very uncommon for Solr to make breaking changes in core functionality between releases, so SolrNet should work with any fairly recent version of Solr (3.1+). Note that it's up to you to use features that are supported by your Solr version. For example, you will get an error if you try to use grouping with Solr 3.1
I'm getting a Bad Request error when calling
Commit()
You're probably using an outdated version. Upgrade to a recent build
I'm getting a 404 (not found) response from Solr when doing any operation with SolrNet You're probably missing the core name in the URL you're passing to Startup.Init
I'm getting an error "'SolrConnection' already registered in container" Make sure you call
Startup.Init
only once per document type in your application.
I created my SolrNet document type, but when I add an instance to Solr I get an 'unknown field' error. You need to edit the schema.xml in Solr to reflect the fields mapped in your .NET document type. Schema reference:
http://wiki.apache.org/solr/SchemaXml
https://cwiki.apache.org/confluence/display/solr/Overview+of+Documents,+Fields,+ and+Schema+Design You can also use the schema/mapping validation for guidance.
How do I get the
score
pseudo-field?
Solr does not return the score by default, so you have to fetch it explicitly. In SolrNet this translates to
Fields = new[] {"*","score"}
in your
QueryOptions
instance.
Also, you have to map it to a property in your document type. For example: [SolrField("score")] double? Score { get; set; }
I'm getting a "URI too long" error You're hitting the GET request limit of the Solr web container (i.e. Jetty or Tomcat). You can either:
edit this limit in the web container configuration, or
make SolrNet use POST instead of GET. You can do this by installing the SolrNet.Impl.PostSolrConnection decorator (here's an example)
View more...
Comments