Skip to content

Commit

Permalink
Add support for value substitution in string based queries (#2201)
Browse files Browse the repository at this point in the history
* core updated to have new bison parser

* Wrong commit used for core

* New query was not applied on top of the previous one

* PR feedback applied

* Forgot a null check

* Updated unit test to match changed in error message

* Updated changelog

* Revert "Updated changelog"

This reverts commit 89ae7f5.

* Updated changelog, again

* Fallback to original ordering if the new query doesn't have one

* Fixed usage of query odering

* Updated unit test to use the user facing name of class

* Changed realm-core to have new changes

* Core points to new commit

* Updated changelog

* Update CHANGELOG.md

Co-authored-by: Nikola Irinchev <irinchev@me.com>

* Update core submodule and added std::move

* Added value substitution in string based query

* All types have a unit test for string based search

* Changelog updated

* Added object store to gitignore and set lf as ending line in code style rules

* Changed style of object instantiation in array

* Added unit test for in memory object in string based query

* Renamed tests to be more coherent with the chosen style

* Improved some code style

* Added comment to explain PrimitiveValue <--> realm_value_t relationship

* Renamed a var

* Fixed changelog and gitignore

* Applied feedback from PR

* Applied general feedback from PR

* Reshaped tests and added more

* Point to right core and updated DictionaryHandle to respect signature of GetFilteredResults

* Some renaming

* Fixed changelog

* Some of the PR feedback applied

* Simplified tests and added test for embedded objects and extended embedded obj to have Guid, ObjectId and byte[]

* Added RealmInteger tests for string query value sustitution

* Forgot to rethrow properly

* Test nullable null value correctly passed to core in query string value substitution

* Small style fixes

* Simplify boxing + add tostring overloads for test case data (#2228)

* Reverted to correct core submodule to be equal to master

* Fixed documentation for the Filter method

* Updated realm-core to fix unit test

Co-authored-by: Nikola Irinchev <irinchev@me.com>
  • Loading branch information
LaPeste and nirinchev committed Feb 24, 2021
1 parent b30f384 commit 5287703
Show file tree
Hide file tree
Showing 16 changed files with 354 additions and 16 deletions.
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ indent_style = space
tab_width = 4

# New line preferences
end_of_line = crlf
end_of_line = lf
insert_final_newline = false

#### .NET Coding Conventions ####
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
var obj = new MyObject();
obj.Denominations.Add("quarter", 0.25d);
```
* Added support for value substitution in string based queries. This enables expressions following [this syntax](https://github.com/realm/realm-js/blob/master/docs/tutorials/query-language.md): `realm.All<T>().Filter("field1 = $0 && field2 = $1", 123, "some-string-value")`. (Issue [#1822](https://github.com/realm/realm-dotnet/issues/1822))
* Reduced the size of the native binaries by ~5%. (PR [#2239](https://github.com/realm/realm-dotnet/pull/2239))

### Compatibility
Expand Down
4 changes: 2 additions & 2 deletions Realm/Realm/DatabaseTypes/RealmCollectionBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,9 @@ internal RealmCollectionBase(Realm realm, RealmObjectBase.Metadata metadata)
}
}

internal RealmResults<T> GetFilteredResults(string query)
internal RealmResults<T> GetFilteredResults(string query, RealmValue[] arguments)
{
var handle = Handle.Value.GetFilteredResults(query);
var handle = Handle.Value.GetFilteredResults(query, arguments);
return new RealmResults<T>(Realm, handle, Metadata);
}

Expand Down
7 changes: 5 additions & 2 deletions Realm/Realm/Extensions/CollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ public static IDisposable SubscribeForNotifications<T>(this IDictionary<string,
/// A Queryable collection, obtained by calling <see cref="Realm.All{T}"/>.
/// </param>
/// <param name="predicate">The predicate that will be applied.</param>
/// <param name="arguments">Values used for substitution in the predicate. Note that all primitive types are accepted as they are implicitly converted to RealmValue.</param>
/// <returns>A queryable observable collection of objects that match the predicate.</returns>
/// <remarks>
/// This method can be used in combination with LINQ filtering, but it is strongly recommended
Expand All @@ -210,16 +211,18 @@ public static IDisposable SubscribeForNotifications<T>(this IDictionary<string,
/// var results1 = realm.All&lt;Foo&gt;("Bar.IntValue > 0");
/// var results2 = realm.All&lt;Foo&gt;("Bar.IntValue > 0 SORT(Bar.IntValue ASC Bar.StringValue DESC)");
/// var results3 = realm.All&lt;Foo&gt;("Bar.IntValue > 0 SORT(Bar.IntValue ASC Bar.StringValue DESC) DISTINCT(Bar.IntValue)");
/// var results4 = realm.All&lt;Foo&gt;("Bar.IntValue > $0 || (Bar.String == $1 &amp;&amp; Bar.Bool == $2)", 5, "small", true);
/// </code>
/// </example>
/// <seealso href="https://github.com/realm/realm-js/blob/master/docs/tutorials/query-language.md">
/// Examples of the NSPredicate syntax
/// </seealso>
/// <seealso href="https://academy.realm.io/posts/nspredicate-cheatsheet/">NSPredicate Cheatsheet</seealso>
public static IQueryable<T> Filter<T>(this IQueryable<T> query, string predicate)
public static IQueryable<T> Filter<T>(this IQueryable<T> query, string predicate, params RealmValue[] arguments)
{
Argument.NotNull(arguments, nameof(arguments));
var realmResults = Argument.EnsureType<RealmResults<T>>(query, $"{nameof(query)} must be a query obtained by calling Realm.All.", nameof(query));
return realmResults.GetFilteredResults(predicate);
return realmResults.GetFilteredResults(predicate, arguments);
}

[EditorBrowsable(EditorBrowsableState.Never)]
Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Handles/CollectionHandleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public ResultsHandle Snapshot()
throw new NotSupportedException("Snapshotting this collection is not supported.");
}

public abstract ResultsHandle GetFilteredResults(string query);
public abstract ResultsHandle GetFilteredResults(string query, RealmValue[] arguments);

public abstract CollectionHandleBase Freeze(SharedRealmHandle frozenRealmHandle);

Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Handles/DictionaryHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public override ThreadSafeReferenceHandle GetThreadSafeReference()
return new ThreadSafeReferenceHandle(result);
}

public override ResultsHandle GetFilteredResults(string query)
public override ResultsHandle GetFilteredResults(string query, RealmValue[] arguments)
{
throw new NotImplementedException("Dictionaries can't be filtered yet.");
}
Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Handles/ListHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ public override ThreadSafeReferenceHandle GetThreadSafeReference()
return new ThreadSafeReferenceHandle(result);
}

public override ResultsHandle GetFilteredResults(string query)
public override ResultsHandle GetFilteredResults(string query, RealmValue[] arguments)
{
throw new NotImplementedException("Lists can't be filtered yet.");
}
Expand Down
21 changes: 18 additions & 3 deletions Realm/Realm/Handles/ResultsHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,10 @@ private static class NativeMethods
public static extern IntPtr snapshot(ResultsHandle results, out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "results_get_filtered_results", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr get_filtered_results(ResultsHandle results, [MarshalAs(UnmanagedType.LPWStr)] string query_buf, IntPtr query_len, out NativeException ex);
public static extern IntPtr get_filtered_results(ResultsHandle results,
[MarshalAs(UnmanagedType.LPWStr)] string query_buf, IntPtr query_len,
[MarshalAs(UnmanagedType.LPArray), In] PrimitiveValue[] arguments, IntPtr args_count,
out NativeException ex);

[DllImport(InteropConfig.DLL_NAME, EntryPoint = "results_find_object", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr find_object(ResultsHandle results, ObjectHandle objectHandle, out NativeException ex);
Expand Down Expand Up @@ -186,9 +189,21 @@ public override ThreadSafeReferenceHandle GetThreadSafeReference()
return new ThreadSafeReferenceHandle(result);
}

public override ResultsHandle GetFilteredResults(string query)
public override ResultsHandle GetFilteredResults(string query, RealmValue[] arguments)
{
var ptr = NativeMethods.get_filtered_results(this, query, (IntPtr)query.Length, out var ex);
var primitiveValues = new PrimitiveValue[arguments.Length];
var handles = new RealmValue.HandlesToCleanup?[arguments.Length];
for (var i = 0; i < arguments.Length; i++)
{
(primitiveValues[i], handles[i]) = arguments[i].ToNative();
}

var ptr = NativeMethods.get_filtered_results(this, query, (IntPtr)query.Length, primitiveValues, (IntPtr)primitiveValues.Length, out var ex);
foreach (var handle in handles)
{
handle?.Dispose();
}

ex.ThrowIfNecessary();
return new ResultsHandle(this, ptr);
}
Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Handles/SetHandle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ public override ThreadSafeReferenceHandle GetThreadSafeReference()
return new ThreadSafeReferenceHandle(result);
}

public override ResultsHandle GetFilteredResults(string query)
public override ResultsHandle GetFilteredResults(string query, RealmValue[] arguments)
{
throw new NotImplementedException("Sets can't be filtered yet.");
}
Expand Down
1 change: 1 addition & 0 deletions Realm/Realm/Native/PrimitiveValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

namespace Realms.Native
{
// This type is marshalled through C++ wrappers' realm_value_t
[StructLayout(LayoutKind.Explicit)]
[DebuggerDisplay("PrimitiveValue({Type})")]
internal unsafe struct PrimitiveValue
Expand Down
Loading

0 comments on commit 5287703

Please sign in to comment.