deepstream list subscribing to data - javascript

is it possible in deepstream to subscribe to data using a list? it appears that changes to the data does not trip the subscribe() function, only something like an addEntry() appears to affect the list subscription.
const deepstream = require('deepstream.io-client-js') ;
const util = require('util') ;
const client = deepstream('localhost:6020').login();
var obj_1 = { 'sequelizeName':'Mark', 'sequelizeAddr':'123 Elm Lane' , 'sequelizeId':'1111'};
var obj_2 = { 'sequelizeName':'Lori', 'sequelizeAddr':'948 Maple Street' , 'sequelizeId':'2222'};
const rec_1 = client.record.getRecord('obj_one');
const rec_2 = client.record.getRecord('obj_two');
rec_1.set(obj_1);
rec_2.set(obj_2);
var listTest = client.record.getList('listTest');
listTest.setEntries( ['obj_one' ,'obj_two' ] );
listTest.subscribe( (result) => {
console.log('LIST SUBSCRIBE: ' + util.inspect(result));
})
setTimeout( () => {
obj_1.sequelizeAddr = '321 New Address';
rec_1.set(obj_1); // how can this change show up in the list subscribe?
}, 2000 );
I have been encouraged to try a new approach using lists, but I am unclear how to subscribe to changes in the data itself using a list, except to somehow have some sort of "generic" or "global" subscribe, which i am not sure is even possible.
Or is there some way I can subscribe using an anonymous record?

Lists are just arrays of strings. Your list content is not connected to the actual record. You can't even assume that a list entry is a record name. You would need to subscribe to each record name in a list manually to get its content updates.

this suggestion was made by both wolfram and phillipp:
class User{
constructor( recordName ) {
this.record = ds.record.getRecord( recordName );
this.record.subscribe( this._processUpdate.bind( this ) );
}
_processUpdate( data ) {
if( this.record.name === '...') {
// do stuff
}
}
}
this works great. thank you both.

Related

Firestore listener removes a message from pagination when adding a new message in React Native

I am trying to do Firestore reactive pagination. I know there are posts, comments, and articles saying that it's not possible but anyways...
When I add a new message, it kicks off or "removes" the previous message
Here's the main code. I'm paginating 4 messages at a time
async getPaginatedRTLData(queryParams: TQueryParams, onChange: Function){
let collectionReference = collection(firestore, queryParams.pathToDataInCollection);
let collectionReferenceQuery = this.modifyQueryByOperations(collectionReference, queryParams);
//Turn query into snapshot to track changes
const unsubscribe = onSnapshot(collectionReferenceQuery, (snapshot: QuerySnapshot) => {
snapshot.docChanges().forEach((change: DocumentChange<DocumentData>) => {
//Now save data to format later
let formattedData = this.storeData(change, queryParams)
onChange(formattedData);
})
})
this.unsubscriptions.push(unsubscribe)
}
For completeness this is how Im building my query
let queryParams: TQueryParams = {
limitResultCount: 4,
uniqueKey: '_id',
pathToDataInCollection: messagePath,
orderBy: {
docField: orderByKey,
direction: orderBy
}
}
modifyQueryByOperations(
collectionReference: CollectionReference<DocumentData> = this.collectionReference,
queryParams: TQueryParams) {
//Extract query params
let { orderBy, where: where_param, limitResultCount = PAGINATE} = queryParams;
let queryCall: Query<DocumentData> = collectionReference;
if(where_param) {
let {searchByField, whereFilterOp, valueToMatch} = where_param;
//collectionReferenceQuery = collectionReference.where(searchByField, whereFilterOp, valueToMatch)
queryCall = query(queryCall, where(searchByField, whereFilterOp, valueToMatch) )
}
if(orderBy) {
let { docField, direction} = orderBy;
//collectionReferenceQuery = collectionReference.orderBy(docField, direction)
queryCall = query(queryCall, fs_orderBy(docField, direction) )
}
if(limitResultCount) {
//collectionReferenceQuery = collectionReference.limit(limitResultCount)
queryCall = query(queryCall, limit(limitResultCount) );
}
if(this.lastDocInSortedOrder) {
//collectionReferenceQuery = collectionReference.startAt(this.lastDocInSortedOrder)
queryCall = query(queryCall, startAt(this.lastDocInSortedOrder) )
}
return queryCall
}
See the last line removed is removed when I add a new message to the collection. Whats worse is it's not consistent. I debugged this and Firestore is removing the message.
I almost feel like this is a bug in Firestore's handling of listeners
As mentioned in the comments and confirmed by you the problem you are facing is occuring due to the fact that some values of the fields that your are searching in your query changed while the listener was still active and this makes the listener think of this document as a removed one.
This is proven by the fact that the records are not being deleted from Firestore itself, but are just being excluded from the listener.
This can be fixed by creating a better querying structure, separating the old data from new data incoming from the listener, which you mentioned you've already done in the comments as well.

socket.io send data to matching socket's

