How to create a composite index in Ravendb - c#

I have documents collection "Messages" in my base (RavenDB)
Document definition like:
class Message
{
string Content;
Tag[] Tags;
Location[] Locations;
string[] Actions;
bool IsActive;
}
Tag class definition:
class Tag
{
string Value;
Translation[] Translations;
}
Location class:
class Location
{
string Code;
Translation[] Translations;
}
Translation class:
class Translation
{
string LanguageCode;
string Value;
}
So, I want to create a index that will allow me to make queries by several fields:
Full-text search by Message.Content
Only messages with IsActive==true
Messages that contains my action in Message.Actions
Messages that contains tag with myValue and myLanguageCode
Locations that contains location with some myCode and myLanguageCode
I would like to query to be on all the conditions simultaneously
So, how should i define index for RavenDB?

Well, after a short studies of RavenDB auto dynamic indexes i created something like
new IndexDefinition
{
Map = #"
from doc in docs.Messages
where IsActive==true
from docActionsItem in (IEnumerable<dynamic>)doc.Actions
from docTagsItem in (IEnumerable<dynamic>)doc.Tags
from docTagsItemTranslationsItem in (IEnumerable<dynamic>)docTagsItem.Translations
from docLocationsItem in (IEnumerable<dynamic>)doc.Locations
from docLocationsItemTranslationsItem in (IEnumerable<dynamic>)docLocationsItem.Translations
select new {
TagsValue = docTagsItem.Value,
Content = doc.Content,
Actions=docActionsItem,
TagsTranslationsLanguageCode = docTagsItemTranslationsItem.LanguageCode,
TagsTranslationsValue = docTagsItemTranslationsItem.Value,
LocationsCode = docLocationsItem.Code,
LocationsTranslationsLanguageCode=docLocationsItemTranslationsItem.LanguageCode,
LocationsTranslationsValue=docLocationsItemTranslationsItem.Value
}",
Analyzers =
{
{"Content", typeof(StandardAnalyzer).FullName},
}
}

There's no need to define an index upfront to do queries in RavenDB. Just create the linq-query - RavenDB will then dynamically create an index for you without any additional cost.
The only use-case where you would possible want to create an index by your own, would be if want do specify a different Lucene.NET analyzer to do what you call "full-text search" on the field Message.Content. But even that should be as simple as just creating the linq-query for the index and pass it into RavenDB on startup. If you want to know how to do that, I recommend to have a look into Ayende RaccoonBlog sample or the officials docs, which will be updated soon (FYI -> ravendb/docs has the new docs).

Related

DTO and ViewBag issue MVC C#

Hi i have a problem getting data from two sources and combining into one DTO and then sending to the View
I have a list of Users in my Database and i have a list of Jobs in my WCF Service. What i am trying to do is make an Add Job page where i get all of the Users current Jobs from the Database and make a new DropDown list in the View that display all the Jobs a user doesnt have. So i intersect the two lists and create a new list with all obtained job removed.
I have tried doing it but cannot see where im going wrong.
public ActionResult AddJob(String usrCode)
{
var jobs = jobClient.getAllJobs();
var allJobCodes = (from s in jobs select s.jobCode).ToList();
var thisJobCode = (from s in db.UserJobs
where s.usrCode == usrCode
select s.jobCode).ToList();
var notObtained = allJobCodes.Except(thisJobCode);
IEnumerable<String> list1 = allJobCodes.AsEnumerable();
IEnumerable<String> list2 = notObtained.AsEnumerable();
IEnumerable<String> list3 = list2.Select(x => new UserJobsDTO()
{ jobCode = x });
IEnumerable<UserJobsDTO> list = list3(jobs, notObtained);
ViewBag.jobCode = new SelectList(list, "jobCode", "Description");
var model = new UserJobsDTO { usrCode = usrCode, jobCode = list};
return View("AddJob", model);
}
public class UserJobsDTO
{
public string usrCode { get; set; }
public IEnumerable<String> jobCode { get; set; }
public String Description { get; set; }
}
Can anyone help me out? The main problems at the moment are with jobCode = x complaining about....
Error 1 Cannot implicitly convert type
'string' to 'System.Collections.Generic.IEnumerable<string>'
And list3 variable complaining about this....
Error 2 'list3' is a 'variable' but is used like a 'method'
The errors state exactly what the problems are...
Cannot implicitly convert type
'string' to 'System.Collections.Generic.IEnumerable'
Job code is a collection:
public IEnumerable<String> jobCode { get; set; }
But you're trying to assign a non-collection value to it:
list2.Select(x => new UserJobsDTO()
{ jobCode = x })
So either you need to make jobCode a string (such that any given instance of the DTO has one value) or set the entire collection to it (such that any given instance of the DTO has the list of values).
'list3' is a 'variable' but is used like a 'method'
I literally have no idea what you're even trying to do here:
IEnumerable<UserJobsDTO> list = list3(jobs, notObtained);
But, as the error states, list3 is a variable not a method. You can't invoke a variable like a method.
It's very confusing what you're trying to accomplish in this code. And, honestly, the best advice at this time would just be to step through in a debugger, examine the runtime values that you have, and really think about how to structure those values for your view.
In particular, it will be very helpful to semantically discern between singular values and plural values. For example, terms like UserJobsDTO or jobCode imply certain levels of singularity and plurality, but their types and structures don't agree with that. (How can a collection of strings be a single "code"?)
Just the names and types of the structures you're using are very important in being able to logically understand and express what you're trying to do.

