Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V4.0 Geo Spatial Search Support #367

Open
wants to merge 85 commits into
base: release/4.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
85 commits
Select commit Hold shift + click to select a range
d60264e
WIP Spatial API
nzdev Feb 10, 2023
9453cad
Spatial WIP
nzdev Feb 10, 2023
6bd8a14
WIP spatial ops
nzdev Feb 10, 2023
82313aa
wip spatial sort
nzdev Feb 10, 2023
2545f88
wip spatial storage
nzdev Feb 11, 2023
1de36e0
WIP Spatial
nzdev Feb 11, 2023
a7b981b
WIP spatial tidyup
nzdev Feb 11, 2023
b1689c7
rename
nzdev Feb 11, 2023
ab275f2
tidy
nzdev Feb 11, 2023
8462393
WIP Spatial Disatance Sort working
nzdev Feb 11, 2023
9c40f91
WIP Move Sort logic
nzdev Feb 11, 2023
227db67
WIP remove wkt conversion
nzdev Feb 11, 2023
ba18c3a
Remove unused method
nzdev Feb 11, 2023
ba09f06
remove unused usings
nzdev Feb 11, 2023
342b5b1
Remove unused usings
nzdev Feb 11, 2023
f3f1258
Tidy. Support shape collection
nzdev Feb 11, 2023
b7e9f08
Move Spatial to it's own library due to lucene spatial depending on l…
nzdev Feb 11, 2023
a8d1185
Tidy namespaces
nzdev Feb 11, 2023
5d7d79e
Throw unsuppported as uncertain that ToSpatialDistanceSortField imple…
nzdev Feb 11, 2023
137df45
Filter support
nzdev Feb 11, 2023
85f8ef0
Simplify latitude and longitude points.
nzdev Feb 11, 2023
a158125
LineString support
nzdev Feb 11, 2023
201fcf9
docs
nzdev Feb 11, 2023
ca7d02f
Set filter support
nzdev Feb 11, 2023
0336d2b
Add query test
nzdev Feb 11, 2023
32d5752
Move shapefactory add query test.
nzdev Feb 11, 2023
5a0a1dc
Add docs
nzdev Feb 11, 2023
3197065
sort docs
nzdev Feb 11, 2023
ec1b2bb
fix spelling mistake
nzdev Feb 13, 2023
827dd06
fix spelling
nzdev Feb 13, 2023
1537328
merge release 3.0
nzdev Mar 25, 2023
f92ca59
abstractions
nzdev Jul 30, 2023
774342b
reorder
nzdev Jul 30, 2023
8365d87
Revert "reorder"
nzdev Jul 30, 2023
14b33b4
wip
nzdev Jul 31, 2023
c6fca6c
push
nzdev Jul 31, 2023
36916df
LuceneFilter
nzdev Jul 31, 2023
4638f63
Wip
nzdev Jul 31, 2023
0a9fb58
WIP
nzdev Jul 31, 2023
76f2ae6
WIP
nzdev Jul 31, 2023
6d8926f
WIP
nzdev Jul 31, 2023
8ea66d7
WIP
nzdev Jul 31, 2023
7ec5b96
WIP
nzdev Jul 31, 2023
5d57262
Wip filter
nzdev Jul 31, 2023
f52f140
wip
nzdev Aug 2, 2023
739a710
merge
nzdev Aug 2, 2023
7f67b32
merge
nzdev Aug 2, 2023
f785ff5
WIP
nzdev Aug 2, 2023
0fc5191
Wip
nzdev Aug 2, 2023
8765ad7
wip
nzdev Aug 2, 2023
b175c94
wip
nzdev Aug 2, 2023
693b236
Merge branch 'release/4.0' of https://github.com/Shazwazza/Examine in…
nzdev Aug 18, 2023
4ce2198
test not
nzdev Aug 18, 2023
02573da
Range filter
nzdev Aug 18, 2023
42198f7
xdoc
nzdev Aug 18, 2023
80d796f
merge v4 beta1
nzdev Dec 7, 2023
e672962
Filter query support
nzdev Dec 7, 2023
02556ff
Nested Query Filter
nzdev Dec 7, 2023
ee23263
internal
nzdev Dec 7, 2023
7df1076
internal
nzdev Dec 7, 2023
b8c814a
tidy
nzdev Dec 7, 2023
5055af1
Fix dequeue
nzdev Dec 7, 2023
84edbd0
Filter chaining
nzdev Dec 7, 2023
e9bbc8e
Filter occurance support
nzdev Dec 7, 2023
be8426c
Remove IChainStart
nzdev Dec 7, 2023
85b09cb
Index float as single rather than a double
nzdev Dec 7, 2023
dd70636
float
nzdev Dec 7, 2023
b635740
Merge branch 'bugfix/single-float' of https://github.com/nzdev/Examin…
nzdev Dec 7, 2023
2334205
Filter tests
nzdev Dec 7, 2023
1761dc7
doc examples
nzdev Dec 7, 2023
d54bd4c
Test and docs for lucene Filter
nzdev Dec 7, 2023
244fc78
Wip merge
nzdev Dec 7, 2023
181f4d1
Remove IFiltering
nzdev Dec 7, 2023
4974861
nested spatial filter
nzdev Dec 7, 2023
3c26925
revert
nzdev Dec 7, 2023
328fd65
Fix sorting
nzdev Dec 7, 2023
6208fe3
Update Spatial docs
nzdev Dec 7, 2023
9e0e140
Add docs
nzdev Dec 7, 2023
25b8ea5
tidy
nzdev Dec 7, 2023
f4138a9
remove target
nzdev Dec 7, 2023
70ecd38
docs update
nzdev Dec 7, 2023
4467d49
add examine image
nzdev Dec 8, 2023
0d69e07
Remove chaining support as it's removed in lucene 5
nzdev Dec 24, 2023
19b82d5
Remove filter chaining as not supported in lucene 5+
nzdev Dec 24, 2023
81a0728
Simplified spatial interface names from IExamineSpatial to ISpatial
Jan 18, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/articles/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ These are the default field value types provided with Examine. Each value type c
| FacetTaxonomyDateDay | Just like DateTime but with precision only to the day. Stored in the Taxonomy Facet sidecar index. | ✅ |✅ | ✅ | ❌ | ✅ | - |
| FacetTaxonomyDateHour | Just like DateTime but with precision only to the hour. Stored in the Taxonomy Facet sidecar index. | ✅ |✅ | ✅ | ❌ | ✅ | - |
| FacetTaxonomyDateMinute | Just like DateTime but with precision only to the minute. Stored in the Taxonomy Facet sidecar index. | ✅ |✅ | ✅ | ❌ | ✅ | - |
| GeoSpatialWKT | GeoSpatial field using WKT GeoSpatial format. Uses the GeoSpatialPrefixTreeStrategy | ✅ | ❌ | ✅ | ✅ | ✅ | - |

