How can I get item Ids without querying entire items? - azure-cosmosdb

I am trying to get a list of item ids with the code below, not a list of items, but I am not getting any Ids although the response message has Count set to the expected number of items. Could you please let me know if it is possible to get item ids only without item contents and how? Thanks.
string query = "select r.Id from root r where r.itemType = #itemType";
QueryDefinition queryDefinition = new QueryDefinition(query);
queryDefinition.WithParameter("#itemType", ItemType.Banana);
using (var feedIterator = container.GetItemQueryStreamIterator(queryDefinition))
{
while (feedIterator.HasMoreResults)
{
using (ResponseMessage responseMessage = await feedIterator.ReadNextAsync())
{
using (StreamReader streamReader = new StreamReader(responseMessage.Content))
{
using (JsonTextReader jsonTextReader = new JsonTextReader(streamReader))
{
JsonSerializer jsonSerializer = new JsonSerializer();
JObject content = jsonSerializer.Deserialize<JObject>(jsonTextReader);
if (content.ContainsKey("Documents"))
{
foreach (var doc in content["Documents"])
{
// why doc is empty?
}
}
}
}
}
}
}

To just get the ids back without each one being it's own document, change your query to select value(r.Id) from root r where r.itemType = #itemType

Related

Correctly locating a string in string array

I have an array of strings that contains usernames that I'd like to extract and add it to a list. So for example, I have this string => "UserName: JohnSmith" and I want to extract JohnSmith. The below is the code I'm attempting to get working:
var documentSource = content.Replace("\n" , "").Trim();
string[] contents = documentSource.Split(',');
List<Model> modelList = new List<Model>();
Model model = new Model();
foreach (var item in contents)
{
//this is where I'd like to locate the username or usernames
if(item.Any())
{
model.userName = //username only;
modelList.Add(model);
}
}
return modelList;
This is the kind of problem that can be easily solved with regular expressions:
using System.Text.RegularExpressions;
// ...
var regex = new Regex("UserName: ([^,]*)");
// ..
foreach(var item in contents)
{
var match = regex.Match(item);
if(match.Success)
{
modelList.Add(
new Model
{
userName = match.Groups[1].Value
}
);
}
}
In it's simplest form, since you know that each username start after "UserName: " part that is fixed, it would be something like this:
var documentSource = content.Replace("\n" , "").Trim();
string[] contents = documentSource.Split(',', StringSplitOptions.RemoveEmptyEntries);
var modelList = new List<Model>();
foreach (var item in contents)
{
var username = item.Substring("UserName: ".Length).Trim();
if (!string.IsNullOrEmpty(username))
{
Model model = new Model();
model.userName = username;
modelList.Add(model);
}
}
return modelList;
Since you know that you have one : inside of the username string and the second part of the : is your actual username that you want, so, you can split the string in two parts and get the second one. Something like below:
foreach (var item in contents)
{
var username = item.Split(':').Last()?.Trim();
if(!string.IsNullOrEmpty(username))
{
modelList.Add(new Model{ userName = username });
}
}

How should I get one item from Cosmos using the v3 API?

I want to retrieve one item from Cosmos DB and there must be a better way than what I am doing here.
I've tried other commands, but this seems to actually work.
public async Task<ToDoItem> GetAsync(string id)
{
FeedIterator<ToDoItem> results = container.GetItemQueryIterator<ToDoItem>("select top 1 * from Items i where i.id = '" + id + "'");
FeedResponse<ToDoItem> item = await results.ReadNextAsync();
return item.Resource.FirstOrDefault();
}
I expect to be able to do this with one line that executes on the
server and doesn't force me to look at a set of items.
Here is an example from the official document to query,
using (ResponseMessage responseMessage = await container.ReadItemStreamAsync(
partitionKey: new PartitionKey("Account1"),
id: "SalesOrder1"))
The SDK is still in development - this may help:
using (ResponseMessage response = await _container.ReadItemStreamAsync(id: pageId, partitionKey: partitionKey))
{
if (!response.IsSuccessStatusCode)
{
//Handle and log exception
}
await using (Stream stream = response.Content)
{
using (var streamReader = new StreamReader(stream))
{
string content = streamReader.ReadToEnd();
}
}
}

