I have a very simple problem, but I could not yet figure it out how to do it with Breeze.
I have an array of IDs, which I want to exclude from Breeze Query. In SQL, I would do
SELECT *
FROM Items
WHERE ID NOT IN [...Array...]
How do I do the same with Breeze? Is there an operator to do this, or how can I do this manually?
You can add multiple predicates to your Breeze query. Be careful of adding too many this way, but this is a perfect example of how we could do this -
function getAllTodos(todoIds) {
var Predicate = breeze.Predicate;
var query = breeze.EntityQuery
.from("Todos")
.orderBy("CreatedAt");
var compoundPredicates;
if (todoIds) {
var criteriaPredicate;
$.each(todoIds(), function(index, item) {
criteriaPredicate = (index === 0)
? Predicate.create('todoId', '!=', item)
: criteriaPredicate.or('todoId', '!=', item);
if (Predicate.isPredicate(criteriaPredicate)) {
compoundPredicates = compoundPredicates.and(criteriaPredicate);
}
}
}
}
This (or something similar) allows you to compound predicates together and get all todo's where the ID is not equal to a value found in an array.
Related
I'm trying to copy one field to another field in the same table with 10,000 + records, in batches of 50 using the Scripting App.
What am I doing wrong in this code block? It only copies the first record. If I remove the await, it'll copy 15 records then stop.
let table = base.getTable('Merchants');
let view = table.getView('Grid view');
let query = await view.selectRecordsAsync();
let records = query.records;
updateLotsOfRecords(records);
async function updateLotsOfRecords(records) {
let i = 0;
while (i < records.length) {
const recordBatch = records.slice(i, i + 50);
for (let record of recordBatch) {
let sourceValue = record.getCellValue('Merchant');
await table.updateRecordAsync(record, { 'LogoBase64': sourceValue });
}
i += 50;
}
}
you should use updateRecordsAsync function, not updateRecordAsync
When using single update function in loop, there is no sense to divide it into batches.
You exceed some limit of calls per second, that's why it stops.
For multiple updates, you need to use updateRecordsAsync, like this
while (recordsToWrite.length > 0) {
await updates.updateRecordsAsync(recordsToWrite.slice(0, 50));
recordsToWrite = recordsToWrite.slice(50);
}
Data that you should pass to it, more complex. I learned JS for 3 months and still have difficulties understandins all these "arrays of arrays of objects, passed via object's property". But that's the key to unerstand JS.
It's quite hard to leave basic/pascal habits, with plenty of inserted FOR loops, and GOTO sometimes))
I think, you already found the answer for 2 months, so my answer may be useless, but when i write it here, maybe i understand it better for myself. And help to some beginners also.
For single write, you pass (record, Object), where object is {field:'Value}
For multiple, you should pass
Array of Objects, where
Object is {id:recordID, fields:{object2}} , where
object2 is array of obj3 [ {obj3},{obj3}, {obj3} ], where
obj3 is a { 'Name or ID of field': fieldvalue }
you script might be:
let query = await view.selectRecordsAsync();
let updates=query.records.map(rec=>{
Map method can be applied for arrays, and 'query.records' is array of records. Here
'rec' is loop variable inside this "arrowfunction"
now let's create obj3 , in our case { 'Name or ID of field': fieldvalue }
{'LogoBase64':rec.getCellValue('Merchant')}
wrap it into fields property
fields:{'LogoBase64':rec.getCellValue('Merchant')}
and add record id
wrapping as Object.
To avoid complex string with linebreaks, and to make object creation easier, we can do it with function:
{rec.id, fields:{'LogoBase64':rec.getCellValue('Merchant')}}
fuction myObj(rec){return {rec.id, fields:{'LogoBase64':rec.getCellValue('Merchant')}}
map(rec=>myObj(rec)) - can be written as map(myObj)
we need array of objects, and map method gets first array, doing something with each element and return other array, of results. like we need.
and now finally we get
let table = base.getTable('Merchants');
let view = table.getView('Grid view');
let query = await view.selectRecordsAsync();
function myObj(rec){return {'id':rec.id,'fields':{'Logobase64':rec.getCellValue('Merchant')}}};
let updates=query.records.map(myObj);
while (updates.length > 0) {
await table.updateRecordsAsync(updates.slice(0, 50));
updates = updates.slice(50); }
I have users structure lke this:
{
"users": {
"uniqueID1": {
"name": "Anon",
"friends": {
"uniqueID2": true,
"uniqueID3": true
}
}
"uniqueID2": { },
"uniqueID3": { },
}
}
I want to show a user's friends' names. I have to access $user/friends/ to get list of unique IDs, and iterate the list to get friend's information. But iterating the unique ID is making multiple queries, and I have to always check if all of my queries are finished. According to the doc, it seems multiple queries will not impact the performance too much, but if I want to update my view only when all of the queries are finished, I have to check how many queries are finished.
Is there no way of 'execute a completion block when all queries are finished'?
Pseudocode
var totalNumOfFriends = 0
var tempArray = NewArray()
ref(/users/uniqueID1/friends).observeEventType{ snapshot
var uIDList = snapshot.children's keys
totalNumOfFriends = uIDList .count
for uID in uIDList {
var nameRef = ref(/users/uID/name) i.e. /users/uniqueID3/name
nameRef.observeSingleEventOfType { snapshot
var username = snapshot.value
tempArray.append(username)
if tempArray.count == totalNumOfFriends {
// If counts are the same, tempArray has all of my friends' names
// Now update view using tempArray
}
}
}
}
Pseudocode explanation:
Get list of unique IDs from /users/uniqueID1/friends
'Save' number of unique IDs. (Explained in step 4)
For each unique IDs from the list, get user's name by using ref like this /users/uniquedID2/name
For each name retrieved, add it to temporary array. Once the count of the temporary array equals to the count from step 2, update my view as I have retrieved all the names.
Firebase has no built-in way to signal when a number of queries has finished. But you can easily implement this in your own code. Your approach with a counter that checks how many items have already been loaded is the most common approach for that.
I'm using Entity Framework 6 with ASP.Net MVC 5. When using a database context object, is there a way to use a variable for the table name, without having to manually write the query?
For example:
var tableName = "NameOfTable";
result = context.tableName.Find(...);
I know that particular code won't work, because tableName is not defined in context, but is there a way to achieve the desired effect?
There are some similar questions on this site, but they never really solved the problem and they were for earlier versions of entity framework, so I'm hoping that there is an answer now.
Here's a simple solution using a switch to associate a particular Type to a table. You could also maintain use some sort of Dictionary<string, Type> object.
var tableName = "Table1";
// Get proper return type.
Type returnType;
switch(tableName) {
case "Table1":
returnType = typeof(Table1EntityType);
break;
case "Table2":
returnType = typeof(Table2EntityType);
break;
}
var query = context.Set(returnType);
// Filter against "query" variable below...
var result = query.Where(...);
-or-
var tableName = "Table1";
Dictionary<string, Type> tableTypeDict = new Dictionary<string, Type>()
{
{ "Table1", Table1Type },
{ "Table2", Table2Type }
};
var query = context.Set(tableTypeDict[tableName]);
// Filter against "query" variable below...
var result = query.Where(...);
EDIT: Modified for Entity Framework
EDIT2: Use typeof per #thepirat000 's suggestion
In addition to the helpful answers above, I also want to add this in case it helps someone else.
If you are getting this error on the "Where" clause in Mark's answer:
'DbSet does not contain a definition for 'Where' and no acceptable extension method 'Where' accepting an argument of the type 'DbSet' could be found.
Installing the Nuget Package "System.Linq.Dynamic.Core" made the error disappear for us.
If you need to access the LINQ methods and the column names from the table, you can code something like this:
var tableName = "MyTableName";
var tableClassNameSpace = "MyProject.Models.EntityModels";
using (var dbContext = new MyEntities())
{
var tableClassName = $"{tableClassNameSpace}.{tableName}";
var dynamicTableType = Type.GetType(tableClassName); // Type
var dynamicTable = dbContext.Set(dynamicTableType); // DbSet
var records = dynamicTable
.AsQueryable()
.ToDynamicList()
.OrderBy(d => d.MyColumnName)
.Select(d => new { d.MyColumnName })
.ToList();
// do stuff
}
I am currently doing two PFQueries at once, one which is a PFUser.query and one which is a standard object query. These are both trying to execute roughly at the same time. I get no errors in the code until it runs it says:
'Cannot do a comparison query for type: Swift._NSSwiftArrayImpl'
I can only assume this is because I am trying to do two different queries at once?
Can somebody add some input into what it means a bit more and how maybe to overcome it?
the code I have used for both queries is:
var query = PFQuery(className: "Traits")
query.whereKey("name", equalTo: self.names)
var waveUsers = query.findObjects()
if waveUsers != nil {
self.profile.waves.removeAll(keepCapacity: true)
for waveUser in waveUsers{
self.profile.waves.append(waveUser["waves"] as Int)
}
}
and the PFUser Query:
var query = PFUser.query()
query.whereKey("location", nearGeoPoint:geopoint)
query.limit = 20
var users = query.findObjects()
if users != nil {
self.profile.names.removeAll(keepCapacity: true)
self.profile.images.removeAll(keepCapacity: true)
self.profile.genders.removeAll(keepCapacity: true)
self.profile.locations.removeAll(keepCapacity: true)
self.profile.status.removeAll(keepCapacity: true)
self.profile.rStatus.removeAll(keepCapacity: true)
self.profile.age.removeAll(keepCapacity: true)
for user in users {
{
self.profile.names.append(user["name"] as Int)
self.profile.images.append(user["image"] as NSData)
self.profile.genders.append(user["gender"] as NSString)
self.profile.locations.append(user["location"] as PFGeoPoint)
self.profile.rStatus.append(user["relationship"] as NSString)
self.profile.age.append(user["age"] as NSInteger)
self.profile.status.append(user["status"] as NSString)
self.appsTableView.reloadData()
user.save()
}
}
thanks in advance
If you want to test whether the "name" col can be found in your self.names array, then use containsAllObjectsInArray:
equalTo operates on single objects (it can test for a single object's presence in an array type column, but you're attempting the other way around).
EDIT - Lets say you have an array of strings, and you'd like to find objects whose string column an be found in the array...
// find people in the People class who were SNL cast members
var array = ["belushi", "akroyd", "radner"];
var queries = [];
// assume underscorejs
_.each(array, function(name) {
var query = new Parse.Query("People");
query.equalTo("lastName", name);
queries.push(query.find());
});
Parse.Query.or(queries).then(function() {
var snlCastMembers = _.toArray(arguments);
});
I want fetch the details of a Collection in Odata services like the following URL
http://my.company.com/odata/Employee('1234')/Details
I tried with the following code to do so. Not sure whether fromEntityKey is the right thing to do or anything else.
manager = new breeze.EntityManager(collectionData.serviceName);
var empType = manager.metadataStore.getEntityType("Employees");
var entityKey = new EntityKey(empType, '1234');
var query = EntityQuery.fromEntityKey(entityKey);
But it gives me an error "Be sure to execute a query or call fetchMetadata first."
I also tried that from this link. But I'm still getting the same.
Can any one help me on this?
You can't use manager.metadataSote.getEntityType("Employees") until metadata has been retrieved from the server. By default this occurs during the first query operation, but your code is attempting to use the metadata before it has been retrieved.
Also, I think that you are confusing the name of your resource "Employees" with the type of the instances returned by your resource, probably "Employee". I would also check whether your key's datatype is numeric or a string. The example below assume its numeric (unlike your example where the datatype of the key is presumably a string because you are quoting it).
So you have two approaches, either force the metadata to be fetched before you compose your query, like this:
manager = new breeze.EntityManager(serviceName);
manager.fetchMetadata().then(function () {
var empType = manager.metadataStore.getEntityType("Employee");
var entityKey = new EntityKey(empType, 1);
var query = EntityQuery.fromEntityKey(entityKey);
// if you want to also see the queries details
query = query.expand("Details");
return manager.executeQuery(query);
}).then(function (data) {
var results = data.results;
ok(results.length === 1, "should have returned a single record");
var emp = results[0];
));
or if you know the string name of the 'key' ("Id" in the example below) field, use it directly
manager = new breeze.EntityManager(serviceName);
var query = EntityQuery.from("Employees")
.where("Id", "==", 1)
.expand("Details");
manager.executeQuery(query).then(function(data) {
var results = data.results;
var emp = results[0];
});