简体   繁体   中英

mongoose find() sorting and organizing returned results from a product database in js

I have a problem with organizing my mongoDB data to send to my page in my res and cant figure out how to do the correct js. Here is a simplified version of my schema

var productSchema = new mongoose.Schema({

medium:     String,
brand:      String,
group:      String

});

Here is what a typical entry looks like

 medium :"Acrylic",
 brand  :"liquitex",
 group  :"heavy body"

there are many more entries in the schema, but these are the only ones I need to be able to sort and organize the returned results with. The problem is I have a route that returns all colors in my database and I want to be able to display them in sections on my page that are grouped under Brand, and then has the individual colors listed under the correct group.

The problem is there are paints from other brands that fall into the heavy body group and so when I use a filter function to sort my data by group, some brands get mixed together. I cant filter by brand, because some brands have acrylic and watercolor so then those get lumped together. I need some way to filter the returned results of a

mongoose.find({})

that can use the group data as a filter, but then filter those results by the brands so they get separated into the correct brand categories.

I have this so far:

this is all a stripped down version of my app.js file:

    //finds all colors in the DB
    Color.find({}).lean().exec(function( err, colors)

                var groups  = [];
                // find all groups in the databse
                colors.forEach( function(color){    
                    groups.push(color["group"]);
                }); 


    //returns only unique names to filter out duplicates
                var groupTypes  = Array.from(new Set(groups));



            var tempVariableBrands = [];
// this sorts all returned paints into their respective group, but we get paints from multiple brands under the same group and that is not good
            groupTypes.forEach( function(group){
                            var name = group;
                            var result = colors.filter(obj => { return obj.group === group });
                            tempVariable.push(  {group : name, result } );      
                        });


        // the tempVariable gets sent to my page like so
        res.render("landing", {colorEntry:tempVariable} );

and this works fine to allow me to display each paint by its grouping, but that fails when there is more than one paint from a different manufacturer that is considered the same group like a "heavy body". This is my ejs on my page that works fine:

<% colorEntry.forEach( function(entry){ %>                  
                    <div class="brandBlock">                        
                        <div class="brandTitle">
    <span><%=entry.result[0].brand%> - <%=entry.result[0].group%></span> 

I for the life of me cant seem to figure out the combination of filter() and maybe map() that would allow this kind of processing to be done. My database has like 600 documents, colors from a number of different manufacturers and I don't know how to get this as a returned structure: lets say this is a few colors in the DB that get returned from a mongoose find:

[{ medium: "Oil",
   brand: "Gamblin",
   group: "Artists oil colors"},
 { medium: "Acrylic",
   brand: "Liquitex",
   group:  "Heavy Body"},
 { medium: "Acrylic",
   brand: "Golden",
   group: "Heavy Body"}
]

i need to organize it like this or something similar. It can be anything that just sorts this data into a basic structure like this, I am not confined to any set standard or anything, this is just for personal use and a site I am trying to build to learn more.

 returnedColors = [ { brand: "Gamblin", group: "Artists oil colors", { 50 paints colors returned} },
                    { brand: "liquitex" , group: "heavy body", { 20 paint colors returned } },
                    { brand:  "golden"  , group: "heavy body",{ 60 paint colors returned} }
                  ];

I am not a web developer and only write some web code every 6 months or so and have been trying how to figure this out for the last 2 days. I can't wrap my head around some of the awesome filter and map combo's i have seen and cant get this to work. Any help or advice would be great. I am sure there are many areas for improvement in this code, but everything was working up until I entered paints that were from different brands that had the same group type and i had to try to rewrite this sorting code to deal with it. It boils down to needing to be able to iterate over the entire set of returned documents from the DB and then sort them based off 2 values.

UPDATE: I was able to get something that works and returns the data in the format that I need to be able to send it to my ejs file and display it properly. The code is rather ugly and probably very redundant, but it technically works. It starts off by using the group value to run over paints since each set of paints will have a group name, but can sometimes share a group name with a paint from another brand like "heavy body".

groupTypes.forEach( function(group){
                    var name = group;
                    var result = colors.filter(obj => { return obj.group === group });                  

                    // this gets brand names per iteration of this loop so that we will know if more than one brand of paint
                    // has the same group identity.
                    var brands  = [];
                    result.forEach( function(color){    
                        brands.push(color["brand"]);    
                    }); 
                    // This filters the brand names down to a unique list of brands
                    var brandNames  = Array.from(new Set(brands));

                    // if there is more than one brand, we need to filter this into two separate groups

                    if( brandNames.length > 1){
                        //console.log("You have duplicates");

                        brandNames.forEach( x => {
                            var tmpResult = [...result];
                            var resultTmp = result.filter(obj => { return obj.brand === x });

                            result = resultTmp;
                            //console.log("FILTERED RESULT IS: ", result);
                            tempVariable.push(  {brand: x ,group : name, result } );
                            result  = [...tmpResult];
                        });
                    }else{                      
                        tempVariable.push(  {brand: result[0].brand ,group : name, result } );
                    }
                });

if anyone can reduce this to something more efficient, I would love to see the "better" way or "right" way of doing something like this.

UPDATE2

Thanks to the answer below, I was put on the right track and was able to rewrite a bunch of that long code with this:

    Color.aggregate([       
    {
        $sort: { name: 1}
    },
    {
        $group: {
            _id: { brand: '$brand', group: '$group' },
            result: { $push: '$$ROOT' }
        }
    },
    { $sort: { '_id.brand': 1 } }

    ], function( err, colors){
    if(err){
        console.log(err);
    }else{
        res.render("landing", {colorEntry:colors, isSearch:1, codes: userCodes, currentUser: req.user, ads: vs.randomAds()} );
    }       
});

Much cleaner and appears to achieve the same result.

Since you're using MongoDB, "right" way is to utilize an Aggregation framework , precisely, $group stage.

Product.aggregate([{
    $group: {
        _id: { group: '$group', brand: '$brand' },
        products: { $push: '$$ROOT' }
    }
}])

This will output array of objects containing every combination of brand and group, and push all relevant products to corresponding subarray.

Combine it with $project and $sort stages to shape your data further.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM