When I created the stored procedure, this is the default sample code provided by Azure. My container(Students) is very simple and has only 2 items. The partition key is id.
{name: "aaa", id: "1"}
{name: "bbb", id: "2"}
Here is the sample stored procedure code provided by Azure CosmosDB.
// SAMPLE STORED PROCEDURE
function sample(prefix) {
var collection = getContext().getCollection();
// Query documents and take 1st item.
var isAccepted = collection.queryDocuments(
collection.getSelfLink(),
'SELECT * FROM root r',
function (err, feed, options) {
if (err) throw err;
// Check the feed and if empty, set the body to 'no docs found',
// else take 1st element from feed
if (!feed || !feed.length) {
var response = getContext().getResponse();
response.setBody('no docs found');
}
else {
var response = getContext().getResponse();
var body = { prefix: prefix, feed: feed[0] };
response.setBody(JSON.stringify(body));
}
});
if (!isAccepted) throw new Error('The query was not accepted by the server.');
}
Why it always return "no docs found"? I have tried different sql queries like "select * from Students" or "select * from root" or "select * from c" but none of them work.
When you want to execute stored procedure, you need to pass value of partition key. In your case, you need to pass "1" or "2" as value of partition key(not "id") like this:
Related
I am working with a PostgreSQL database using Prisma. I have a bulk update command which I want to fail if any of the records have changed since my last read.
My schema:
model OrderItem {
id String #id #default(uuid()) #db.Uuid
quantity Int
lastUpdated DateTime #updatedAt #map("last_updated")
##map("order_item")
}
I have written a query which works, but I built the query manually rather than using Prisma's safe query builder tools.
My query:
type OrderItemType = {
id: string;
quantity: number;
lastUpdated: Date;
}
type OrderItemUpdateDataType = {
quantity: number;
}
const updateByIds = async (
orderItemIdLastUpdatedTuples: ([OrderItemType['id'], OrderItemType['lastUpdated']])[],
orderItemUpdateData: OrderItemUpdateDataType,
) => {
// Optimistic concurrency - try updating based on last known "last updated" state. If mismatch, fail.
await prisma.$transaction(async (prisma) => {
// TODO: Prefer prisma.$queryRaw. Prisma.join() works on id[], but not on [id, lastUpdated][]
const idLastUpdatedPairs = orderItemIdLastUpdatedTuples
.map(([id, lastUpdated]) => `(uuid('${id}'), '${lastUpdated.toISOString()}')`)
.join(', ');
const query = `SELECT * FROM order_item WHERE (id, last_updated) in ( ${idLastUpdatedPairs} )`;
const items = await prisma.$queryRawUnsafe<OrderItem[]>(query);
// If query doesn't match expected update count then another query has outraced and updated since last read.
const itemIds = orderItemIdLastUpdatedTuples.map(([id]) => id);
if (items.length !== orderItemIdLastUpdatedTuples.length) {
throw new ConcurrentUpdateError(`Order Items ${itemIds.join(', ')} were stale. Failed to update.`);
}
await prisma.orderItem.updateMany({
where: { id: { in: itemIds } },
data: orderItemUpdateData,
});
});
};
This function wants to update a set of items. It accepts a list of tuples - id/lastUpdated pairs. It starts an explicit transaction, then performs an unsafe SELECT query to confirm the items to affect haven't been updated, then updates. This is following the guidance of Prisma's docs here - https://www.prisma.io/docs/concepts/components/prisma-client/transactions#interactive-transactions-in-preview
I was hoping to achieve the same results using prisma.$queryRaw rather than prisma.$queryRawUnsafe or even using implicit transactions rather than an explicit transaction wrapper. I wasn't able to find a syntax for expressing "where in tuple" using either of these approaches, though.
I am able to express what I want using implicit transactions when updating a single record. An example here would look like:
const { count } = await prisma.orderItem.updateMany({
where: { id, lastUpdated },
data: orderItemUpdateData,
});
and when using an explicit, safe query I stumbled on joining the array of tuples properly.
From the Prisma documentation, https://www.prisma.io/docs/concepts/components/prisma-client/raw-database-access#tagged-template-helpers, there exists a Prisma.join command (which happens implicitly when using their tagged template helper syntax) but I wasn't able to generate a valid output when feeding it an array of tuples.
Did I miss anything? Does Prisma support joining a tuple using their safe query template syntax?
I'm newbie using Vertx, I'm building a basic Api Rest with Vertx + Mongo using Javascript.
I'm looking for some way to automaticly validate the incoming documents before insert or update (for example, something like schemas in Mongoose).
I got the following:
POST entry point Inserting a new cat
var BodyHandler = require("vertx-web-js/body_handler");
var Router = require("vertx-web-js/router");
var router = Router.router(vertx);
router.post("/cat")
.produces("application/json")
.handler(BodyHandler.create().handle)
.handler(controller.createCat);
Controller's function Inserting a new cat
createCat: function (ctx) {
var response = ctx.response();
var body = ctx.getBodyAsJson() || {};
console.log('inserting cat')
connection.mongoClient.insert("cats", { name: body.name }, function (res, res_err) {
if (res_err == null) {
var id = res;
console.log("Inserted cat with id " + id);
response.putHeader("content-type", "application/json");
response.end(JSON.stringify(id));
} else {
console.log('err')
res_err.printStackTrace();
}
});
}
The problem is that I can insert empty documents resulting documents with only one field: the mongo ID.
TL;DR - I want to know if there is some way to tell Mongo that name field is ALWAYS required.
Solution I used:
mongoClient.runCommand("collMod", { collMod: "cats", validator: { $and: [ {"name": {$type: "string", $exists: true}} ] }, validationLevel: "strict", validationAction: "error" }, function(res, res_err) {
if (res_err) res_err.printStackTrace()
else console.log('The schema of cats collection has been updated')
});
In mongodb there is a concept of validation with query filters for collection: https://docs.mongodb.com/manual/core/schema-validation/#query-expressions
If the "name" field is always required, you can create your collection like this:
db.createCollection("cats", {
validator: {
$and: [ {"name": {$type: "string", $exists: true}} ]
})
you can create your own schema in a separate json file, then create your own validation method when the context is provided by ur router.
I assume different endpoint has different validation.
you can also create a middleware using gateleen
you have plenty of options, mongoose its benefits but the whole point of using mongodb is not creating a schema. Therefore, u can assume that the request should have specific mandatory fields to validate.
Im using Firebase Firestore and want to update an array field under a userprofile with the latest chat thread's id.. Im guessing that I have to pull the entire array (if it exists) from the chat node under that user, then I need to append the new id (if it doesnt exist) and update the array.. It works when theres only 1 value in the array then it fails after that with the following error:
Transaction failed: { Error: Cannot convert an array value in an array value.
at /user_code/node_modules/firebase-admin/node_modules/grpc/src/node/src/client.js:554:15 code: 3, metadata: Metadata { _internal_repr: {} } }
and here is my firebase cloud function, can anyone tell me where im going wrong ?
exports.updateMessages = functions.firestore.document('messages/{messageId}/conversation/{msgkey}').onCreate( (event) => {
/// console.log('function started');
const messagePayload = event.data.data();
const userA = messagePayload.userA;
const userB = messagePayload.userB;
// console.log("userA " + userA);
// console.log("userB " + userB);
// console.log("messagePayload " + JSON.stringify(messagePayload, null, 2) );
const sfDocRef = admin.firestore().doc(`users/${userB}`);
return admin.firestore().runTransaction( (transaction) => {
return transaction.get(sfDocRef).then( (sfDoc) => {
const array = [];
array.push(...[event.params.messageId, sfDoc.get('chats') ]);
transaction.update(sfDocRef, { chats: array } );
});
}).then( () => {
console.log("Transaction successfully committed!");
}).catch( (error) => {
console.log("Transaction failed: ", error);
});
});
You're nesting arrays in your code here:
const array = [];
array.push(...[event.params.messageId, sfDoc.get('chats') ]);
This leads to an array with two values, the first one being the new messageId and the second value contains an array all of your previous values, e.g.
[ "new message id", ["previous id", "older id"] ]
This type of nested array is something that Firestore (apparently) doesn't allow to be stored.
The solution is simple:
const array = [event.params.messageId, ...sfDoc.get('chats')];
The fact that you have to first load the array to then add a single element to it is one of reasons Firebasers recommend not storing data in arrays. Your current data looks like it'd be better off as a set, as shown in the Firestore documenation:
{
"new message id": true,
"previous id": true,
"older id": true
}
That way adding a chat ID is as simple as:
sfDocRef.update({ "chats."+event.params.messageId, true })
I have looked further into the matter, and I would follow the advice that Frank gave you in his post; allocate the data in collections rather than with arrays as they have greater versatility for Firebase 1. Researching under the examples listed in the Firebase website looking for anything related to a chat, I’ve found the data structure and code for messages that are used by Firechat as they might be of use for you.
In the source code, they use a collection for the their message-id -userId pair with the following topology 2 :
The exact way how the saving is executed at the repository is 3 :
It executes an append of the message into the Room-id collection. Instead of this structure, you could use an userID - messageID pair as it might fit you better.
I have 2 collections "photos" and "users" and each document in "users" has one or more photo IDs with an array.
photos > 5528c46b > name: "Photo1"
a1e820eb > name: "Photo2"
32d410a7 > name: "Photo3"
users > acd02b1d > name: "John", photos: ["5528c46b"]
67f60ad3 > name: "Tom", photos: ["5528c46b", "32d410a7"]
7332ec75 > name: "Sara", photos: ["a1e820eb"]
9f4edcc1 > name: "Anna", photos: ["32d410a7"]
I want to get all users who have one or more specific photo IDs.
Are there any ways to do that?
See Henry's answer, as we've no made Array Contains queries available.
Unfortunately not yet, although it's on our roadmap.
In the meantime, you'll need to use a map instead, in the form of:
photos: {
id1: true
id2: true
}
Now you can find all users with id1 by filtering by photos.id1 == true.
Read more about querying such sets in the Firebase documentation.
Added 'array-contains' query operator for use with .where() to find documents where an array field contains a specific element.
https://firebase.google.com/support/release-notes/js 5.3.0
Update: also available in #google-cloud/firestore: https://github.com/googleapis/nodejs-firestore/releases/tag/v0.16.0
Update 2 https://firebase.googleblog.com/2018/08/better-arrays-in-cloud-firestore.html
Update 3 now available in Admin Node.js SDK v6.0.0 https://github.com/firebase/firebase-admin-node/releases
Here is a bit of expansion on the answer as some seem to be confused about having to make indexes for each key, Firestore already indexes your data for simple queries thus you can do a simple query like
documentReference.where('param','==','value').onSnapshot(...)
but you can not do a compound query unless you index your data for those parameters. So you would need indexes to be able to do something like this:
documentReference.where('param','==','value').where(..otherparams...).onSnapshot(...)
So as long as you need the photos for an id you can save them as
usersCollection : (a collection)
uidA: (a document)
photoField: (a field value that is a map or object)
fieldID1 : true (a property of the photoField)
fieldID2 : true (a property of the photoField)
etc ...
and you can simply query user(s) that have, let's say, fieldID1 in their photoField without needing to form any index and like query below.
firestore.doc('usersCollection/uidA').where('photoField.fieldID1','==',true).onSnapshot(...)
Firestore has now added an 'in' query as of November 2019.
According to the announcement article:
With the in query, you can query a specific field for multiple values
(up to 10) in a single query. You do this by passing a list containing
all the values you want to search for, and Cloud Firestore will match
any document whose field equals one of those values.
With Firebase Version 9 (Dec, 2021 Update):
You can use "array-contains" with one single photo document ID in the "while()" to get all users who have it:
import {
query,
collection,
where,
getDocs
} from "firebase/firestore";
// Here
const q = query(
collection(db, "users"),
where("photos", "array-contains", "5528c46b")
);
// Here
const usersDocsSnap = await getDocs(q);
usersDocsSnap .forEach((doc) => {
console.log(doc.data()); // "John's doc", "Tom's doc"
});
You can alse use "array-contains-any" with one or more photo document IDs with an array in the "while()" to get more corresponding users:
import {
query,
collection,
where,
getDocs
} from "firebase/firestore";
// Here
const q = query(
collection(db, "users"),
where("photos", "array-contains-any", ["5528c46b", "a1e820eb"])
);
// Here
const usersDocsSnap = await getDocs(q);
usersDocsSnap .forEach((doc) => {
console.log(doc.data()); // "John's doc", "Tom's doc", "Sara's doc"
});
i am using the angular-fullstack yeoman generator. created a schema for a Product, and a set of api crud operations. all works well. now in the get list operations, i don't want to receive all the fields, only a subset. like a select in sql. i would also wish to alter one value. instead of the price, i need price * 1.1 .
how to do that?
here is the code for the index method (returns list of products):
// Gets a list of Products
export function index(req, res) {
Product.findAsync()
.then(respondWithResult(res))
.catch(handleError(res));
}
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
}
};
}
As stated in the documentation, .find() takes two params, query and projection.
// params
Product.findAsync(query, projection)
You can use projection to "select" a subset of fields;
// example
Product.findAsync({}, { _id: 1, name: 1, description: 1 })
// result, only the three specified field will be returned
[
{ _id: 'abc123', name: 'Some name', description: 'Some description'},
{...}
]
If you want to manipulate data I think you have to use the aggregation pipeline