when a user connects to my socket
I add to a session map:
io.on('connection', function (socket) {
sessionMap.set(socket.id,socket);
}
my session Map
var SessionMap = {};
module.exports = {
set: function(key,value){
SessionMap[key] = value;
},
get: function(key){
return SessionMap[key]
},
delete: function(key){
delete SessionMap[key]
},
all: function(){
return SessionMap
}
}
And also save my user socket id in a class player:
socket.on('addPlayer-Queue', (result) => {
sessionMap.set(socket.id,socket);
queue.addPlayer(new Player({
id: result.id,
name: result.name,
mmr: result.mmr
}, socket.id));
And I have a function that selects two players that are connected (where I save in an array) and create a "battle" and then I wanted to send to the socket that was selected / matched for this battle
the battle dice
This is my role that selects both players and creates a battle:
searching() {
const firstPlayer = this.getRandomPlayer();
const secondPlayer = this.players.find(
playerTwo =>
playerTwo.mmr < this.calculateLessThanPercentage(firstPlayer) &&
playerTwo.mmr > this.calculateGreaterThanPercentage(firstPlayer) &&
playerTwo.id != firstPlayer.id
);
if (!secondPlayer) {
return null;
}
const matchedPlayers = [firstPlayer, secondPlayer];
this.removePlayers(matchedPlayers);
return new Match(matchedPlayers);
}
}
And also when connecting I use a set interval to be performing this function every 1 second
But my difficulty is how I would send the data from this battle to the corresponding socket's
my relation socket with player
When a player enters my event I create a player by going through socket id
And I also make a session map of this socket
sessionMap.set(socket.id,socket);
my class player:
class Player {
constructor(player,socketId) {
this.id = player.id
this.socketId = socketId
this.name = player.name
this.mmr = player.mmr
}
}
module.exports = Player;
const getMatchConfigurationFor = player => {
/* configure and return the payload notifying the player of the match */
}
const configurePlayersForNewMatch = () => matchedPlayers.forEach(player =>
sessionMap.get(player.socketid)
.broadcast.to(player.socketid)
.emit(messageTags.MATCH_CONFIGURATION,
getMatchConfigurationFor(player)))
regarding where to do this work .. the single responsibility principle says that a function should have a singular clear purpose. So the search method should search for matching players, not configure the match. You should do this work in another function that is called while configuring the match, which itself is called after the search returns successfully. I've provided the wrapper function for that here: it is written in a fashion to expect the relevant pieces are in scope. You could rewrite it as a proper function with parameters if you prefer.
This is a work in progress solution for Felipe, posted by request.
After a match is found, you'd probably want to emit a MatchFound object to both clients detailing information about the match (including information about their opponent). Once a client gets this, you can initiate anything the client needs for a match (load a level, display names, or a lobby).

How to create a `context.Provider`/`context.Consumer`-like structure to pass values in a bot app?

I'm trying to pass a property, that is inside the first position of an array of objects, to another module so I can use this value later. I've tried to pass it as module(args), but it keeps reading the default value which is 0. Is there a way to do this?
I tried to implement some React.context but the Bot framework Emulator is refusing it.
/////////////////Module that ll acquire the value/////////////////////////////
getCard(bot, builder, params) {
let configValues = { ...params[0] }
bot.dialog(`${configValues.path}`, function (session) {
var msg = new builder.Message(session);
const cardItem = (obj) => {
return (new builder.HeroCard(session)
.title(`${obj.title}`)
.text(`R$ ${obj.price}`)
.images([builder.CardImage.create(session, `${obj.img}`)])
.buttons([
builder.CardAction.imBack(session, `${obj.price} Item adicionado!`, 'add to cart')
// !onClick event must add the current obj.price to
// the configValues.total(Ex: configValues.total += obj.price)!
])
)
}
msg.attachmentLayout(builder.AttachmentLayout.carousel)
msg.attachments(
eval(params.map(obj => cardItem(obj)))
);
//!in here before end the dialog is where i want to update
// the configValues.total so i can show it in the -> Checkout module
session.send(msg).endDialog()
}).triggerAction({ matches: configValues.regex });
}
}
//////////////CheckOut.Module///////////////////////////////
{...}
let configValues = { ...params[0] }
let state = {
nome: "",
endereco: "",
pagamento: "",
total: configValues.total // this is the value to be read
}
bot.dialog('/intent', [
{...},
(session, results) => {
state.pagamento = results.response
session.send(
JSON.stringify(state) // here is the place to be printed
)
{...}
]
).triggerAction({ matches: /^(finalizar|checar|encerrar|confirmar pedido|terminar)/i })
Since you solved your original problem, I'll answer the one in your comment.
Your problem is here:
cartId.map((obj, i , arr) => {
// if (!obj.total) {
// obj.total.reduce(i => i += i)
// }
const newtotal = new total
newtotal.getTotals(bot, builder, obj, arr)
})
cartId contains the totals for each of your items. When you call map on it, you're passing each item individually to getTotals, which passes each item to checkout()
The reason you can't sum all of the totals and can only sum one item's total is that you pass cartId to checkout and cartId has been changed to just a single item. Instead, there's a couple of different things you could do:
Pass the whole cartId from cartItems and use something like for (var key in cartItems) in totalConstructor() and checkoutConstructor(). This is probably the easiest, but not very memory efficient.
Use BotBuilder's State Storage to store your totals array in userData, then sum that at the end. This might be more difficult to implement, but would be a much better route to go. Here's a sample that can help you get started.

Manage Filter ID Gmail

I am trying to create a filter for every people's email in a group. However, sometimes the a person's email can changes like adding new email or change to another mail email. That means I have to update the filter accordingly. I tried to create filter with ID that with the person's name. So I can retrieve that filter to modify it. However, it seems that the Gmail filter's ID are created automatically.
var filter = Gmail.newFilter()
var email = 'something#some.com'
filter.id = 'person'
filter.criteria = Gmail.newFilterCriteria()
filter.criteria.query = email
filter.action = Gmail.newFilterAction()
filter.action.addLabelIds = ['Label_1']
Gmail.Users.Settings.Filters.create(filter, 'me')
When I get back the filter, it will say that it cannot be found
var filter2 = Gmail.Users.Settings.Filters.get('me', 'person')
This will return Not Found error even I can see the filter in setting in my Gmail. The actually ID for the filter I created above is ANe1Bmgel8OKlEXD-uArX77ISk35Lph1MbWWjA.
My question is what's the good way to manager filters through changes, keep them updated?
Instead of:
Gmail.Users.Settings.Filters.create(filter, 'me');
Try:
var filterProps = Gmail.Users.Settings.Filters.create(filter, 'me');
var filterId = filterProps.id;
Per the API documentation for creating a filter,
If successful, this method returns a Users.settings.filters resource in the response body.
The code above worked for me in a similar situation. To keep track of the filterId outside of Gmail, you could then tie the person's name to the id via the property service:
PropertiesService.getScriptProperties().setProperty(nameVar, filterId);
and then retrieve it like so:
PropertiesService.getScriptProperties().getProperty(nameVar);
Per the API documentation for a Filter resource:
id: string The server assigned ID of the filter.
Thus, you cannot assign the ID, and any value you set for it in the call to .create is ignored.
You can still programmatically retrieve this filter, using .list and Array#filter, e.g.
function getAllFilters_(userId) {
const options = {};
const filters = [];
do {
var search = Gmail.Users.Settings.Filters.list(userId, options);
if (search.filter && search.filter.length) {
Array.prototype.push.apply(filters, search.filter);
options.pageToken = search.nextPageToken;
} while (options.pageToken);
return filters;
}
function getFiltersWithCriteria_(filterList, criteria) {
// `criteria` is an object of parameters that must be exactly matched in the filters' criteria property
const props = Object.keys(criteria);
const matches = filterList.filter(function (gFilter) {
// gFilter is a https://developers.google.com/gmail/api/v1/reference/users/settings/filters#resource
var fCriteria = gFilter.criteria;
return props.every(function (prop) {
return criteria[prop] === fCriteria[prop];
});
});
return matches;
}
function foo() {
const filters = getAllFilters_("me");
const matched = getFiltersWithCriteria_(filters, {
from: "george#example.com",
hasAttachment: true
});
console.log({message: "Matching filters", matches: matched});
}

Update specific object in array in state

I'm trying to update an object in an array which is in my state.
I have an array of objects 'this.state.webs' which is presented in multiple div`s on a page. Each one has an onclick method which send the object to a function, then I do an API call and returns a set of 'sub webs' which I want to add to the object in the property 'subs'.
My state:
this.state = {
webs: this.props.webs
}
My template:
<Nav
groups={[
{
links: this.state.webs
}
]}
expandedStateText={'expanded'}
collapsedStateText={'collapsed'}
selectedKey={'key3'}
onLinkClick={this._openWeb.bind(this)}
/>
Onclick function:
private async _openWeb(r, n): Promise<void> {
const service = new MyService();
var subs = await service.getSubs(n);
n.subs = subs;
### How do I update 'n' with the subs? setState({ ? })
}
So, when a user clicks a web, I am fetching some sub webs and then I want to update the parent object n with the children (subs).
You can update your webs array with everything before n, a clone of n with the updated subs, and everything after n:
private async _openWeb(r, n): Promise<void> {
const service = new MyService();
const subs = await service.getSubs(n);
const { webs } = this.state;
const nIndex = webs.indexOf(n);
this.setState({
webs: [
...webs.slice(0, nIndex),
{ ...n, subs },
...webs.slice(nIndex + 1)
]
})
}
Based on the answer from Tholle and Filip W, I came up with my own approach.
Gladly to recieve some comments if this approach isn't recommended, but I find it easier to understand than the suggestion from Tholle.
const webs = [...this.state.webs];
const index = webs.indexOf(n);
webs[index].links = links;
webs[index].isExpanded = true;
this.setState({webs})
From my experiences with presenting array of data it was useful to also send an index of the targeted object (or div where click happened).
So something like this could be useful:
const webs = this.state.webs
webs[index].subs = subs
this.setState({webs})

Categories

Resources