将对象数组添加到 mongodb 问题

Posted

技术标签:

【中文标题】将对象数组添加到 mongodb 问题【英文标题】:Adding array of objects to mongodb issue 【发布时间】:2020-05-11 03:55:15 【问题描述】:

我在向 mongodb 添加对象数组时遇到问题。问题与我收到帖子 ngOnInit() 的时间有关,并且在我开始向邀请组添加任何内容之前有一个像这样的 _id 条目

如果我添加 this.inviteGroup = [] 以摆脱 _id 第一个条目,那么我可以成功地将我的邀请添加到数据库中,就像这张图片一样。有没有办法不拥有与我的猫鼬模式相关的 _id? 但是很自然地 this.inviteGroup = [] 可以做到,所以我一次只能有一个条目,因为它会擦除页面加载时的所有内容。我怎样才能使那个 _id 条目不再存在,以便当我执行 .push() 时它不会导致页面重新加载,因为它会抛出 .push()。我想为每个邀请在 db 中有多个条目。是我的猫鼬模型有问题吗?感谢您的帮助!

猫鼬模式定义

 inviteGroup: 
    bidderId:  type: String, lowercase: true, trim: true ,
    username:  type: String, lowercase: true, trim: true 
  

app.js

app.patch("/api/listings/:id", (req, res) => 
  console.log("INVITE GRdddOUP IS");
  console.log(req.body);
  console.log(req.body[0].biddingUserId);
  let invites;
  if (req.body[0].biddingUserId) 
    invites = req.body;
    console.log("INVITE IS");
  
  console.log(invites);
  if (invites) 
    console.log("INVITE GROUP IS");
    console.log(req.params.id);
    Post.findByIdAndUpdate(
       _id: req.params.id ,
      
        inviteGroup: invites
      ,
      function(err, docs) 
        if (err) 
          console.log(err);
          res.json(err);
         else 
          return true;
          console.log(docs);
        
      
    );

组件.ts

import 
  Component,
  OnInit,
  ViewChild,
  OnDestroy,
  AfterViewInit
 from "@angular/core";
import  Router  from "@angular/router";
import 
  MatTableDataSource,
  MatPaginator,
  MatSort,
  MatDialog
 from "@angular/material";
import  NgForm, FormControl  from "@angular/forms";
import  SubmitListingService  from "../submit-listing/submit-auction.service";
import  BidderInvite  from "./bidder-invite.model";
import  Observable, Subject  from "rxjs";
import  startWith, map, takeUntil  from "rxjs/operators";
import  Page  from "ngx-pagination/dist/pagination-controls.directive";
import  BidderInviteRetrieved  from "./bidder-invite-retrieved";
@Component(
  selector: "app-private-auction-invite",
  templateUrl: "./private-auction-invite.component.html",
  styleUrls: ["./private-auction-invite.component.css"]
)
export class PrivateAuctionInviteComponent
  implements OnInit, AfterViewInit, OnDestroy 
  allMyPeopleAreInvited: boolean;
  auctionId: string;
  dataSource: MatTableDataSource<any> = new MatTableDataSource();
  timeout: any = null;
  posts: BidderInviteRetrieved[];
  artistId: string;
  bidderId: string;
  inviteGroup: BidderInvite[] = [];
  test: any[] = [];
  value: string;
  usernameFound: string;
  userSearched: string;
  invites: BidderInvite[] = [];
  destroy = new Subject();
  inviteName: string;
  filteredOptions: Observable<string[]>;
  myControl = new FormControl();
  selectedValue: string;

  url: string;
  displayedColumnsInvites: string[] = ["User", "revokeInvite"];
  options: string[] = [];

  @ViewChild(MatSort,  static: false ) set sort(sort: MatSort) 
    this.dataSource.sort = sort;
  

  @ViewChild(MatPaginator,  static: false ) set paginator(
    paginator: MatPaginator
  ) 
    this.dataSource.paginator = paginator;
  

  constructor(
    private router: Router,
    private submitListingService: SubmitListingService
  ) 

  ngOnInit() 
    this.inviteGroup = [];
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;
    this.allMyPeopleAreInvited = false;
    this.url = this.router.url;
    const value = this.router.url.split("/");

    this.auctionId = value[2];
    this.artistId = value[3];


    this.submitListingService
      .getPrivateAuctionInviteList(this.auctionId)
      .pipe(takeUntil(this.destroy))
      .subscribe(res => 
        this.inviteGroup = res.posts;
        console.log("res");
        console.log(res);
        console.log(this.inviteGroup);

        if (this.inviteGroup["_id"].length > 2) 
          this.inviteGroup = [];
          console.log(this.inviteGroup);
        
      );

    this.filteredOptions = this.myControl.valueChanges.pipe(
      startWith(""),
      map(value => this._filter(value))
    );
  
  ngAfterViewInit() 
    this.dataSource.paginator = this.paginator;
    this.dataSource.sort = this.sort;

    this.dataSource = new MatTableDataSource(this.inviteGroup);

    this.dataSource.data = this.inviteGroup;
  

  sendInvite(form: NgForm) 
    if (form.invalid) 
      return;
    

    let counter: number;
    counter = 0;
    console.log("USER " + this.value);
    console.log("POST LEGNTH: " + this.posts.length);
    for (let i = 0; i < this.posts.length; i++) 
      counter = counter++;
      console.log("post");
      console.log(form.value.username);

      let user = this.posts[i].username.trim().toLowerCase();
      let enteredUser = form.value.username.trim().toLowerCase();
      console.log("COUNTER LOOP NUMBER: " + counter);


      if (enteredUser === user) 
        this.bidderId = this.posts[i].id;
        console.log(this.inviteGroup);

        let invites = this.inviteGroup;
        console.log("INVITE LENGTH =  " + this.inviteGroup.length);
        console.log(invites.indexOf);
        this.inviteGroup.push(
          biddingUserId: this.bidderId,
          username: this.posts[i].username
        );

        console.log(this.inviteGroup);
        console.log("invite group");
        console.log(this.inviteGroup);
        //this.posts = [];

        this.dataSource.data = this.inviteGroup;
        console.log("invite group");
      
    

    console.log("BIDDER ID " + this.bidderId);
    if (this.bidderId === null || this.bidderId === undefined) 
      console.log("SOMETHING WENT WRONG");
    
    console.log("made it to next section");

    let invites = this.inviteGroup;
    console.log("invites[0].username");


    console.log("filtering....");

    invites = invites.filter((obj, pos, arr) => 
      return (
        arr.map(mapObj => mapObj["bidderId"]).indexOf(obj["bidderId"]) === pos
      );
    );

    console.log("invites");
    console.log(invites);
    this.submitListingService
      .sendPrivateAuctionInvite(this.auctionId, invites)
      .pipe(takeUntil(this.destroy))
      .subscribe(res => 
        console.log("res");
        console.log(res);
      );
  

  private onKeySearch(event: any) 
    console.log("EVENT IS ");
    console.log(event);

    clearTimeout(this.timeout);
    var $this = this;
    this.timeout = setTimeout(function() 
      if (event.keyCode !== 13) 
        $this.executeListing(event.target.value);
      
    , 1000);
  

  private executeListing(bidderName: string) 
    console.log("BIDDERNAME");
    console.log(bidderName);
    if (bidderName === "[Object object]") 
      return;
    
    if (bidderName.length < 4) 
      return;
    
    if (bidderName.length > 3) 
      this.submitListingService
        .getUserIdAutoComplete(bidderName)
        .pipe(takeUntil(this.destroy))
        .subscribe(res => 
          console.log("res");
          console.log(res);
          this.posts = res.posts;
          console.log(this.posts);

          //   this.artists = res.posts;
        );
    
  
  private _filter(value: string): string[] 
    const filterValue = value.toLowerCase();

    return this.options.filter(
      option => option.toLowerCase().indexOf(filterValue) === 0
    );
    console.log("OPTION IS " + filterValue);
  
  storeUserPrivaeAuctionInvite(user: Page) 
    console.log("USER VALUE I S" + user);
  

  ngOnDestroy() 
    this.destroy.next();
    this.destroy.complete();
  

角度服务

 sendPrivateAuctionInvite(id: string, inviteGroup1: BidderInvite[]) 

   // console.log(inviteGroup1);
    return this.http.patch(
      `http://localhost:3000/api/listings/$id/`,
      inviteGroup1
    );
  

BidderInvite 模型

export interface BidderInvite 
  biddingUserId: string;
  username: string;

【问题讨论】:

【参考方案1】:

如果我理解你的问题,问题与更新方法缺少运算符有关。

当你使用类似的东西时:

Post.findByIdAndUpdate(
   _id: req.params.id ,
  
    inviteGroup: invites // <-- Update statement
  ,
  function(err, docs) 
    //...
  
);

它将替换inviteGroup 字段的完整值。

要将项目添加到数据库中的现有数组中,您需要使用$push$addToSet 运算符,以及$each 运算符。

$push$addToSet 运算符每次只添加/追加一个项目,因此必须使用 $eachinvites 数组中存在的每个项目进行交互。在以下示例中,我将包括它,因为我相信这是您需要的。但是,请花时间阅读每个运算符的链接文档,以便找到更多示例。

$push 运算符将指定的值附加到数组中,如果添加的值已存在于字段中,则不会进行额外的验证。如:

//document on mongodb, before the update
//  _id : "1", inviteGroup : [] 


//Invites from the request
// invites = [  bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1" ];

//update method
Post.findByIdAndUpdate(
   _id: req.params.id , //req.params.id = "1"
   $push :  inviteGroup:  $each : invites   ,
  function(err, docs) 
    //...
  
);

//document on mongodb, after the update
/*

  _id : "1",
  inviteGroup : [  bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1" ]

*/

如果你用相同的值再次调用更新方法:

Post.findByIdAndUpdate(
   _id: req.params.id , //req.params.id = "1"
   $push :  inviteGroup:  $each : invites   ,
  function(err, docs)  
);

// the end document will be like:
/*

  _id : "1",
  inviteGroup : [
     bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1",
     bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1"
  ]

*/

以同样的方式,$addToSet 运算符将一个值添加到数组中,除非该值已经存在,在这种情况下,$addToSet 对该数组没有任何作用。喜欢:

//document on mongodb, before the update
//  _id : "1", inviteGroup : [] 


//Invites from the request
// invites = [  bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1" ];


//update method
Post.findByIdAndUpdate(
   _id: req.params.id , //req.params.id = "1"
   $addToSet :  inviteGroup:  $each : invites   ,
  function(err, docs) 
    //...
  
);

//document on mongodb, after the update
/*

  _id : "1",
  inviteGroup : [  bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1" ]

*/

如果你用相同的值再次调用更新方法:

Post.findByIdAndUpdate(
   _id: req.params.id , //req.params.id = "1"
   $addToSet :  inviteGroup:  $each : invites   ,
  function(err, docs)  
);

//the end document will be the same because the same value was already on the list:
/*

  _id : "1",
  inviteGroup : [  bidderId:"5e2350c7f88cfb331c4f67de", username:"artist1" ]

*/

嗯,我希望这就是你要找的。 =]

