Embedded fields in azure cognitive search - azure-cognitive-search

I'm trying to create scoring profiles for an index in azure cognitive search. However, my datasource contains embedded fields - it included a IList called metadataList of the custom object type MetaData. How can I refer to the fields of MetaData when creating the scoring profiles?
Here's part of the class I am using to model my data:
[SerializePropertyNamesAsCamelCase]
public class MetaDataFileIndex
{
[IsRetrievable(true)]
public IList<MetaDataIndex> MetadataList { get; set; }
//Some other properties bellow
}
and
[SerializePropertyNamesAsCamelCase]
public class MetaDataIndex
{
[IsRetrievable(true), IsSearchable]
[Analyzer(AnalyzerName.AsString.ZhHantLucene)]
public string PrimarySortText { get; set; }
//Some other properties bellow
}
Here's some of the code I used for creating the index:
fields = FieldBuilder.BuildForType<MetaDataFileIndex>();
var index = new Index()
{
Name = name,
Fields = fields,
ScoringProfiles = new List<ScoringProfile>()
{
new ScoringProfile
{
Name = "fieldBooster",
TextWeights = new TextWeights
{
Weights = new Dictionary<string, double>
{
{ "metadataList/primarySortText", [some number here] },
\\Rest of the code
This is giving me an error. When I use a service client to create or update the index, I receive the "request invalid error".
at Microsoft.Azure.Search.IndexesOperations.<CreateOrUpdateWithHttpMessagesAsync>d__9.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at Microsoft.Azure.Search.IndexesOperationsExtensions.<CreateOrUpdateAsync>d__1.MoveNext()
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at
I believe it is because of the way I referred to the "primary sort text field" in the scoring profile, since, when I ran the same code but did not use a list of another object type in MetaDataFileIndex, it gave me no error. How can I refer to embedded fields like that?
EDIT: My Azure Cognitive Search version is 11.2.0 and it is called Azure.Search.Documents

Related

Dapper Extension Get & Update returns errors

I tried to play with Dapper Extension & MS Access and succeeded up to certain extent. My code is listed below. All the functions works properly (Insert/Count/GetList/Delete) except Get & Update. I have summarised the code below. If anybody wants I can paste all the code here
My Product class
public class Products
{
public string ProductNumber { get; set; }
public string Description { get; set; }
}
And in my main class. I tried to get the product and update it as below. con.Get<Products> function returns an exception with "Sequence contains more than one element" message and con.Update<Products> returns an exception with "At least one Key column must be defined".
using (var con = new OleDbConnection("Provider=Microsoft.Jet.OLEDB.4.0;Data Source=test.mdb"))
{
string ProductNumber = "12";
var product4 = con.Get<Products>(ProductNumber);
product4.ProductNumber = "Baz";
con.Update<Products>(product4);
Console.ReadLine();
}
Even though con.Get<Products> fails con.GetList<Products>(predicate) works perfectly. I did follow this link for setup
If DapperExtensions can't infer a key property called ID from your class, you'll need to explicitly specify one via a class mapper. Assuming the ProductNumber is the primary key in the database, the following example class mapper should set the ProductNumber to be the primary key for DapperExtensions.
using Dapper;
using DapperExtensions;
using DapperExtensions.Mapper;
public class ProductsMapper : ClassMapper<Products>
{
public ProductsMapper()
{
Map(x => x.ProductNumber).Key(KeyType.Assigned);
AutoMap();
}
}
This class can sit somewhere within the same assembly as the rest of your code. Dapper Extensions will automatically pick it up. If you have your classes and Dapper code in separate assemblies, you can point it to your mapper with the following line:
DapperExtensions.DapperExtensions.SetMappingAssemblies({ typeof(ProductsMapper).Assembly })

DbSet.Load() method is too slow

I have an SQLite database, which contains one table named "Main". Each record of this table contains only two fields: ID (integer, primary key) and name (string). There are 100 records in the database.
Using Entity Framework Power Tools I've created the Code First model from the existing database. The model is rather simple:
// MainMap.cs
public class MainMap : EntityTypeConfiguration<Main>
{
public MainMap()
{
// Primary Key
this.HasKey(t => t.ID);
// Properties
this.Property(t => t.name)
.IsRequired()
.HasMaxLength(50);
// Table & Column Mappings
this.ToTable("Main");
this.Property(t => t.ID).HasColumnName("ID");
this.Property(t => t.name).HasColumnName("name");
}
}
// Main.cs
public partial class Main
{
public long ID { get; set; }
public string name { get; set; }
}
// mainContext.cs
public partial class mainContext : DbContext
{
static mainContext()
{
Database.SetInitializer<mainContext>(null);
}
public mainContext()
: base("Name=mainContext")
{
}
public DbSet<Main> Mains { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new MainMap());
}
}
Now I'm trying to get the records from the database:
mainContext context = new mainContext();
context.Mains.Load();
Now I can use context.Mains.Local with a comfort for different purposes (actually, I bind it to ListView's ItemsSource).
The problem is that context.Main.Load() line executes for about 2.7 seconds. I think, it is too much time as for retrieving about 100 records from a simple database. Although, I'm a newcomer to databases, so, maybe I'm not right and 2.7 seconds is rather suitable period of time. My CPU is Intel i3-3220 (2x3.30 GHz), Entity Framework's version is 6.0.
Maybe, my Code First model is poor, or maybe EF doesn't provide high performance, or maybe there is no need to call Load() method to obtain records (but if I don't call it, context.Mains.Local is empty).
So, how can I increase the performance of getting the records from the database?
Any help and hints will be appreciated.
i ran some tests with both SQLite and SQL Server. on my laptop (corei7 2630QM 2.00GHZ & win7 64bit) the load time for both was ~1.5sec.
then i tried to warm it up with something like
context.Database.Exists();
and the time reduced to ~700ms for both.
i used "Prefer 32-bit" and "Optimize code" options in build tab of the project properties. these options produced best results.
try these and see if the load time changes.

DbSet.Attach() only updates single table but not referencing ones

I have two tables: Word and Adjective, both with some properties. Primary key of both tables is ID, Adjective.ID also references Word.ID as foreign key so there is a 1-1 relationship.
I also have a repository for any kind of table with an Update function.
public void Update(T entity) {
var entry = DatabaseContext.Entry(entity);
DatabaseSet.Attach(entity);
entry.State = EntityState.Modified;
}
I take a value from the database, convert it into a ViewModel looking like this (of course it's actually a little more complex):
public class WordModel {
public int ID { get; set; }
public string OriginalWord { get; set; }
}
public class AdjectiveModel : WordModel {
public string Translation { get; set; }
}
Then I alter the values of properties Word and Translation, convert and write it back. After conversion I have an object like this:
Word = {
ID = 1
OriginalWord = y
Adjective = {
ID = 1
Translation = z
}
}
Upon updating however, only one table gets updated.
Database.Words.Update(Word) only updates the OriginalWord value in the Word table,
Database.Adjectives.Update(Word.Adjective) only updates the Translation value in the Adjective table.
When running the updates for both tables sequentially I get an InvalidOperationException: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key.
Creating a new database entry works perfectly.
I cannot believe I have to update both tables on their own and then save the context for each. I have created the database Repository via a Tutorial that obviously didn't explain well enough what's going on with the DbSet and the DbContext, which leaves me a little helpless here.
Sadly I have no link (it is quite a while ago I created the database project)
So, what am I doing wrong here?
You entity Word contains an entity Adjective, it is then the root of the object graph. Now generally here's what you should keep in mind in the following situations :
All objects in the graph are new (new word and new adjective)
use myDbContext.Words.Add(myNewWordObjectGraph); to have the correct state you want.
Only root is new (new word and a pre-existing non modified adjective)
use myDbContext.Entry(myNewWord).state = EntityState.Added; to have the correct state you want.
Root is modified and some nodes are modified (word and adjective both exist in the DB and both have been modified)
use myDbContext.Entry(myWord).State = EntityState.Modified; and myDbContext.Entry(myAdjective).State = EntityState.Modified; to have the correct state you want. i.e. call myDbContext.Entry(myObject).State = EntityState.Modified; for each modified object in the graph whether it's the root or some other node.
Root is unchanged and/or Modified and some nodes are added, others are also unchanged and/or modified
use myDbContext.MyRootObjectDbSet.Add(myRootObject); ; this will mark all the objects in the graph as EntityState.Added including the unchanged and/or modified objects. so the next call should be for each unchanged and/or modified object in order to correct its state : myDbContext.Entry(myObject).State = ThisObjectSCorrectState;.
I Hope that helps
EDIT
Calling DbSet.Attach(...) just adds the object to the objects tracked by EF. If you modify an object before calling DbSet.Attach(...), the modifications won't be persisted to DB when you call SaveChages(), so attaching an object as is before modification, calling DbSet.Attach(...) and then modifying the object is the way to make EF aware of the modifications.
Based on the way your update method's defined I would assume your repository looks something like this maybe?
//Not threadsafe as it contains a transient object 'DbContext'.
public class Repository<T> : IRespository<T> where T : class
{
private readonly MyDbContext context;
public Repository(MtDbContext context)
{
this.context = context
}
//...
public void Update(T entity) {... }
public void Commit() { context.SaveChanges(); }
}
I would suggest changing the update method to the following :
public void Update(T entity)
{
context.Entry(entity).State = EntityState.Modified;
}
And this update method would be called for each object you updated in the graph using the same instance of the repository enclosing the DbContext.

Solrnet can't get mapping to work

I am using VS2012, .NET 4.5 and SolrNet. I am struggling with solrnet mappings. I've succesfully started Apache Solr with jetty on http://localhost:8983/solr. My class which I want to add to solr is:
public class Register
{
[SolrUniqueKey("id")]
public string Id { get; set; }
[SolrField("body")]
public string Body { get; set; }
}
I succesfully connect to solr, but I can't put my document into it:
Startup.Init<Register>(solrAddress);
Solr = ServiceLocator.Current.GetInstance<ISolrOperations<Register>>();
var reg = new Register
{
Id = "SP2514N",
Body = #"Dosel je prosel"
};
Solr.Add(reg);
Solr.Commit();
Here I receive error, that 'body' is unknown field. I've also used MappingManager, like this:
var mgr = new MappingManager();
var property = typeof(Register).GetProperty("Id");
mgr.Add(property, "id");
mgr.SetUniqueKey(property);
mgr.Add(typeof(Register).GetProperty("Body"), "body");
But, again, my field was not mapped. What am I doing wrong? Isn't the mapping to solr supposed to be done through code? Do I need a special xml file?
You need to confirm that you have a body field defined in your schema. If you are just using the default schema that comes with Solr, it does not include a body field. You can copy an existing similar entry in the schema.xml file, like description to get you going.
For more reference on configuring the Solr schema please refer to the following:
Solr Reference Guide - Overview of Documents, Fields and Schema Design
schema.xml - SolrTutorial.com

Using stored procedures (Linq-to-SQL, not EF) in WCF RIA - Silverlight 4

For the love of heaven and earth I really wish someone could help me out with this issue. It seems everyone has something to say about EF but nothing about Linq-to-SQL.
I am trying to grab some data from my table via a stored procedure, believe me, that's all.
I added the Linq-to-SQL model (LAMP.dbml)
added the stored procedure (getAffectedParcel) from the server explorer. getAffectedParcel takes 2 strings as parameters
Build the application.
Added a domain service class (LAMPService)
Selected the (LAMPDataContext) as the data context class (normally I would tick generate metadata, but since I am not working with tables it's not enabled for ticking)
Added the following function to the LAMPService.cs:
public IEnumerable < getAffectedParcelResult > GetTheAffectedParcels(String v, String vf)
{
return this.DataContext.getAffectedParcel(v, vf).AsEnumerable();
}
Added the following code to a Silverlight page in an attempt to consume the stored procedure:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcels("18606004005", "").Value;
foreach (getAffectedParcelResult GAP in q)
{
MessageBox.Show(GAP.Owner);
}
}
catch (Exception ex)
{
MessageBox.Show (ex.Message.ToString());
}
Build and run application. An error occurs stating:
Object reference not set to an instance of an object.
I have tried ~1000,000 ways to see if this thing would work, but to no avail. Please don't tell me to use Entity Framework, I want to use Linq-to-SQL. Can someone (anyone) help me out here.
//houdini
Calling a stored procedure from the Silverlight client happens in the Async world. Let's consider an example from the AdventureWorks database...
Here's what the Domain Service method looks like. It is calling the EF on a stored procedure in the database called 'BillOfMaterials'.
public IQueryable<BillOfMaterial> GetBillOfMaterials()
{
return this.ObjectContext.BillOfMaterials;
}
Back on the client side, here is the code for setting up the call...
public GetSp()
{
InitializeComponent();
DomainService1 ds1 = new DomainService1();
var lo = ds1.Load(ds1.GetBillOfMaterialsQuery());
lo.Completed += LoCompleted;
}
First, the Domain Service is created, and then it is used to load the results of the stored procedure. In this particular case, the result of this is an instance of 'LoadOperation'. These things are async, so the LoadOperation needs to have a callback for when it is finished. The callback code looks like this...
public ObservableCollection<BillOfMaterial> MyList { get; set; }
void LoCompleted(object sender, EventArgs e)
{
LoadOperation lo = sender as LoadOperation;
if(lo!=null)
{
MyList = new ObservableCollection<BillOfMaterial>();
foreach(BillOfMaterial bi in lo.AllEntities)
{
MyList.Add(bi);
}
dataGrid1.ItemsSource = MyList;
}
}
In this method, the 'sender' is dereferenced into the LoadOperation instance, and then all the goodies from the database can be accessed. In this trivial example, a list is built and passed to DataGrid as the ItemsSource. It's good for understanding, but you would probably do something else in practice.
That should solve your problem. :)
The best advice I can give on Silverlight and RIA is never do ANYTHING on your own until you have tried it in AdventureWorks. You will just waste your time and beat your head against the wall.
Firstly, it seems like your DomainService code is written for Invoke() rather than Query(). You should use Query as it enables you to update data back to the server.
Solution: you should add a [Query] attribute to GetTheAffectedParcels on the domain service.
[Query]
public IQueryable<Parcel>
GetTheAffectedParcels(string ParcelNumber, string LotNumber)
{
// etc.
}
Secondly, RIA Services needs to know which is the primary key on the Parcel class.
Solution: Apply a MetadataType attribute to the Parcel class, which allows you to add metadata to the Parcel class indirectly, since it is generated by Linq2Sql and you couldn't add annotations directly to the ParcelId - it'd get wiped away.
[MetadataType(typeof(ParcelMetadata)]
public partial class Parcel
{
}
public class ParcelMetadata
{
[System.ComponentModel.DataAnnotations.Key]
public int ParcelId {get; set; }
}
Thirdly, modify your client like this. Instead try this on the Silverlight client:
LAMPContext db = new LAMPContext();
try
{
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
label1.Text = op.Error.Message;
op.MarkErrorAsHandled();
}
else
{
foreach (var parcel in op.Entities)
{
// your code here
}
}
}
}
catch (Exception ex)
{
label1.Text = op.ex.Message;
}
Much thanks to Chui and Garry who practically kicked me in the right direction :) [thanks guys...ouch]
This is the procedure I finally undertook:
-After adding the data model(LINQ2SQL) and the domain service, I created a partial class [as suggested by Chui] and included the following metadata info therein:
[MetadataTypeAttribute(typeof(getAffectedParcelResult.getAffectedParcelResultMetadata))]
public partial class getAffectedParcelResult
{
internal sealed class getAffectedParcelResultMetadata
{
[Key]
public string PENumber { get; set; }
}
}
Then, Adjusted the Domain Service to include the following:
[Query]
public IQueryable<getAffectedParcelResult> GetTheAffectedParcels(string v, string vf)
{
// IEnumerable<getAffectedParcelResult> ap = this.DataContext.getAffectedParcel(v, vf);
return this.DataContext.getAffectedParcel(v, vf).AsQueryable();
}
Then Build the app, afterwhich the getAffectedParcelResult store procedure appeared in the Data Sources panel. I wanted to access this via code however. Therefore, I accessed it in silverlight [.xaml page] via the following:
LAMPContext db = new LAMPContext();
var q = db.GetTheAffectedParcelsQuery("18606004005", "");
db.Load(q, (op) =>
{
if (op.HasError)
{
MessageBox.Show(op.Error.Message);
op.MarkErrorAsHandled();
}
else
{
foreach (getAffectedParcelResult gap in op.Entities)
{
ownerTextBlock.Text = gap.Owner.ToString();
}
}
},false);
This worked nicely. The thing is, my stored procedure returns a complex type so to speak. As of such, it was not possible to map it to any particular entity.
Oh and by the way this article helped out as well:
http://onmick.com/Home/tabid/154/articleType/ArticleView/articleId/2/Pulling-Data-from-Stored-Procedures-in-WCF-RIA-Services-for-Silverlight.aspx

Resources