Mongoose - What is the third argument passed to the `model.save` callback? - save

var product = new ProductSchema();
model.save(function(err, product, number) {
});
What is the number argument stands for?

It is the number of documents that were affected:
http://mongoosejs.com/docs/api.html#model_Model-save

Related

How to find an object by a property from a List of List of objects in dart?

Hello I am new to dart and trying to find an item by property name in a list of list.
class Product{
String id;
String title;
Product(this.id,this.title);
}
void main(){
List<List<Product>> allProdcuts=[
//New Prodcuts
[
Product("1","Hammer"),
Product("3","Nails"),
Product("2","Screws"),
],
futureItems,
//Old Prodcuts
[
Product("4","Rock"),
Product("5","Paper"),
Product("6","Scissor"),
],
//Rare Items
[
Product("7","Plank"),
Product("8","Wires"),
Product("9","Box"),
],
];
print(allProdcuts.where((itemsList)=>itemsList.contains((product)=>product.title='Wires')));
//Returns ()
}
I have tried using for a single List:
List<Product> futureItems= [
Product("101","Galactic Hammer"),
Product("301","Galactic Nails"),
Product("201","Galactic Screws"),
];
print(newProduct.firstWhere((p)=>p.title=='Hammer'));
//Instance of 'Product'
Also tried this:
print(allProdcuts.map((itemList)=>itemList.firstWhere((p)=>p.title=='Nails')));
// Bad state: No elementError: Bad state: No element.
But there is an element with the title='Nails'.I don't understand what I am doing wrong.
You are calling itemList.firstWhere((p)=>p.title=='Nails') on each list, also the ones with no element with title "Nails". Since firstWhere throws if there is no matching value, it does that for two of your three lists. Also, in the example, itemsList.contains(...) does not take a callback, so you are just checking whether a function is in the list, which it isn't. You might want to use any for that, but it won't solve the problem here.
To do this efficiently, I'd probably create helper function:
Product findByTitle(List<List<Product>> allProducts, String title) {
for (var products in allProducts) {
for (var product in products) {
if (product.title == title) return product;
}
}
// Or return `null`.
throw ArgumentError.value(title, "title", "No element with that title");
}
The return in the middle allows you to skip out of the double iteration the moment you have a match, something which is harder to do with firstWhere/map/forEach etc.
One alternative solutions would be:
var product = allProducts.expand((l) => l.where((p) => p.title == title)).first;
which finds all the products with the given title and flattens them into a single iterable, then picks the first one (if there are any). Because iterables are lazy, it will actually stop at the first match.
There are many ways to solve this.
One example is to use the forEach() method:
allProdcuts.forEach(
(List<Product> l)=>l.forEach(
(Product p){
if (p.title=="Nails")
print(p.id);
}
)
);
The for each method receives a function and applies this function to every element on the list. If you have a lists of lists, you can do this twice to get a function applied to each element of the sub lists.
The above code prints 3, which is the desired result.
Another solution would be to flatten the list first, so you can have an easier search later.
print(allProdcuts.any((innerListOfProducts) =>
innerListOfProducts.any((product) => product.title == 'Wires')));
This code will return true if 'Wires' is in the inner list, and false otherwise.

Cosmos DB stored procedure: I can query the DB, but when I try to upsert I get a 'not same partition' error

