Duplicate console.log output of gun map when using gundb - gun

Does this function duplicate results as a bug or am I causing this? The output always has 1 or more records duplicated. In this example, Bank of China is always listed twice in output.
gun.get('savings_accounts').map(function (name, ID) {
console.log( name.name, ID );
}, true)
My code:
localStorage.clear();
var gun = Gun();
////////////////////////////////////////////////////// create record
var acc1 = gun.put({
name: "Bank of America",
accType: "Savings",
last4: "4123",
favorite: true,
status: true,
created: "some date created"
});
var acc2 = gun.put({
name: "Bank of China",
accType: "Savings",
last4: "5123",
favorite: true,
status: true,
created: "some date created"
});
gun.get('savings_accounts').map(function (name, ID) {
console.log( name.name, ID );
}, true)

From the author of GunDB, Mark Nadal
1) gun.get('savings_accounts').map().val(cb) is what you want for normal / procedural / easy things.
HOWEVER...
2) gun is actually functional/reactive (FRP), or also known as streaming/event oriented. The data might/will get called multiple times (if you don't use .val) because A) in-memory replies, B) your browser's localStorage replies, C) the server will reply, D) server will relay to other browser peers which each might reply with data.
^ that is the "realtime" part of gun.
.val only fires once (well per item on the chain, so if you do map().val(cb) the val will get fired multiple times but only once from each item in the list).
use .val(cb) if you are doing procedural things.
Use .on(cb) (which is what .map(cb) uses internally. Most API methods internally use .on) if you want to subscribe to realtime updates of the data.
you'll slowly find that the realtime/FRP/event/streaming as being a much cleaner way to write your apps.

Related

Is there a way to run the supplied prisma query as a raw sql query?

