I have the cars collection in firebase with the following format and examples:
{
{
make: BMW,
model: X7,
color: white
},
{
make: BMW,
model: X7,
color: white
},
{
make: BMW,
model: X7,
color: black
},
{
make: Audi,
model: Q7,
color: gray
}
}
What I would like to receive from my query is something like this:
[
{
make: BMW,
model: X7,
colors: [white, black]
},
{
make: Audi,
model: Q7,
colors: [gray]
}
]
It doesn't have to be exactly at this format but I hope that I made my purpose clear. How can I achieve this efficiently using firebase?
Firestore screenshot
My code to receive all the documents:
const admin = require('firebase-admin')
const db = admin.firestore()
module.exports.getVehicles = async (data, context) => {
const vehiclesQuery = db.collection('vehicles').get()
const vehicles = []
vehiclesQuery.forEach(doc => {
vehicles.push(doc.data())
})
return vehicles
}
There isn't any direct way to get data in that format. You would have to modify the data using Javascript after fetching all the documents. Also you are missing the await before get() statement:
module.exports.getVehicles = async (data, context) => {
const vehiclesQuery = await db.collection('vehicles').get()
const res = {}
vehiclesQuery.docs.forEach(doc => {
const { color, make, model } = doc
if (!res[make+'-'+model]) {
res[make+'-'+model] = [color]
} else {
res[make+'-'+model].push(color)
}
})
const vehicles = []
Object.entries(res).forEach((v) => {
const [make, model] = v[0].split('-')
vehicles.push({make, model, colors: v[1]})
})
console.log(vehicles)
return vehicles
}
Related
I`m creating a bookstore and I have 3 models: Book, Author and Genre.
Books stores an array of authors ids and the array of genres ids. Author stores the array of books ids. Genre too has the array of books ids.
BookSchema = new mongoose.Schema({
title: String,
authors: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Author"
}
],
image: String,
description: String,
price: Number,
genres: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Genre"
}
],
});
AuthorSchema = new mongoose.Schema({
name: String,
books: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Book"
}
],
});
GenreSchema = new mongoose.Schema({
name: String,
books: [
{
type: mongoose.Schema.Types.ObjectId,
ref: "Book"
}
],
});
Then I have a data array that stores information about the book we need to create like this
{
title: "The Monster at the End of This Book",
authors: ["Jon Stone"],
img: "https://images-na.jpg",
price: "0.35",
description: "Carve out family time for ...",
genres: ["Children's Humour (Books)"]
},
I`m trying to add authors, genres and books. After that I want to associate authors with books
Book.deleteMany({}, () => {
insertBooks().then(insertGenres).then(insertAuthors).then(connectBooksToAuthors);
}).then(function () {
connectBooksToGenres();
})
async function insertAuthors() {
let authorsArr = [];
data.forEach(dataPiece => {
dataPiece.authors.forEach(author => {
if (authorsArr.indexOf(author) === -1) {
authorsArr.push(author)
}
})
})
authorsArr.forEach(author => {
Author.findOne({name: author}, (err, a) => {
if (!a) {
Author.create({name: author});
}
})
})
}
async function insertGenres() {
let genresArr = [];
data.forEach(dataPiece => {
dataPiece.genres.forEach(genre => {
if (genresArr.indexOf(genre) === -1) {
genresArr.push(genre);
}
})
})
genresArr.forEach(genre => {
Genre.findOne({name: genre}, (err, g) => {
if (!g) {
Genre.create({name: genre});
}
})
})
}
async function insertBooks() {
data.forEach((dataPiece) => {
let obj = {
"title": `${dataPiece.title}`,
'description': `${dataPiece.description}`,
"price": `${dataPiece.price}`,
};
Book.create(obj);
})
}
async function connectBooksToAuthors() {
data.forEach(dataPiece => {
Book.findOne({"title": `${dataPiece.title}`}, (err, book) => {
let authorsArr = [];
dataPiece.authors.forEach(authorsName => {
Author.findOne({name: authorsName}, (err, author) => {
author.books.push(book);
author.save();
authorsArr.push(author);
if (authorsArr.length === dataPiece.authors.length) {
book.authors = [...authorsArr];
book.save();
}
});
});
})
})
}
async function connectBooksToGenres() {
data.forEach(dataPiece => {
Book.findOne({"title": `${dataPiece.title}`}, (err, book) => {
let genresArr = [];
dataPiece.genres.forEach(genreName => {
Genre.findOne({name: genreName}, (err, genre) => {
genre.books.push(book);
genre.save();
genresArr.push(genre);
if (genresArr.length === dataPiece.genres.length) {
book.genres = [...genresArr];
book.save();
}
});
});
})
})
}
When I run the code I get this exception:
(node:29028) UnhandledPromiseRejectionWarning: VersionError: No matching document found for id "5fa1dbbb969d727164f1f59e" version 0 modifiedPaths "genres"
at generateVersionError (C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:421:10)
at model.Model.save (C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:478:28)
at C:\Users\sasha\Desktop\Bookstore\seed.js:234:34
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:4844:16
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:4844:16
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\helpers\promiseOrCallback.js:24:16
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\model.js:4867:21
at C:\Users\sasha\Desktop\Bookstore\node_modules\mongoose\lib\query.js:4420:11
at C:\Users\sasha\Desktop\Bookstore\node_modules\kareem\index.js:135:16
at processTicksAndRejections (internal/process/task_queues.js:79:11)
at runNextTicks (internal/process/task_queues.js:66:3)
at processImmediate (internal/timers.js:434:9)
In the database books arrays with authors are filed. Ganres arrays are not The problem might be in book.save() calls from connectBooksToAuthors and connectBooksToGenres but I dont really know how to fix it
I'm trying to create a container and define the composite indexes that I want using Azure Cosmos DB, Azure functions, and node with typescript. I have a config file that defines my options which I read from. I then try to pass in the composite index json object into my createIfNotExists containers function, but no luck. Any help would be appreciated!
config.ts
export const config = {
endpoint: <MyUrl>,
key: <MyKey>,
databaseId: MyDBId,
myContainerId: <MyContainerId>,
myPartitionKey: { kind: "Hash", paths: ["/id"] },
myContainerOptions: {
"compositeIndexes":[
[
{
"path":"/myId",
},
{
"path":"/myOtherId",
}
]
]
}
};
my azure function code:
const myId= (req.query.myId || (req.body && req.body.myId));
const myOtherId = (req.query.myOtherId || (req.body && req.body.myOtherId));
const { endpoint, key, databaseId, myContainerId, myPartitionKey, myContainerOptions } = config;
const client = new CosmosClient({ endpoint, key});
const database = client.database(databaseId);
const container = database.container(myContainerId);
await create(client, databaseId, myContainerId, myPartitionKey, myContainerOptions);
DbContext.ts
export async function create(client, databaseId, containerId, partitionKey, options?) {
const { database } = await client.databases.createIfNotExists({ id: databaseId });
const { container } = await database.containers.createIfNotExists({ id: containerId, partitionKey}, options);
console.log(`Created Database: ${database.id}\n Created container:\n${container.id}`);
}
Composite indexes should be part of container definition and not part of options.
Can you try with the following:
const containerDefinition = {
id: containerId,
partitionKey,
indexingPolicy: {
compositeIndexes: [
[
{ "path":"/myId"},
{ "path":"/myOtherId"}
]
]
}
};
const { container } = await database.containers.createIfNotExists(containerDefinition, options);
Subscriptions with Nexus are undocumented but I searched Github and tried every example in the book. It's just not working for me.
I have cloned Prisma2 GraphQL boilerplate project & my files are as follows:
prisma/schema.prisma
datasource db {
provider = "sqlite"
url = "file:dev.db"
default = true
}
generator photon {
provider = "photonjs"
}
generator nexus_prisma {
provider = "nexus-prisma"
}
model Pokemon {
id String #default(cuid()) #id #unique
number Int #unique
name String
attacks PokemonAttack?
}
model PokemonAttack {
id Int #id
special Attack[]
}
model Attack {
id Int #id
name String
damage String
}
src/index.js
const { GraphQLServer } = require('graphql-yoga')
const { join } = require('path')
const { makeSchema, objectType, idArg, stringArg, subscriptionField } = require('#prisma/nexus')
const Photon = require('#generated/photon')
const { nexusPrismaPlugin } = require('#generated/nexus-prisma')
const photon = new Photon()
const nexusPrisma = nexusPrismaPlugin({
photon: ctx => ctx.photon,
})
const Attack = objectType({
name: "Attack",
definition(t) {
t.model.id()
t.model.name()
t.model.damage()
}
})
const PokemonAttack = objectType({
name: "PokemonAttack",
definition(t) {
t.model.id()
t.model.special()
}
})
const Pokemon = objectType({
name: "Pokemon",
definition(t) {
t.model.id()
t.model.number()
t.model.name()
t.model.attacks()
}
})
const Query = objectType({
name: 'Query',
definition(t) {
t.crud.findManyPokemon({
alias: 'pokemons'
})
t.list.field('pokemon', {
type: 'Pokemon',
args: {
name: stringArg(),
},
resolve: (parent, { name }, ctx) => {
return ctx.photon.pokemon.findMany({
where: {
name
}
})
},
})
},
})
const Mutation = objectType({
name: 'Mutation',
definition(t) {
t.crud.createOnePokemon({ alias: 'addPokemon' })
},
})
const Subscription = subscriptionField('newPokemon', {
type: 'Pokemon',
subscribe: (parent, args, ctx) => {
return ctx.photon.$subscribe.pokemon()
},
resolve: payload => payload
})
const schema = makeSchema({
types: [Query, Mutation, Subscription, Pokemon, Attack, PokemonAttack, nexusPrisma],
outputs: {
schema: join(__dirname, '/schema.graphql')
},
typegenAutoConfig: {
sources: [
{
source: '#generated/photon',
alias: 'photon',
},
],
},
})
const server = new GraphQLServer({
schema,
context: request => {
return {
...request,
photon,
}
},
})
server.start(() => console.log(`🚀 Server ready at http://localhost:4000`))
The related part is the Subscription which I don't know why it's not working or how it's supposed to work.
I searched Github for this query which results in all projects using Subscriptions.
I also found out this commit in this project to be relevant to my answer. Posting the related code here for brevity:
import { subscriptionField } from 'nexus';
import { idArg } from 'nexus/dist/core';
import { Context } from './types';
export const PollResultSubscription = subscriptionField('pollResult', {
type: 'AnswerSubscriptionPayload',
args: {
pollId: idArg(),
},
subscribe(_: any, { pollId }: { pollId: string }, context: Context) {
// Subscribe to changes on answers in the given poll
return context.prisma.$subscribe.answer({
node: { poll: { id: pollId } },
});
},
resolve(payload: any) {
return payload;
},
});
Which is similar to what I do. But they do have AnswerSubscriptionPayload & I don't get any generated type that contains Subscription in it.
How do I solve this? I think I am doing everything right but it's still not working. Every example on GitHub is similar to above & even I am doing the same thing.
Any suggestions?
Edit: Subscriptions aren't implemented yet :(
I seem to have got this working despite subscriptions not being implemented. I have a working pubsub proof of concept based off the prisma2 boilerplate and Ben Awad's video tutorial https://youtu.be/146AypcFvAU . Should be able to get this up and running with redis and websockets to handle subscriptions until the prisma2 version is ready.
https://github.com/ryanking1809/prisma2_subscriptions
Subscriptions aren't implemented yet.
I've opened up an issue to track it.
I'll edit this answer as soon as it's implemented in Prisma 2.
I just created a constructor function to create new Users for a JSON file.
The structure should be like:
{
"users": {
"userName1": {
"salary": [
"1234"
]
},
"userName2": {
"salary": [
"4321"
]
}
}
}
My code looks like this atm:
export const userDataControllerMixin = {
data() {
return {
userObj: {},
};
},
methods: {
NewUser(user, salary) {
this.user = user;
this.salary = salary;
user = {
salary,
};
},
// GETTING INPUT FROM USERS DIALOGBOX
getInput(inputName, inputSalary) {
const userName = document.querySelector(inputName).value;
const userSalary = document.querySelector(inputSalary).value;
const userData = new this.NewUser(userName, userSalary);
console.log(userData);
},
The structur i get is wrong, it looks like this:
NewUser {user: "asd ", salary: "123"}
When you use the word this, it means the current father, in your case NewUser
To get the variable the way you want, you need to do this:
NewUser(user, salary) {
this[user] = {
'salary':salary
};
},
In VueJS there is no need for querySelectors, since inputs are binded with v-model
Check out: https://v2.vuejs.org/v2/guide/forms.html
Because of that, we can reduce the app to one function, that reads the username and salary properties and adds them to the userObj.
I've made a working example here: https://codepen.io/bergur/pen/agZwQL?editors=1011
new Vue({
el: '#app',
data() {
return {
username: '',
salary: '',
userObj: {}
}
},
methods: {
newUser() {
this.userObj[this.username] = {
salary: [Number(this.salary)]
}
console.log(this.userObj)
}
}
})
My app has a feature where users can filter results based on "blood group" and "city", and areas. Results will be retrieved from DB using Axios for Vuejs through "URL" query strings. Example url is: http://example.com/api/results?blood=a+&city=london
It should work in a way that when a user select just blood group from select menu: the url would exclude the city parameter. But from my current code, I can't get it stripped of, as a result, the database query returns no results on the basis that cityreturns null value.
Here's what I have in my Vue component:
<script>
export default {
props: ['user'],
data() {
return {
auth_user: this.user,
results: {},
blood_groups: "",
cities: "",
districts: "",
areas: "",
donorUrl: "/api/donors",
requestedBlood: "",
requestedCity: "",
requestedDist: "",
requestedArea: "",
params: {}
};
},
created() {
this.fetchDonors();
this.fetchCities();
},
methods: {
fetchDonors() {
let url = "/api/donors";
axios.get(url).then(response => {
this.results = response.data.data;
this.blood_groups = [...new Set(response.data.data.map(x=> x.blood_group))];
});
},
fetchCities() {
let url = "/api/location_type/cities";
axios.get(url).then(response => {
this.cities = response.data.cities
})
},
selected_blood_group(event) {
this.requestedBlood = event.target.value;
this.get();
},
get_city(event) {
this.requestedCity = event.target.value;
this.get();
},
get() {
let request = {
params: {
blood: this.requestedBlood,
city: this.requestedCity,
dist: this.requestedDist,
area: this.requestedArea
}
}
axios.get('/api/donors', request).then(response => {
this.results = response.data.data
})
}
},
};
</script>
My query is how can I remove or check if any of the following properties contains empty value, so that I do not include them in axios params?
let request = {
params: {
blood: this.requestedBlood,
city: this.requestedCity,
dist: this.requestedDist,
area: this.requestedArea
}
}
You can try below code.
Create a new object(called testParams) and add that object in params.suppose requestedCity is selected(not only but any variable is selected ). Then you can do like below.
if(requestedCity.length!=0)
{
testParams["city"]=requestedCity; // OTHERWISE DON'T ADD IN testParams object
}
Finally while making request through axios add testParams in params object like below.
axios.get('/yourUrl/',{
params:{
testParams //here vue will automatically sets 'testParams':testParams
}
})
I got it working with the following approach:
let request = {
blood: this.requestedBlood,
city: this.requestedCity,
dist: this.requestedDist,
area: this.requestedArea
}
for(let k in request)
if(!request[k]) delete request[k];
axios.get('/api/donors', {
params: request
}).then(response => {
this.results = response.data.data
})