Parse database - save info in table as json

I'm using ASP.NET MVC 4 with the Parse database (https://www.parse.com/). I have a table called app and a field called reviews. In this field i want to save a json object with the following format:
[{"Title":"a title", "url":"a url"}, {"Title":"another title", "url":"another url"}, etc...]
As I have read in the documentation (https://www.parse.com/docs/dotnet_guide#objects-types) Parse supports objects that implement IDictionary<string, T> and IList<T> so i have the following code:
var requestFormInformation = new ParseObject("app");
requestFormInformation["Reviews"] = requestForm.Reviews.ToList();
Reviews property is an IEnumerable<Review> and this is the Review class
public class Review
{
public string Title { get; set; }
public string Url { get; set; }
}
There is no error thrown when i save this code and save in the database but the item is not saved.
You might want to place your code into a try-catch (ParseException) to see if the save fails for some reason.
Sidenote: the Parse help page suggests that you start the class name with a uppercase character and the key name with a lowercase character. "app" should be "App" and "Reviews" should be "reviews".
Another thing could be authentication or authorization. Are you properly authenticated and do you have the rights to save this object.
You need to be clear about how you want to save the data. You say you want to save it as JSON, which is a string serialization format, so the column should be of type string, and you'll have to handle JSON serialize/deserialize yourself. I don't think this is what you actually want, so forget about JSON as that is an implementation detail inside Parse.
If instead you want it to just serialize your collection you don't have to do anything special. As per the documentation on object types you can store anything that implements IList or IDictionary including nested objects.
I would suggest you try an experiment with a simple class that has a List as one of its properties:
var foo = new ParseObject("Foo");
var reviews = new List<Review>();
reviews.Add(new Review { Title = "Review 1" });
reviews.Add(new Review { Title = "Review 2" });
foo["reviews"] = reviews;
await foo.SaveAsync();
Once that is working, test incremental changes till you find out where things have gone wrong.

c# Linq to Sql dynamic Data Context assignment

`Hi,
Can somebody please give me a pointer on this? I have 8 servers each with 8 databases which look identical exept server/database name. We are talking thousands of tables.
I create my data contexts with sqlmetal.exe
After creating my data contexts, I import them into the application and then I run comparison scripts over the databases to compare results.
My problem is dynamically switching between data contexts.
Datacontext.DAL.DUK1 duk1sdi = new Datacontext.DAL.DUK1(connectionString);
Datacontext.DAL.DUK3 duk3sdi = new Datacontext.DAL.DUK3(connectionString);
string fromOne = runQuery(duk1sdi);
string fromThree = runQuery(duk3sdi);
public static string runQuery(DataContext duk)
{
var query =
from result in duk.TableA
select result.Total;
string returnString = query;
return returnString;
}
I have no problem with the query running when the duk is predefined, however how do I define and pass the datacontext to the function?
The error I get is:
Error 1 'System.Data.Linq.DataContext' does not contain a definition
for 'TableA' and no extension method 'TableA' accepting a first
argument of type 'System.Data.Linq.DataContext' could be found (are
you missing a using directive or an assembly reference?)
You could use the GetTable<T> method, where T is the type of the table, e.g. TableA.
public static string runQuery(DataContext duk) {
var table = duk.GetTable<TableA>();
var query = from result in table select result.Total;
...
}
However, all types of TableA will need to be the same type, strictly (I'm pretty sure).
Otherwise you would need to literally branch the logic for the handling of each context. Since you can extend your DataContext instances (in general, maybe not in your specific case) then you could have them share an interface that exposes a collection property of TableA, but you would need a higher level context wrapper to pass around then - unless you pass around the collection by altering the method signature.
You can use interfaces. Check this answer, but be sure to script the interfaces using a .tt file with the amount of tables you have.
Edit:
If you have generated contexts which you want to use interchangeably in a reusable method, you have the problem that the generated TableA classes are not reusable, since they are different types (even though the names may match, but that doesn't make them equal). Therefore you need to abstract the actual types, and one way to do this, is to use interfaces. You build your reusable method around an interface which abstracts the specific context-type and table-type. The downside is that you have to implement the interfaces on the generated contexts and tabletypes. This though is something you can solve using a .tt script.
Pseudo code:
// Define interface for table
public interface ITableA {
// ... properties
}
// Define interface for context
public interface IMyContext {
IQueryable<ITableA> TableA { get; }
}
// Extend TableA from DUK1
public partial class TableA: ITableA {
}
// Extend DUK1
public partial class Datacontext.DAL.DUK1: IMyContext {
IQueryable<ITableA> IMyContext.TableA {
get { return TableA; }
}
}
// Same for DUK3 and TableA FROM DUK3
// Finally, your code
Datacontext.DAL.DUK1 duk1sdi = new Datacontext.DAL.DUK1(connectionString);
Datacontext.DAL.DUK3 duk3sdi = new Datacontext.DAL.DUK3(connectionString);
string fromOne = runQuery(duk1sdi);
string fromThree = runQuery(duk3sdi);
public static string runQuery(IMyContext duk) {
// Note: method accepts interface, not specific context type
var query = from result in duk.TableA
select result.Total;
string returnString = query;
return returnString;
}
If your schema is identical between databases, why script the dbml for all of them? Just create one context with it's associated classes and dynamically switch out the connection string when instantiating the context.
var duk1sdi = new Datacontext.DAL.DUK1(connectionString1);
var duk3sdi = new Datacontext.DAL.DUK1(connectionString2);
Thanks, guys, I think I found the simplist solution for me based a bit of both your answers and by RTFM (Programming Microsoft Linq in Microsoft .NET Framework 4 by Paulo Pialorsi and Marco Russo)
In this way I don't have to use the large DBML files. It is a shame because I'm going to have to create hundreds of tables in this way, but I can now switch between connection strings on the fly.
First I create the table structure. (outside the program code block)
[Table(Name = "TableA")]
public class TableA
{
[Column] public int result;
}
Then I define the table for use:
Table<TableA> TableA = dc.GetTable<TableA>();
And then I can query from it:
var query =
from result in TableA
select TableA.result;

How to create a custom property in a Linq-to-SQL entity class?

I have two tables Studies and Series. Series are FK'd back to Studies so one Study contains a variable number of Series.
Each Series item has a Deleted column indicating it has been logically deleted from the database.
I am trying to implement a Deleted property in the Study class that returns true only if all the contained Series are deleted.
I am using O/R Designer generated classes, so I added the following to the user modifiable partial class for the Study type:
public bool Deleted
{
get
{
var nonDeletedSeries = from s in Series
where !s.Deleted
select s;
return nonDeletedSeries.Count() == 0;
}
set
{
foreach (var series in Series)
{
series.Deleted = value;
}
}
}
This gives an exception "The member 'PiccoloDatabase.Study.Deleted' has no supported translation to SQL." when this simple query is executed that invokes get:
IQueryable<Study> dataQuery = dbCtxt.Studies;
dataQuery = dataQuery.Where((s) => !s.Deleted);
foreach (var study in dataQuery)
{
...
}
Based on this http://www.foliotek.com/devblog/using-custom-properties-inside-linq-to-sql-queries/, I tried the following approach:
static Expression<Func<Study, bool>> DeletedExpr = t => false;
public bool Deleted
{
get
{
var nameFunc = DeletedExpr.Compile();
return nameFunc(this);
}
set
{ ... same as before
}
}
I get the same exception when a query is run that there is no supported translation to SQL. (
The logic of the lambda expression is irrelevant yet - just trying to get past the exception.)
Am I missing some fundamental property or something to allow translation to SQL? I've read most of the posts on SO about this exception, but nothing seems to fit my case exactly.
I believe the point of LINQ-to-SQL is that your entities are mapped for you and must have correlations in the database. It appears that you are trying to mix the LINQ-to-Objects and LINQ-to-SQL.
If the Series table has a Deleted field in the database, and the Study table does not but you would like to translate logical Study.Deleted into SQL, then extension would be a way to go.
public static class StudyExtensions
{
public static IQueryable<study> AllDeleted(this IQueryable<study> studies)
{
return studies.Where(study => !study.series.Any(series => !series.deleted));
}
}
class Program
{
public static void Main()
{
DBDataContext db = new DBDataContext();
db.Log = Console.Out;
var deletedStudies =
from study in db.studies.AllDeleted()
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
}
}
This maps your "deleted study" expression into SQL:
SELECT t0.study_id, t0.name
FROM study AS t0
WHERE NOT EXISTS(
SELECT NULL AS EMPTY
FROM series AS t1
WHERE (NOT (t1.deleted = 1)) AND (t1.fk_study_id = t0.study_id)
)
Alternatively you could build actual expressions and inject them into your query, but that is an overkill.
If however, neither Series nor Study has the Deleted field in the database, but only in memory, then you need to first convert your query to IEnumerable and only then access the Deleted property. However doing so would transfer records into memory before applying the predicate and could potentially be expensive. I.e.
var deletedStudies =
from study in db.studies.ToList()
where study.Deleted
select study;
foreach (var study in deletedStudies)
{
Console.WriteLine(study.name);
}
When you make your query, you will want to use the statically defined Expression, not the property.
Effectively, instead of:
dataQuery = dataQuery.Where((s) => !s.Deleted);
Whenever you are making a Linq to SQL query, you will instead want to use:
dataQuery = dataQuery.Where(DeletedExpr);
Note that this will require that you can see DeletedExpr from dataQuery, so you will either need to move it out of your class, or expose it (i.e. make it public, in which case you would access it via the class definition: Series.DeletedExpr).
Also, an Expression is limited in that it cannot have a function body. So, DeletedExpr might look something like:
public static Expression<Func<Study, bool>> DeletedExpr = s => s.Series.Any(se => se.Deleted);
The property is added simply for convenience, so that you can also use it as a part of your code objects without needing to duplicate the code, i.e.
var s = new Study();
if (s.Deleted)
...

Saving business rules per user

public interface IRule
{
bool Check(string input);
}
I have an interface that defines a rule. A rule is just a generic business rule or constraint that a user can create. So I have two sample rules:
public class ContainsRule : IRule
{
public string MustContain { get; set; }
public bool Check(string input)
{
return input.Contains(this.MustContain);
}
}
public class LengthRule : IRule
{
public int MaxLength { get; set; }
public bool Check(string input)
{
return input.Length <= this.MaxLength;
}
}
Rules may have more than one property that can be set, but in this example, each of these rules only have one property.
A user can create their own set of rules that should be saved. For example, a user have these three rules:
IRule[] rules = new IRule[]
{
new ContainsRule { MustContain = "foo" },
new ContainsRule { MustContain = "bar" },
new LengthRule { MaxLength = 5}
};
I need to persist this information to a database or some data store for each user. Since each user can have their own set of rules, I'm not sure what the database tables should look like.
User | ClassName | Parameters
-----------------------------------------------
1 | Namespace.ContainsRule | MustContain:foo
1 | Namespace.ContainsRule | MustContain:bar
1 | Namespace.LengthRule | MaxLength:5
My initial guess would be to create a table that looks something like the above, where parameters should a string. This means I would need to parse out the information and use reflection or something to set the properties. I would need to use the activator to create the class using the ClassName column. Another suggestion was instead of creating a delimited string for all the properties, there would be another table. Each of the properties would be its own row that has a foreign key relationship back to a row in the table above.
However, both examples don't seem to be the best way of saving these rules. Is there a better way to do this?
Why not use XML serialisation. The database table would contain the User # and the XML serialisation of the indvidual rule.
I believe that you are retrieving from database.
If so, try my suggestion.
Store the retrieved data into datatable, or convert them to datatable before applying the rule.
So the rule can be put as normal sql in datatable's filter property.
Instead of rule name and parameters, you can change it to a column containing
column like '%foo%'
Then apply this on the datatable's filter.
PS: I have converted List to Datatable and used such filter before
It seems to me, simply put, you need to map your objects to SQL tables (if only there was a technology that could to do this automatically... :)
Based on what you have posted, I envisage at least six SQL tables, more if there are further subclasses of rule e.g.
Rules
MustContainTextRules (subclass of Rules, 1:0..m)
MaxLengthRules (subclass of Rules, 1:0..1)
[Possibily further subclasses of Rules e.g. MinLengthRules?]
Rulesets ("a set of Rules")
Users
RulesetOwnership (relationship table between Users and Rulesets)
Whether they are distinct tables may be influenced by whether the rules are interrelated e.g. if the MinLengthRule value for a Ruleset cannot exceed its MaxLengthRule value then you may find it difficult to write a SQL constraint to validate this when they are in different tables.

Categories

Resources