将对象数组添加到 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
运算符每次只添加/追加一个项目,因此必须使用 $each
与 invites
数组中存在的每个项目进行交互。在以下示例中,我将包括它,因为我相信这是您需要的。但是,请花时间阅读每个运算符的链接文档,以便找到更多示例。
$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 问题的主要内容,如果未能解决你的问题,请参考以下文章
MongoDB/Mongoose - 仅当某个字段是唯一的时才将对象添加到对象数组中
使用 graphql-compose-mongoose 将对象添加到 mongodb 模型中的对象数组