I have been stuck with this and similar dilemmas for a while now and can't seem to find an acceptable solution. Prisma currently doesn't allow for upsertMany in it's queries, or in my case a nested upsertMany. I have found some accepted solutions and the most popular one is using $transaction from prisma to write a loop of upserts which doesn't work for me as each query contains up to 200 items to upsert (currently testing 100 items takes roughly 10sec to run so it really isn't an acceptable solution). Prisma offers the ability to use $queryRaw to use a raw SQL query which may offer a more acceptable solution. I've supplied the code to their currently accepted solution as well as the query (that isn't currently possible) that I'm hoping someone could help me turn into a raw sql query or a better solution.
Their accepted solution:
prisma.$transaction(data.map((d) =>
prisma.MODEL.upsert({
where:{
id: d.id
},
update:{
foo:d.foo,
},
create:{
foo: d.foo,
bar: d.bar
},
}),
),
);
How I would need to implement it as I would like to create one MODEL with a related array of MODELS and another array of related MODELS:
let transactionOne = prisma.$transaction(
arrayOne.map((d)=>
prisma.MODEL.upsert({
where:{
id: d.id,
},
update:{
foo: d.foo,
},
create:{
foo: d.foo,
bar: d.bar,
},
}),
)
);
let transactionTwo = prisma.$transaction(
arrayTwo.map((d)=>
prisma.MODEL.upsert({
where:{
id: d.id,
},
update:{
foo: d.foo,
},
create:{
foo: d.foo,
bar: d.bar,
},
}),
)
);
let data = await prisma.MODEL.create({
data:{
foo: foo,
bar: bar,
example:{
connect: transactionOne.map((a)=>({id: a.id})),
},
exampleTwo:{
connect: transactionTwo.map((b)=>({id: b.id})),
},
},
});
My example above like mentioned before takes just over 10 seconds with 100 items and may possibly require 200 items so 20 seconds per call. Is there an easier way to make this faster or like I said a way to create a raw SQL query with the same results?
Thanks for any help!

database writes need to be verified in an integration test?

I basically have an api that writes to 2 different tables, and am using supertest to make api calls to write a certain data to the database e.g. POST /userCertificates writes his address in the address table, education in status table like so:
I basically do this as one of the integration tests:
payload = {user_id: 1, name: abc, address: {a: x, b: y}, education: {a: x, b: y} ...}
it('Post to the database'), ()=>{
request.post(/userCertificate).send(payload).expect(200)
.then(res =>
expect(payload.userAddress).toEqual(res.body.userAddress)
// more expects to verify the response received)
}
The /userCertificates api basically does this:
creates a new user with name and user_id
saves data of address to the address table and establishes FK relation.
saves data of education to the education table and establishes FK
relation.
But, the real confusion is, do I also need to use a dao layer that directly talks with the database to absolutely verify that the data has been written to the 2 tables? E.g.
for my case above(in the same describe block):
NOTE: user_id is a foreign key in the address and education table
it('Verify data written to the address table', ()=> {
const data = AddressDao.find({user_id: 1})
if(data.length <= 0) {
throw 'Data not written to the database
}
}
it('Verify data written to the education table', ()=> {
const data = EducationDao.find({user_id: 1})
if(data.length <= 0) {
throw 'Data not written to the database
}
}
But, not just in the case of writing to multiple tables, is such verification of data
written to the database required?

FaunaDB: how to fetch a custom column

I'm just learning FaunaDB and FQL and having some trouble (mainly because I come from MySQL). I can successfully query a table (eg: users) and fetch a specific user. This user has a property users.expiry_date which is a faunadb Time() type.
What I would like to do is know if this date has expired by using the function LT(Now(), users.expiry_date), but I don't know how to create this query. Do I have to create an Index first?
So in short, just fetching one of the users documents gets me this:
{
id: 1,
username: 'test',
expiry_date: Time("2022-01-10T16:01:47.394Z")
}
But I would like to get this:
{
id: 1,
username: 'test',
expiry_date: Time("2022-01-10T16:01:47.394Z"),
has_expired: true,
}
I have this FQL query now (ignore oauthInfo):
Query(
Let(
{
oauthInfo: Select(['data'], Get(Ref(Collection('user_oauth_info'), refId))),
user: Select(['data'], Get(Select(['user_id'], Var('oauthInfo'))))
},
Merge({ oauthInfo: Var('oauthInfo') }, { user: Var('user') })
)
)
How would I do the equivalent of the mySQL query SELECT users.*, IF(users.expiry_date < NOW(), 1, 0) as is_expired FROM users in FQL?
Your use of Let and Merge show that you are thinking about FQL in a good way. These are functions that can go a long way to making your queries more organized and readable!
I will start with some notes, but they will be relevant to the final answer, so please stick with me.
The Query function
https://docs.fauna.com/fauna/current/api/fql/functions/query
First, you should not need to wrap anything in the Query function, here. Query is necessary for defining functions in FQL that will be run later, for example, in the User-Defined Function body. You will always see it as Query(Lambda(...)).
Fauna IDs
https://docs.fauna.com/fauna/current/learn/understanding/documents
Remember that Fauna assigns unique IDs for every Document for you. When I see fields named id, that is a bit of a red flag, so I want to highlight that. There are plenty of reasons that you might store some business-ID in a Document, but be sure that you need it.
Getting an ID
A Document in Fauna is shaped like:
{
ref: Ref(Collection("users"), "101"), // <-- "id" is 101
ts: 1641508095450000,
data: { /* ... */ }
}
In the JS driver you can use this id by using documentResult.ref.id (other drivers can do this in similar ways)
You can access the ID directly in FQL as well. You use the Select function.
Let(
{
user: Get(Select(['user_id'], Var('oauthInfo')))
id: Select(["ref", "id"], Var("user"))
},
Var("id")
)
More about the Select function.
https://docs.fauna.com/fauna/current/api/fql/functions/select
You are already using Select and that's the function you are looking for. It's what you use to grab any piece of an object or array.
Here's a contrived example that gets the zip code for the 3rd user in the Collection:
Let(
{
page: Paginate(Documents(Collection("user")),
},
Select(["data", 2, "data", "address", "zip"], Var("user"))
)
Bring it together
That said, your Let function is a great start. Let's break things down into smaller steps.
Let(
{
oauthInfo_ref: Ref(Collection('user_oauth_info'), refId)
oauthInfo_doc: Get(Var("oathInfoRef")),
// make sure that user_oath_info.user_id is a full Ref, not just a number
user_ref: Select(["data", "user_id"], Var("oauthInfo_doc"))
user_doc: Get(Var("user_ref")),
user_id: Select("id", Var("user_ref")),
// calculate expired
expiry_date: Select(["data", "expiry_date"], Var("user_doc")),
has_expired: LT(Now(), Var("expiry_date"))
},
// if the data does not overlap, Merge is not required.
// you can build plain objects in FQL
{
oauthInfo: Var("oauthInfo_doc"), // entire Document
user: Var("user_doc"), // entire Document
has_expired: Var("has_expired") // an extra field
}
)
Instead of returning the auth info and user as separate points if you do want to Merge them and/or add additional fields, then feel free to do that
// ...
Merge(
Select("data", Var("user_doc")), // just the data
{
user_id: Var("user_id"), // added field
has_expired: Var("has_expired") // added field
}
)
)

Find entity with most relations filtered by criteria

model Player {
id String #id
name String #unique
game Game[]
}
model Game {
id String #id
isWin Boolean
playerId String
player Player #relation(fields: [playerId], references: [id])
}
I would like to find a player with most wins. How would I do that with prisma? If there is no prisma "native" way to do it, what is the most efficient way to do this with raw SQL?
The best I could think of is:
prisma.player.findMany({
include: {
game: {
where: {
isWin: true,
},
},
},
})
But it has huge downside that you need to filter and order results in Node manually, and also store all results in memory while doing so.
Using the groupBy API you can find the player with the most wins using two queries.
1. Activate orderByAggregateGroup
You'l need to use the orderByAggregateGroup preview feature and use Prisma version 2.21.0 or later.
Update your Prisma Schema as follows
generator client {
provider = "prisma-client-js"
previewFeatures = ["orderByAggregateGroup"]
}
// ... rest of schema
2. Find playerId of most winning player
Use a groupBy query to do the following:
Group games by the playerId field.
Find the count of game records where isWin is true.
Order them in descending order by the count mentioned in 2.
Take only 1 result (since we want the player with the most wins. You can change this to get the first-n players as well).
The combined query looks like this:
const groupByResult = await prisma.game.groupBy({
by: ["playerId"],
where: {
isWin: true,
},
_count: {
isWin: true,
},
orderBy: {
_count: {
isWin: "desc",
},
},
take: 1, // change to "n" you want to find the first-n most winning players.
});
const mostWinningPlayerId = groupByResult[0].playerId;
I would suggest checking out the Group By section of the Aggregation, grouping, and summarizing article in the prisma docs, which explains how group by works and how to use it with filtering and ordering.
3. Query player data with findUnique
You can trivially find the player using a findUnique query as you have the id.
const mostWinningPlayer = await prisma.player.findUnique({
where: {
id: mostWinningPlayerId,
},
});
Optionally, if you want the first "n" most winning players, just put the appropriate number in the take condition of the first groupBy query. Then you can do a findMany with the in operator to get all the player records. If you're not sure how to do this, feel free to ask and I'll clarify with sample code.

pubnub get history from multiple channels

I am using pubnub for chat , and using userid as channel to send messages , however when i want to retrieve conversation between two users , i need to get data from both channels, how can i do that?
I have data on both channels e.g. "userAid" and "userBid" but if i query
this.pubnub.history(
{ channel: ['userAid,'userBid'], reverse: true, count: 15 },
(status, res) => {
});```
it does not return any result , if i query with only one channel it works
The History SDK call is generally meant to fetch history from a single channel. If you need to fetch history from multiple channels, you need to use Batch History methods.
Refer to https://www.pubnub.com/docs/react-native-javascript/api-reference-storage-and-playback#batch-history for more details.
An example call might be as follows, but the link above provides a list of all the parameters that can be set. Please note that the fetchMessages method can be used to fetch history from a single channel as well.
pubnub.fetchMessages(
{
channels: ['ch1', 'ch2', 'ch3'],
start: "15343325214676133",
end: "15343325004275466",
count: 25
},
(status, response) => {
// handle response
}
);
It seems history can only get messages from one channel.
Try this:
pubnub.fetchMessages({
channels: ['ch1', 'ch2', 'ch3'],
start: '15343325214676133',
end: '15343325004275466',
count: 15,
}, (status, response) => {
// handle status, response
});
Pubnub docs on getting the history:
https://www.pubnub.com/developers/chat-resource-center/docs/reference/message-history