I understand that stored procedures run in the scope of a single partition key.
It is also possible to do operations that change data, not just read it.
ID must be string, so I must roll my own autoincrementer for a separate property to use in documents.
I am trying to make a simple autoincrement number generator that runs in a single stored procedure.
I am partitioning data mimicking a file tree, using forward slashes to separate+concatenate significant bits that make my partition names. Like so:
/sometype/foo/bar/
/sometype/ids/
The first item is always the document type, and every document type will have a 'ids' sub-partition.
Instead of holding documents, the /sometype/ids/ partition will hold and reserve all numerical ids that have been created for this document type, for autoincrement purposes.
this satisfies uniqueness within a partition, stored procedure execution scope, and unique document count within a document type, which is good for my purposes.
I got stumped in a stored procedure where I want to get a specified id, or create it if it does not exist.
I can query my partition with the stored procedure, but the upsert throws an error, using the same partition key.
I designed my database with "pkey" as the name of the property that will holds my partition keys.
Here is the code:
//this stored procedure is always called from a partition of type /<sometype>/ids/ , where <sometype> os one of my document types.
//the /sometype/ids/ is a partition to reserve unique numerical ids, as Cosmos DB does not have a numerical increment out of the box, I am creating a facility for that.
//the actual documents of /sometype/ will be subpartitioned as well for performance.
function getId(opkey, n, id) {
// gets the requested number if available, or next one.
//opkey: string - a partition key of cosmos db of the object that is going to consume the generated ID, if known. must start with /<sometype>/ which is the same that is being used to call this SP
//n: integer - a numerical number for the autoincrement
//id = '' : string - a uuid of the document that is using this id, if known
if (opkey === undefined) throw new Error('opkey cannot be null. must be a string. must be a valid partition key on Cosmos DB.');
n = (n === undefined || n === null)?0:n;
id = (id === undefined || id === null)?'':id;
var collection = getContext().getCollection();
//make opkey parameter into an array
var split_pkey = opkey.split('/');
//recreate the pkey /<sometype>/ids/ because I can't find a reference to this string inside the context.
var idpkey = '/'+split_pkey[1]+'/ids/';
//first query as SQL
//get highest numerical value.
var q = 'SELECT TOP 1 * FROM c \
WHERE c.pkey = \''+idpkey+'\' ORDER BY c.n desc';
//helper function to create uuids. can I ditch it?
function CreateUUID() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
q
,
function (firstError, feed, options) {
if (firstError) throw "firstError:"+firstError;
//console.log(collection.options.);
console.log(idpkey+', '+n+', '+id+"-");
var maxn = 0;
// take 1st element from feed
if (!feed || !feed.length) {
//var response = getContext().getResponse();
//response.setBody(null);
}
else {
maxn = feed[0].n;
//var response = getContext().getResponse();
//var body = { original: '', document: '', feed: feed[0] };
//response.setBody(JSON.stringify(body));
}
console.log(maxn);
//query for existing numerical value
q = 'SELECT TOP 1 * FROM c \
WHERE c.pkey = \''+idpkey+'\' \
AND \
c.number = '+n+' \
OR \
c.id = \''+id+'\'';
var isAccepted2 = collection.queryDocuments(
collection.getSelfLink(),
q
,
function (secondFetchError, feed2, options2) {
if (secondFetchError) throw "second error:"+secondFetchError;
//if no numerical value found, create a new (autoincrement)
if (!feed || !feed.length) {
console.log("|"+idpkey);
var uuid = CreateUUID();
var newid = {
id:uuid,
pkey:idpkey,
doc_pkey:opkey,
n:maxn+1
};
//here I used the javascript query api
//it throws an error claiming the primary key is different and I don't know why, I am using idpkey all the time
var isAccepted3 = collection.upsertDocument(
collection.getSelfLink(),
newid
,
function (upsertError,feed3,options3){
if (upsertError) throw "upsert error:"+upsertError;
//if (upsertError) console.log("upsert error:|"+idpkey+"|");
//var response = getContext().getResponse();
//response.setBody(feed[0]);
});
if (!isAccepted3) throw new Error('The third query was not accepted by the server.');
console.log(" - "+uuid);
}
else {
//if id found, return it
//maxn = feed[0].n;
var response = getContext().getResponse();
response.setBody(feed[0]);
//var body = { original: '', document: '', feed: feed[0] };
//response.setBody(JSON.stringify(body));
}
});
if (!isAccepted2) throw new Error('The second query was not accepted by the server.');
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
The error message is :
"Requests originating from scripts cannot reference partition keys other than the one for which client request was submitted."
I don't understand why it thinks it is in error, as I am using the variable idpkey in all queries to hold the correct pkey.
Talk about brain fart!
I was violating my own rules because I was misspelling the partition name in the request, making the first part of the partition key /sometype/ different from the parameter sent, causing a mismatch between the execution scope's partition key and the idpkey variable, resulting in the error.

How do I query all documents in a Firestore collection for all strings in an array? [duplicate]

From the docs:
You can also chain multiple where() methods to create more specific queries (logical AND).
How can I perform an OR query?
Example:
Give me all documents where the field status is open OR upcoming
Give me all documents where the field status == open OR createdAt <= <somedatetime>
OR isn't supported as it's hard for the server to scale it (requires keeping state to dedup). The work around is to issue 2 queries, one for each condition, and dedup on the client.
Edit (Nov 2019):
Cloud Firestore now supports IN queries which are a limited type of OR query.
For the example above you could do:
// Get all documents in 'foo' where status is open or upcmoming
db.collection('foo').where('status','in',['open','upcoming']).get()
However it's still not possible to do a general OR condition involving multiple fields.
With the recent addition of IN queries, Firestore supports "up to 10 equality clauses on the same field with a logical OR"
A possible solution to (1) would be:
documents.where('status', 'in', ['open', 'upcoming']);
See Firebase Guides: Query Operators | in and array-contains-any
suggest to give value for status as well.
ex.
{ name: "a", statusValue = 10, status = 'open' }
{ name: "b", statusValue = 20, status = 'upcoming'}
{ name: "c", statusValue = 30, status = 'close'}
you can query by ref.where('statusValue', '<=', 20) then both 'a' and 'b' will found.
this can save your query cost and performance.
btw, it is not fix all case.
I would have no "status" field, but status related fields, updating them to true or false based on request, like
{ name: "a", status_open: true, status_upcoming: false, status_closed: false}
However, check Firebase Cloud Functions. You could have a function listening status changes, updating status related properties like
{ name: "a", status: "open", status_open: true, status_upcoming: false, status_closed: false}
one or the other, your query could be just
...where('status_open','==',true)...
Hope it helps.
This doesn't solve all cases, but for "enum" fields, you can emulate an "OR" query by making a separate boolean field for each enum-value, then adding a where("enum_<value>", "==", false) for every value that isn't part of the "OR" clause you want.
For example, consider your first desired query:
Give me all documents where the field status is open OR upcoming
You can accomplish this by splitting the status: string field into multiple boolean fields, one for each enum-value:
status_open: bool
status_upcoming: bool
status_suspended: bool
status_closed: bool
To perform your "where status is open or upcoming" query, you then do this:
where("status_suspended", "==", false).where("status_closed", "==", false)
How does this work? Well, because it's an enum, you know one of the values must have true assigned. So if you can determine that all of the other values don't match for a given entry, then by deduction it must match one of the values you originally were looking for.
See also
in/not-in/array-contains-in: https://firebase.google.com/docs/firestore/query-data/queries#in_and_array-contains-any
!=: https://firebase.googleblog.com/2020/09/cloud-firestore-not-equal-queries.html
I don't like everyone saying it's not possible.
it is if you create another "hacky" field in the model to build a composite...
for instance, create an array for each document that has all logical or elements
then query for .where("field", arrayContains: [...]
you can bind two Observables using the rxjs merge operator.
Here you have an example.
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/merge';
...
getCombinatedStatus(): Observable<any> {
return Observable.merge(this.db.collection('foo', ref => ref.where('status','==','open')).valueChanges(),
this.db.collection('foo', ref => ref.where('status','==','upcoming')).valueChanges());
}
Then you can subscribe to the new Observable updates using the above method:
getCombinatedStatus.subscribe(results => console.log(results);
I hope this can help you, greetings from Chile!!
We have the same problem just now, luckily the only possible values for ours are A,B,C,D (4) so we have to query for things like A||B, A||C, A||B||C, D, etc
As of like a few months ago firebase supports a new query array-contains so what we do is make an array and we pre-process the OR values to the array
if (a) {
array addObject:#"a"
}
if (b) {
array addObject:#"b"
}
if (a||b) {
array addObject:#"a||b"
}
etc
And we do this for all 4! values or however many combos there are.
THEN we can simply check the query [document arrayContains:#"a||c"] or whatever type of condition we need.
So if something only qualified for conditional A of our 4 conditionals (A,B,C,D) then its array would contain the following literal strings: #["A", "A||B", "A||C", "A||D", "A||B||C", "A||B||D", "A||C||D", "A||B||C||D"]
Then for any of those OR combinations we can just search array-contains on whatever we may want (e.g. "A||C")
Note: This is only a reasonable approach if you have a few number of possible values to compare OR with.
More info on Array-contains here, since it's newish to firebase docs
If you have a limited number of fields, definitely create new fields with true and false like in the example above. However, if you don't know what the fields are until runtime, you have to just combine queries.
Here is a tags OR example...
// the ids of students in class
const students = [studentID1, studentID2,...];
// get all docs where student.studentID1 = true
const results = this.afs.collection('classes',
ref => ref.where(`students.${students[0]}`, '==', true)
).valueChanges({ idField: 'id' }).pipe(
switchMap((r: any) => {
// get all docs where student.studentID2...studentIDX = true
const docs = students.slice(1).map(
(student: any) => this.afs.collection('classes',
ref => ref.where(`students.${student}`, '==', true)
).valueChanges({ idField: 'id' })
);
return combineLatest(docs).pipe(
// combine results by reducing array
map((a: any[]) => {
const g: [] = a.reduce(
(acc: any[], cur: any) => acc.concat(cur)
).concat(r);
// filter out duplicates by 'id' field
return g.filter(
(b: any, n: number, a: any[]) => a.findIndex(
(v: any) => v.id === b.id) === n
);
}),
);
})
);
Unfortunately there is no other way to combine more than 10 items (use array-contains-any if < 10 items).
There is also no other way to avoid duplicate reads, as you don't know the ID fields that will be matched by the search. Luckily, Firebase has good caching.
For those of you that like promises...
const p = await results.pipe(take(1)).toPromise();
For more info on this, see this article I wrote.
J
OR isn't supported
But if you need that you can do It in your code
Ex : if i want query products where (Size Equal Xl OR XXL : AND Gender is Male)
productsCollectionRef
//1* first get query where can firestore handle it
.whereEqualTo("gender", "Male")
.addSnapshotListener((queryDocumentSnapshots, e) -> {
if (queryDocumentSnapshots == null)
return;
List<Product> productList = new ArrayList<>();
for (DocumentSnapshot snapshot : queryDocumentSnapshots.getDocuments()) {
Product product = snapshot.toObject(Product.class);
//2* then check your query OR Condition because firestore just support AND Condition
if (product.getSize().equals("XL") || product.getSize().equals("XXL"))
productList.add(product);
}
liveData.setValue(productList);
});
For Flutter dart language use this:
db.collection("projects").where("status", whereIn: ["public", "unlisted", "secret"]);
actually I found #Dan McGrath answer working here is a rewriting of his answer:
private void query() {
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("STATUS")
.whereIn("status", Arrays.asList("open", "upcoming")) // you can add up to 10 different values like : Arrays.asList("open", "upcoming", "Pending", "In Progress", ...)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
for (DocumentSnapshot documentSnapshot : queryDocumentSnapshots) {
// I assume you have a model class called MyStatus
MyStatus status= documentSnapshot.toObject(MyStatus.class);
if (status!= null) {
//do somthing...!
}
}
}
});
}

Passing query parameters in Dapper using OleDb

This query produces an error No value given for one or more required parameters:
using (var conn = new OleDbConnection("Provider=..."))
{
conn.Open();
var result = conn.Query(
"select code, name from mytable where id = ? order by name",
new { id = 1 });
}
If I change the query string to: ... where id = #id ..., I will get an error: Must declare the scalar variable "#id".
How do I construct the query string and how do I pass the parameter?
The following should work:
var result = conn.Query(
"select code, name from mytable where id = ?id? order by name",
new { id = 1 });
Important: see newer answer
In the current build, the answer to that would be "no", for two reasons:
the code attempts to filter unused parameters - and is currently removing all of them because it can't find anything like #id, :id or ?id in the sql
the code for adding values from types uses an arbitrary (well, ok: alphabetical) order for the parameters (because reflection does not make any guarantees about the order of members), making positional anonymous arguments unstable
The good news is that both of these are fixable
we can make the filtering behaviour conditional
we can detect the category of types that has a constructor that matches all the property names, and use the constructor argument positions to determine the synthetic order of the properties - anonymous types fall into this category
Making those changes to my local clone, the following now passes:
// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
using (var conn = new System.Data.OleDb.OleDbConnection(
Program.OleDbConnectionString))
{
var row = conn.Query("select Id = ?, Age = ?", new DynamicParameters(
new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
) { RemoveUnused = false } ).Single();
int age = row.Age;
int id = row.Id;
age.IsEqualTo(23);
id.IsEqualTo(12);
}
}
Note that I'm currently using DynamicParameters here to avoid adding even more overloads to Query / Query<T> - because this would need to be added to a considerable number of methods. Adding it to DynamicParameters solves it in one place.
I'm open to feedback before I push this - does that look usable to you?
Edit: with the addition of a funky smellsLikeOleDb (no, not a joke), we can now do this even more directly:
// see https://stackoverflow.com/q/18847510/23354
public void TestOleDbParameters()
{
using (var conn = new System.Data.OleDb.OleDbConnection(
Program.OleDbConnectionString))
{
var row = conn.Query("select Id = ?, Age = ?",
new { foo = 12, bar = 23 } // these names DO NOT MATTER!!!
).Single();
int age = row.Age;
int id = row.Id;
age.IsEqualTo(23);
id.IsEqualTo(12);
}
}
I've trialing use of Dapper within my software product which is using odbc connections (at the moment). However one day I intend to move away from odbc and use a different pattern for supporting different RDBMS products. However, my problem with solution implementation is 2 fold:
I want to write SQL code with parameters that conform to different back-ends, and so I want to be writing named parameters in my SQL now so that I don't have go back and re-do it later.
I don't want to rely on getting the order of my properties in line with my ?. This is bad. So my suggestion is to please add support for Named Parameters for odbc.
In the mean time I have hacked together a solution that allows me to do this with Dapper. Essentially I have a routine that replaces the named parameters with ? and also rebuilds the parameter object making sure the parameters are in the correct order.
However looking at the Dapper code, I can see that I've repeated some of what dapper is doing anyway, effectively it each parameter value is now visited once more than what would be necessary. This becomes more of an issue for bulk updates/inserts.
But at least it seems to work for me o.k...
I borrowed a bit of code from here to form part of my solution...
The ? for parameters was part of the solution for me, but it only works with integers, like ID. It still fails for strings because the parameter length isn't specifed.
OdbcException: ERROR [HY104] [Microsoft][ODBC Microsoft Access Driver]Invalid precision value
System.Data.Odbc. OdbcParameter.Bind(OdbcStatementHandle hstmt,
OdbcCommand command, short ordinal, CNativeBuffer parameterBuffer, bool allowReentrance)
System.Data.Odbc.OdbcParameterCollection.Bind(OdbcCommand command, CMDWrapper cmdWrapper, CNativeBuffer parameterBuffer)
System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader, object[] methodArguments, SQL_API odbcApiMethod)
System.Data.Odbc.OdbcCommand.ExecuteReaderObject(CommandBehavior behavior, string method, bool needReader)
System.Data.Common.DbCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
Dapper.SqlMapper.QueryAsync(IDbConnection cnn, Type effectiveType, CommandDefinition command) in SqlMapper.Async.cs
WebAPI.DataAccess.CustomerRepository.GetByState(string state) in Repository.cs
var result = await conn.QueryAsync(sQuery, new { State = state });
WebAPI.Controllers.CustomerController.GetByState(string state) in CustomerController .cs
return await _customerRepo.GetByState(state);
For Dapper to pass string parameters to ODBC I had to specify the length.
var result = await conn.QueryAsync<Customer>(sQuery, new { State = new DbString { Value = state, IsFixedLength = true, Length = 4} });

