React JS: How to pass an array from firebase in query parameter? - javascript

I'm using multiselection in one of my parameters and I would like to know how to query those parameters like for example if
I want to query parameters that has 1 (doesn't matter if there are other values)
Value one has : 1, 2, 3
Value two has: 5, 1, 6
Value three has: 5, 6, 9
It should only bring Value one and two
I know you can do something like (for non array values):
const librosRef = db.collection('libros');
const queryRef = librosRef.where('grado', '==', '4° Grado');
and it would bring all the documents in that collection that has 4° Grado but if I try to do that while using a multiselection it doesn't bring anything.
This is what I'm trying (doesn't work for array which is what I'm trying to figure out):
const productosRef = db.collection('productosAIB');
const queryRef = productosRef.where('grado', '==', '4° Grado');
useEffect(() => {
queryRef.orderBy("precio")
.get()
.then((snapshot) => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setProductos(tempData);
});
}, []);
Example of how it gets stored in the Firebase:
And this is how it looks in the table (without the query because if I add the query it doesn't show anything )

It sounds like you're trying to query for documents based on the existence of a value in an array. These are called array membership queries in Firestore.
For this, you would use array-contains to match a single field in an array, and array-contains-any to match any value from an array.
To query based on single value in array
const queryRef = productosRef.where('grado', 'array-contains', '4° Grado');
multiple values passed in as an array
const queryRef = productosRef.where('grado', 'array-contains-any', ['4° Grado', 'next array element']);
NOTE: array-contains-any can support up to 10 comparison values.
For more information on array membership queries you can see the documentation here

Related

how to apply filter on field using multiple values in mongoose

i have a model that contain field state type of enum and accept only three values
active
disabled
deleted
and currently am filtering to get all the active ones by
const query = {state : "active" ... other filters here}
const posts = await post.find(query)
but I have a case where I need to get the posts that have stated both active and disabled and I searched in mongoose docs for something I can use but I find nothing at the moment i want something clean that achieve this code
const query1 = {state : "active" ... other filters here}
const posts1 = await post.find(query1)
const query2 = {state : "disabled" ... other filters here}
const posts2 = await post.find(query2)
const posts = {...posts1, ...posts2}
const query = { state: { $in: [ "active", "disabled"] } }
const posts = await post.find(query)
This will result in a list of posts whose state is either active or disabled.
The $in operator selects the documents where the value of a field
equals any value in the specified array. To specify an $in expression,
use the following prototype:
From MongoDB docs.

how to select fields from Firestore where fields values are not the same

Using Firestore how do we select documents that do not have the same field values as another document has.
Example: all documents have the same fields but for that field some of the values are the same. doc1 "description"= yes, doc2 "description"= working, doc3 "description" = working
I would like for my output to be:
"yes" and "Working"
Currently I get:
yes, working, working
templateRef
.where('owner', '==', firebase.auth().currentUser.uid)
//.where('description', '!==', 'description')
//.limit(1)
.get()
.then(querySnapshot => {
const data = [];
querySnapshot.forEach(doc => {
const d = {
id: doc.id,
...doc.data(),
};
data.push(d);
});
return data;
})
There is no way to compare two documents in one Firestore query, as the value for a query always has to be specified as a literal. So your only option is to first load the one document, take the value from it, and then pass that value to the second query.

React - How to map from two SQL tables simultaneously?

I am pulling from a SQL table device, and displaying its content to a table by mapping from device. I am trying to add a column that pulls information from another SQL table, group, but I haven't figured out how to adjust the mapping in order to pull from both device and group. I am sure the issue is caused since group isn't declared in this scope but I cannot solve how it should be declared in this portion of the script.
Both tables shared a common column, group_id, and I have added useSelector for both:
const device = useSelector((state) => state.device);
const group = useSelector((state) => state.group);
<Table
tableHeaderColor="warning"
tableHead={['Device Name', 'Location', 'Group', 'Release Version']}
tableData={device.deviceData.map((device) => {
return [
device['device_name'],
device['location_name'],
group['group_name'],
device['release'],
];
})}
/>
An alternative fix I have tried is finding the group_name since both tables device and group share the group_id column, but it causes a group.find is not a function error. I am unsure if my syntax is incorrect, as I'm working from this site as a resource.
tableData={device.deviceData.map((device) => {
return [
device['device_name'],
device['location_name'],
group.find(group => group.group_id === device.group_id).group_name,
device['release'],
];
})}
Many thanks for any advice
UPDATE:
Thank you for the answers and comments so far. Here is some additional information:
The reducer does contain the initial state empty array for group (groupdata)
const initialState = {
groupData: [],
result: '',
};
Here are the screenshots of the SQL tables device and group. They do not have the same number of entries, as group lists the groups that a number of devices can be assigned to. Hence there are many more entries under device than group.
device SQL table
group SQL table
If you using group.find you need to make sure the reducer contains the initial state empty array for group
Doing this in a map in the actual JSX might get a bit confusing since you're pulling from two different datasources. Also, assuming that the array in
device.deviceData and the group array share the same number of entries and correlate to each other, searching in group on each loop through device.deviceData seems like an unnecessary performance hit. My preference might be to create my source data in a plain for loop outside of the return JSX, and then just plug it in directly:
let tableData = []
for (let i = 0; i < device.deviceData.length; i += 1) {
const currentDevice = device.deviceData[i];
const currentGroup = group[i];
const entry = [
currentDevice['device_name'],
currentDevice['location_name'],
currentGroup['group_name'],
currentDevice['release'],
];
tableData.push(entry);
}
Then I would simply pass tableData to the <Table /> tableData prop.
I think group is an object and has a structure similar to device.deviceData. By that assumption, group.groupData should be the array which will be useful to us.
What we can do is build a Map of group_id -> group_name which we use later for getting relevant device group_name in Table component.
Here is a code snippet which should give you an idea (this is a JS based answer. I think your use-case can make use of joins at sql level for less work here) :-
const device = {
deviceData: [{
group_id: 1
},
{
group_id: 2
}
]
}
const group = {
groupData: [{
group_id: 1,
group_name: 'Group-1'
},
{
group_id: 2,
group_name: 'Group-2'
}
]
}
const buildMap = () => {
const gMap = {};
for (const {
group_id,
group_name
} of group.groupData) {
gMap[group_id] = group_name;
}
return gMap;
}
const groupMap = buildMap();
device.deviceData.forEach(d => console.log(groupMap[d.group_id]));

Multiple Firebase listeners in useEffect and pushing new event into state

I want to retrieve a list of products in relation to the user's position, for this I use Geofirestore and update my Flatlist
When I have my first 10 closest collections, I loop to have each of the sub-collections.
I manage to update my state well, but every time my collection is modified somewhere else, instead of updating my list, it duplicates me the object that has been modified and adds it (updated) at the end of my list and keep the old object in that list too.
For example:
const listListeningEvents = {
A: {Albert, Ducon}
B: {Mickael}
}
Another user modified 'A' and delete 'Ducon', I will get:
const listListeningEvents = {
A: {Albert, Ducon},
B: {Mickael},
A: {Albert}
}
And not:
const listListeningEvents = {
A: {Albert},
B: {Mickael},
}
That's my useEffect:
useEffect(() => {
let geoSubscriber;
let productsSubscriber;
// 1. getting user's location
getUserLocation()
// 2. then calling geoSubscriber to get the 10 nearest collections
.then((location) => geoSubscriber(location.coords))
.catch((e) => {
throw new Error(e.message);
});
//Here
geoSubscriber = async (coords) => {
let nearbyGeocollections = await geocollection
.limit(10)
.near({
center: new firestore.GeoPoint(coords.latitude, coords.longitude),
radius: 50,
})
.get();
// Empty array for loop
let nearbyUsers = [];
// 3. Getting Subcollections by looping onto the 10 collections queried by Geofirestore
productsSubscriber = await nearbyGeocollections.forEach((geo) => {
if (geo.id !== user.uid) {
firestore()
.collection("PRODUCTS")
.doc(geo.id)
.collection("USER_PRODUCTS")
.orderBy("createdDate", "desc")
.onSnapshot((product) => {
// 4. Pushing each result (and I guess the issue is here!)
nearbyUsers.push({
id: product.docs[0].id.toString(),
products: product.docs,
});
});
}
});
setLoading(false);
// 4. Setting my state which will be used within my Flatlist
setListOfProducts(nearbyUsers);
};
return () => {
if (geoSubscriber && productsSubscriber) {
geoSubscriber.remove();
productsSubscriber.remove();
}
};
}, []);
I've been struggling since ages to make this works properly and I'm going crazy.
So I'm dreaming about 2 things :
Be able to update my state without duplicating modified objects.
(Bonus) Find a way to get the 10 next nearest points when I scroll down onto my Flatlist.
In my opinion the problem is with type of nearbyUsers. It is initialized as Array =[] and when you push other object to it just add new item to at the end (array reference).
In this situation Array is not very convenient as to achieve the goal there is a need to check every existing item in the Array and find if you find one with proper id update it.
I think in this situation most convenient will be Map (Map reference). The Map indexes by the key so it is possible to just get particular value without searching it.
I will try to adjust it to presented code (not all lines, just changes):
Change type of object used to map where key is id and value is products:
let nearbyUsersMap = new Map();
Use set method instead of push to update products with particular key:
nearbyUsersMap.set(product.docs[0].id.toString(), product.docs);
Finally covert Map to Array to achieve the same object to use in further code (taken from here):
let nearbyUsers = Array.from(nearbyUsersMap, ([id, products]) => ({ id, products }));
setListOfProducts(nearbyUsers);
This should work, but I do not have any playground to test it. If you get any errors just try to resolve them. I am not very familiar with the geofirestore so I cannot help you more. For sure there are tones of other ways to achieve the goal, however this should work in the presented code and there are just few changes.

Fetching JSON objects based on key values

This code does a great job of fetching and rendering everything within the JSON array, but what if I am interested in only listing the objects with a particular key-value (like gender)? Would that happen during the fetch or the render?
const URL = "https://ghibliapi.herokuapp.com/people";
const main = document.getElementById("main");
main.innerHTML = "<p>Loading...";
fetch(URL).then((response) => response.json()).then((people) => main.innerHTML = getListOfNames(people));
const getListOfNames = (people) => {
const names = people.map((person) => `<li>${person.name} - ${person.gender} </li>`).join("\n");
return `<ul>${names}</ul>`;
};
The ideal case would be using GraphQL so you only fetch the data fields you need based on your criteria, in this case, there is no difference between changing the getListOfNames function for just outputting a person when its person.gender matches your criteria or simply passing to it a filtered array of people after fetching them all
You would have to configure the api endpoint to accept filters if you'd like to return filtered results. Otherwise, you'd filter on render.
With underscore you'd do _.where(response, {gender: 'male'})

Categories