Elastic Search Update API by script in NEST 6.5.4 for .NET - elasticsearch

I am using Nest 6.5.4. I am not able to perform Update with script on a particular document in an index.
I have tried many ways but I am getting a syntax error.
My query is as follows.
var clientProvider = new ElasticClientProvider();
var projectModel = new ProjectModel();
var res = clientProvider.Client.Update<ProjectModel>(projectModel, i => i
.Index("attachment_index")
.Type("attachments")
.Id(projectId)
.Script(script=>script.Source("ctx._source.fileInfo.fileViewCount= ctx._source.fileInfo.fileViewCount + 1"))
);
It is throwing an error "Update Descriptor does not have a definition for Id"
The same query is working when tried in Kibana
POST attachment_index/attachments/1/_update
{
"script": {
"source":"ctx._source.fileInfo.fileViewCount += 1"
}
}
I dont know where I am getting error.

client.UpdateAsync<ProjectModel, object>(
new DocumentPath<ProjectModel>(id),
u => u
.Index(ConfigurationManager.AppSettings.Get("indexname"))
.Type(ConfigurationManager.AppSettings.Get("indextype"))
.Doc(ProjectModel)
.DocAsUpsert()
.Refresh(Elasticsearch.Net.Refresh.True));
This will work and let me know if you are still facing any issues.

There is no .Id() method on UpdateDescriptor<T, TPartial> because an id is a required parameter for an Update API call, so this constraint is enforced through the constructors.
The first parameter to .Update<T>(...) is a DocumentPath<T> from which an index, type and id can be derived for the update API call. If the ProjectModel CLR POCO has an Id property with a value, this will be used for Id of the call. For example
public class ProjectModel
{
public int Id { get; set; }
}
var client = new ElasticClient();
var projectModel = new ProjectModel { Id = 1 };
var updateResponse = client.Update<ProjectModel>(projectModel, i => i
.Index("attachment_index")
.Type("attachments")
.Script(script => script
.Source("ctx._source.fileInfo.fileViewCount= ctx._source.fileInfo.fileViewCount + 1"))
);
which results in
POST http://localhost:9200/attachment_index/attachments/1/_update
{
"script": {
"source": "ctx._source.fileInfo.fileViewCount= ctx._source.fileInfo.fileViewCount + 1"
}
}
If you want to explicitly specify the Id, you can pass the value for DocumentPath<T>
var updateResponse = client.Update<ProjectModel>(1, i => i
.Index("attachment_index")
.Type("attachments")
.Script(script => script
.Source("ctx._source.fileInfo.fileViewCount= ctx._source.fileInfo.fileViewCount + 1"))
);

Related

Devart.Data.Oracle.EFCore - How to use sequence to set PK column value?

