Skip to content
Browse files

Allowing to query on references

  • Loading branch information...
1 parent 624f1a8 commit c686acd45ce83eb6f59551b1cf0d7155a0132bda @ayende ayende committed Oct 19, 2011
View
5 Raven.Abstractions/Json/JsonDynamicConverter.cs
@@ -59,7 +59,10 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
return val.Value;
var array = token as RavenJArray;
if (array != null)
- return new DynamicJsonObject.DynamicList(array.Select(DynamicJsonObject.TransformToValue).ToArray());
+ {
+ var dynamicJsonObject = new DynamicJsonObject(new RavenJObject());
+ return new DynamicJsonObject.DynamicList(array.Select(dynamicJsonObject.TransformToValue).ToArray());
+ }
var typeName = token.Value<string>("$type");
if(typeName != null)
View
131 Raven.Abstractions/Linq/DynamicJsonObject.cs
@@ -30,6 +30,8 @@ public interface IDynamicJsonObject
/// </summary>
public class DynamicJsonObject : DynamicObject, IEnumerable<object>, IDynamicJsonObject
{
+ private DynamicJsonObject parent;
+
public IEnumerator<object> GetEnumerator()
{
return
@@ -96,6 +98,13 @@ public DynamicJsonObject(RavenJObject inner)
this.inner = inner;
}
+
+ private DynamicJsonObject(DynamicJsonObject parent, RavenJObject inner)
+ {
+ this.parent = parent;
+ this.inner = inner;
+ }
+
/// <summary>
/// Provides the implementation for operations that get member values. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as getting a value for a property.
/// </summary>
@@ -127,7 +136,7 @@ public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out ob
return true;
}
- public static object TransformToValue(RavenJToken jToken)
+ public object TransformToValue(RavenJToken jToken)
{
switch (jToken.Type)
{
@@ -136,12 +145,19 @@ public static object TransformToValue(RavenJToken jToken)
var values = jObject.Value<RavenJArray>("$values");
if (values != null)
{
- return new DynamicList(values.Select(TransformToValue).ToArray());
+ return new DynamicList(this, values.Select(TransformToValue).ToArray());
+ }
+ var refId = jObject.Value<string>("$ref");
+ if(refId != null)
+ {
+ var ravenJObject = FindReference(refId);
+ if (ravenJObject != null)
+ return new DynamicJsonObject(this, ravenJObject);
}
- return new DynamicJsonObject(jObject);
+ return new DynamicJsonObject(this, jObject);
case JTokenType.Array:
- var ar = jToken as RavenJArray; // cannot result in null because jToken.Type is set to Array
- return new DynamicList(ar.Select(TransformToValue).ToArray());
+ var ar = (RavenJArray) jToken;
+ return new DynamicList(this, ar.Select(TransformToValue).ToArray());
case JTokenType.Date:
return jToken.Value<DateTime>();
case JTokenType.Null:
@@ -165,6 +181,63 @@ public static object TransformToValue(RavenJToken jToken)
}
}
+ private RavenJObject FindReference(string refId)
+ {
+ var p = this;
+ while (p.parent != null)
+ p = p.parent;
+
+ return p.Scan().FirstOrDefault(x => x.Value<string>("$id") == refId);
+ }
+
+ private IEnumerable<RavenJObject> Scan()
+ {
+ var objs = new List<RavenJObject>();
+ var lists = new List<RavenJArray>();
+
+ objs.Add(inner);
+
+ while (objs.Count > 0 || lists.Count > 0)
+ {
+ var objCopy = objs;
+ objs = new List<RavenJObject>();
+ foreach (var obj in objCopy)
+ {
+ yield return obj;
+ foreach (var property in obj.Properties)
+ {
+ switch (property.Value.Type)
+ {
+ case JTokenType.Object:
+ objs.Add((RavenJObject)property.Value);
+ break;
+ case JTokenType.Array:
+ lists.Add((RavenJArray)property.Value);
+ break;
+ }
+ }
+ }
+
+ var listsCopy = lists;
+ lists = new List<RavenJArray>();
+ foreach (var list in listsCopy)
+ {
+ foreach (var item in list)
+ {
+ switch (item.Type)
+ {
+ case JTokenType.Object:
+ objs.Add((RavenJObject)item);
+ break;
+ case JTokenType.Array:
+ lists.Add((RavenJArray)item);
+ break;
+ }
+ }
+ }
+ }
+ }
+
/// <summary>
/// Gets the value for the specified name
/// </summary>
@@ -219,6 +292,7 @@ public object GetDocumentId()
/// </summary>
public class DynamicList : DynamicObject, IEnumerable<object>
{
+ private readonly DynamicJsonObject parent;
private readonly object[] inner;
/// <summary>
@@ -230,6 +304,11 @@ public DynamicList(object[] inner)
this.inner = inner;
}
+ internal DynamicList(DynamicJsonObject parent, object[] inner):this(inner)
+ {
+ this.parent = parent;
+ }
+
/// <summary>
/// Provides the implementation for operations that invoke a member. Classes derived from the <see cref="T:System.Dynamic.DynamicObject"/> class can override this method to specify dynamic behavior for operations such as calling a method.
/// </summary>
@@ -258,29 +337,43 @@ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, o
return base.TryInvokeMember(binder, args, out result);
}
+ private IEnumerable<dynamic> Enumerate()
+ {
+ foreach (var item in inner)
+ {
+ var ravenJObject = item as RavenJObject;
+ if(ravenJObject != null)
+ yield return new DynamicJsonObject(parent, ravenJObject);
+ var ravenJArray = item as RavenJArray;
+ if (ravenJArray != null)
+ yield return new DynamicList(parent, ravenJArray.ToArray());
+ yield return item;
+ }
+ }
+
public dynamic First(Func<dynamic, bool> predicate)
{
- return inner.First(predicate);
+ return Enumerate().First(predicate);
}
public dynamic FirstOrDefault(Func<dynamic, bool> predicate)
{
- return inner.FirstOrDefault(predicate);
+ return Enumerate().FirstOrDefault(predicate);
}
public dynamic Single(Func<dynamic, bool> predicate)
{
- return inner.Single(predicate);
+ return Enumerate().Single(predicate);
}
public IEnumerable<dynamic> Distinct()
{
- return new DynamicList(inner.Distinct().ToArray());
+ return new DynamicList(Enumerate().Distinct().ToArray());
}
public dynamic SingleOrDefault(Func<dynamic, bool> predicate)
{
- return inner.SingleOrDefault(predicate);
+ return Enumerate().SingleOrDefault(predicate);
}
/// <summary>
@@ -289,12 +382,12 @@ public dynamic SingleOrDefault(Func<dynamic, bool> predicate)
/// <returns></returns>
public IEnumerator<object> GetEnumerator()
{
- return ((IEnumerable<object>)inner).GetEnumerator();
+ return Enumerate().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
- return ((IEnumerable)inner).GetEnumerator();
+ return Enumerate().GetEnumerator();
}
/// <summary>
@@ -404,39 +497,39 @@ public int Count
/// </summary>
public int Sum(Func<dynamic, int> aggregator)
{
- return inner.Sum(aggregator);
+ return Enumerate().Sum(aggregator);
}
/// <summary>
/// Redirector for sum operation
/// </summary>
public decimal Sum(Func<dynamic, decimal> aggregator)
{
- return inner.Sum(aggregator);
+ return Enumerate().Sum(aggregator);
}
/// <summary>
/// Redirector for sum operation
/// </summary>
public float Sum(Func<dynamic, float> aggregator)
{
- return inner.Sum(aggregator);
+ return Enumerate().Sum(aggregator);
}
/// <summary>
/// Redirector for sum operation
/// </summary>
public double Sum(Func<dynamic, double> aggregator)
{
- return inner.Sum(aggregator);
+ return Enumerate().Sum(aggregator);
}
/// <summary>
/// Redirector for sum operation
/// </summary>
public long Sum(Func<dynamic, long> aggregator)
{
- return inner.Sum(aggregator);
+ return Enumerate().Sum(aggregator);
}
/// <summary>
@@ -450,12 +543,12 @@ public int Length
public IEnumerable<object> Select(Func<object, object> func)
{
- return new DynamicList(inner.Select(func).ToArray());
+ return new DynamicList(parent, inner.Select(func).ToArray());
}
public IEnumerable<object> SelectMany(Func<object, IEnumerable<object>> func)
{
- return new DynamicList(inner.SelectMany(func).ToArray());
+ return new DynamicList(parent, inner.SelectMany(func).ToArray());
}
}
View
83 Raven.Tests/Bugs/JsonReferences.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Newtonsoft.Json;
+using Raven.Client.Embedded;
+using Xunit;
+
+namespace Raven.Tests.Bugs
+{
+ public class JsonReferences
+ {
+ [Fact]
+ public void can_index_on_a_reference2()
+ {
+ using (var store = new EmbeddableDocumentStore
+ {
+ RunInMemory = true
+ })
+ {
+
+ store.Initialize();
+ using (var session = store.OpenSession())
+ {
+ var category = new Category()
+ {
+ Name = "Parent"
+ };
+
+ category.Add(new Category()
+ {
+ Name = "Child"
+ });
+
+ session.Store(category);
+ session.SaveChanges();
+ }
+
+ using (var session = store.OpenSession())
+ {
+ var results0 = session.Query<Category>()
+ .Customize(x=>x.WaitForNonStaleResults(TimeSpan.FromHours(1)))
+ .ToList();
+ Assert.Equal(1, results0.Count);
+
+ // WORKS
+ var results1 = session.Query<Category>()
+ .Customize(x => x.WaitForNonStaleResults())
+ .Where(x => x.Children.Any(y => y.Name == "Child")).
+ ToList();
+ Assert.Equal(1, results1.Count);
+
+ // FAILS
+ var results2 = session.Query<Category>()
+ .Customize(x => x.WaitForNonStaleResults())
+ .Where(x => x.Children.Any(y => y.Parent.Name == "Parent"))
+ .ToList();
+ Assert.Equal(1, results2.Count);
+ }
+ }
+ }
+
+ [JsonObject(IsReference = true)]
+ public class Category
+ {
+ public string Id { get; set; }
+ public string Name { get; set; }
+ public Category Parent { get; set; }
+ public List<Category> Children { get; set; }
+
+ public Category()
+ {
+ Children = new List<Category>();
+ }
+
+ public void Add(Category category)
+ {
+ category.Parent = this;
+ Children.Add(category);
+ }
+ }
+ }
+
+}
View
1 Raven.Tests/Raven.Tests.csproj
@@ -183,6 +183,7 @@
<Compile Include="Bugs\Indexing\CanHaveAnIndexNameThatStartsWithDynamic.cs" />
<Compile Include="Bugs\Indexing\InvalidIndexes.cs" />
<Compile Include="Bugs\Issue355.cs" />
+ <Compile Include="Bugs\JsonReferences.cs" />
<Compile Include="Bugs\LarsErik.cs" />
<Compile Include="Bugs\Iulian\GeneratesCorrectTemporaryIndex.cs" />
<Compile Include="Bugs\LiveProjections\Entities\Place.cs" />

0 comments on commit c686acd

Please sign in to comment.
Something went wrong with that request. Please try again.