### Custom field value types

A field value type is defined by [`IIndexFieldValueType`](xref:Examine.Lucene.Indexing.IIndexFieldValueType)
Expand Down
245 changes: 245 additions & 0 deletions docs/articles/filtering.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
---
title: Filtering
permalink: /filtering
uid: filtering
order: 3
---
Filtering
===

_**Tip**: There are many examples of filtering in the [`FluentApiTests` source code](https://github.com/Shazwazza/Examine/blob/release/3.0/src/Examine.Test/Examine.Lucene/Search/FluentApiTests.cs) to use as examples/reference._

## Common

Obtain an instance of [`ISearcher`](xref:Examine.ISearcher) for the index to be searched from [`IExamineManager`](xref:Examine.IExamineManager).

### Terms and Phrases

When filtering on fields like in the example above you might want to search on more than one word/term. In Examine this can be done by simply adding more terms to the term filter.

### Term Filter

```csharp
var searcher = myIndex.Searcher;
var results = searcher.CreateQuery()
// Look for any addresses that has "Hills" or "Rockyroad" or "Hollywood"
.WithFilter(
filter =>
{
filter.TermFilter(new FilterTerm("Address", "Hills Rockyroad Hollywood"));
})
.All()
.Execute();
```

### Terms Filter

```csharp
var searcher = myIndex.Searcher;
var results = searcher.CreateQuery()
// Look for any addresses that has "Hills" or "Rockyroad" or "Hollywood"
.WithFilter(
filter =>
{
filter.TermsFilter(new[] {new FilterTerm("Address", "Hills"), new FilterTerm("Address", "Rockyroad"), new FilterTerm("Address", "Hollywood") });
})
.All()
.Execute();
```

### Term Prefix Filter

```csharp
var searcher = myIndex.Searcher;
var results = searcher.CreateQuery()
// Look for any addresses that starts with "Hills"
.WithFilter(
filter =>
{
filter.TermPrefixFilter(new FilterTerm("Address", "Hills"));
})
.All()
.Execute();
```

## Range Filters

Range Filters allow one to match documents whose field(s) values are between the lower and upper bound specified by the Range Filter

### Int Range

Example:

```csharp
var searcher = myIndex.Searcher;
var query = searcher.CreateQuery();
query.WithFilter(
filter =>
{
filter.IntRangeFilter("SomeInt", 0, 100, minInclusive: true, maxInclusive: true);
}).All();
var results = query.Execute(QueryOptions.Default);
```

This will return results where the field `SomeInt` is within the range 0 - 100 (min value and max value included).

### Long Range

Example:

```csharp
var searcher = myIndex.Searcher;
var query = searcher.CreateQuery();
query.WithFilter(
filter =>
{
filter.LongRangeFilter("SomeLong", 0, 100, minInclusive: true, maxInclusive: true);
}).All();
var results = query.Execute(QueryOptions.Default);
```

This will return results where the field `SomeLong` is within the range 0 - 100 (min value and max value included).

### Float Range

Example:

```csharp
var searcher = myIndex.Searcher;
var query = searcher.CreateQuery();
query.WithFilter(
filter =>
{
filter.FloatRangeFilter("SomeFloat", 0f, 100f, minInclusive: true, maxInclusive: true);
}).All();
var results = query.Execute(QueryOptions.Default);
```

This will return results where the field `SomeFloat` is within the range 0 - 100 (min value and max value included).

### Double Range

Example:

```csharp
var searcher = myIndex.Searcher;
var query = searcher.CreateQuery();
query.WithFilter(
filter =>
{
filter.FloatRangeFilter("SomeDouble", 0.0, 100.0, minInclusive: true, maxInclusive: true);
}).All();
var results = query.Execute(QueryOptions.Default);
```

This will return results where the field `SomeDouble` is within the range 0 - 100 (min value and max value included).

## Booleans

### Or

```csharp
var searcher = myIndex.Searcher;
var results = searcher.CreateQuery()
// Look for any addresses that start with "Hills" or "Valleys"
.WithFilter(
filter =>
{
filter.TermPrefixFilter(new FilterTerm("Address", "Hills"))
.OrFilter()
filter.TermPrefixFilter(new FilterTerm("Address", "Valleys"));
})
.All()
.Execute();
```

### And

```csharp
var searcher = myIndex.Searcher;
var results = searcher.CreateQuery()
// Look for any addresses that has "Hills" and keyword "Examine"
.WithFilter(
filter =>
{
filter.TermFilter(new FilterTerm("Address", "Hills"))
.AndFilter()
filter.TermFilter(new FilterTerm("Keyword", "Examine"));
})
.All()
.Execute();
```

### Not

```csharp
var searcher = myIndex.Searcher;
var results = searcher.CreateQuery()
// Look for any addresses that has "Hills" and keyword "Examine"
.WithFilter(
filter =>
{
filter.TermFilter(new FilterTerm("Address", "Hills"))
.NotFilter()
filter.TermFilter(new FilterTerm("Keyword", "Examine"));
})
.All()
.Execute();
```

### And Not

```csharp
var searcher = myIndex.Searcher;
var results = searcher.CreateQuery()
// Look for any addresses that has "Hills" and not keyword "Examine"
.WithFilter(
filter =>
{
filter.TermFilter(new FilterTerm("Address", "Hills"))
.AndNotFilter(innerFilter => innerFilter.TermFilter(new FilterTerm("Keyword", "Examine")));
})
.All()
.Execute();
```

## Spatial

Examine supports Spatial Filtering.
The Examine.Lucene.Spatial package needs to be installed.

### Spatial Operations

Below are the available Spatial Operations in Examine that are supported by the Examine.Lucene.Spatial package. Available operations may vary by provider.

- ExamineSpatialOperation.Intersects
- ExamineSpatialOperation.Overlaps
- ExamineSpatialOperation.IsWithin
- ExamineSpatialOperation.BoundingBoxIntersects
- ExamineSpatialOperation.BoundingBoxWithin
- ExamineSpatialOperation.Contains
- ExamineSpatialOperation.IsDisjointTo
- ExamineSpatialOperation.IsEqualTo

### Spatial Filtering

The `.SpatialOperationFilter()` method adds a filter to the query results to remove any results that do not pass the filter.
The example below demonstrates filtering results where the shape stored in the "spatialWKT" field must intersect the rectangle defined.

```csharp
var query = searcher.CreateQuery()
.WithFilter(
filter => filter.SpatialOperationFilter("spatialWKT", ExamineSpatialOperation.Intersects, (shapeFactory) => shapeFactory.CreateRectangle(0.0, 1.0, 0.0, 1.0))
);
```

## Custom lucene filter

```csharp
var searcher = indexer.Searcher;
var query = searcher.CreateQuery();

var query = (LuceneSearchQuery)query.NativeQuery("hello:world").And(); // Make query ready for extending
query.LuceneFilter(new TermFilter(new Term("nodeTypeAlias", "CWS_Home"))); // Add the raw lucene query
var results = query.Execute();
```
28 changes: 28 additions & 0 deletions docs/articles/indexing.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,34 @@ Data is easily deleted from the index by the unique identifier you provided in y
indexer.DeleteFromIndex("SKU987");
```

### Indexing Spatial Shapes

For the Lucene Provider, the package Examine.Lucene.Spatial must be installed.

As you can see, the values being passed into the ValueSet are type `IExamineSpatialShape` created using the `IExamineSpatialShapeFactory` retrieved from the ValueType of the field. A [field definition](configuration#custom-field-definitions) must be set.

Example for Geo Spatial field storing WKT:

```cs
{FieldDefinitionTypes.GeoSpatialWKT, name => new WKTSpatialIndexFieldValueType(name, loggerFactory, SpatialIndexFieldValueTypeBase.GeoSpatialPrefixTreeStrategyFactory(),true)},
```
Indexing example:

```cs
var geoSpatialFieldType = myIndex.FieldValueTypeCollection.ValueTypes.First(f
=> f.FieldName.Equals("spatialWKT", StringComparison.InvariantCultureIgnoreCase)) as ISpatialIndexFieldValueTypeShapesBase;

var fieldShapeFactory = geoSpatialFieldType.ExamineSpatialShapeFactory;

myIndex.IndexItem(
ValueSet.FromObject(1.ToString(), "content",
new {
nodeName = "my name 1",
updateDate = now.AddDays(2).ToString("yyyy-MM-dd"),
spatialWKT = fieldShapeFactory.CreatePoint(0.0,0.0) })
);
```

## Events

#### [IIndex.IndexOperationComplete](xref:Examine.IIndex#Examine_IIndex_IndexOperationComplete)
Expand Down
21 changes: 21 additions & 0 deletions docs/articles/sorting.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,24 @@ var orderedDescendingResults = searcher
.OrderByDescending(new SortableField("name", SortType.String))
.Execute();
```

## Spatial Sorting

Example order by distance from a Point.

```cs
// Retrieve the Shape Factory from the field
var geoSpatialFieldType = myIndex.FieldValueTypeCollection.ValueTypes.First(f
=> f.FieldName.Equals("spatialWKT", StringComparison.InvariantCultureIgnoreCase)) as ISpatialIndexFieldValueTypeBase;

var fieldShapeFactory = geoSpatialFieldType.SpatialShapeFactory;

// Define the location to compare against
var searchLocation = fieldShapeFactory.CreatePoint(0.0, 0.0);

// Order by the distance between the center of the Shape in the "spatialWKT" vs the search location, Ascending.
var orderedDescendingResults = searcher
.CreateQuery("content")
.OrderBy(new SortableField("spatialWKT", searchLocation))
).Execute();
```
2 changes: 2 additions & 0 deletions docs/articles/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
href: indexing.md
- name: Searching
href: searching.md
- name: Filtering
href: filtering.md
- name: Sorting
href: sorting.md
- name: Paging
Expand Down
2 changes: 1 addition & 1 deletion docs/docs-v1-v2/searching.md
Original file line number Diff line number Diff line change
Expand Up @@ -248,4 +248,4 @@ var query = searcher.CreateQuery();
var query = (LuceneSearchQuery)query.NativeQuery("hello:world").And(); // Make query ready for extending
query.LuceneQuery(NumericRangeQuery.NewInt64Range("numTest", 4, 5, true, true)); // Add the raw lucene query
var results = query.Execute();
```
```
9 changes: 9 additions & 0 deletions docs/docs-v1-v2/sorting.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,17 @@ var orderedDescendingResults = searcher
.Field("writerName", "administrator")
.OrderByDescending(new SortableField("name", SortType.String))
.Execute();

// Mixing ascending and descending
var orderedDescendingResults = searcher
.CreateQuery("content")
.Field("writerName", "administrator")
.OrderByDescending(new SortableField("name", SortType.String)),
.OrderBy(new SortableField("date", SortType.String))
.Execute();
```


## Limiting results

To limit results we can use the `QueryOptions` class when executing a search query. The `QueryOptions` class provides the ability to skip and take.
Expand Down
14 changes: 13 additions & 1 deletion src/Examine.Core/FieldDefinitionTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,16 @@ public static class FieldDefinitionTypes
public const string Integer = "int";

/// <summary>
/// Will be indexed as a float
/// Will be indexed as a single
/// </summary>
public const string Float = "float";

/// <summary>
/// Will be indexed as a single
/// </summary>
[Obsolete("To remove in Examine V5. Use Float")]
public const string Single = "single";

/// <summary>
/// Will be indexed as a double
/// </summary>
Expand Down Expand Up @@ -203,5 +209,11 @@ public static class FieldDefinitionTypes
public const string FacetTaxonomyFullTextSortable = "facettaxonomyfulltextsortable";



/// <summary>
/// GEO Spatial Shape. Index as WKT
/// </summary>
public const string GeoSpatialWKT = "spatial.geo.wkt";

}
}
15 changes: 15 additions & 0 deletions src/Examine.Core/ISpatialIndexFieldValueTypeShapesBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Examine.Search;

namespace Examine
{
/// <summary>
/// Spatial Index Field Value Type Shape Factory
/// </summary>
public interface ISpatialIndexFieldValueTypeShapesBase
{
/// <summary>
/// Gets the Shape Factory for the fields spatial strategy
/// </summary>
ISpatialShapeFactory SpatialShapeFactory { get; }
}
}
Loading
Loading