.NET Already Open DataReader

I get this error when running this code. I have looked for solution though I don't like the idea of using MARS as people have suggested as it may contain a lot of data, is there any other option here? Also can I edit a variable in a database without rewriting all of them as I do here, will this save server power or make no difference?
There is already an open DataReader associated with this Command which must be closed first.
public ActionResult CheckLinks(Link model)
{
var userId = User.Identity.GetUserId();
var UserTableID = db.UserTables.Where(c => c.ApplicationUserId == userId).First().ID;
foreach (var item in db.Links.Where(p => p.UserTable.ID == UserTableID))
{
string pageContent = null;
HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create(item.Obdomain);
HttpWebResponse myres = (HttpWebResponse)myReq.GetResponse();
using (StreamReader sr = new StreamReader(myres.GetResponseStream()))
{
pageContent = sr.ReadToEnd();
}
string live = "";
if (pageContent.Contains(item.Obpage))
{
live = "Yes";
}
else { live = "No"; }
var link = new Link { Obdomain = item.Obdomain, ClientID = item.ClientID, Obpage = item.Obpage, BuildDate = item.BuildDate, Anchor = item.Anchor, IdentifierID = item.IdentifierID, live = (Link.Live)Enum.Parse(typeof(Link.Live), live), UserTableID = item.UserTableID };
db.Entry(link).State = EntityState.Modified;
db.SaveChanges();
}
return RedirectToAction("Index");
}
Entity Framework allows only one active command per context at a time. You should add .ToList() at the end of the following statement:
db.Links.Where(p => p.UserTable.ID == UserTableID).ToList();
So your code could look like this:
var items = db.Links.Where(p => p.UserTable.ID == UserTableID).ToList();
foreach (var item in items)

.net Querying a Global Secondary Index in DynamoDB via DynamoDBContext

I have a dynamoDB table with a schema as follows:
var request = new CreateTableRequest
{
TableName = tableName,
KeySchema = new List<KeySchemaElement>
{
new KeySchemaElement("CompanyId", KeyType.HASH),
new KeySchemaElement("Timestamp", KeyType.RANGE)
},
AttributeDefinitions = new List<AttributeDefinition>
{
new AttributeDefinition("CompanyId", ScalarAttributeType.S),
new AttributeDefinition("Timestamp", ScalarAttributeType.N),
new AttributeDefinition("UserId", ScalarAttributeType.S)
},
GlobalSecondaryIndexes = new List<GlobalSecondaryIndex>
{
new GlobalSecondaryIndex
{
IndexName = "UserIndex",
KeySchema = new List<KeySchemaElement>
{
new KeySchemaElement("UserId", KeyType.HASH),
new KeySchemaElement("Timestamp", KeyType.RANGE)
},
Projection = new Projection {ProjectionType = "ALL"},
ProvisionedThroughput = new ProvisionedThroughput(5, 6)
}
},
ProvisionedThroughput = new ProvisionedThroughput(5, 6)
};
I can query the primary key successfully as follows:
var client = new AmazonDynamoDBClient();
using (var context = new DynamoDBContext(client))
{
var sortKeyValues = new List<object>{minTimestamp};
result = await context.QueryAsync<AuditLogEntry>(companyId, QueryOperator.GreaterThanOrEqual, sortKeyValues,
new DynamoDBOperationConfig {OverrideTableName = TableName}).GetRemainingAsync();
}
And I can query the global secondary index without any constraint on the range key as follows:
var client = new AmazonDynamoDBClient();
using (var context = new DynamoDBContext(client))
{
result = await context.QueryAsync<AuditLogEntry>(userId, new DynamoDBOperationConfig {OverrideTableName = TableName, IndexName = indexName})
.GetRemainingAsync();
}
But when I try to query the index with a range key constraint:
var client = new AmazonDynamoDBClient();
using (var context = new DynamoDBContext(client))
{
var sortKeyValues = new List<object> {minTimestamp};
result = await context.QueryAsync<AuditLogEntry>(userId, QueryOperator.GreaterThan, sortKeyValues, new DynamoDBOperationConfig {OverrideTableName = TableName, IndexName = indexName}).GetRemainingAsync();
}
I get the following error:
Exception thrown: 'System.InvalidOperationException' in AWSSDK.DynamoDBv2.dll
Additional information: Local Secondary Index range key conditions are used but no index could be inferred from model. Specified index name = UserIndex
Googling this error hasn't thrown any light on the issue. The reference to Local Secondary Index has me confused because I'm using a Global index, but I just can't see what's wrong with my code.
I've been able to get the query working by querying directly on the AmazonDynamoDBClient rather than using DynamoDBContext, but I'd really like to understand what I'm doing wrong and be able to use DynamoDBContext.
Any ideas would be appreciated.
In your model definition for AuditLogEntry you need to decorate properties that are part of the global secondary index with attributes - [DynamoDBGlobalSecondaryIndexRangeKey] and or [DynamoDBGlobalSecondaryIndexHashKey]. Example below.
public class AuditLogEntry {
// other properties ...
[DynamoDBProperty("UserId")]
[DynamoDBGlobalSecondaryIndexHashKey("UserIndex")]
public string UserId { get; set; }
}