Parse.js + AngularJS app relational data query issue

I have two parse classes; Companies and Ratings. It is a one to many relationship. Companies can have many Ratings. This is the statement I would perform in SQL:
SELECT Companies.name, Ratings.rating
FROM Companies
INNER JOIN Ratings
ON Ratings.name_id = Companies.name_id
ORDER BY Companies.name
I want the equivalent of this in Parse, but I'm not sure of how to go about it. Here is what I've currently tried:
function getRatings() {
var tableA = new Parse.Query(Companies);
var tableB = new Parse.Query(Ratings);
tableB.equalTo("name_id", tableA.name_id);
tableB.find({
success: function(results) {
$scope.$apply(function() {
$scope.companies = results.map(function(obj) {
return {
id: obj.get("name_id"),
name: obj.get(tableA.name),
rating: obj.get("rating"),
parseObject: obj
};
});
});
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
}
I am calling this function when the controller loads. This code displays the rating in my output, but not the name of the company.
I am trying to get all the companies listed in the companies object, then pair them with all the ratings they have in the ratings object. Their common key is name_id. This is the code I am using within my Angular view:
<div class="span12">
<div ng-repeat="company in companies | filter: query | orderBy: orderList"
class="well company-description">
<h1>{{company.name}}</h1>
<h3>Rating: {{company.rating}}</h3>
</div>
</div>
If I am way off base on this, please let me know
Get rid of the name_id column in the Ratings class. This isn't how you're supposed to define relationship using Parse.
There are a couple of options for you to choose.
Option 1
Using the Parse data browser, add a new column under the Companies class, called ratings. It should be a column of type Relation and point to Ratings as the target class. (Let me know if you need more information on how to do this.)
Then, when you create or edit a company, add ratings as follows:
var Companies = Parse.Object.extend("Companies");
var Ratings = Parse.Object.extend("Ratings");
var company = new Companies({name: "Some Company"});
company.relation("ratings").add(new Ratings({stars: 5}));
company.save();
Then, when querying Companies, do so as follows:
new Parse.Query(Companies).find({
success: function(companies) {
for (var i = 0; i < companies.length; i++) {
companies[i].relation("ratings").query().find({
success: function(ratings) {
// Finally, I have the ratings for this company
}
}
}
}
});
Option 2
Using the Parse data browser, add a new column under the Companies class, called ratings. It should be a column of type Array.
Then, when you create or edit a company, add ratings as follows:
var Companies = Parse.Object.extend("Companies");
var Ratings = Parse.Object.extend("Ratings");
var company = new Companies({
name: "Some Company",
ratings: [new Ratings({stars: 5})]
});
company.save();
Then, when querying Companies, do so as follows:
new Parse.Query(Companies).include("ratings").find({
success: function(companies) {
// Yay, I have access to ratings via companies[0].get("ratings")
}
});
include("ratings") tells Parse to include the actual objects, rather than pointers to objects for the given key.
Conclusion
Option 1 is better if you are expecting to have a large amount of ratings for each company, and if you don't always plan on retrieving all the ratings each time you query the companies.
Option 2 is better if the number of ratings for each company is relatively small, and you always want ratings to come back when you query companies.
I found out how to resolve the Uncaught You can't add an unsaved Parse.Object to a relation. error.
var addRating = new Ratings({stars: rating}); // save rating first, then associate it with a company
addRating.save({
success: function() {
var addCompany = new Companies({name: name});
addCompany.relation("ratings").add(addRating);
addCompany.save();
}
});
The rating has to be saved first, then the company relation can be added later on... makes sense, but took me awhile to figure it out! :S

Resources