Indication as an identifier in RavenDB

The RavenDb documentation says:

Numeric or Guid Id properties are supported and will work without problems. In this case, RavenDB automatically converts the internal string identifier to a numeric value or the Guid value specified in the object and vice versa.

I saved the following objects:

class A { public Guid Id { get; set; } public Guid BId { get; set; } } class B { public Guid Id { get; set; } public string Name { get; set; } } 

Then I created the following projection:

 class AB { public Guid Id { get; set; } // This should be the Id of A public Guid BId { get; set; } // This should be the Id of B public string BName { get; set; } // This should be the name of B } 

I created the following index to create the projection:

 class MyIndex : AbstractIndexCreationTask<AB> { public MyIndex() { Map = docs => from d in docs select new { d.Id, d.BId, BName = string.Empty }; TransformResults = (database, results) => from r in results let b = database.Load<B>("bs/" + r.BId.ToString()) select new { r.Id, r.BId, BName = b.Name }; } } 

When I use the following query:

 session.Query<AB, MyIndex>().FirstOrDefault(t => t.Id == guid); 

I get this exception:

Error converting the value "bs / cc0a65ae-dd36-4437-8a57-fa20b91eeef7" to the input "System.Guid". Path 'Id'.

Questions:

  • This is caused by a conversion in my projection, since Id is a string, not my pointer. However, leaving it will not return the identifier. What should I do?

  • I need to use the string construction "bs/" + r.BId.ToString() to load the linked document. Is there any way not to do this? Is there any function that will allow me the doc tag?

  • Is there a general way to fully highlight a document tag?

My limitations.

I will create Guid and cannot let RavenDb generate it for me. I know that the document identifier is actually a string, but I really need to use the Guid that I create. I would rather have the Id property of my objects.

I am using Raven.Client 1.0.972

+5
source share
5 answers

You can achieve this using the MultiMap / Reduce index, but you will need hacking:

1) You will need to reduce the use of strings, not directions. You can still return values ​​as pointers in your AB class, as I will demonstrate below.

2) You cannot name the first property of your AB class "Id", since the raven will try to translate it to "__document_id". So call it β€œAId” and it will work fine.

3) In the display phase, you need to manipulate the lines yourself to interrupt the document key prefix.

Here is an example program that brings it all together. This demonstrates that it really works, but I think it also shows why Ayende prefers string identifiers, so you don't have to deal with this mess.

 using System; using System.Linq; using Raven.Client.Document; using Raven.Client.Indexes; namespace RavenScratchTest { class Program { static void Main() { var documentStore = new DocumentStore { Url = "http://localhost:8080" }; documentStore.Initialize(); IndexCreation.CreateIndexes(typeof(Program).Assembly, documentStore); using (var session = documentStore.OpenSession()) { var b = new B { Id = Guid.NewGuid(), Name = "Foo" }; var a = new A { Id = Guid.NewGuid(), BId = b.Id }; session.Store(a); session.Store(b); session.SaveChanges(); } using (var session = documentStore.OpenSession()) { var a = session.Query<A>().Customize(x => x.WaitForNonStaleResults()).First(); var b = session.Query<B>().Customize(x => x.WaitForNonStaleResults()).First(); Console.WriteLine("A: Id = {0}", a.Id); Console.WriteLine(" BId = {0}", a.BId); Console.WriteLine(); Console.WriteLine("B: Id = {0}", b.Id); Console.WriteLine(" Name = {0}", b.Name); Console.WriteLine(); var guid = a.Id; var ab = session.Query<AB, MyIndex>().Customize(x => x.WaitForNonStaleResults()) .FirstOrDefault(t => t.AId == guid); if (ab == null) Console.WriteLine("AB: NULL"); else { Console.WriteLine("AB: AId = {0}", ab.AId); Console.WriteLine(" BId = {0}", ab.BId); Console.WriteLine(" BName = {0}", ab.BName); Console.WriteLine(); } } Console.WriteLine(); Console.WriteLine("Done."); Console.ReadLine(); } } class A { public Guid Id { get; set; } public Guid BId { get; set; } } class B { public Guid Id { get; set; } public string Name { get; set; } } class AB { public Guid AId { get; set; } public Guid BId { get; set; } public string BName { get; set; } } class MyIndex : AbstractMultiMapIndexCreationTask<MyIndex.ReduceResult> { public MyIndex() { AddMap<A>(docs => from a in docs select new { AId = a.Id.ToString().Split('/')[1], a.BId, BName = (string)null }); AddMap<B>(docs => from b in docs select new { AId = (string)null, BId = b.Id.ToString().Split('/')[1], BName = b.Name }); Reduce = results => from result in results group result by result.BId into g select new { g.FirstOrDefault(x => x.AId != null).AId, BId = g.Key, g.FirstOrDefault(x => x.BName != null).BName }; } internal class ReduceResult { public string AId { get; set; } public string BId { get; set; } public string BName { get; set; } } } } 
+5
source

You can provide the RavenDB identifier explicitly when saving:

 session.Store(doc, explicitIdValueString); 

explicitIdValueString may be a Guid string. This value will be used to identify the document in the entire database and will not have a type tag name prefix. You can also customize the tag name or ID generation strategy together by overriding the conventions on IDocumentStore.Conventions , for example FindTypeTagName , which is Func<Type, string> .

+2
source

The main problem is that although RavenDB can work with a numeric / integer on the client, on the server side, RavenDB uses string identifiers.

In general, it is not recommended to use guides / numeric identifiers.

+2
source

Suppose you have users and you want to generate guid identifiers for them.

 new User { Id = "users/" + Guid.NewGuid().ToString("N") } 

For sanity purposes, I readily create keys in documents to set them as immutable.

 public class User { public User(Guid? guid = null) { IdPart = (guid ?? Guid.NewGuid()).ToString("N") } string IdPart { get; } string Id => $"Users/{IdPart}" 

Sometimes IdPart is actually the whole key. Suppose we have "Users / abc". If the user has a project. I usually create a document similar to:

 public class Project { public User(Guid? userId = null) { UserId = "Users/" + (guid ?? Guid.NewGuid()).ToString("N"); Id = $"{UserId}/project/" } 

Pay attention to trailing project/ , this will lead to the appearance of a raven to create a HiLo value after the slash.

This concept can be used to easily mix both assigned identifiers, natural identifiers, and characters of the / hilo / identity sequence, while at the same time facilitating readable identifiers as opposed to 1 . 1 what? But User/abc/project/1 , I can tell you what it is. The first project created by abc

+1
source
 class MyIndex : AbstractIndexCreationTask<AB> { public MyIndex() { Map = docs => from d in docs select new { d.Id, d.BId, BName = string.Empty }; TransformResults = (database, results) => from r in results let b = database.Load<B>("bs/" + r.BId.ToString()) select new { Id = Guid.Parse(r.Id.ToString().Split('/').Last()), r.BId, BName = b.Name }; } } 
-1
source

Source: https://habr.com/ru/post/1437302/


All Articles