【讨论】:

【参考方案2】:

您的架构定义应该是:

 inviteGroup: 
    type: [inviteSchema]
    default: undefined //if you want to unset [] 
 

 invite: 
    bidderId:  type: String, lowercase: true, trim: true ,
    username:  type: String, lowercase: true, trim: true 
  

(见https://mongoosejs.com/docs/schematypes.html#arrays)

尝试缓存以防止重新加载。 (见https://github.com/isaacs/node-lru-cache)和How to stop MongoDB from reloading data every time I refresh a page?

尝试在带有_id: 0 的查询输出中排除_id。见https://docs.mongodb.com/v3.2/tutorial/project-fields-from-query-results/#return-all-but-the-excluded-field

【讨论】:

投影解决了我的问题。谢谢!【参考方案3】:

试试这个。希望对你有帮助。

    Post.findByIdAndUpdate(
           _id: req.params.id ,
          
            inviteGroup: invites
          ,
          select: _id: 0, // sets the document fields to return
          function(err, docs) 
            if (err) 
              console.log(err);
              res.json(err);
             else 
              return true;
              console.log(docs);
            
          
        );

【讨论】:

只有在我不注释掉 this.inviteGroup = []; 时才会起作用,因为如果它被注释掉,那么当我 .push() 进入时,第一个 JSON 控制台图像中的 ._id 将导致页面重新加载JSON 对象。但这导致我们在 mongodb 中一次仍然只有一个条目,因为 this.inviteGroup = [] 每次都在页面加载时清除结果,所以我不能将下一个邀请附加到对象。它将每次都推入一个空对象而不是追加。

以上是关于将对象数组添加到 mongodb 问题的主要内容,如果未能解决你的问题,请参考以下文章

将对象添加到java mongodb中的数组

MongoDB/Mongoose - 仅当某个字段是唯一的时才将对象添加到对象数组中

使用 graphql-compose-mongoose 将对象添加到 mongodb 模型中的对象数组

如何正确地将对象 $push 到 MongoDB 模式属性中,类型:数组

如何将对象添加到数组的顶部位置?

如何在mongodb的数组对象的嵌套数组中添加元素