Update database from viewmodel but add new records too

I have a view that has a list of items (which can be added to dynamically via jQuery).
When I POST the viewmodel back to the controller, if the code can't find the ID, how do I insert new items and save them to the database.
My initial code is below - the updates are saved, but the new items aren't saved:
//
// POST: /Objective/Edit/model
[HttpPost]
public ActionResult Edit(ObjectivesEdit model)
{
if (model.Objectives != null)
{
foreach (var item in model.Objectives)
{
// find the database row
Objective objective = db.objectives.Find(item.ID);
if (objective != null)
{
// Set the database row to the posted values
objective.objective = item.objective;
objective.score = item.score;
objective.possscore = item.possscore;
}
else // database item not found, so add a new item
{
// add a new objective
// This doesn't seem to add/save a new record
Objective obj = new Objective();
obj.objective = item.objective;
obj.score = item.score;
obj.possscore = item.possscore;
}
}
// Save the changes to the database
db.SaveChanges();
}
return View(model);
}
Thanks for any help,
Mark
You don't add the newly created objective to your context.
else // database item not found, so add a new item
{
// add a new objective
// This doesn't seem to add/save a new record
Objective obj = new Objective();
obj.objective = item.objective;
obj.score = item.score;
obj.possscore = item.possscore;
// Missing line.
db.objectives.Add(obj);
}
if you're using EF 4.0 (i.e. db is of type ObjectContext), you should use the db.AddObject(obj).
Update based on your comment:
One way is to retrieve all added items after saving changes. Another way is to modify your model when creating a new objective. Changed parts are marked with *:
foreach (var item in model.Objectives.ToList()) // *:Notice the ToList()
{
// find the database row
Objective objective = db.objectives.Find(item.ID);
if (objective != null)
{
// Set the database row to the posted values
objective.objective = item.objective;
objective.score = item.score;
objective.possscore = item.possscore;
}
else // database item not found, so add a new item
{
// add a new objective
// This doesn't seem to add/save a new record
Objective obj = new Objective();
obj.objective = item.objective;
obj.score = item.score;
obj.possscore = item.possscore;
db.AddObject(obj)
// Save the changes to the database
db.SaveChanges(); // *: save in loop to get thee ID.
item.ID = obj.ID; // *: assign the ID to the model.
}
}
return View(model);

Resources