I'm using Entity Framework Core 2.1.4 with Oracle 11 database and Devart.Data.Oracle.EFCore provider.
Database first approach.
I want to get from sequence value for ID column (primary key) on inserting without setting this explicitly every time.
So, based on similar infos with SQL Server, I did it as following:
Entity
public class Foo
{
public int Id { get; set; }
public double Value { get; set; }
}
Mapping (OnModelCreating method)
modelBuilder.HasSequence<int>("SEQ_FOOS", schema: "SCHEMA")
.StartsAt(1)
.IncrementsBy(1);
modelBuilder.Entity<Foo>(entity =>
{
entity.ForOracleToTable("FOOS");
entity.HasKey(e => e.Id);
entity.Property(e => e.Id).ForOracleHasColumnName("ID").IsRequired().ForOracleHasDefaultValueSql("SELECT SEQ_FOO.NEXTVAL FROM DUAL");
entity.Property(e => e.Value).HasColumnName("VALUE");
});
Adding value:
using (var dbContext = new FooDbContext())
{
var foo = new Foo()
{
Value = 5
};
dbContext.Foos.Add(foo);
dbContext.SaveChanges();
}
On SaveChanges:
OracleException: ORA-01400: cannot insert NULL into ("SCHEMA"."FOOS"."ID")
I also logged EF query. As you can see, there is no ID column in insert:
INSERT INTO SCHEMA.FOOS (VALUE)
VALUES (:p0)
I was trying to use simply SEQ_FOO.NEXTVAL instead of full select or default EF methods (like HasDefaultValueSql) but nothing worked. Even if I type:
ForOracleHasDefaultValueSql("asdasd");
There is no errors with this - only the same exception as above. It seems like EF never call that SQL.
Am I missing something important? Or maybe it's internal Devart problem?
Ok, I have solution. It seems we need to use ValueGenerator. My implementation below.
Mapping
entity.Property(e => e.Id)
.ForOracleHasColumnName("ID")
.IsRequired()
.ValueGeneratedOnAdd()
.HasValueGenerator((_, __) => new SequenceValueGenerator(_defaultSchema, "SEQ_FOOS"));
SequenceValueGenerator (please note that ValueGenerator is EF Core type)
internal class SequenceValueGenerator : ValueGenerator<int>
{
private string _schema;
private string _sequenceName;
public SequenceValueGenerator(string schema, string sequenceName)
{
_schema = schema;
_sequenceName = sequenceName;
}
public override bool GeneratesTemporaryValues => false;
public override int Next(EntityEntry entry)
{
using (var command = entry.Context.Database.GetDbConnection().CreateCommand())
{
command.CommandText = $"SELECT {_schema}.{_sequenceName}.NEXTVAL FROM DUAL";
entry.Context.Database.OpenConnection();
using (var reader = command.ExecuteReader())
{
reader.Read();
return reader.GetInt32(0);
}
}
}
}
It seems to work as I needed.
Mapping:
private void FooMapping(ModelBuilder modelBuilder)
{
//modelBuilder.HasSequence<int>("SEQ_FOOS", schema: "SCHEMA")
// .StartsAt(1)
// .IncrementsBy(1);
modelBuilder.Entity<Foo>(entity =>
{
entity.ForOracleToTable("FOOS");
entity.HasKey(e => e.Id);
//entity.Property(e => e.Id).ForOracleHasColumnName("ID").IsRequired().ForOracleHasDefaultValueSql("SELECT SEQ_FOO.NEXTVAL FROM DUAL");
entity.Property(e => e.Value).HasColumnName("VALUE");
});
}
Code:
// https://www.devart.com/dotconnect/oracle/docs/?dbmonitor.html
var monitor = new OracleMonitor() { IsActive = true };
using (var dbContext = new FooModel())
{
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
var foo = new Foo()
{
Value = 5
};
dbContext.Foos.Add(foo);
dbContext.SaveChanges();
}
Check SQL generated in dbMonitor. Is that what you need?
Did not figure this out. I have similar problem on Oracle 18C - I need to fill PK in the table, PK is a NUMBER, not IDENTITY (this is obviously a defect and will be changed later on, but now I have to deal with that since I don't have rights to change DB structure, however I need to prepare CRUD demo). I don't want to use some C# value generator, but instead - DB remedy. So I tried to use the following (but it did not work - the expression is ignored):
b.HasKey(x => x.Id);
b.Property(x => x.Id).HasColumnName("C_LICENCE").IsRequired().ValueGeneratedOnAdd().HasDefaultValueSql("select round(dbms_random.value(100000, 999999)) from dual");
I suspect it's probably because int primary column is never null :) But anyway, I need to somehow force it to be generated via SQL always.

elasticsearch date_range query witn nest api

I run a query in marvel plugin and it works fine(return 4 document) as you see below
And I want to make this query in C# with NEST, I expected same result with the plugin for this expression but it returns 0..
function call:
EsCodes myes = new EsCodes();
myes.CreateConnection("localhost", "dota2");
var result = myes.getDatasBetweenDates("01/01/2010 00:00:00", "01/01/2015 00:00:00");
connection:
private static ElasticClient es = new ElasticClient();
public void CreateConnection(string hostname, string defaultIndex)
{
var node = new Uri(string.Format("http://{0}:9200", hostname));
var settings = new ConnectionSettings(node).SetDefaultIndex(defaultIndex).UsePrettyResponses();
es = new ElasticClient(settings);
}
and function:
public List<IndexModel> getDatasBetweenDates(string startdate, string enddate)
{
ElasticClient myes = this.getConnection();
List<IndexModel> indices = new List<IndexModel>();
if (myes != null)
{
var result = myes.Search<dynamic>(q => q.Query(p => p.Range(v => v.OnField("dateofplay").GreaterOrEquals(startdate).LowerOrEquals(enddate))).Size(10)).Documents;
}
...
return indices;
}
What is wrong with that ?
EDIT: I recognized the url created by NEST contaims "../dota2/object/_search.."
I don't understand why it included that kind of parameter.. its the point of my issue. why it generates object in the url ?
Note that you're not using the same dates in both queries.
In Marvel you use "22/06/2010 00:00:00" and "16/09/2015 00:00:00"
In your code you use "22/06/2012 00:00:00" and "16/09/2014 00:00:00"
So maybe that's the only reason why you don't get any results from your code.

How to query relational data using subclasses? parse.com and Unity

Im trying to query all elements of subclass in Unity. I have found SDK constraint or missing something here.
According to documentation querying subclasses is possible.
> var query = new ParseQuery<Armor>()
.WhereLessThanOrEqualTo("rupees", ((Player)ParseUser.CurrentUser).Rupees);
query.FindAsync().ContinueWith(t =>
{
IEnumerable<Armor> result = t.Result;
});
Im however using relation table and cannot specify
Here is my code:
IEnumerator LoadMyDesigns(Action<RequestResult> result) {
ParseUser user = ParseUser.CurrentUser;
ParseRelation<Design> relation = user.GetRelation<Design>("designs");
Task<IEnumerable<Design>> task = relation.Query.FindAsync();
while (!task.IsCompleted) yield return new WaitForEndOfFrame();
if (task.IsFaulted) {
//error
foreach(var e in task.Exception.InnerExceptions) {
ParseException parseException = (ParseException) e;
Debug.LogError("Error message " + parseException.Message);
Debug.LogError("Error code: " + parseException.Code);
result(new RequestResult(true, parseException.Message));
}
}
else {
result(new RequestResult(true, new List<Design>(task.Result)));
}
}
And error:
ArgumentNullException: Must specify a ParseObject class name when creating a ParseQuery.
So the question is how do I specify query subclass type when using relations?
Thanks.
I've struggled with the same problem and in my case I needed to provide the propertyName again in de GetRelationProperty call.
For example:
[ParseFieldName("designs")]
public ParseRelation<Design> Designs
{
get { return GetRelationProperty<Design>("Designs"); }
}
Try querying your designs Table.
Make a new query for class "Designs" where equal("owner", PFUser.currentUser())
This should return all of the designs for the current User.

WCF Data Services + LINQ Projection into a custom type

I'm trying to project parts of a Display and its list of locations from a WCF Data service into a custom type. Is this doable in WCF Data Services in a Silverlight client? There is some help here, but it doesn't show getting a list back as well as simple strings.
Currently I'm getting "NotSupportedException: Constructing or initializing instances of the type UserQuery+Info with the expression d.Base.Title is not supported.".
It would be a bonus if you could tell me how to do Expand on Locations in this syntax (I know about Displays.Expand("Locations")) or if I need it.
LINQPad snippet
var displays = from d in Displays.Where(d => d.Id == 3136)
select new Info
{
Name = d.Base.Title,
};
displays.Dump();
}
public class Info
{
private string name;
public string Name
{
get
{
return this.name;
}
set
{
this.name = value;
}
}
public IEnumerable<Location> locations;
public IEnumerable<Location> Locations
{
get{ return this.locations;}
set{ this.locations = value;}
}
The problem is that you are effectively asking your WCF server to construct some type it has no knowledge about. Since it is unable to do so, you have to it yourself on your computer:
Displays
.Where(d => d.Id == 3136)
.AsEnumerable()
.Select(d => new Info { Name = d.Base.Title })
This will run the Where() on the server, but the Select() on your computer.
As already noted by svick you can't ask the server for types it doesn't understand (at least not using OData that is). But you can still only ask for properties you want and nothing more.
Since I don't have your service available the below sample uses the demo service on odata.org:
DemoService ctx = new DemoService(new Uri("http://services.odata.org/OData/OData.svc/"));
var q =
ctx.Products
.Where(p => p.ID == 1)
.Select(p =>
new Product
{
Category = new Category
{
Name = p.Category.Name
}
});
var r =
q.AsEnumerable()
.Select(p =>
new
{
CategoryName = p.Category.Name
});
The first query "q" will run compoletely on server (except for creation of the client side objects) and it will only get the Name of the category (and metadata about all the entities in question). It will translate to URL like /Products(1)?$expand=Category&$select=Category/Name.
The second query starts with the AsEnumerable, which effectively executes the first query and then it just performs a simple transform into an anonymous type. This is done completely on the client (no server interaction).

IN and NOT IN with Linq to Entities (EF4.0)

This has been ruining my life for a few days now, time to ask...
I am using Entity Framework 4.0 for my app.
A Location (such as a house or office) has one or more facilities (like a bathroom, bedroom, snooker table etc..)
I want to display a checkbox list on the location page, with a checkbox list of facilities, with the ones checked that the location currently has.
My View Model for the facilities goes like this...
public class FacilityViewItem
{
public int Id { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
}
So when im passing the Location View Model to the UI, i want to pass a List<T> of facilities where T is of type FacilityViewItem.
To get the facilities that the location already has is simple - i make a query using Location.Facilities which returns an EntityCollection where T is of type Facility. This is because Facilities is a navigation property....
var facs = from f in location.Facilities
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name,
Checked = true
};
So here is where my problem lies - i want the rest of the facilities, the ones that the Location does not have.
I have tried using Except() and Any() and Contains() but i get the same error.
Examples of queries that do not work...
var restOfFacilities = from f in ctx.Facilities
where !hasFacilities.Contains(f)
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name
};
var restOfFacilities = ctx.Facilities.Except(facilitiesThatLocationHas);
var notFacs = from e in ctx.Facilities
where !hasFacilities.Any(m => m.FacilityId == e.FacilityId)
select new FacilityViewItem()
{
Id = e.FacilityId,
Name = e.Name
};
And the error i get with every implementation...
System.NotSupportedException was unhandled
Message=Unable to create a constant value of type 'Chapter2ConsoleApp.Facility'. Only primitive types ('such as Int32, String, and Guid') are supported in this context.
What am i overlooking here?
ironically enough i solved it in a matter of hours after i posted the question on here, after days of suffering.
The error is basically saying 'i dont know how to calculate what items are not included by comparing strongly typed objects. Give me a list of Ints or some simple types, and i can take care of it'.
So, first you need to get a list of the primary keys, then use that in the contains clause...
//get the primary key ids...
var hasFacilityIds = from f in hasFacilities
select f.FacilityId;
//now use them in the contains clause...
var restOfFacilities = from f in ctx.Facilities
where !hasFacilityIds.Contains(f.FacilityId)
select new FacilityViewItem()
{
Id = f.FacilityId,
Name = f.Name
};
The first query seems fine, but you need to compare the Ids:
var restOfFacilities = from f in ctx.Facilities
where !facs.Select(fac => fac.Id).Contains(f.Id)
select f;
I wanna see what's hasFacilities, anyway, as L2E shows, "Only primitive types ('such as Int32, String, and Guid') are supported in this context", so I suppose you must retrieve first the data and put into a collection of FacilityViewItem.
var restOfFacilities = ctx
.Facilities
.Where(f => !hasFacilities.Contains(f))
.Select(f => new { f.FacilityId, f.Name })
.ToList()
.Select(f => new FacilityViewItem {
Id = f.FacilityId,
Name = f.Name
});
var notFacs = ctx
.Facilities
.Where(e => !hasFacilities.Any(m => m.FacilityId == e.FacilityId))
.Select(e => new { e.FacilityId, e.Name })
.ToList()
.Select(e => new FacilityViewItem {
Id = e.FacilityId,
Name = e.Name
});
hope it helps

Resources